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();
+ }
+ }*/
+
+
+};
+