diff --git a/7DRL.vcxproj b/7DRL.vcxproj index 72d71c6..8b9b998 100644 --- a/7DRL.vcxproj +++ b/7DRL.vcxproj @@ -126,7 +126,15 @@ - + + + + + + + + + @@ -191,6 +199,7 @@ + @@ -199,8 +208,18 @@ + + + + + + + + + + - + @@ -216,6 +235,8 @@ + + diff --git a/7DRL.vcxproj.filters b/7DRL.vcxproj.filters index 78dd21e..2a810b5 100644 --- a/7DRL.vcxproj.filters +++ b/7DRL.vcxproj.filters @@ -207,7 +207,34 @@ Source Files - + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + Source Files @@ -264,14 +291,50 @@ Header Files - - Header Files - Header Files Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/Test.xp b/Test.xp new file mode 100644 index 0000000..f0cc764 Binary files /dev/null and b/Test.xp differ diff --git a/diesel.wav b/diesel.wav new file mode 100644 index 0000000..dece953 Binary files /dev/null and b/diesel.wav differ diff --git a/distort.wav b/distort.wav new file mode 100644 index 0000000..4604811 Binary files /dev/null and b/distort.wav differ diff --git a/engine_off.wav b/engine_off.wav new file mode 100644 index 0000000..52e2d10 Binary files /dev/null and b/engine_off.wav differ diff --git a/engines_high.wav b/engines_high.wav new file mode 100644 index 0000000..240a9a8 Binary files /dev/null and b/engines_high.wav differ diff --git a/engines_low.wav b/engines_low.wav new file mode 100644 index 0000000..f3f355e Binary files /dev/null and b/engines_low.wav differ diff --git a/moving_fast.wav b/moving_fast.wav new file mode 100644 index 0000000..92ce8fc Binary files /dev/null and b/moving_fast.wav differ diff --git a/moving_slow.wav b/moving_slow.wav new file mode 100644 index 0000000..f85b7cd Binary files /dev/null and b/moving_slow.wav differ diff --git a/radio.wav b/radio.wav new file mode 100644 index 0000000..12f5131 Binary files /dev/null and b/radio.wav differ diff --git a/rc11.png b/rc11.png index 320e4f6..12d6099 100644 Binary files a/rc11.png and b/rc11.png differ diff --git a/src/Date.h b/src/Date.h new file mode 100644 index 0000000..5895b53 --- /dev/null +++ b/src/Date.h @@ -0,0 +1,40 @@ +#pragma once + +struct Date +{ + int day; + int hour; + int minute; + int second; + + int to_seconds() + { + return second + minute * 60 + hour * 60 * 60 + day * 24 * 60 * 60; + } + + void from_seconds(int s) + { + day = s / (24 * 60 * 60); + s = s % (24 * 60 * 60); + hour = s / (60 * 60); + s = s % (60 * 60); + minute = s / 60; + s = s % 60; + second = s; + } + + std::string format() + { + std::string str; + str += std::to_string(day); + str += ':'; + str += std::to_string(hour); + str += ':'; + str += std::to_string(minute); + str += ':'; + str += std::to_string(second); + + return str; + } + +}; \ No newline at end of file diff --git a/src/Drawing.h b/src/Drawing.h index 6657dfe..862cf28 100644 --- a/src/Drawing.h +++ b/src/Drawing.h @@ -111,4 +111,76 @@ class Drawing return false; } + + static void draw_rectangle(TCODConsole* target, int x0, int y0, int w, int h, bool dline = false) + { + int hline = 196; + int vline = 179; + int ulc = 218; + int urc = 191; + int blc = 192; + int brc = 217; + + if (dline) + { + hline = 205; + vline = 186; + ulc = 201; + urc = 187; + blc = 200; + brc = 188; + } + + for (int x = x0; x < x0 + w; x++) + { + target->putChar(x, y0, hline); + target->putChar(x, y0 + h, hline); + } + + for (int y = y0; y < y0 + h; y++) + { + target->putChar(x0, y, vline); + target->putChar(x0 + w, y, vline); + } + + // Corners + + target->putChar(x0, y0, ulc); + target->putChar(x0 + w, y0, urc); + target->putChar(x0, y0 + h, blc); + target->putChar(x0 + w, y0 + h, brc); + } + + // Single letter hold button + static bool draw_button(TCODConsole* target, int x0, int y0, int rx, int ry, int ch) + { + TCOD_mouse_t pos = TCODMouse::getStatus(); + + int w = 2; + int h = 2; + + bool clicked = false; + if (pos.cx - rx >= x0 && pos.cx - rx <= x0 + w && pos.cy - ry >= y0 && pos.cy - ry <= y0 + h && pos.lbutton) + { + clicked = true; + } + + draw_rectangle(target, x0, y0, w, h, false); + + if (clicked) + { + target->setDefaultForeground(TCODColor::lightGreen); + } + else + { + target->setDefaultForeground(TCODColor::white); + } + + target->setChar(x0 + w / 2, y0 + h / 2, ch); + + + target->setDefaultForeground(TCODColor::white); + + return clicked; + } }; \ No newline at end of file diff --git a/src/Help.h b/src/Help.h index 26370ae..f126cf4 100644 --- a/src/Help.h +++ b/src/Help.h @@ -11,14 +11,11 @@ class Help F11 - Open/Close this menu\n\ ESC - Close Menus\n\ Space - Pause\n\ -\n\n\ -This window shows contextual help depending on your currently open screen\n\ -To open other screens, click on any of the machines in the submarine. \ -If there is nobody operating the machine, click on a crewmember, rightclick \ -the machine, and select \"move\".\n\n\ +Left-click - Open Machines\n\ +Right-click - Contextual Menu\n\ -------------------------------------\n\ What should I do?\n\n\ -Wait for orders from central command, you will receive them on the radio\n\n\ +Wait for orders from central command, you will receive them on the communicator\n\n\ -------------------------------------\n\ How can I interact with the crew?\n\n\ All orders are done via rightclicking, you can find further help on the \"Crewmember\" menu \ diff --git a/src/Main.cpp b/src/Main.cpp index 24ab4c6..0bdd6e9 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -8,6 +8,9 @@ #include "soloud/include/soloud_wav.h" SoLoud::Soloud* g_soloud; +Status* g_status; +TCOD_key_t g_key; +TCODRandom* g_random; int main(int argc, char** argv) { @@ -19,20 +22,17 @@ int main(int argc, char** argv) g_soloud = new SoLoud::Soloud; g_soloud->init(); - Status status; - FlightScene flight_scene(&status); + g_status = new Status(); + g_random = new TCODRandom(); + + FlightScene flight_scene = FlightScene(); TCODSystem::setFps(60); while (!TCODConsole::isWindowClosed()) { - TCOD_key_t key; TCOD_mouse_t mouse; - TCODSystem::checkForEvent(TCOD_EVENT_KEY_PRESS | TCOD_EVENT_MOUSE, &key, &mouse); - if (key.vk != TCODK_NONE) - { - flight_scene.turn(key); - } + TCODSystem::checkForEvent(TCOD_EVENT_KEY_PRESS | TCOD_EVENT_MOUSE, &g_key, &mouse); flight_scene.update(TCODSystem::getLastFrameLength()); diff --git a/src/Speech.h b/src/Speech.h new file mode 100644 index 0000000..550cd87 --- /dev/null +++ b/src/Speech.h @@ -0,0 +1,69 @@ +#pragma once +#include + +class Speech +{ +public: + + static std::string positive() + { + if (rand() % 2 == 0) + { + if (rand() % 2 == 0) + { + return "Affirmative, "; + } + else + { + return "Received, "; + } + } + else + { + if (rand() % 2 == 0) + { + return "Yes sir, "; + } + else + { + return "Solid copy sir, "; + } + } + } + + static std::string heading() + { + if (rand() % 2 == 0) + { + return "setting heading to the "; + } + else + { + return "changing heading to the "; + } + } + + static std::string heading_done() + { + if (rand() % 2 == 0) + { + return "Heading set, sir!"; + } + else + { + return "Sir, heading is set"; + } + } + + static std::string speed_done() + { + if (rand() % 2 == 0) + { + return "Velocity set, sir!"; + } + else + { + return "Sir, we are at the target velocity"; + } + } +}; \ No newline at end of file diff --git a/src/defines.h b/src/defines.h index b070e19..a0cf435 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,7 +1,36 @@ #pragma once #include "soloud.h" +#include "Status.h" #define WIDTH 90 #define HEIGHT 70 -extern SoLoud::Soloud* g_soloud; \ No newline at end of file +constexpr float PI = 3.1415926535f; + +extern SoLoud::Soloud* g_soloud; +extern Status* g_status; + +extern TCOD_key_t g_key; + +extern TCODRandom* g_random; + +enum Direction +{ + N, + NE, + E, + SE, + S, + SW, + W, + NW +}; + +enum Speed +{ + STOP, + SLOW, + MEDIUM, + FAST, + FULL +}; \ No newline at end of file diff --git a/src/flight/FlightMap.cpp b/src/flight/FlightMap.cpp index 47b7c8c..ca186b1 100644 --- a/src/flight/FlightMap.cpp +++ b/src/flight/FlightMap.cpp @@ -1,9 +1,95 @@ #include "FlightMap.h" +static float grad_func(float x) +{ + float v = 1 - sqrt(x); + return v * v * v * v; +} + +MapTile FlightMap::get_subtile(float x, float y) +{ + + + int tx = (int)floor(x); + int ty = (int)floor(y); + + float sx = x - (float)tx; + float sy = y - (float)ty; + + float dec = 20.0f; + + sx = floor(sx * dec) / dec; + sy = floor(sy * dec) / dec; + + x = (float)tx + sx; + y = (float)ty + sy; + MapTile tile = get_tile(tx, ty); + if (tile != WALL) + { + // We add some noise to borders with a wall on them + bool up = false; + bool right = false; + bool left = false; + bool down = false; + + up = get_tile(tx, ty - 1) == WALL; + right = get_tile(tx + 1, ty) == WALL; + left = get_tile(tx - 1, ty) == WALL; + down = get_tile(tx, ty + 1) == WALL; + + float grad = 0.0f; + if (up) + { + grad += grad_func(sy); + } + if (left) + { + grad += grad_func(sx); + } + if (down) + { + grad += grad_func(1 - sy); + } + if (right) + { + grad += grad_func(1 - sx); + } + + if (grad >= 1.0f) + { + grad = 1.0f; + } + + if (grad <= 0.0f) + { + grad = 0.0f; + } + + float coords[2]; + coords[0] = x * 1.0f; + coords[1] = y * 1.0f; + + if ((sub_tile_noise.get(&coords[0]) + 1.0f) * 0.5f <= grad) + { + return WALL; + } + else + { + return tile; + } + } + + return tile; +} + +std::vector& FlightMap::get_entities() +{ + return entities; +} -FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height) +FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height), sub_tile_noise(2) { this->width = width; this->height = height; @@ -15,7 +101,7 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height) { for (int y = 0; y < height; y++) { - tiles[y * width + x].wall = true; + tiles[y * width + x] = WALL; } } @@ -29,9 +115,9 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height) while (open < target_open && steps < max_steps) { - if (tiles[dy * width + dx].wall) + if (tiles[dy * width + dx] == WALL) { - tiles[dy * width + dx].wall = false; + tiles[dy * width + dx] = CLEAR; open++; } @@ -90,9 +176,92 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height) { for (int y = 0; y < height; y++) { - vmap.setProperties(x, y, !tiles[y * width + x].wall, !tiles[y * width + x].wall); + vmap.setProperties(x, y, !(tiles[y * width + x] == WALL), !(tiles[y * width + x] == WALL)); } } + + TCODNoise noise = TCODNoise(2, TCOD_NOISE_PERLIN); + + // Generate air map + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + float coords[2]; + coords[0] = (float)x / 10.0f; + coords[1] = (float)y / 10.0f; + + if (noise.get(&coords[0]) >= 0.0f && tiles[y * width + x] == CLEAR) + { + tiles[y * width + x] = AIR; + } + } + } + + int stations = 0; + + while (stations < 4) + { + // Place stations far away from the center + int x = rng.getInt(0, width - 1); + int y = rng.getInt(0, height - 1); + + float xf = (float)(x - width / 2) / (float)(width / 2); + float yf = (float)(y - height / 2) / (float)(height / 2); + + float dist = sqrt(xf * xf + yf * yf); + + if (dist >= 0.7f && tiles[y * width + x] != WALL) + { + tiles[y * width + x] = STATION; + stations++; + entities.push_back(Entity(E_STATION, (float)x + 0.5f, (float)x + 0.5f)); + } + + } + + int nests = 0; + + // Place nests semi randomly, with a bias from far away from the center + while (nests < 15) + { + // Place stations far away from the center + int x = rng.getInt(0, width - 1); + int y = rng.getInt(0, height - 1); + + if (tiles[y * width + x] != WALL) + { + tiles[y * width + x] = NEST; + nests++; + entities.push_back(Entity(E_NEST, (float)x + 0.5f, (float)x + 0.5f)); + } + + } + + // Place allied bases + int bases = 1; + while (bases < 5) + { + // Place stations far away from the center + int x = rng.getInt(0, width - 1); + int y = rng.getInt(0, height - 1); + + if (tiles[y * width + x] != WALL) + { + tiles[y * width + x] = BASE; + bases++; + lbases.push_back(std::make_pair(x, y)); + entities.push_back(Entity(E_BASE, (float)x + 0.5f, (float)x + 0.5f)); + } + + } + + + tiles[(height / 2) * height + (width / 2)] = BASE; + lbases.push_back(std::make_pair(width / 2, height / 2)); + entities.push_back(Entity(E_BASE, (float)(width / 2) + 0.5f, (float)(height / 2) + 0.5f)); + + // Create all default entities } FlightMap::~FlightMap() diff --git a/src/flight/FlightMap.h b/src/flight/FlightMap.h index 5c9f52a..e0272ae 100644 --- a/src/flight/FlightMap.h +++ b/src/flight/FlightMap.h @@ -2,20 +2,68 @@ #include #include -struct MapTile +enum MapTile { - bool wall; + CLEAR, + WALL, + AIR, + STATION, + NEST, + BASE //< Fatherland base, not derelict station +}; + +enum EntityType +{ + E_NEST, + E_STATION, + E_BASE +}; + +struct Entity +{ + EntityType type; + float x, y; + + Entity(EntityType type, float x, float y) + { + this->type = type; + this->x = x; + this->y = y; + } }; class FlightMap { public: + std::vector entities; + + TCODNoise sub_tile_noise; + + // These features are always centered + std::vector> lbases; + int width, height; std::vector tiles; TCODMap vmap; + MapTile get_tile(int x, int y) + { + if (x < 0 || y < 0 || x >= width || y >= height) + { + return WALL; + } + else + { + return tiles[y * width + x]; + } + } + + MapTile get_subtile(float x, float y); + + std::vector& get_entities(); + FlightMap(int width, int height, size_t seed); ~FlightMap(); }; diff --git a/src/flight/FlightScene.cpp b/src/flight/FlightScene.cpp index 201442f..42271f2 100644 --- a/src/flight/FlightScene.cpp +++ b/src/flight/FlightScene.cpp @@ -2,76 +2,135 @@ -void FlightScene::turn(TCOD_key_t key) + +void FlightScene::update(float dt) { + t += dt; - if (open_window == SONAR) + if (g_key.vk == TCODK_F11) { + help_open = !help_open; + } - if (key.vk == TCODK_UP) - { - py--; - } - if (key.vk == TCODK_DOWN) - { - py++; - } + vehicle.update(dt); - if (key.vk == TCODK_RIGHT) - { - px++; - } + if (!sent_start_message && t >= 6.0f) + { + std::string str = "\ +Command to U101.\n\ +Prepare your vehicle for further orders from the fatherland.\n\ +We are now going to send the location of all our outposts.\n"; - if (key.vk == TCODK_LEFT) + for (int i = 0; i < map.lbases.size(); i++) { - px--; + auto t = map.lbases[i]; + str += "O" + std::to_string(i + 1) + ": " + std::to_string(t.first) + ", " + std::to_string(t.second) + ".\n"; } - sonar.draw_world(px, py, map); + str += "\nYou are currently on the outpost O"; + str += std::to_string(map.lbases.size()); + + vehicle.radio->push_message(str); + sent_start_message = true; } - if (key.vk == TCODK_F11) + if (g_key.vk == TCODK_KP8) { - help_open = !help_open; + vehicle.move_order(N); } -} -void FlightScene::update(float dt) -{ - sonar.update(dt); -} + if (g_key.vk == TCODK_KP2) + { + vehicle.move_order(S); + } -void FlightScene::render() -{ - TCODConsole::blit(&spaceship, 0, 0, - spaceship.getWidth(), spaceship.getHeight(), TCODConsole::root, sonar.console.getWidth(), 0); + if (g_key.vk == TCODK_KP6) + { + vehicle.move_order(E); + } + if (g_key.vk == TCODK_KP4) + { + vehicle.move_order(W); + } + if (g_key.vk == TCODK_KP1) + { + vehicle.move_order(SW); + } - status->draw(1, sonar.console.getHeight(), WIDTH - 1, HEIGHT - 1); + if (g_key.vk == TCODK_KP7) + { + vehicle.move_order(NW); + } - // Draw frames - for (int i = 0; i < WIDTH; i++) + if (g_key.vk == TCODK_KP9) + { + vehicle.move_order(NE); + } + + if (g_key.vk == TCODK_KP3) { - TCODConsole::root->setChar(i, 49, TCOD_CHAR_DHLINE); + vehicle.move_order(SE); } - if (open_window == SONAR) + + if (g_key.vk == TCODK_1) { + vehicle.speed_order(STOP); + } - int x0 = (TCODConsole::root->getWidth() - sonar.console.getWidth()) / 2; - int y0 = (TCODConsole::root->getHeight() - sonar.console.getHeight()) / 2; + if (g_key.vk == TCODK_2) + { + vehicle.speed_order(SLOW); + } - sonar.draw(x0, y0); + if (g_key.vk == TCODK_3) + { + vehicle.speed_order(MEDIUM); + } + if (g_key.vk == TCODK_4) + { + vehicle.speed_order(FAST); + } - Drawing::draw_window(TCODConsole::root, x0 - 1, y0 - 1, sonar.console.getWidth(), sonar.console.getHeight(), "Sonar"); + if (g_key.vk == TCODK_5) + { + vehicle.speed_order(FULL); + } + + + + + +} - TCODConsole::blit(&sonar.console, 0, 0, - sonar.console.getWidth() - 1, sonar.console.getHeight() - 1, TCODConsole::root, x0, y0); +void FlightScene::render() +{ + TCODConsole::root->setDefaultBackground(TCODColor(4, 24, 30)); + TCODConsole::root->clear(); + TCODConsole::root->setDefaultBackground(TCODColor::black); + + int vx = (WIDTH - vehicle.width) / 2; + int vy = 40 / 2 - vehicle.height / 2; + vehicle.draw(TCODConsole::root, vx, vy); + + TCODConsole::root->rect(0, 40, WIDTH, HEIGHT, true); + + status->draw(1, HEIGHT * 0.8, WIDTH - 1, HEIGHT - 1); + + // Draw frames + for (int i = 0; i < WIDTH; i++) + { + TCODConsole::root->setChar(i, 40, TCOD_CHAR_DHLINE); } + // Draw window + vehicle.draw_window(TCODConsole::root); + + if (help_open) { Drawing::draw_window(TCODConsole::root, 0, 0, 40, HEIGHT - 1, "Help", true); @@ -85,27 +144,17 @@ void FlightScene::render() } -FlightScene::FlightScene(Status* status) : - sonar(soloud), spaceship(WIDTH - 49, 49), map(map_size, map_size, 11234) + +FlightScene::FlightScene() : map(map_size, map_size, 11234), vehicle(&map) { underwater.load("underwater.wav"); underwater.setLooping(true); this->soloud = g_soloud; - this->status = status; - - px = map_size / 2; - py = map_size / 2; - - sonar.sonar_active = true; - sonar.draw_world(px, py, map); - - + this->status = g_status; soloud->play(underwater); - open_window = SONAR; - help_str = Help::help_main; } diff --git a/src/flight/FlightScene.h b/src/flight/FlightScene.h index b2f47f9..3e1055d 100644 --- a/src/flight/FlightScene.h +++ b/src/flight/FlightScene.h @@ -5,48 +5,41 @@ #include "FlightMap.h" #include "soloud.h" #include "soloud_wav.h" -#include "Sonar.h" + #include "../Drawing.h" #include "../Help.h" +#include "../vehicle/Vehicle.h" +constexpr int map_size = 500; -constexpr int map_size = 512; - -enum Window -{ - NONE, - SONAR -}; class FlightScene { public: + float t = 0.0f; + bool sent_start_message = false; + + SoLoud::Soloud* soloud; SoLoud::Wav underwater; FlightMap map; - Window open_window; - + Vehicle vehicle; - Sonar sonar; - bool help_open; std::string help_str; - int px, py; - - TCODConsole spaceship; Status* status; - void turn(TCOD_key_t key); void update(float dt); void render(); - FlightScene(Status* status); + + FlightScene(); ~FlightScene(); }; diff --git a/src/flight/Gamemaster.cpp b/src/flight/Gamemaster.cpp new file mode 100644 index 0000000..a2e033f --- /dev/null +++ b/src/flight/Gamemaster.cpp @@ -0,0 +1,17 @@ +#include "Gamemaster.h" + + + + +void Gamemaster::update(float dt) +{ +} + +Gamemaster::Gamemaster(Vehicle* veh) +{ + vehicle = veh; +} + +Gamemaster::~Gamemaster() +{ +} diff --git a/src/flight/Gamemaster.h b/src/flight/Gamemaster.h new file mode 100644 index 0000000..f6b77e8 --- /dev/null +++ b/src/flight/Gamemaster.h @@ -0,0 +1,33 @@ +#pragma once +#include "../vehicle/Vehicle.h" + +// The gamemaster spawns enemies and generates orders from high command, driving the lore forward +// Lore: +// U101 is named after the succesful U101 from WW2, but this time its deployed in the +// Jupiter Icy Moon, Europa. +// Your task is to get rid of the local population for the building of further colonies. +// BUT you quickly find that you are not the only humans on the icy moon, finding a few +// derelict space stations that you should explore +// The ultimate goal is to find a survivor in the wrecks and find intelligent life outside +// the earth with very similar qualities. +// High command will show opposition to this, eventually turning you against the other +// submarines + +class Gamemaster +{ +private: + + Vehicle* vehicle; + +public: + + // Activates once we discover intelligent life + bool is_fatherland_enemy = false; + int clear_station_count = 0; + + void update(float dt); + + Gamemaster(Vehicle* veh); + ~Gamemaster(); +}; + diff --git a/src/flight/Sonar.cpp b/src/flight/Sonar.cpp deleted file mode 100644 index d0d21f2..0000000 --- a/src/flight/Sonar.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "Sonar.h" - - - -void Sonar::update(float dt) -{ - sonar_radius += dt * 14.0f; - - if (!sonar_active) - { - sonar_radius = 0.0f; - } - - if (sonar_radius >= 40.0f) - { - sonar_radius = 0.0f; - g_soloud->play(ping); - } -} - -void Sonar::draw(int rx, int ry) -{ - // Fade old pixels - for (int x = 0; x < 49; x++) - { - for (int y = 0; y < 49; y++) - { - TCODColor cur = console.getCharForeground(x, y); - - cur.setValue(cur.getValue() * 0.984f); - - - console.setCharForeground(x, y, cur); - } - } - - - - for (int x = 0; x < 49; x++) - { - for (int y = 0; y < 49; y++) - { - float xf = (float)(x - 24.0f); - float yf = (float)(y - 24.0f); - float dist = sqrt(xf * xf + yf * yf); - if (abs(dist - sonar_radius) <= 2.0f) - { - console.setCharForeground(x, y, TCODColor(80, 255, 80)); - } - } - } - - - - console.setCharForeground(24, 24, TCODColor(255, 255, 255)); - console.setChar(24, 24, 127); - - if (Drawing::draw_switch(&console, 8, 51, rx, ry, &sonar_active, "Active Sonar") && sonar_active) - { - g_soloud->play(ping); - } -} - -void Sonar::draw_world(int px, int py, FlightMap& map) -{ - // Run a visibility algorithm and draw - map.vmap.computeFov(px, py, sonar_active ? 24 : 4, true, FOV_BASIC); - - for (int x = -24; x < 24; x++) - { - for (int y = -24; y < 24; y++) - { - int mx = x + px; - int my = y + py; - - int sx = x + 24; - int sy = y + 24; - - bool seen = true; - - if (mx < 0 || my < 0 || mx >= map.width || my >= map.height) - { - seen = false; - } - - if (!map.vmap.isInFov(mx, my)) - { - seen = false; - } - - if (seen) - { - if (map.tiles[my * map.width + mx].wall) - { - //console.setChar(sx, sy, 219); // Block - console.setChar(sx, sy, 178); - } - else - { - //radar.setChar(sx, sy, 249); - console.setChar(sx, sy, 176); // Dust style - } - - } - else - { - console.setChar(sx, sy, 250); - //radar.setChar(sx, sy, ' '); - } - - } - } -} - - - -Sonar::Sonar(SoLoud::Soloud * soloud) : console(49, 56) -{ - this->soloud = soloud; - ping.load("ping.wav"); -} - -Sonar::~Sonar() -{ - -} - diff --git a/src/flight/Sonar.h b/src/flight/Sonar.h deleted file mode 100644 index 3a8202b..0000000 --- a/src/flight/Sonar.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "libtcod.h" -#include "soloud.h" -#include "soloud_wav.h" - -#include "FlightMap.h" -#include "../Drawing.h" - -class Sonar -{ -public: - - SoLoud::Soloud* soloud; - - TCODConsole console; - float sonar_radius; - bool sonar_active; - SoLoud::Wav ping; - - void update(float dt); - void draw(int rx, int ry); - void draw_world(int px, int py, FlightMap& map); - - Sonar(SoLoud::Soloud* soloud); - ~Sonar(); -}; - diff --git a/src/vehicle/Crewmember.cpp b/src/vehicle/Crewmember.cpp new file mode 100644 index 0000000..84e20f1 --- /dev/null +++ b/src/vehicle/Crewmember.cpp @@ -0,0 +1,128 @@ +#include "Crewmember.h" +#include "workbench/Workbench.h" + +static SoLoud::Wav* radio = nullptr; + + +void Crewmember::speak(std::string text) +{ + if (radio == nullptr) + { + radio = new SoLoud::Wav(); + radio->load("radio.wav"); + } + + g_soloud->play(*radio); + + voice.setText((". . ." + text).c_str()); + g_soloud->play(voice, 6.0f); + + g_status->strings.push_back(name + ": " + text); +} + +Crewmember::Crewmember(std::string name, int voice_pitch, float voice_speed, int voice_oscillator) +{ + path = nullptr; + path_t = 0.0f; + this->name = name; + + voice = SoLoud::Speech(); + voice.setParams(voice_pitch, voice_speed, 0.5f, voice_oscillator); +} + +Crewmember::Crewmember() +{ + path = nullptr; + path_t = 0.0f; + TCODRandom rng = TCODRandom(); + // Procedural + + std::array first_names = + { + "James", + "John", + "Robert", + "Michael", + "William", + "David", + "Richard", + "Joseph", + "Guamedo", + "Thomas", + "Charles", + "Donald", + "Mark", + "Brian", + "Alex" + }; + + std::array last_names = + { + "Smith", + "Johnson", + "Williams", + "Jones", + "Brown", + "Davis", + "Miller", + "Wilson", + "Moore", + "Taylor", + "Anderson", + "Thomas", + "Jackson", + "White", + "Garcia", + "Martinez", + "Young" + }; + + name = first_names[rand() % first_names.size()] + " " + last_names[rand() % last_names.size()]; + + int wave = KW_TRIANGLE; + if (rng.getFloat(0.0f, 1.0f) >= 0.5f) + { + wave = KW_PULSE; + } + else if(rng.getFloat(0.0f, 1.0f) >= 0.5f) + { + wave = KW_WARBLE; + } + else if(rng.getFloat(0.0f, 1.0f) >= 0.5f) + { + wave = KW_SQUARE; + } + + voice.setParams(rng.getInt(900, 1900), rng.getFloat(7.0f, 10.0f), rng.getFloat(0.4f, 0.6f), wave); +} + +bool Crewmember::can_work_in(Workbench * bench) +{ + /*for (size_t i = 0; i < assigned.size(); i++) + { + if (bench == assigned[i]) + { + return true; + } + } + + return false; + */ + + return !is_captain; +} + +void Crewmember::path_to(int dx, int dy, TCODMap& map) +{ + if (path != nullptr) + { + delete path; + path = nullptr; + } + path = new TCODPath(&map); + if (!path->compute(x, y, dx, dy)) + { + delete path; + path = nullptr; + } +} diff --git a/src/vehicle/Crewmember.h b/src/vehicle/Crewmember.h new file mode 100644 index 0000000..8d19e6f --- /dev/null +++ b/src/vehicle/Crewmember.h @@ -0,0 +1,43 @@ +#pragma once +#include "libtcod.h" +#include "soloud.h" +#include "soloud_speech.h" +#include "soloud_wav.h" +#include "../defines.h" + + +#include +#include + +class Workbench; + + + + +class Crewmember +{ +public: + + SoLoud::Speech voice; + + std::string name; + + int x, y; + + bool is_captain; + + // The first one is priority (index 0) + std::vector assigned; + + void speak(std::string text); + + Crewmember(std::string name, int voice_pitch, float voice_speed, int voice_oscillator); + Crewmember(); + + bool can_work_in(Workbench* bench); + + void path_to(int dx, int dy, TCODMap& map); + + TCODPath* path; + float path_t = 0.0f; +}; \ No newline at end of file diff --git a/src/vehicle/Vehicle.cpp b/src/vehicle/Vehicle.cpp new file mode 100644 index 0000000..98d161e --- /dev/null +++ b/src/vehicle/Vehicle.cpp @@ -0,0 +1,784 @@ +#include "Vehicle.h" + + + +void Vehicle::update(float dt) +{ + for (size_t i = 0; i < workbenches.size(); i++) + { + if (workbenches[i]->update(dt)) + { + workbenches[i]->close(); + workbench_open = false; + } + } + + auto old = get_tile(); + bool was_breathing = breathing; + + x += sin(angle) * velocity * dt * 0.15f; + y -= cos(angle) * velocity * dt * 0.15f; + + if (get_tile() != old) + { + // Check for tile update + MapTile in_now = in_map->get_tile(get_tile().first, get_tile().second); + if (in_now == AIR) + { + breathing = true; + if (!was_breathing) + { + if (maneouver->is_crewed()) + { + maneouver->get_crewman()->speak("We have entered an oxygen rich zone."); + } + } + } + else + { + breathing = false; + if (was_breathing) + { + if (maneouver->is_crewed()) + { + maneouver->get_crewman()->speak("We have exited the oxygen rich zone."); + } + } + } + } + + float base_vol = 0.8f; + + float tri = max(-fabs(2.0f * velocity - 1.0f) + 1.0f, 0.0f); + float tri2 = max(-fabs(2.0f * velocity - 2.0f) + 1.0f, 0.0f); + + g_soloud->setVolume(engines_low_h, tri * 0.25 * base_vol); + g_soloud->setVolume(engines_high_h, tri2 * 0.8f * base_vol); + g_soloud->setVolume(moving_slow_h, tri * 0.4f * base_vol); + g_soloud->setVolume(moving_fast_h, velocity * base_vol); + + + flip_timer -= velocity * dt * 10.0f; + + if (flip_timer <= 0.0f) + { + tiles[m0y * width + m0x].ch = tiles[m0y * width + m0x].ch == 192 ? 218 : 192; + tiles[m1y * width + m1x].ch = tiles[m1y * width + m1x].ch == 192 ? 218 : 192; + + flip_timer = 1.0f; + } + + + + while (bubbles.size() <= 50) + { + float y = g_random->getFloat(0.0f, 40.0f); + Bubble b = Bubble(); + + b.x = 0.0f; + b.y = y; + + bubbles.push_back(b); + } + + for (auto it = bubbles.begin(); it != bubbles.end();) + { + it->t += dt; + it->x += dt * velocity * 10.0f; + + if (g_random->getFloat(0.0f, 1.0f) >= 0.99f) + { + it->y += g_random->getInt(-1, 1); + } + + + it->b = (sin(it->t * it->f) + 1.0f) * 30.0f; + + if (it->x >= WIDTH) + { + it = bubbles.erase(it); + } + else + { + it++; + } + } + + blinkt -= dt; + if (blinkt <= 0.0f) + { + blinkt = 0.5f; + blink = !blink; + } + + for (int i = 0; i < crew.size(); i++) + { + crew[i].path_t -= dt; + + if (crew[i].path != nullptr && crew[i].path_t <= 0.0f) + { + int nx, ny; + crew[i].path->walk(&crew[i].x, &crew[i].y, true); + + crew[i].path_t = 0.1f; + } + } +} + +void Vehicle::draw(TCODConsole* target, int ox, int oy) +{ + TCOD_mouse_t pos = TCODMouse::getStatus(); + + + for (int i = 0; i < bubbles.size(); i++) + { + target->setChar(bubbles[i].x, bubbles[i].y, bubbles[i].ch); + TCODColor col = TCODColor((int)bubbles[i].b, 77 + (int)bubbles[i].b, 102 + (int)bubbles[i].b); + target->setCharForeground(bubbles[i].x, bubbles[i].y, col); + } + + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + if (!(tiles[y * width + x].is_outside && tiles[y * width + x].ch != ']')) + { + target->setChar(x + ox, y + oy, tiles[y * width + x].ch); + target->setCharForeground(x + ox, y + oy, tiles[y * width + x].fore); + target->setCharBackground(x + ox, y + oy, tiles[y * width + x].back); + + } + } + } + + // Draw torpedoes + for (int i = 0; i < torpedoes.size(); i++) + { + Torpedo& tp = torpedoes[i]; + target->setChar(tp.x + ox, tp.y + oy, 238); + target->setChar(tp.x + 1 + ox, tp.y + oy, TCOD_CHAR_DHLINE); + target->setChar(tp.x + 2 + ox, tp.y + oy, TCOD_CHAR_DHLINE); + target->setChar(tp.x + 3 + ox, tp.y + oy, TCOD_CHAR_DHLINE); + target->setChar(tp.x + 4 + ox, tp.y + oy, 232); + target->setCharForeground(tp.x + ox, tp.y + oy, TCODColor(140, 70, 0)); + target->setCharForeground(tp.x + 1 + ox, tp.y + oy, TCODColor(158, 158, 158)); + target->setCharForeground(tp.x + 2 + ox, tp.y + oy, TCODColor(158, 158, 158)); + target->setCharForeground(tp.x + 3 + ox, tp.y + oy, TCODColor(158, 158, 158)); + target->setCharForeground(tp.x + 4 + ox, tp.y + oy, TCODColor(100, 100, 100)); + } + + // Draw crew + bool selected_crew = false; + for (int i = 0; i < crew.size(); i++) + { + Crewmember& c = crew[i]; + + target->setChar(c.x + ox, c.y + oy, c.is_captain ? 2 : 1); + target->setCharForeground(c.x + ox, c.y + oy, TCODColor(240, 240, 240)); + + if (pos.cx - ox == c.x && pos.cy - oy == c.y) + { + target->setCharBackground(c.x + ox, c.y + oy, TCODColor(200, 200, 200)); + target->setCharForeground(c.x + ox, c.y + oy, TCODColor(128, 128, 128)); + + if (pos.lbutton_pressed && !in_context_menu) + { + selected = &crew[i]; + selected_crew = true; + } + } + + if (&crew[i] == selected) + { + target->setCharForeground(c.x + ox, c.y + oy, TCODColor(0, 0, 0)); + target->setCharBackground(c.x + ox, c.y + oy, TCODColor(255, 255, 255)); + } + + + } + + if (pos.lbutton_pressed && !selected_crew && !in_context_menu) + { + selected = nullptr; + } + + if (!workbench_open) + { + for (int i = 0; i < workbenches.size(); i++) + { + bool highlight = false; + + for (auto v2 : workbenches[i]->tiles) + { + if (pos.cx - ox == v2.first && pos.cy - oy == v2.second) + { + highlight = true; + break; + } + } + + if (highlight && !in_context_menu) + { + for (auto v2 : workbenches[i]->tiles) + { + target->setCharBackground(v2.first + ox, v2.second + oy, TCODColor(200, 200, 200)); + } + + if (pos.lbutton_pressed && !selected_crew && !in_context_menu) + { + if (workbenches[i]->is_crewed()) + { + workbenches[i]->open(); + workbench_open = workbenches[i]; + selected = nullptr; + } + else + { + // Play error sound TODO + } + } + } + } + } + + // Context menu stuff + if (selected != nullptr && pos.rbutton_pressed) + { + in_context_menu = true; + ctx_x = pos.cx - ox; + ctx_y = pos.cy - oy; + + if (ctx_x < 0 || ctx_x >= width || ctx_y < 0 || ctx_y >= height) + { + in_context_menu = false; + } + } + + if (selected != nullptr) + { + if (pos.mbutton_pressed) + { + selected->path_to(pos.cx - ox, pos.cy - oy, *tcod_map); + } + } + + if (in_context_menu) + { + int rx = ctx_x + ox; + int ry = ctx_y + oy; + + if (blink) + { + target->setChar(rx, ry, 'X'); + target->setCharForeground(ctx_x + ox, ctx_y + oy, TCODColor::white); + } + + std::vector menu_items; + if (!tiles[ctx_y * width + ctx_x].blocks_player) + { + menu_items.push_back("Move Here"); + } + + menu_items.push_back("View Crewmember"); + + + + int menu_x, menu_y; + int menu_w = 0, menu_h = menu_items.size() + 1; + + for (int i = 0; i < menu_items.size(); i++) + { + if (menu_items[i].size() >= menu_w) + { + menu_w = menu_items[i].size(); + } + } + + menu_w += 3; + + // Draw menu, choose direction carefully + if (rx >= WIDTH - menu_w - 1) + { + menu_x = rx - menu_w - 1; + } + else + { + menu_x = rx + 1; + } + + if (ry >= HEIGHT - menu_h - 1) + { + menu_y = ry - menu_h - 1; + } + else + { + menu_y = ry; + } + + for (int x = menu_x; x < menu_x + menu_w + 1; x++) + { + for (int y = menu_y; y < menu_y + menu_h + 1; y++) + { + target->setChar(x, y, ' '); + target->setCharForeground(x, y, TCODColor::white); + target->setCharBackground(x, y, TCODColor::black); + } + } + + Drawing::draw_rectangle(target, menu_x, menu_y, menu_w, menu_h, false); + + target->setAlignment(TCOD_CENTER); + + for (int i = 0; i < menu_items.size(); i++) + { + bool highlight = false; + + if (pos.cx >= menu_x && pos.cx <= menu_x + menu_w + && pos.cy == menu_y + 1 + i) + { + highlight = true; + } + + if (highlight) + { + target->setDefaultForeground(TCODColor::black); + target->setDefaultBackground(TCODColor::white); + } + + for (int x0 = 1; x0 < menu_w; x0++) + { + target->setCharBackground(menu_x + x0, menu_y + i + 1, target->getDefaultBackground()); + } + + target->printf(menu_x + menu_w / 2, menu_y + 1 + i, menu_items[i].c_str()); + + target->setDefaultForeground(TCODColor::white); + target->setDefaultBackground(TCODColor::black); + + if (highlight && pos.lbutton_pressed) + { + if (menu_items[i] == "Move Here") + { + selected->path_to(ctx_x, ctx_y, *tcod_map); + } + } + + } + + if (pos.lbutton_pressed) + { + in_context_menu = false; + } + + target->setAlignment(TCOD_LEFT); + } + +} + +void Vehicle::draw_window(TCODConsole* target) +{ + if (workbench_open) + { + int x0 = (TCODConsole::root->getWidth() - workbench_open->get_size().first) / 2; + int y0 = (TCODConsole::root->getHeight() - workbench_open->get_size().second) / 2; + + workbench_open->draw(x0, y0); + + + Drawing::draw_window(TCODConsole::root, x0 - 1, y0 - 1, + workbench_open->get_size().first, workbench_open->get_size().second, workbench_open->get_name()); + + TCODConsole::blit(workbench_open->get_console(), 0, 0, + workbench_open->get_size().first - 1, workbench_open->get_size().second - 1, target, x0, y0); + } +} + +void Vehicle::move_order(Direction dir) +{ + if(maneouver->is_crewed()) + { + std::string dir_name; + if (dir == N) + { + dir_name = "North"; + maneouver->wanted_angle = 0.0f; + } + else if (dir == E) + { + dir_name = "East"; + maneouver->wanted_angle = PI / 2.0f; + } + else if (dir == S) + { + dir_name = "South"; + maneouver->wanted_angle = PI; + } + else if(dir == W) + { + dir_name = "West"; + maneouver->wanted_angle = 3.0f * (PI / 2.0f); + } + else if (dir == NE) + { + dir_name = "North East"; + maneouver->wanted_angle = PI / 4.0f; + } + else if (dir == NW) + { + dir_name = "North West"; + maneouver->wanted_angle = 7.0f * (PI / 4.0f); + } + else if (dir == SE) + { + dir_name = "South East"; + maneouver->wanted_angle = 3.0f * (PI / 4.0f); + } + else + { + dir_name = "South West"; + maneouver->wanted_angle = 5.0f * (PI / 4.0f); + } + + + + maneouver->get_crewman()->speak(Speech::positive() + Speech::heading() + dir_name); + } +} + +void Vehicle::speed_order(Speed speed) +{ + if (maneouver->is_crewed()) + { + std::string name; + if (speed == STOP) + { + name = "Stopping!"; + maneouver->wanted_velocity = 0.0f; + } + else if (speed == SLOW) + { + name = "Moving slowly!"; + maneouver->wanted_velocity = 0.25f; + } + else if (speed == MEDIUM) + { + name = "Moving normally!"; + maneouver->wanted_velocity = 0.5f; + } + else if (speed == FAST) + { + name = "Moving fast!"; + maneouver->wanted_velocity = 0.75f; + } + else + { + name = "Full speed ahead!"; + maneouver->wanted_velocity = 1.0f; + } + + maneouver->get_crewman()->speak(name); + } +} + + +Vehicle::Vehicle(FlightMap* map) +{ + + + blink = true; + blinkt = 0.5f; + + selected = nullptr; + breathing = false; + + while (bubbles.size() <= 50) + { + float x = g_random->getFloat(0.0f, (float)WIDTH); + float y = g_random->getFloat(0.0f, (float)40); + Bubble b = Bubble(); + + b.x = x; b.y = y; + + bubbles.push_back(b); + } + + + x = (float)map->width / 2.0f + 0.5f; + y = (float)map->height / 2.0f + 0.5f; + angle = 0.0f; + velocity = 0.0f; + flip_timer = 1.0f; + + engines_high.load("engines_high.wav"); engines_high.setLooping(true); + engines_low.load("engines_low.wav"); engines_low.setLooping(true); + moving_fast.load("moving_fast.wav"); moving_fast.setLooping(true); + moving_slow.load("moving_slow.wav"); moving_slow.setLooping(true); + + engines_high_h = g_soloud->play(engines_high); + engines_low_h = g_soloud->play(engines_low); + moving_fast_h = g_soloud->play(moving_fast); + moving_slow_h = g_soloud->play(moving_slow); + + this->in_map = map; + + TCODConsole loaded_sub = TCODConsole(80, 60); + loaded_sub.loadXp("Test.xp"); + + // We have to "post process" the submarine into the map + // Find lowest and highest tiles of the submarine + + int min_x = 10000, min_y = 10000, max_x = -10000, max_y = -10000; + + for (int x = 0; x < loaded_sub.getWidth(); x++) + { + for (int y = 0; y < loaded_sub.getHeight(); y++) + { + // Found tile + if (!(loaded_sub.getCharBackground(x, y) == TCODColor(4, 24, 30) + && (loaded_sub.getChar(x, y) == 0 || loaded_sub.getChar(x, y) == 32))) + { + if (x <= min_x) + { + min_x = x; + } + if (y <= min_y) + { + min_y = y; + } + if (x >= max_x) + { + max_x = x; + } + if (y >= max_y) + { + max_y = y; + } + } + } + } + + max_x++; + max_y++; + + width = max_x - min_x; + height = max_y - min_y; + + tiles.resize(width * height); + + for (int x = min_x; x < max_x; x++) + { + for (int y = min_y; y < max_y; y++) + { + int tx = x - min_x; + int ty = y - min_y; + + VehicleTile* tile = &tiles[ty * width + tx]; + + tile->ch = loaded_sub.getChar(x, y); + tile->fore = loaded_sub.getCharForeground(x, y); + tile->back = loaded_sub.getCharBackground(x, y); + + if (tile->ch == 238) + { + // Torpedo, replace with space and add a torpedo at location + Torpedo tp = Torpedo(); + tp.x = tx; tp.y = ty; + torpedoes.push_back(tp); + + tile->ch = ' '; + } + + // Load crewmember in chairs and assign them the occupation + if (tile->ch == 239) + { + Crewmember c = Crewmember(); + c.x = tx; c.y = ty; + c.is_captain = tile->fore == TCODColor(222, 211, 195); + + Workbench* work = nullptr; + + + if (c.is_captain) + { + // Captain seat + } + else + { + // Find workbench + if (loaded_sub.getChar(x, y - 1) == 30) + { + work = new Sonar(g_soloud); + // Sonar + work->tiles.push_back(std::make_pair(tx, ty - 1)); + work->tiles.push_back(std::make_pair(tx + 1, ty - 1)); + work->tiles.push_back(std::make_pair(tx - 1, ty - 1)); + + sonar = (Sonar*)work; + + } + else if (loaded_sub.getChar(x, y - 1) == 223) + { + // Maneouver + work = new Maneouver(); + + work->tiles.push_back(std::make_pair(tx, ty - 1)); + work->tiles.push_back(std::make_pair(tx + 1, ty - 1)); + work->tiles.push_back(std::make_pair(tx - 1, ty - 1)); + + maneouver = (Maneouver*)work; + } + else if (loaded_sub.getChar(x, y + 1) == 220) + { + if (loaded_sub.getChar(x + 1, y + 1) == 63) + { + // Radio + work = new Radio(); + + work->tiles.push_back(std::make_pair(tx, ty + 1)); + work->tiles.push_back(std::make_pair(tx + 1, ty + 1)); + work->tiles.push_back(std::make_pair(tx - 1, ty + 1)); + + radio = (Radio*)work; + } + else + { + work = new Listening(); + + // Listening station + work->tiles.push_back(std::make_pair(tx, ty + 1)); + work->tiles.push_back(std::make_pair(tx + 1, ty + 1)); + work->tiles.push_back(std::make_pair(tx - 1, ty + 1)); + + listening = work; + } + } + else if (loaded_sub.getChar(x - 1, y) == 155) + { + work = new Periscope(); + + // Periscope + work->tiles.push_back(std::make_pair(tx - 1, ty)); + + periscope = work; + } + else if (loaded_sub.getChar(x - 1, y) == 221) + { + work = new Machines(); + + // Machinist + work->tiles.push_back(std::make_pair(tx - 1, ty - 1)); + work->tiles.push_back(std::make_pair(tx - 1, ty + 0)); + work->tiles.push_back(std::make_pair(tx - 1, ty + 1)); + + machines = work; + } + else if(loaded_sub.getChar(x, y + 1) == '{') + { + work = new Battery(); + + // Battery specialist + work->tiles.push_back(std::make_pair(tx, ty + 1)); + work->tiles.push_back(std::make_pair(tx + 1, ty + 1)); + work->tiles.push_back(std::make_pair(tx + 2, ty + 1)); + work->tiles.push_back(std::make_pair(tx + 3, ty + 1)); + work->tiles.push_back(std::make_pair(tx + 4, ty + 1)); + work->tiles.push_back(std::make_pair(tx + 5, ty + 1)); + work->tiles.push_back(std::make_pair(tx, ty + 1)); + + work->tiles.push_back(std::make_pair(tx, ty - 7)); + work->tiles.push_back(std::make_pair(tx + 1, ty - 7)); + work->tiles.push_back(std::make_pair(tx + 2, ty - 7)); + work->tiles.push_back(std::make_pair(tx + 3, ty - 7)); + work->tiles.push_back(std::make_pair(tx + 4, ty - 7)); + work->tiles.push_back(std::make_pair(tx + 5, ty - 7)); + + battery = work; + } + else + { + std::cout << "Malformed vehicle workbench at " << x << ", " << y << std::endl; + } + + work->crew = &this->crew; + work->set_vehicle(this); + work->cx = tx; + work->cy = ty; + work->tiles.push_back(std::make_pair(work->cx, work->cy)); + + workbenches.push_back(work); + + + c.assigned.push_back(work); + } + + crew.push_back(c); + } + + if (tile->ch == 192) + { + m0x = tx; m0y = ty; + } + + if (tile->ch == 218) + { + m1x = tx; m1y = ty; + } + + if (loaded_sub.getCharBackground(x, y) == TCODColor(4, 24, 30)) + { + tile->is_outside = true; + tile->blocks_player = true; + tile->blocks_light = true; + tile->blocks_water = false; + } + else + { + tile->is_outside = false; + + tile->blocks_player = false; + tile->blocks_light = false; + tile->blocks_water = false; + // Load properties + + if ((tile->ch >= 179 && tile->ch <= 218) || tile->ch == '=') + { + // Walls (= -> Torpedo tube) + tile->blocks_player = true; + tile->blocks_light = true; + tile->blocks_water = true; + tile->health = 100.0f; + } + + if (tile->ch == 'o') + { + // Airlock + tile->blocks_player = false; + tile->blocks_light = true; + tile->blocks_water = true; + tile->health = 60.0f; + } + } + } + } + + tcod_map = new TCODMap(width, height); + + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + bool transparent = !tiles[y * width + x].blocks_light; + bool walkable = !tiles[y * width + x].blocks_player; + + + tcod_map->setProperties(x, y, transparent, walkable); + } + } +} + + +Vehicle::~Vehicle() +{ + delete tcod_map; +} + diff --git a/src/vehicle/Vehicle.h b/src/vehicle/Vehicle.h new file mode 100644 index 0000000..889cd51 --- /dev/null +++ b/src/vehicle/Vehicle.h @@ -0,0 +1,142 @@ +#pragma once +#include +#include "libtcod.h" +#include "../defines.h" +#include "Crewmember.h" +#include "../Speech.h" +#include "../flight/FlightMap.h" + +#include "workbench/Sonar.h" +#include "workbench/Radio.h" +#include "workbench/Listening.h" +#include "workbench/Machines.h" +#include "workbench/Maneouver.h" +#include "workbench/Battery.h" +#include "workbench/Periscope.h" + + +struct VehicleTile +{ + float health; + + float water; + int ch; + TCODColor fore; + TCODColor back; + + bool blocks_water; + bool blocks_player; + bool blocks_light; + + bool is_outside; +}; + +struct Torpedo +{ + int x, y; +}; + + + +class Vehicle +{ +public: + + TCODMap* tcod_map; + + Crewmember* selected; + bool in_context_menu; + int ctx_x, ctx_y; + + bool breathing; + + float blinkt; + bool blink; + + struct Bubble + { + float x, y; + int ch; + float b; + float t; + float f; + + Bubble() + { + t = 0.0f; + b = 0.0f; + f = g_random->getFloat(0.4f, 2.0f); + + if (g_random->getFloat(0.0f, 1.0f) >= 0.5f) + { + ch = 'o'; + } + else + { + ch = 'O'; + } + } + }; + + std::vector bubbles; + + float flip_timer; + + SoLoud::Wav engines_high; + SoLoud::Wav engines_low; + SoLoud::Wav moving_fast; + SoLoud::Wav moving_slow; + + SoLoud::handle engines_high_h; + SoLoud::handle engines_low_h; + SoLoud::handle moving_fast_h; + SoLoud::handle moving_slow_h; + + Workbench* workbench_open; + + // World tile position, floor to get int position + float x, y; + + float angle; + float velocity; + + int width, height; + + // Tiles are the "dumb" foundation of the submarine, + // they only have visual and physical (water) effects + std::vector tiles; + std::vector torpedoes; + std::vector crew; + std::vector workbenches; + + Workbench *periscope, *machines, *battery, *listening; + Sonar* sonar; + Radio* radio; + Maneouver* maneouver; + + int m0x, m0y, m1x, m1y; + + void update(float dt); + void draw(TCODConsole* target, int x, int y); + void draw_window(TCODConsole* target); + + void move_order(Direction dir); + void speed_order(Speed speed); + + std::pair get_tile() + { + return std::make_pair((int)floor(x), (int)floor(y)); + } + + std::pair get_subtile() + { + auto t = get_tile(); + return std::make_pair(x - t.first, y - t.second); + } + + FlightMap* in_map; + + Vehicle(FlightMap* map); + ~Vehicle(); +}; + diff --git a/src/vehicle/workbench/Battery.cpp b/src/vehicle/workbench/Battery.cpp new file mode 100644 index 0000000..168b40b --- /dev/null +++ b/src/vehicle/workbench/Battery.cpp @@ -0,0 +1,26 @@ +#include "Battery.h" + + + +Battery::Battery() +{ +} + + +Battery::~Battery() +{ +} + +bool Battery::update(float dt) +{ + return false; +} + +void Battery::draw(int rx, int ry) +{ +} + +TCODConsole * Battery::get_console() +{ + return nullptr; +} diff --git a/src/vehicle/workbench/Battery.h b/src/vehicle/workbench/Battery.h new file mode 100644 index 0000000..fba0ad8 --- /dev/null +++ b/src/vehicle/workbench/Battery.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include "../../Date.h" +#include "libtcod.h" + +#include "Workbench.h" + +class Battery : public Workbench +{ +public: + Battery(); + ~Battery(); + + // Inherited via Workbench + virtual bool update(float dt) override; + virtual void draw(int rx, int ry) override; + virtual TCODConsole * get_console() override; + + virtual std::string get_name() override + { + return "Battery Control Panel"; + } +}; + diff --git a/src/vehicle/workbench/Listening.cpp b/src/vehicle/workbench/Listening.cpp new file mode 100644 index 0000000..2969ad6 --- /dev/null +++ b/src/vehicle/workbench/Listening.cpp @@ -0,0 +1,26 @@ +#include "Listening.h" + + + +Listening::Listening() +{ +} + + +Listening::~Listening() +{ +} + +bool Listening::update(float dt) +{ + return false; +} + +void Listening::draw(int rx, int ry) +{ +} + +TCODConsole * Listening::get_console() +{ + return nullptr; +} diff --git a/src/vehicle/workbench/Listening.h b/src/vehicle/workbench/Listening.h new file mode 100644 index 0000000..1981963 --- /dev/null +++ b/src/vehicle/workbench/Listening.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include "../../Date.h" +#include "libtcod.h" + +#include "Workbench.h" + +class Listening : public Workbench +{ +public: + Listening(); + ~Listening(); + + // Inherited via Workbench + virtual bool update(float dt) override; + virtual void draw(int rx, int ry) override; + virtual TCODConsole * get_console() override; + + virtual std::string get_name() override + { + return "Listening Station"; + } +}; + diff --git a/src/vehicle/workbench/Machines.cpp b/src/vehicle/workbench/Machines.cpp new file mode 100644 index 0000000..ca58e92 --- /dev/null +++ b/src/vehicle/workbench/Machines.cpp @@ -0,0 +1,26 @@ +#include "Machines.h" + + + +Machines::Machines() +{ +} + + +Machines::~Machines() +{ +} + +bool Machines::update(float dt) +{ + return false; +} + +void Machines::draw(int rx, int ry) +{ +} + +TCODConsole * Machines::get_console() +{ + return nullptr; +} diff --git a/src/vehicle/workbench/Machines.h b/src/vehicle/workbench/Machines.h new file mode 100644 index 0000000..b68641a --- /dev/null +++ b/src/vehicle/workbench/Machines.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include "../../Date.h" +#include "libtcod.h" + +#include "Workbench.h" + +class Machines : public Workbench +{ +public: + Machines(); + ~Machines(); + + // Inherited via Workbench + virtual bool update(float dt) override; + virtual void draw(int rx, int ry) override; + virtual TCODConsole * get_console() override; + + virtual std::string get_name() override + { + return "Machine Control"; + } +}; + diff --git a/src/vehicle/workbench/Maneouver.cpp b/src/vehicle/workbench/Maneouver.cpp new file mode 100644 index 0000000..8ae32ce --- /dev/null +++ b/src/vehicle/workbench/Maneouver.cpp @@ -0,0 +1,193 @@ +#include "Maneouver.h" +#include "../Vehicle.h" + + +int angle_to_pixel(float angle) +{ + int i = (int)round(((angle + PI) / (2.0f * PI)) * 44) % 45; + + return i; +} + +inline float wrap_angle(float angle) +{ + float two_pi = 2.0f * PI; + return angle - two_pi * floor(angle / two_pi); +} + +Maneouver::Maneouver() : console(50, 50) +{ + wanted_angle = 0.0f; + engine_off.load("engine_off.wav"); + said = true; + said_speed = true; +} + + +Maneouver::~Maneouver() +{ +} + +bool Maneouver::update(float dt) +{ + this->dt = dt; + + if (g_key.vk == TCODK_ESCAPE) + { + return true; + } + + // Update velocity + float diff = wanted_velocity - get_vehicle()->velocity; + if (fabs(diff) <= 0.0005) + { + get_vehicle()->velocity = wanted_velocity; + + if (!said_speed) + { + if (is_crewed()) + { + get_crewman()->speak(Speech::speed_done()); + } + + said_speed = true; + } + } + else + { + float v = diff > 0 ? 1.0f : -1.0f; + float vel = diff > 0 ? 0.05f : 0.14f; + get_vehicle()->velocity += v * dt * vel; + + said_speed = false; + } + + float real_wanted = wrap_angle(wanted_angle); + float real_vehicle = wrap_angle(get_vehicle()->angle); + + float dir = 1.0f; + diff = real_wanted - real_vehicle; + + if (fabs(diff) <= 0.005) + { + get_vehicle()->angle = wanted_angle; + + if (!said) + { + if (is_crewed()) + { + get_crewman()->speak(Speech::heading_done()); + } + + said = true; + } + } + else + { + said = false; + + if (fabs(diff) > PI) + { + dir = -1.0f; + } + + if (diff < 0) + { + dir = -dir; + } + + + get_vehicle()->angle = real_vehicle + dir * dt * 1.0f; + + } + + return false; +} + +void Maneouver::draw(int rx, int ry) +{ + console.clear(); + + // Compass with wanted and current heading + + int by = 8; + Drawing::draw_rectangle(&console, 1, by, 46, 4, true); + console.putChar(angle_to_pixel(wrap_angle(wanted_angle)) + 2, by + 1, 31); + console.putChar(angle_to_pixel(wrap_angle(get_vehicle()->angle)) + 2, by + 3, 30); + console.putChar(angle_to_pixel(0.0f) + 2, by + 2, 'N'); + console.putChar(angle_to_pixel(PI / 2.0f) + 2, by + 2, 'E'); + console.putChar(angle_to_pixel(-PI / 2.0f) + 2, by + 2, 'W'); + + // Special one, south is in both sides... + console.putChar(2, by + 2, 'S'); + console.putChar(44 + 2, by + 2, 'S'); + + float fast = 1.0f; + float slow = 0.05f; + + if (Drawing::draw_button(&console, 1, 4, rx, ry, 174)) + { + wanted_angle -= dt * fast; + } + + if (Drawing::draw_button(&console, 5, 4, rx, ry, '<')) + { + wanted_angle -= dt * slow; + } + + if (Drawing::draw_button(&console, 9, 4, rx, ry, '>')) + { + wanted_angle += dt * slow; + } + + if (Drawing::draw_button(&console, 13, 4, rx, ry, 175)) + { + wanted_angle += dt * fast; + } + + Drawing::draw_rectangle(&console, 49 - 6, 4, 4, 2); + console.printf(49 - 5, 5, "%i", (int)round(wrap_angle(get_vehicle()->angle) / (2.0f * PI) * 360.0f)); + + Drawing::draw_rectangle(&console, 49 - 12, 4, 4, 2); + console.printf(49 - 11, 5, "%i", (int)round(wrap_angle(wanted_angle) / (2.0f * PI) * 360.0f)); + + if (Drawing::draw_button(&console, 1, 17, rx, ry, 30)) + { + wanted_velocity += dt * 0.2f; + } + + if (Drawing::draw_button(&console, 5, 17, rx, ry, 31)) + { + wanted_velocity -= dt * 0.2f; + } + + if (Drawing::draw_button(&console, 49 - 4, 17, rx, ry, 'X')) + { + // Play sound + g_soloud->play(engine_off, get_vehicle()->velocity * 2.0f); + wanted_velocity = 0.0f; + } + + if (wanted_velocity >= 1.0f) + { + wanted_velocity = 1.0f; + } + + if (wanted_velocity <= 0.0f) + { + wanted_velocity = 0.0f; + } + + + Drawing::draw_rectangle(&console, 1, 20, 46, 3, true); + for (int i = 0; i < wanted_velocity * 45; i++) + { + console.putChar(i + 2, 21, 254); + } + + for (int i = 0; i < get_vehicle()->velocity * 45; i++) + { + console.putChar(i + 2, 22, 254); + } + +} diff --git a/src/vehicle/workbench/Maneouver.h b/src/vehicle/workbench/Maneouver.h new file mode 100644 index 0000000..017aa44 --- /dev/null +++ b/src/vehicle/workbench/Maneouver.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include "../../Date.h" +#include "libtcod.h" + +#include "Workbench.h" +#include "../../Drawing.h" + +class Maneouver : public Workbench +{ +private: + float dt; + +public: + + SoLoud::Wav engine_off; + + float wanted_angle; + float wanted_velocity; + + bool said; + bool said_speed; + + TCODConsole console; + + Maneouver(); + ~Maneouver(); + + // Inherited via Workbench + virtual bool update(float dt) override; + virtual void draw(int rx, int ry) override; + virtual TCODConsole* get_console() override + { + return &console; + } + + virtual std::string get_name() override + { + return "Maneouvering Station"; + } +}; + diff --git a/src/vehicle/workbench/Periscope.cpp b/src/vehicle/workbench/Periscope.cpp new file mode 100644 index 0000000..b7b3aec --- /dev/null +++ b/src/vehicle/workbench/Periscope.cpp @@ -0,0 +1,201 @@ +#include "Periscope.h" +#include "../Vehicle.h" + + +Periscope::Periscope() : console(49, 49) +{ +} + + +Periscope::~Periscope() +{ +} + +bool Periscope::update(float dt) +{ + if (g_key.vk == TCODK_ESCAPE) + { + return true; + } + + blinkt -= dt; + if (blinkt <= 0.0f) + { + blinkt = 0.5f; + blink = !blink; + } + + return false; +} + +void Periscope::draw(int rx, int ry) +{ + + int w = 49; + int h = 49; + + TCODMap view_map = TCODMap(w, h); + + std::vector& entities = get_vehicle()->in_map->get_entities(); + + float vx = get_vehicle()->x; + float vy = get_vehicle()->y; + + vx = floor(vx * 20.0f) / 20.0f; + vy = floor(vy * 20.0f) / 20.0f; + + for (int x = 0; x < w; x++) + { + for (int y = 0; y < h; y++) + { + // xf, yf (-1, 1) + float xf = (float)x / (float)w; + float yf = (float)y / (float)h; + xf = (xf - 0.5f) * 2.0f; + yf = (yf - 0.5f) * 2.0f; + + float tx = vx + xf; + float ty = vy + yf; + + int xr = (int)floor(tx); + int yr = (int)floor(ty); + + MapTile in_tile = get_vehicle()->in_map->get_subtile(tx, ty); + + if (in_tile == WALL) + { + // Fill with rock + console.putChar(x, y, '#'); + view_map.setProperties(x, y, false, false); + } + else + { + console.putChar(x, y, 250); + view_map.setProperties(x, y, true, true); + } + + + } + } + + + // Draw entities and features + for (int i = 0; i < entities.size(); i++) + { + + + float exf = entities[i].x - vx; + float eyf = entities[i].y - vy; + + int ex = (int)floor(exf * (float)w * 0.5f + 24.0f); + int ey = (int)floor(eyf * (float)h * 0.5f + 24.0f); + + if (ex >= 0 && ex < w && ey >= 0 && ey < h) + { + switch (entities[i].type) + { + case E_BASE: + console.setChar(ex, ey, 127); + break; + case E_NEST: + console.setChar(ex, ey, 15); + break; + case E_STATION: + console.setChar(ex, ey, 234); + break; + default: + break; + } + } + } + + view_map.computeFov(24, 24, w, true, FOV_BASIC); + for (int x = 0; x < w; x++) + { + for (int y = 0; y < h; y++) + { + float xf = (float)x / (float)w; + float yf = (float)y / (float)h; + xf = (xf - 0.5f) * 2.0f; + yf = (yf - 0.5f) * 2.0f; + + float dist = sqrt(xf * xf + yf * yf) * 7.0f; + float bright = 1.0f / (dist * dist); + + TCODColor lit = TCODColor(207.0f, 1.0f / bright, bright); + + if (view_map.isInFov(x, y)) + { + console.setCharForeground(x, y, lit); + } + else + { + console.setCharForeground(x, y, TCODColor::black); + } + } + } + + console.putChar(24, 24, '@'); + + if (blink) + { + float a = get_vehicle()->angle; + float p = PI / 8.0f; + + if (a <= 1.0f * p) + { + //N (0-22.5) + console.setCharForeground(24, 23, TCODColor::lightGrey); + console.setChar(24, 23, 179); + } + else if (a <= 3.0f * p) + { + //NE (22.5-45) + console.setCharForeground(25, 23, TCODColor::lightGrey); + console.setChar(25, 23, 47); + } + else if (a <= 5.0f * p) + { + // E (45-67.5) + console.setCharForeground(25, 24, TCODColor::lightGrey); + console.setChar(25, 24, 196); + } + else if (a <= 7.0f * p) + { + // SE (67.5-90) + console.setCharForeground(25, 25, TCODColor::lightGrey); + console.setChar(25, 25, 92); + } + else if (a <= 9.0f * p) + { + // S + console.setCharForeground(24, 25, TCODColor::lightGrey); + console.setChar(24, 25, 179); + } + else if (a <= 11.0f * p) + { + // SW + console.setCharForeground(23, 25, TCODColor::lightGrey); + console.setChar(23, 25, 47); + } + else if (a <= 13.0f * p) + { + // W + console.setCharForeground(23, 24, TCODColor::lightGrey); + console.setChar(23, 24, 196); + } + else if (a <= 15.0f * p) + { + // NW + console.setCharForeground(23, 23, TCODColor::lightGrey); + console.setChar(23, 23, 92); + } + else + { + // N + console.setCharForeground(24, 23, TCODColor::lightGrey); + console.setChar(24, 23, 179); + } + } + +} diff --git a/src/vehicle/workbench/Periscope.h b/src/vehicle/workbench/Periscope.h new file mode 100644 index 0000000..a45b9c4 --- /dev/null +++ b/src/vehicle/workbench/Periscope.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include "../../Date.h" +#include "libtcod.h" + +#include "Workbench.h" +#include "../../Drawing.h" + + + +class Periscope : public Workbench +{ +private: + +public: + + bool blink = false; + float blinkt = 0.5f; + + int msg; + + std::vector messages; + + TCODConsole console; + + Periscope(); + ~Periscope(); + + // Inherited via Workbench + virtual bool update(float dt) override; + virtual void draw(int rx, int ry) override; + virtual TCODConsole* get_console() override + { + return &console; + } + + virtual std::string get_name() override + { + return "Periscope"; + } +}; + diff --git a/src/vehicle/workbench/Radio.cpp b/src/vehicle/workbench/Radio.cpp new file mode 100644 index 0000000..c17c9d3 --- /dev/null +++ b/src/vehicle/workbench/Radio.cpp @@ -0,0 +1,135 @@ +#include "Radio.h" + + + +void Radio::draw(int rx, int ry) +{ + console.clear(); + + Drawing::draw_rectangle(&console, 1, 1, console.getWidth() - 4, console.getHeight() - 8); + + if (messages.size() != 0) + { + console.printRect(2, 2, console.getWidth() - 5, console.getHeight() - 9, messages[msg].c_str()); + + if (Drawing::draw_button(&console, console.getWidth() - 9, console.getHeight() - 10, rx, ry, 186)) + { + if (g_soloud->isValidVoiceHandle(handle)) + { + g_soloud->stop(handle); + } + } + + if (Drawing::draw_button(&console, console.getWidth() - 6, console.getHeight() - 10, rx, ry, 62)) + { + say_message(messages[msg]); + } + } + + if (Drawing::draw_button(&console, 1, console.getHeight() - 5, rx, ry, 174)) + { + msg = 0; + } + + if (Drawing::draw_button(&console, 4, console.getHeight() - 5, rx, ry, '<')) + { + msg--; + if (msg < 0) + { + msg = 0; + } + } + + if (Drawing::draw_button(&console, console.getWidth() - 9, console.getHeight() - 5, rx, ry, '>')) + { + msg++; + if (msg > 0) + { + msg = 0; + } + } + + if (Drawing::draw_button(&console, console.getWidth() - 6, console.getHeight() - 5, rx, ry, 175)) + { + msg = messages.size() - 1; + if (msg < 0) + { + msg = 0; + } + } + + + + console.setAlignment(TCOD_CENTER); + console.printf(console.getWidth() / 2, console.getHeight() - 4, "%i/%i", msg + 1, messages.size()); + console.setAlignment(TCOD_LEFT); +} + +Radio::Radio() : console(70, 50) +{ + msg = 0; + + voice_in.setParams(1500, 9.0f, 0.5f, KW_NOISE); + + + distort.load("distort.wav"); + distort.setLooping(true); + distort_handle = g_soloud->play(distort, 0.0f); +} + +void Radio::say_message(std::string& msg) +{ + + voice_in.setText((msg + ". . . Out.").c_str()); + + handle = g_soloud->play(voice_in, 7.0f); + +} + +void Radio::push_message(std::string nmsg) +{ + + + messages.push_back(nmsg); + dirty = true; + + // If anybody is on the radio, say new message received + if (is_crewed() && !is_open()) + { + get_crewman()->speak("Captain, new radio message"); + } + + msg = messages.size() - 1; + +} + +Radio::~Radio() +{ +} + + + +bool Radio::update(float dt) +{ + if (g_key.vk == TCODK_ESCAPE) + { + return true; + } + + if (g_soloud->isValidVoiceHandle(handle)) + { + g_soloud->setVolume(distort_handle, 0.6f); + } + else + { + g_soloud->setVolume(distort_handle, 0.0f); + } + + if (is_open() && dirty) + { + say_message(*(messages.end() - 1)); + dirty = false; + } + + return false; +} diff --git a/src/vehicle/workbench/Radio.h b/src/vehicle/workbench/Radio.h new file mode 100644 index 0000000..156f9a0 --- /dev/null +++ b/src/vehicle/workbench/Radio.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include "../../Date.h" +#include "libtcod.h" + +#include "Workbench.h" +#include "../../Drawing.h" + + + +class Radio : public Workbench +{ +private: + float dt; + + bool dirty = false; + + +public: + + int msg; + + std::vector messages; + + TCODConsole console; + + SoLoud::Speech voice_in; + SoLoud::Wav distort; + + SoLoud::handle handle; + SoLoud::handle distort_handle; + + Radio(); + ~Radio(); + + void say_message(std::string& msg); + + void push_message(std::string msg); + + // Inherited via Workbench + virtual bool update(float dt) override; + virtual void draw(int rx, int ry) override; + virtual TCODConsole* get_console() override + { + return &console; + } + + virtual std::string get_name() override + { + return "Radio"; + } +}; + diff --git a/src/vehicle/workbench/Sonar.cpp b/src/vehicle/workbench/Sonar.cpp new file mode 100644 index 0000000..7757c33 --- /dev/null +++ b/src/vehicle/workbench/Sonar.cpp @@ -0,0 +1,296 @@ +#include "Sonar.h" +#include "../Vehicle.h" + + +bool Sonar::update(float dt) +{ + sonar_radius += dt * 14.0f; + + blinkt -= dt; + if (blinkt <= 0.0f) + { + blinkt = 0.5f; + blink = !blink; + + if (is_open()) + { + draw_world(get_vehicle()->get_tile().first, get_vehicle()->get_tile().second, *get_vehicle()->in_map); + } + } + + if (!sonar_active) + { + sonar_radius = 0.0f; + } + + if (sonar_radius >= 40.0f) + { + sonar_radius = 0.0f; + g_soloud->play(ping); + } + + + + if (is_open()) + { + if (g_key.vk == TCODK_ESCAPE) + { + return true; + } + + draw_world((int)floor(get_vehicle()->x), (int)floor(get_vehicle()->y), *get_vehicle()->in_map); + } + + return false; +} + +void Sonar::draw(int rx, int ry) +{ + // Clear lower area + console.rect(0, 49, 49, 60, true); + + // Fade old pixels + for (int x = 0; x < 49; x++) + { + for (int y = 0; y < 49; y++) + { + TCODColor cur = console.getCharForeground(x, y); + + cur.setValue(cur.getValue() * 0.984f); + + + console.setCharForeground(x, y, cur); + } + } + + + + for (int x = 0; x < 49; x++) + { + for (int y = 0; y < 49; y++) + { + float xf = (float)(x - 24.0f); + float yf = (float)(y - 24.0f); + float dist = sqrt(xf * xf + yf * yf); + if (abs(dist - sonar_radius) <= 2.0f) + { + console.setCharForeground(x, y, TCODColor(80, 255, 80)); + } + } + } + + + + console.setCharForeground(24, 24, TCODColor(255, 255, 255)); + console.setChar(24, 24, '@'); + + // Directional indicator + if (blink) + { + float a = get_vehicle()->angle; + float p = PI / 8.0f; + + if (a <= 1.0f * p) + { + //N (0-22.5) + console.setCharForeground(24, 23, TCODColor::lightGrey); + console.setChar(24, 23, 179); + } + else if (a <= 3.0f * p) + { + //NE (22.5-45) + console.setCharForeground(25, 23, TCODColor::lightGrey); + console.setChar(25, 23, 47); + } + else if (a <= 5.0f * p) + { + // E (45-67.5) + console.setCharForeground(25, 24, TCODColor::lightGrey); + console.setChar(25, 24, 196); + } + else if (a <= 7.0f * p) + { + // SE (67.5-90) + console.setCharForeground(25, 25, TCODColor::lightGrey); + console.setChar(25, 25, 92); + } + else if (a <= 9.0f * p) + { + // S + console.setCharForeground(24, 25, TCODColor::lightGrey); + console.setChar(24, 25, 179); + } + else if (a <= 11.0f * p) + { + // SW + console.setCharForeground(23, 25, TCODColor::lightGrey); + console.setChar(23, 25, 47); + } + else if (a <= 13.0f * p) + { + // W + console.setCharForeground(23, 24, TCODColor::lightGrey); + console.setChar(23, 24, 196); + } + else if (a <= 15.0f * p) + { + // NW + console.setCharForeground(23, 23, TCODColor::lightGrey); + console.setChar(23, 23, 92); + } + else + { + // N + console.setCharForeground(24, 23, TCODColor::lightGrey); + console.setChar(24, 23, 179); + } + } + + if (Drawing::draw_switch(&console, 8, 51, rx, ry, &sonar_active, "Active Sonar")) + { + if (sonar_active) + { + g_soloud->play(ping); + } + draw_world(lpx, lpy, *last_map); + } + + console.setDefaultForeground(TCODColor::white); + + // Position indicator + Drawing::draw_rectangle(&console, 20, 51, 8, 2); + + // Change the central line + console.putChar(20 + 4, 51, 194); + console.putChar(20 + 4, 52, 179); + console.putChar(20 + 4, 53, 193); + + + console.setDefaultForeground(TCODColor(128, 255, 0)); + + auto coarse = get_vehicle()->get_tile(); + + console.printf(21, 52, "%i", coarse.first); + console.printf(25, 52, "%i", coarse.second); + + console.setDefaultForeground(TCODColor::white); + + console.setAlignment(TCOD_CENTER); + + console.printf(24, 50, "Coarse"); + + console.setAlignment(TCOD_LEFT); + + // Subposition indicator + Drawing::draw_rectangle(&console, 30, 51, 8, 2); + + // Change the central line + console.putChar(30 + 4, 51, 194); + console.putChar(30 + 4, 52, 179); + console.putChar(30 + 4, 53, 193); + + + console.setDefaultForeground(TCODColor(128, 255, 0)); + + auto fine = get_vehicle()->get_subtile(); + + console.printf(31, 52, "%i", (int)(fine.first * 1000)); + console.printf(35, 52, "%i", (int)(fine.second * 1000)); + + console.setDefaultForeground(TCODColor::white); + + console.setAlignment(TCOD_CENTER); + + console.printf(34, 50, "Fine"); + + console.setAlignment(TCOD_LEFT); + +} + +void Sonar::draw_world(int px, int py, FlightMap& map) +{ + lpx = px; + lpy = py; + last_map = ↦ + // Run a visibility algorithm and draw + map.vmap.computeFov(px, py, sonar_active ? 24 : 1, true, FOV_BASIC); + + for (int x = -24; x < 24; x++) + { + for (int y = -24; y < 24; y++) + { + int mx = x + px; + int my = y + py; + + int sx = x + 24; + int sy = y + 24; + + bool seen = true; + + if (mx < 0 || my < 0 || mx >= map.width || my >= map.height) + { + seen = false; + } + + if (!map.vmap.isInFov(mx, my)) + { + seen = false; + } + + if (seen) + { + int ch = 0; + + switch (map.tiles[my * map.width + mx]) + { + case CLEAR: + ch = 176; + break; + case WALL: + ch = 178; + break; + case AIR: + ch = 177; + break; + case STATION: + ch = 234; + break; + case NEST: + ch = 15; + break; + case BASE: + ch = 127; + break; + default: + ch = 1; + break; + } + + console.setChar(sx, sy, ch); + + } + else + { + console.setChar(sx, sy, 250); + //radar.setChar(sx, sy, ' '); + } + + } + } +} + + + +Sonar::Sonar(SoLoud::Soloud * soloud) : console(49, 56) +{ + this->soloud = soloud; + ping.load("ping.wav"); + blink = true; + blinkt = 0.5f; +} + +Sonar::~Sonar() +{ + +} + diff --git a/src/vehicle/workbench/Sonar.h b/src/vehicle/workbench/Sonar.h new file mode 100644 index 0000000..37e1d65 --- /dev/null +++ b/src/vehicle/workbench/Sonar.h @@ -0,0 +1,46 @@ +#pragma once + +#include "libtcod.h" +#include "soloud.h" +#include "soloud_wav.h" + +#include "../../flight/FlightMap.h" +#include "../../Drawing.h" + +#include "Workbench.h" + +class Sonar : public Workbench +{ +public: + + float blinkt; + bool blink; + + int lpx, lpy; + FlightMap* last_map; + + SoLoud::Soloud* soloud; + + TCODConsole console; + float sonar_radius; + bool sonar_active; + SoLoud::Wav ping; + + virtual bool update(float dt) override; + virtual void draw(int rx, int ry) override; + void draw_world(int px, int py, FlightMap& map); + + virtual TCODConsole* get_console() override + { + return &console; + } + + virtual std::string get_name() override + { + return "Sonar"; + } + + Sonar(SoLoud::Soloud* soloud); + ~Sonar(); +}; + diff --git a/src/vehicle/workbench/Workbench.h b/src/vehicle/workbench/Workbench.h new file mode 100644 index 0000000..4a1be6c --- /dev/null +++ b/src/vehicle/workbench/Workbench.h @@ -0,0 +1,161 @@ +#pragma once + + +#include +#include +#include +#include +#include "../Crewmember.h" + +class Vehicle; + +enum Window +{ + NONE, + SONAR, + RADIO, + MANEOUVER, + PERISCOPE, + MACHINES, + BATTERY, + LISTENING +}; + +class Workbench +{ +private: + + Vehicle* vehicle; + + bool is_open_v; + + std::stack orders; + + +public: + + std::vector* crew; + + + // Chair x and y + int cx, cy; + // Includes the chair + std::vector> tiles; + + // Return true if we closed + virtual bool update(float dt) = 0; + virtual void draw(int rx, int ry) = 0; + virtual TCODConsole* get_console() = 0; + + std::pair get_size() + { + return std::make_pair(get_console()->getWidth(), get_console()->getHeight()); + } + + Vehicle* get_vehicle() + { + return vehicle; + } + + void set_vehicle(Vehicle* v) + { + this->vehicle = v; + } + + void open() + { + is_open_v = true; + } + + void close() + { + is_open_v = false; + } + + bool is_open() + { + return is_open_v; + } + + virtual std::string get_name() = 0; + + Crewmember* get_crewman() + { + for (Crewmember& cm : *crew) + { + if (cm.can_work_in(this) && cm.x == cx && cm.y == cy) + { + return &cm; + } + } + + return nullptr; + } + + bool is_crewed() + { + for (Crewmember& cm : *crew) + { + if (cm.can_work_in(this) && cm.x == cx && cm.y == cy) + { + return true; + } + } + + return false; + } + + // Order system, TODO + /*void request_crew() + { + if (is_crewed()) + { + return; + } + + // Find first priority members + for (Crewmember& cm : *crew) + { + if (cm.assigned.size() != 0) + { + if (cm.assigned[0] == this && cm.x == cx && cm.y == cy) + { + + } + } + } + + // Find any crew with this workbench assigned + for (Crewmember& cm : *crew) + { + if (cm.can_work_in(this) && cm.x == cx && cm.y == cy) + { + + } + } + } + + std::string pop_order() + { + if (orders.empty()) + { + return ""; + } + + std::string o = orders.top(); + orders.pop(); + return o; + } + + void add_order(std::string name, bool req_crew = true) + { + orders.push(name); + if (req_crew) + { + request_crew(); + } + }*/ + + +}; +