diff --git a/7DRL.exe b/7DRL.exe
new file mode 100644
index 0000000..a30fe84
Binary files /dev/null and b/7DRL.exe differ
diff --git a/7DRL.vcxproj b/7DRL.vcxproj
index 8b9b998..bc215a7 100644
--- a/7DRL.vcxproj
+++ b/7DRL.vcxproj
@@ -116,7 +116,8 @@
true
true
true
- D:\Programming\libtcod-1.15.1-x86_64-msvc\include;
+ C:\Users\Usuario\Source\Repos\7DRL\src\soloud\include;D:\Programming\SDL2\include;D:\Programming\libtcod-1.15.1-x86_64-msvc\include
+ WITH_SDL2;_CRT_SECURE_NO_WARNINGS;_MBCS;%(PreprocessorDefinitions)
true
@@ -126,13 +127,18 @@
+
+
+
+
+
-
-
+
+
@@ -200,6 +206,7 @@
+
@@ -208,14 +215,21 @@
+
+
+
+
+
+
+
-
+
-
+
@@ -237,6 +251,7 @@
+
diff --git a/7DRL.vcxproj.filters b/7DRL.vcxproj.filters
index 2a810b5..ff041d8 100644
--- a/7DRL.vcxproj.filters
+++ b/7DRL.vcxproj.filters
@@ -210,18 +210,12 @@
Source Files
-
- Source Files
-
Source Files
Source Files
-
- Source Files
-
Source Files
@@ -237,6 +231,30 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -303,9 +321,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -318,9 +333,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -336,5 +348,35 @@
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/7DRL.zip b/7DRL.zip
new file mode 100644
index 0000000..d583fda
Binary files /dev/null and b/7DRL.zip differ
diff --git a/SAMPLES.txt b/SAMPLES.txt
new file mode 100644
index 0000000..51e8d95
--- /dev/null
+++ b/SAMPLES.txt
@@ -0,0 +1,2 @@
+Some samples were taken from the ARMA 3 mod JSRS (Explosion sounds) and
+mixed with other effects.
diff --git a/Test.xp b/Test.xp
index f0cc764..2e26405 100644
Binary files a/Test.xp and b/Test.xp differ
diff --git a/alien_die.wav b/alien_die.wav
new file mode 100644
index 0000000..b5bc3a5
Binary files /dev/null and b/alien_die.wav differ
diff --git a/biter/attack.wav b/biter/attack.wav
new file mode 100644
index 0000000..eba5795
Binary files /dev/null and b/biter/attack.wav differ
diff --git a/biter/idle.wav b/biter/idle.wav
new file mode 100644
index 0000000..c1122f7
Binary files /dev/null and b/biter/idle.wav differ
diff --git a/collide.wav b/collide.wav
new file mode 100644
index 0000000..9d5ecef
Binary files /dev/null and b/collide.wav differ
diff --git a/collide2.wav b/collide2.wav
new file mode 100644
index 0000000..d88aa2a
Binary files /dev/null and b/collide2.wav differ
diff --git a/crawler/attack.wav b/crawler/attack.wav
new file mode 100644
index 0000000..10b52d1
Binary files /dev/null and b/crawler/attack.wav differ
diff --git a/crawler/idle.wav b/crawler/idle.wav
new file mode 100644
index 0000000..2f8bdb4
Binary files /dev/null and b/crawler/idle.wav differ
diff --git a/crawler/moving.wav b/crawler/moving.wav
new file mode 100644
index 0000000..347f892
Binary files /dev/null and b/crawler/moving.wav differ
diff --git a/diesel_off.wav b/diesel_off.wav
new file mode 100644
index 0000000..b63779a
Binary files /dev/null and b/diesel_off.wav differ
diff --git a/explo_far_dist.wav b/explo_far_dist.wav
new file mode 100644
index 0000000..3f987a2
Binary files /dev/null and b/explo_far_dist.wav differ
diff --git a/explo_med_dist.wav b/explo_med_dist.wav
new file mode 100644
index 0000000..f5f5860
Binary files /dev/null and b/explo_med_dist.wav differ
diff --git a/explo_near_dist.wav b/explo_near_dist.wav
new file mode 100644
index 0000000..3b5f31f
Binary files /dev/null and b/explo_near_dist.wav differ
diff --git a/four_way.wav b/four_way.wav
new file mode 100644
index 0000000..210544d
Binary files /dev/null and b/four_way.wav differ
diff --git a/grenade.wav b/grenade.wav
new file mode 100644
index 0000000..5a3bb0b
Binary files /dev/null and b/grenade.wav differ
diff --git a/grenade_land.wav b/grenade_land.wav
new file mode 100644
index 0000000..9b1265f
Binary files /dev/null and b/grenade_land.wav differ
diff --git a/gunshot.wav b/gunshot.wav
new file mode 100644
index 0000000..04fd6b3
Binary files /dev/null and b/gunshot.wav differ
diff --git a/hurt.wav b/hurt.wav
new file mode 100644
index 0000000..506d8a6
Binary files /dev/null and b/hurt.wav differ
diff --git a/hurt_alien.wav b/hurt_alien.wav
new file mode 100644
index 0000000..92d6452
Binary files /dev/null and b/hurt_alien.wav differ
diff --git a/hurt_metal.wav b/hurt_metal.wav
new file mode 100644
index 0000000..da4b862
Binary files /dev/null and b/hurt_metal.wav differ
diff --git a/man_die.wav b/man_die.wav
new file mode 100644
index 0000000..775727b
Binary files /dev/null and b/man_die.wav differ
diff --git a/metal_die.wav b/metal_die.wav
new file mode 100644
index 0000000..434c548
Binary files /dev/null and b/metal_die.wav differ
diff --git a/shock.wav b/shock.wav
new file mode 100644
index 0000000..4225987
Binary files /dev/null and b/shock.wav differ
diff --git a/src/Crewmember.cpp b/src/Crewmember.cpp
new file mode 100644
index 0000000..a15a2e3
--- /dev/null
+++ b/src/Crewmember.cpp
@@ -0,0 +1,84 @@
+#include "Crewmember.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, 1.6f);
+
+ g_status->strings.push_back(name + ": " + text);
+}
+
+
+Crewmember::Crewmember()
+{
+ std::array first_names =
+ {
+ "Bob",
+ "John",
+ "Robert",
+ "Michael",
+ "William",
+ "David",
+ "Richard",
+ "Joseph",
+ "Guamedo",
+ "Thomas",
+ "Charles",
+ "Donald",
+ "Mark",
+ "Brian",
+ "Alex"
+ };
+
+ std::array last_names =
+ {
+ "Smith",
+ "Bobbins",
+ "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 (g_random->getFloat(0.0f, 1.0f) >= 0.5f)
+ {
+ wave = KW_PULSE;
+ }
+ else if (g_random->getFloat(0.0f, 1.0f) >= 0.5f)
+ {
+ wave = KW_WARBLE;
+ }
+ else if (g_random->getFloat(0.0f, 1.0f) >= 0.5f)
+ {
+ wave = KW_SQUARE;
+ }
+
+ voice.setParams(g_random->getInt(900, 1900), g_random->getFloat(6.5f, 8.5f), g_random->getFloat(0.4f, 0.6f), wave);
+
+}
\ No newline at end of file
diff --git a/src/Crewmember.h b/src/Crewmember.h
new file mode 100644
index 0000000..63bbfdc
--- /dev/null
+++ b/src/Crewmember.h
@@ -0,0 +1,19 @@
+#pragma once
+#include
+#include "defines.h"
+#include
+#include
+
+class Crewmember
+{
+public:
+ SoLoud::Speech voice;
+ std::string name;
+ bool is_captain;
+
+
+ void speak(std::string text);
+
+ Crewmember();
+};
+
diff --git a/src/Drawing.h b/src/Drawing.h
index 862cf28..496c93e 100644
--- a/src/Drawing.h
+++ b/src/Drawing.h
@@ -4,6 +4,7 @@
#include "defines.h"
static SoLoud::Wav* click = nullptr;
+static SoLoud::Wav* four_way = nullptr;
class Drawing
{
@@ -183,4 +184,107 @@ class Drawing
return clicked;
}
+
+ // 0 = Top
+ // 1 = Right
+ // 2 = Bottom
+ // 3 = Left
+ // x,y are coordiantes of the four choice thing, not text
+ static bool draw_four_choice(TCODConsole* target, int x, int y, int rx, int ry, int* dir,
+ const std::string& opt_u,
+ const std::string& opt_r,
+ const std::string& opt_d,
+ const std::string& opt_l)
+ {
+ if (four_way == nullptr)
+ {
+ four_way = new SoLoud::Wav();
+ four_way->load("four_way.wav");
+ }
+
+ target->putChar(x + 1, y + 0, 201);
+ target->putChar(x + 2, y + 0, 205);
+ target->putChar(x + 3, y + 0, 187);
+
+ target->putChar(x + 0, y + 1, 201);
+ target->putChar(x + 1, y + 1, 188);
+ target->putChar(x + 3, y + 1, 200);
+ target->putChar(x + 4, y + 1, 187);
+
+ target->putChar(x + 0, y + 2, 186);
+ target->putChar(x + 2, y + 2, 7);
+ target->putChar(x + 4, y + 2, 186);
+
+ target->putChar(x + 0, y + 3, 200);
+ target->putChar(x + 1, y + 3, 187);
+ target->putChar(x + 3, y + 3, 201);
+ target->putChar(x + 4, y + 3, 188);
+
+ target->putChar(x + 1, y + 4, 200);
+ target->putChar(x + 2, y + 4, 205);
+ target->putChar(x + 3, y + 4, 188);
+
+ if (*dir == 0)
+ {
+ target->putChar(x + 2, y + 1, 24);
+ }
+ else if (*dir == 1)
+ {
+ target->putChar(x + 3, y + 2, 26);
+ }
+ else if (*dir == 2)
+ {
+ target->putChar(x + 2, y + 3, 25);
+ }
+ else
+ {
+ target->putChar(x + 1, y + 2, 27);
+ }
+
+ // Draw texts
+ target->setAlignment(TCOD_CENTER);
+ target->printf(x + 2, y - 1, opt_u.c_str());
+ target->printf(x + 2, y + 5, opt_d.c_str());
+ target->setAlignment(TCOD_RIGHT);
+ target->printf(x - 1, y + 2, opt_l.c_str());
+ target->setAlignment(TCOD_LEFT);
+ target->printf(x + 5, y + 2, opt_r.c_str());
+
+ // Interaction
+ TCOD_mouse_t pos = TCODMouse::getStatus();
+
+ int pos_cx = pos.cx - rx;
+ int pos_cy = pos.cy - ry;
+
+ int old_dir = *dir;
+
+ if (pos.lbutton_pressed)
+ {
+ if (pos_cx == x + 2 && pos_cy == y + 1)
+ {
+ *dir = 0;
+ }
+ else if (pos_cx == x + 3 && pos_cy == y + 2)
+ {
+ *dir = 1;
+ }
+ else if (pos_cx == x + 2 && pos_cy == y + 3)
+ {
+ *dir = 2;
+ }
+ else if (pos_cx == x + 1 && pos_cy == y + 2)
+ {
+ *dir = 3;
+ }
+ }
+
+ if (*dir != old_dir)
+ {
+ // Play sound
+ g_soloud->play(*four_way);
+ return true;
+ }
+
+ return false;
+ }
};
\ No newline at end of file
diff --git a/src/Help.h b/src/Help.h
index f126cf4..62ff5b5 100644
--- a/src/Help.h
+++ b/src/Help.h
@@ -1,24 +1,339 @@
#pragma once
#include
+#include
class Help
{
public:
- static constexpr const char* help_main = "\
--=Main screen=-\n\n\
-F11 - Open/Close this menu\n\
-ESC - Close Menus\n\
-Space - Pause\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 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 \
-that can be opened on the rightclick dropdown";
-};
+ std::string cur_screen;
+ std::unordered_map screens;
+
+ std::vector return_to;
+
+ Help()
+ {
+// Text template
+// |
+ screens["Main Screen"] = "\
+Welcome to $174$$174$The Legend of U101$175$$175$.\n\
+This is the help screen which will guide you while you play the game, it can be opened\n\
+and closed with $yellow$F1$white$. \n\
+Items coloured in $blue$blue$white$ are links, which can be clicked to learn further.\n\
+\n\
+-@Tutorial@\n\
+-@Controls Overview@\n\
+";
+
+ screens["Tutorial"] = "\
+You start the game inside your submarine, your vehicle has just left one of the few\n\
+human bases under Jupiter-2's (Europa) ice crust, with the objective of cleaning the\n\
+area for further colonization. In order to complete this objective, you must travel\n\
+near nests, marked in the sonar as a $sonar$$15$$white$.\n\
+Once you are sufficiently close, you will get a prompt to disembark.\n\
+\n\
+Before any of that happens you will have to travel through the dangerous underground\n\
+icy caverns. To do so, you will need to carefully manage your crew, issuing orders and\n\
+controlling the many workstations available.\n\n\
+To select a crewmember $1$ ($2$ is you, the captain), $yellow$left click$white$ him. Once\n\
+selected, you can $yellow$right click$white$ to bring up the contextual menu, or\n\
+$yellow$middle click$white$ anywhere to move the crewmember there. See @Controlling Crew@.\n\n\
+To control a workstation, left-click on it. A crewmember must occupy the chair ($brown$$239$$white$)\n\
+in order for the workstation to be usable, and for orders issued to it to take place.\n\
+To exit the workstation, press $yellow$Escape$white$\n\n\
+@Tutorial 1: Overview of the Submarine@\n\
+@Tutorial 2: Preparing@\n\
+@Tutorial 3: Movement@\n\
+@Tutorial 4: Combat@\n\
+@Tutorial 5: Disembarking@\n\
+\n\
+You can pause the game anytime with $yellow$SPACE$white$ so feel free to take your time\n\
+Also, check the @Controls Overview@ to find every single keybind!";
+
+ screens["Tutorial 1: Overview of the Submarine"] = "\
+The leftmost part of the submarine is the torpedo room. The small ($dark$=$white$) symbols are\n\
+the torpedo tubes. You can order torpedos to be loaded by selecting a crewmember\n\
+right-clicking over a torpedo, and selecting $yellow$Load Torpedo$white$.\n\n\
+The next room are the living quarters.\n\n\
+The next two rooms hold two very important work stations, the topmost one is the @Sonar@\n\
+and the bottom one is the @Radio@\n\n\
+The next room contains the @Periscope@ (center), the @Maneuvering Station@(top)\n\
+and the @Targeting Station@(bottom).\n\n\
+The next room contains the @Diesel Engines@.\n\n\
+The last room contains the @Battery Station@ and a few extra torpedoes\n\n";
+
+ screens["Tutorial 2: Preparing"] = "\
+The submarine currently is nothing more that a barely floating piece of metal. You need to\n\
+prepare the multiple systems before you depart. First of all, you need electricity.\n\
+Head over to the @Battery Station@ and toggle on (by left-clicking) the switches:\n\
+- $174$$dark$BAT.A > MOTOR$white$$175$ (Allows moving with the electric engines)\n\
+- $174$$dark$BAT.B > BAT.A$white$$175$ (Uses the B battery to fuel the motor indirectly, extending range)\n\
+- $174$$dark$BAT.A > B.SHK$white$$175$ (Extends usage of the electric shock weapon)\n\n\
+Now you need to load some torpedos.\nLeft-click any crewmember and right-click one of the torpedos,\n\
+then select $yellow$Load Torpedo$white$. Do this until all tubes are loaded.\n\
+Then you will want to set atleast 2 crewmembers to Auto-Repair. The captain ($2$) is\n\
+always a good option. You can also set the radio-man and the diesel engine controller.\n\
+To do this, left-click a crewmember, right-click anywhere and select $yellow$Auto-Repair$white$\n\
+You are now ready to proceed. Click $yellow$R$white$ to return all crew to their positions.\n\n\
+You may have already received a radio message, to view it, click the @Radio@ and listen.";
+
+ screens["Tutorial 3: Movement"] = "\
+You can move by manually using the @Maneouvering Station@, but it's easier and faster to\n\
+use the numpad keys, which allows full directional movement, including diagonals.\n\
+You can issue throttle commands with the numbers on top of the keyboard.\n\n\
+Head over to the sonar, and toggle on the $174$$dark$Active Sonar$white$$175$ switch.\n\
+You will now see the world surrounding you, and hear sonar pings.\n\
+You should toggle the sonar off if you want to carefully avoid some enemy.\n\
+Now go to the @Periscope@. It has a relatively short range (~200m), but it's always on\n\
+and shows the cave walls which the sonar may not warn you about. \n\
+Use the @Periscope@ whenever you are maneovering on a tight passage,\n\
+when approaching nests, or when fighting enemies from a close range.\n\n\
+Try moving in some direction free of walls (Use the numpad) on normal throttle:\n\
+- $yellow$1$white$ = Stop\n\
+- $yellow$2$white$ = Slow\n\
+- $yellow$3$white$ = Normal\n\
+- $yellow$4$white$ = Fast\n\
+- $yellow$5$white$ = Full Speed Ahead (You need two batteries assigned to MOTOR to do this)\n\
+\n\n\
+While you are moving, any battery assigned to MOTOR will deplete. You must be careful,\n\
+if you run out of electricity you could very easily get stuck, and lose the game.\n\
+In order to refill batteries you need to get to oxygen-rich zones,\n\
+marked in the sonar as a ($sonar$$177$$white$). Try maneouvering to one.\n\
+Once you reach the zone, stop and head over to the @Diesel Engines@. \n\
+You want to set atleast one of the generators to output to BATTS (Both batteries), and enable it.\n\
+You can set the other one to wathever you want.\n\
+Keep in mind that running both engines makes a lot of noise and may attract nearby enemies!\n\
+Feel free to start exploring the map, and keep on checking the radio!\n\n\
+Reminder: You can check your coordinates on the sonar. \n\
+They are called \"Coarse Coordinates\", \"Fine Coordinates\" are your sub-tile\n\
+position, not world-position.";
+
+ screens["Tutorial 4: Combat"] = "\
+You may currently be under attack. First of all, pause the game and don't panic.\n\
+If you have prepared correctly, you will have torpedos loaded and ready to fire.\n\
+It may be wise to move one of the crewmembers to the torpedo room.\n\
+Make sure the sonar, targeting station, maneouvering station and periscope are crewed\n\
+during combat, as you will use them a lot. Make sure that the active sonar is toggled\n\
+ on in order to be able to target your enemy even if you lose sight of him.\n\n\
+In order to destroy your objective, go to the @Targeting Station@, left-click the target\n\
+that you want to destroy, and, once it's selected, click $yellow$F$white$ to fire a torpedo.\n\
+Make sure you are aiming roughly towards your target, otherwise you may miss.\n\n\
+Your other weapon is the electric shock, activated with $yellow$E$white$. It consumes\n\
+electricity from the SHK battery, and does a considerable ammount of damage to targets\n\
+near your submarine. Useful against smaller enemies.";
+
+ screens["Tutorial 5: Disembarking"] = "\
+Once you are sufficiently close to either a Nest ($sonar$$15$$white$) or a Station ($sonar$$234$$white$)\n\
+you will get a prompt to disembark on the upper-left corner of the screen.\n\
+Once you click the $yellow$G$white$ key, four of your crewmembers will go inside the target.\n\n\
+Your objective inside is to find and destroy the enemy nest, or the enemy \"commander\".\n\
+You can select any of your crewmembers by left-clicking them. Once selected, you can:\n\
+- Move: Using the $yellow$middle mouse button$white$\n\
+- Change Target: Using the $yellow$right mouse button$white$\n\
+- Throw Grenades: Using $yellow$SHIFT$white$ and the $right mouse button$\n\n\
+When you complete your objective, move every crewmember into the $sonar$Green$white$ zone.\n\n\n\
+Your crew can be killed, so be careful.\nAlso, remember you can always pause the game with $yellow$SPACE$white$!";
+
+ screens["Controls Overview"] = "\
+- $yellow$Numpad Numbers$white$: Issue heading orders\n\
+- $yellow$1$white$: Stop vehicle\n\
+- $yellow$2$white$: Move slowly\n\
+- $yellow$3$white$: Move normally\n\
+- $yellow$4$white$: move fast\n\
+- $yellow$5$white$: Full speed ahead (Only possible with 2 batteries or diesel engines active)\n\
+- $yellow$F$white$: Fire torpedo\n\
+- $yellow$R$white$: Return all crew to assigned positions\n\
+- $yellow$G$white$: Disembark if possible\n\
+- $yellow$E$white$: Electric Shock\n\
+- $yellow$ESCAPE$white$: Go back / Close Workbench / Close popup\n\
+- $yellow$SPACE$white$: Pause the game\n\n\
+-----------------------------------------------------\n\
+Special controls (No numpad or middle mouse button): \n\n\
+- $yellow$Vi keys$white$: Set heading\n\
+- $yellow$Ctrl-Leftclick$white$: Same as middle clicking\n\n\
+You don't need to enable anything, these controls are always enabled!";
+
+ screens["Controlling Crew"] = "\
+You can select crewmembers by left clicking them. Once they are selected you can bring up\n\
+the context menu by right-clicking. You can also move directly by pressing the\n\
+$yellow$middle mouse button$white$. In the context menu you can:\n\
+- Move Here: Moves the crew to given location\n\
+- Assign Location: The crew will go to selected location when $yellow$R$white$ is pressed\n\
+- Load Torpedo: Only shows when you right click over a torpedo\n\
+- Pump Water Out: Only shows when you right click a pump ($235$)\n\
+- Repair Tile: Only shows when you right click a damaged tile\n\
+- Toggle Auto-Repair: Allows the crew to automatically repair damaged tiles he detects\n\n\
+";
+
+
+ cur_screen = "Main Screen";
+
+ }
+
+ void draw()
+ {
+ Drawing::draw_window(TCODConsole::root, 0, 0, 89, HEIGHT - 1, cur_screen, true);
+
+ TCODConsole::root->setDefaultForeground(TCODColor::lightGrey);
+ std::string& cur = screens[cur_screen];
+
+ int x = 1;
+ int y = 1;
+
+ TCODConsole::root->setDefaultForeground(TCODColor::white);
+
+ TCOD_mouse_t mouse = TCODMouse::getStatus();
+
+ if (g_key.vk == TCODK_ESCAPE)
+ {
+ if (return_to.size() != 0)
+ {
+ std::string ret = return_to[return_to.size() - 1];
+ return_to.pop_back();
+
+ cur_screen = ret;
+ }
+ }
+
+ for (int i = 0; i < cur.size(); i++)
+ {
+ bool inc = false;
+
+ char c = cur[i];
+
+ if (c == ' ')
+ {
+ inc = true;
+ }
+ else if (c == '$')
+ {
+ int skip = 1;
+ char j = 1;
+ std::string str = "";
+ while (j != '$')
+ {
+ j = cur[i + skip];
+ str.push_back(j);
+ skip++;
+ }
+
+ str.pop_back();
+ i += skip - 1;
+ const char* strc = str.c_str();
+ char* end;
+ int ch = strtol(strc, &end, 10);
+ if (end == strc)
+ {
+ // It was not a numeric, it was a color code
+ if (str == "yellow")
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor::yellow);
+ }
+ else if (str == "white")
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor::white);
+ }
+ else if (str == "blue")
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor(128, 128, 255));
+ }
+ else if (str == "sonar")
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor(80, 255, 80));
+ }
+ else if (str == "brown")
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor(158, 134, 100));
+ }
+ else if (str == "dark")
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor(180, 180, 180));
+ }
+ }
+ else
+ {
+ TCODConsole::root->putChar(x, y, ch);
+ x++;
+ }
+ }
+ else if(c == '@')
+ {
+ int skip = 1;
+ char j = 1;
+ std::string str = "";
+ while (j != '@')
+ {
+ j = cur[i + skip];
+ str.push_back(j);
+ skip++;
+ }
+
+ str.pop_back();
+ i+=str.size() + 1;
+
+ bool hlight = false;
+ if (mouse.cx >= x && mouse.cx < x + str.size() && mouse.cy == y)
+ {
+ TCODConsole::root->setDefaultBackground(TCODColor::white);
+ TCODConsole::root->setDefaultForeground(TCODColor::black);
+ hlight = true;
+ }
+ else
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor(128, 128, 255));
+ }
+
+ for (int j = 0; j < str.size(); j++)
+ {
+ if (str[j] == '_')
+ {
+ str[j] = ' ';
+ }
+
+ TCODConsole::root->putChar(x + j, y, str[j]);
+ if (hlight)
+ {
+ TCODConsole::root->setCharBackground(x + j, y, TCODColor::white);
+ }
+
+ }
+
+ if (hlight && mouse.lbutton_pressed)
+ {
+ return_to.push_back(cur_screen);
+ cur_screen = str;
+ }
+
+ TCODConsole::root->setDefaultBackground(TCODColor::black);
+
+ x += str.size();
+
+ TCODConsole::root->setDefaultForeground(TCODColor::white);
+
+ }
+ else if (c == '\n')
+ {
+ y++;
+ x = 1;
+ }
+ else if (c != '$' && c > 32)
+ {
+ TCODConsole::root->putChar(x, y, cur[i]);
+ inc = true;
+ }
+
+ if (inc)
+ {
+ x++;
+ if (x >= 88)
+ {
+ y++;
+ x = 1;
+ }
+ }
+ }
+
+ }
+};
\ No newline at end of file
diff --git a/src/Main.cpp b/src/Main.cpp
index 0bdd6e9..47a4314 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -3,21 +3,27 @@
#include "Status.h"
#include "flight/FlightScene.h"
+#include "disembark/EmbarkScene.h"
#include "soloud/include/soloud.h"
#include "soloud/include/soloud_wav.h"
+#include "flight/entity/Buildings.h"
+#include "flight/Gamemaster.h"
+#include "Popup.h"
+
SoLoud::Soloud* g_soloud;
Status* g_status;
TCOD_key_t g_key;
TCODRandom* g_random;
+Gamemaster* g_master;
+Popup* g_popup;
int main(int argc, char** argv)
{
TCODConsole::setCustomFont("rc11.png", TCOD_FONT_LAYOUT_ASCII_INROW);
- TCODConsole::initRoot(WIDTH, HEIGHT, "7DRL", false, TCOD_RENDERER_GLSL);
-
+ TCODConsole::initRoot(WIDTH, HEIGHT, "The Legend of U-101", false, TCOD_RENDERER_GLSL);
g_soloud = new SoLoud::Soloud;
g_soloud->init();
@@ -25,21 +31,118 @@ int main(int argc, char** argv)
g_status = new Status();
g_random = new TCODRandom();
+ g_master = new Gamemaster();
+ g_popup = new Popup();
+
+
+
FlightScene flight_scene = FlightScene();
+ EmbarkScene embark_scene = EmbarkScene();
+
+ flight_scene.update(1.0f);
+
+ g_master->flight_scene = &flight_scene;
+ g_master->embark_scene = &embark_scene;
+ g_master->embarked = false;
+
+ g_master->init();
TCODSystem::setFps(60);
+
+ Help help = Help();
+
+ bool help_open = false;
+ bool paused = false;
+
+ bool show_fps = false;
+
+ g_popup->show("Press the F1 key to show the in-game help menu!\n\n\
+You can press ESCAPE or ENTER to close these pop-ups.");
+
while (!TCODConsole::isWindowClosed())
{
TCOD_mouse_t mouse;
TCODSystem::checkForEvent(TCOD_EVENT_KEY_PRESS | TCOD_EVENT_MOUSE, &g_key, &mouse);
- flight_scene.update(TCODSystem::getLastFrameLength());
+ if (g_key.vk == TCODK_F1)
+ {
+ help_open = !help_open;
+ }
+
+ if (g_key.vk == TCODK_SPACE)
+ {
+ paused = !paused;
+ }
+
+ float dt = TCODSystem::getLastFrameLength();
+ float rdt = dt;
+ if (paused || help_open || g_popup->is_shown)
+ {
+ rdt = 0.0f;
+ }
-
+ g_master->update(rdt);
+
+
+
+ if (g_popup->is_shown)
+ {
+ g_popup->update(dt);
+ }
+
TCODConsole::root->clear();
- flight_scene.render();
+ if (!g_master->embarked)
+ {
+ flight_scene.render();
+ }
+ else
+ {
+ embark_scene.render();
+ }
+
+ if (g_popup->is_shown)
+ {
+ g_popup->draw();
+ }
+
+ if (help_open)
+ {
+ help.draw();
+ }
+
+ if (g_key.vk == TCODK_F3)
+ {
+ show_fps = !show_fps;
+ }
+
+ if (show_fps)
+ {
+ TCODConsole::root->printf(32, 0, "FPS: %i", (int)(1.0f / dt));
+ }
+
+ if (g_master->gameover)
+ {
+ TCODConsole::root->setAlignment(TCOD_CENTER);
+ TCODConsole::root->setDefaultForeground(TCODColor::lighterRed);
+ TCODConsole::root->printf(WIDTH / 2, HEIGHT / 2 - 5, "Game Over");
+ TCODConsole::root->setDefaultForeground(TCODColor::white);
+ TCODConsole::root->printf(WIDTH / 2, HEIGHT / 2 - 4, "All crewmembers died");
+ TCODConsole::root->setAlignment(TCOD_LEFT);
+ }
+
+
+ if (paused || help_open)
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor::red);
+ TCODConsole::root->setAlignment(TCOD_RIGHT);
+ TCODConsole::root->printf(TCODConsole::root->getWidth() - 1, 0, "Paused");
+ TCODConsole::root->setAlignment(TCOD_LEFT);
+ TCODConsole::root->setDefaultForeground(TCODColor::white);
+ }
+
+
TCODConsole::flush();
}
diff --git a/src/Popup.h b/src/Popup.h
new file mode 100644
index 0000000..5e32fdd
--- /dev/null
+++ b/src/Popup.h
@@ -0,0 +1,33 @@
+#pragma once
+#include "Drawing.h"
+
+
+class Popup
+{
+public:
+
+ bool is_shown;
+ std::string txt;
+ void show(const std::string& str)
+ {
+ is_shown = true;
+ txt = str;
+ }
+
+ void update(float dt)
+ {
+ if (g_key.vk == TCODK_ESCAPE || g_key.vk == TCODK_ENTER)
+ {
+ is_shown = false;
+ }
+ }
+
+ void draw()
+ {
+ Drawing::draw_window(TCODConsole::root, 20, 10, 50, 30, "Attention!", true);
+ TCODConsole::root->printRect(22, 12, 47, 27, txt.c_str());
+ }
+
+
+};
+
diff --git a/src/defines.h b/src/defines.h
index a0cf435..98d4641 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -1,7 +1,17 @@
#pragma once
-#include "soloud.h"
+
#include "Status.h"
+
+
+namespace SoLoud
+{
+ class Soloud;
+}
+
+class Gamemaster;
+class Popup;
+
#define WIDTH 90
#define HEIGHT 70
@@ -14,6 +24,9 @@ extern TCOD_key_t g_key;
extern TCODRandom* g_random;
+extern Gamemaster* g_master;
+extern Popup* g_popup;
+
enum Direction
{
N,
diff --git a/src/disembark/BuildingMap.cpp b/src/disembark/BuildingMap.cpp
new file mode 100644
index 0000000..e3779c3
--- /dev/null
+++ b/src/disembark/BuildingMap.cpp
@@ -0,0 +1,263 @@
+#include "BuildingMap.h"
+#include "../flight/Gamemaster.h"
+
+
+void BuildingMap::dig(int x1, int y1, int x2, int y2)
+{
+ if (x2 < x1)
+ {
+ std::swap(x2, x1);
+ }
+
+ if (y2 < y1)
+ {
+ std::swap(y2, y1);
+ }
+
+ for (int x = x1; x <= x2; x++)
+ {
+ for (int y = y1; y <= y2; y++)
+ {
+ if (x > 0 && y > 0 && x < width - 1 && y < height - 1)
+ {
+ tiles[y * width + x].is_wall = false;
+ }
+ }
+ }
+}
+
+void BuildingMap::create_room(bool first, int x1, int y1, int x2, int y2)
+{
+ dig(x1, y1, x2, y2);
+
+ if (first)
+ {
+ // Set spawn location
+ spawn_x0 = x1;
+ spawn_y0 = y1;
+ spawn_x1 = x2;
+ spawn_y1 = y2;
+ }
+ else
+ {
+ // Spawn stuff in the room
+ }
+}
+
+void BuildingMap::draw(TCODConsole& target)
+{
+ int ch = '#';
+ if (!organic)
+ {
+ ch = 240;
+ }
+
+ TCODColor fore_b = TCODColor(200, 200, 200);
+ TCODColor fore_s = TCODColor(70, 70, 70);
+ if (!organic)
+ {
+ fore_b = TCODColor(180, 180, 255);
+ fore_s = TCODColor(60, 60, 100);
+ }
+
+ for (int x = 0; x < width; x++)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ TCODColor fore;
+ TCODColor back;
+
+ if (tiles[y * width + x].seen)
+ {
+ fore = fore_b;
+ back = TCODColor(20, 20, 20);
+
+ if (tiles[y * width + x].alien_blood)
+ {
+ back = TCODColor(54, 0, 217);
+ }
+ else if (tiles[y * width + x].blood)
+ {
+ back = TCODColor(102, 0, 26);
+ }
+ }
+ else if (tiles[y * width + x].seen_before)
+ {
+ fore = fore_s;
+ back = TCODColor(0, 0, 0);
+ }
+ else
+ {
+ fore = TCODColor::black;
+ back = TCODColor::black;
+ }
+
+
+ if (tiles[y * width + x].is_wall)
+ {
+ target.setChar(x, y, ch);
+ }
+ else
+ {
+ fore.setValue(fore.getValue() * 0.5f);
+ target.setChar(x, y, '.');
+ }
+
+ target.setCharForeground(x, y, fore);
+ target.setCharBackground(x, y, back);
+ }
+ }
+
+ for (int i = 0; i < monsters.size(); i++)
+ {
+ if (tiles[monsters[i].y * width + monsters[i].x].seen)
+ {
+ target.setChar(monsters[i].x, monsters[i].y, monsters[i].health > 0.0f ? monsters[i].get_symbol() : '*');
+ target.setCharForeground(monsters[i].x, monsters[i].y, monsters[i].get_foreground());
+ }
+ }
+
+
+}
+
+BuildingMap::BuildingMap(int w, int h, bool organic) : tcod_map(w, h)
+{
+ this->organic = organic;
+
+ width = w;
+ height = h;
+ tiles.resize(w * h);
+
+
+ // Generate the map
+ TCODBsp root = TCODBsp(0, 0, w, h);
+
+ min_size = g_random->getInt(4, 8);
+ max_size = g_random->getInt(min_size, min_size + 8);
+
+ root.splitRecursive(g_random, 8, max_size, max_size,
+ g_random->getFloat(1.3f, 1.8f), g_random->getFloat(1.3f, 1.8f));
+
+ BSPListener listener = BSPListener(this);
+ root.traverseInvertedLevelOrder(&listener, nullptr);
+
+ if (organic)
+ {
+ // Do some random digging
+ int it = 0;
+ int to_dig = (int)(g_random->getFloat(0.2f, 0.5f) * width * height);
+
+ int dx = g_random->getInt(0, width);
+ int dy = g_random->getInt(0, height);
+
+ while (it < to_dig)
+ {
+ dx += g_random->getInt(-1, 1);
+ dy += g_random->getInt(-1, 1);
+
+ if (dx > 0 && dy > 0 && dx < width - 1 && dy < height - 1)
+ {
+ tiles[dy * width + dx].is_wall = false;
+ it++;
+ }
+ else
+ {
+ dx = g_random->getInt(0, width);
+ dy = g_random->getInt(0, height);
+ }
+
+
+ }
+ }
+
+ for (int x = 0; x < width; x++)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ tcod_map.setProperties(x, y, !tiles[y * width + x].is_wall, !tiles[y * width + x].is_wall);
+ }
+ }
+
+ // Spawn bois
+ int wanted[MONSTER_COUNT] = { 0 };
+ int placed[MONSTER_COUNT] = { 0 };
+
+ if (organic)
+ {
+ wanted[M_ARACHNID] = g_random->getInt(6, 14);
+ wanted[M_ANT] = g_random->getInt(4, 9);
+ wanted[M_GOLEM] = g_random->getInt(2, 5);
+ wanted[M_NEST] = 1;
+ }
+ else
+ {
+ wanted[M_ARACHNID] = g_random->getInt(0, 5);
+ wanted[M_ANT] = g_random->getInt(0, 5);
+ wanted[M_GOLEM] = g_random->getInt(0, 2);
+ wanted[M_ROBOT] = g_random->getInt(3, 10);
+ wanted[M_GRUNT] = g_random->getInt(1, 5);
+ }
+
+ for (int i = 0; i < MONSTER_COUNT; i++)
+ {
+ while (placed[i] < wanted[i])
+ {
+ int x = g_random->getInt(0, width - 1);
+ int y = g_random->getInt(0, height - 1);
+ if (x >= spawn_x0 && y >= spawn_y0 && x < spawn_x1 && y < spawn_y1)
+ {
+ continue;
+ }
+
+ if (tiles[y * width + x].is_wall)
+ {
+ continue;
+ }
+
+ placed[i]++;
+ EmbarkMonster m = EmbarkMonster((MonsterType)i, &tcod_map);
+ m.x = x;
+ m.y = y;
+
+ monsters.push_back(m);
+ }
+ }
+
+
+
+}
+
+BuildingMap::~BuildingMap()
+{
+}
+
+BSPListener::BSPListener(BuildingMap* map)
+{
+ this->map = map;
+ room_n = 0;
+}
+
+bool BSPListener::visitNode(TCODBsp * node, void * user)
+{
+ if (node->isLeaf())
+ {
+ int x, y, w, h;
+ w = g_random->getInt(map->min_size, node->w - 2);
+ h = g_random->getInt(map->min_size, node->h - 2);
+ x = g_random->getInt(node->x + 1, node->x + node->w - w - 1);
+ y = g_random->getInt(node->y + 1, node->y + node->h - h - 1);
+ map->create_room(room_n == 0, x, y, x + w - 1, y + h - 1);
+
+ if (room_n != 0)
+ {
+ map->dig(last_x, last_y, x + w / 2, last_y);
+ map->dig(x + w / 2, last_y, x + w / 2, y + h / 2);
+ }
+
+ last_x = x + w / 2;
+ last_y = y + h / 2;
+ room_n++;
+ }
+
+ return true;
+}
diff --git a/src/disembark/BuildingMap.h b/src/disembark/BuildingMap.h
new file mode 100644
index 0000000..20a360b
--- /dev/null
+++ b/src/disembark/BuildingMap.h
@@ -0,0 +1,107 @@
+#pragma once
+#include
+#include "../defines.h"
+
+struct BuildingTile
+{
+ bool is_wall;
+ bool seen_before;
+ bool seen;
+ bool seen_by_someone;
+
+ bool blood;
+ bool alien_blood;
+
+ BuildingTile()
+ {
+ is_wall = true;
+ seen_before = false;
+ seen = false;
+
+ blood = 0.0f;
+ alien_blood = 0.0f;
+ }
+};
+
+class BuildingMap;
+
+enum MonsterType
+{
+ M_ARACHNID,
+ M_ANT,
+ M_GOLEM,
+ M_NEST,
+ M_ROBOT,
+ M_GRUNT,
+ M_ANCIENT,
+ M_COMPUTER,
+
+ MONSTER_COUNT
+};
+
+struct EmbarkMonster
+{
+ TCODPath* path;
+ float walk_interval;
+
+ float walkt;
+ int x, y;
+ float health;
+
+ float attackt;
+ float attack_interval;
+ float damage;
+
+ bool seen;
+
+ float t0, t1, t2;
+
+ MonsterType type;
+
+ EmbarkMonster(MonsterType type, TCODMap* map);
+
+ int get_symbol();
+ TCODColor get_foreground();
+
+};
+
+
+class BSPListener : public ITCODBspCallback
+{
+private:
+ BuildingMap* map;
+ int room_n;
+ int last_x, last_y;
+public:
+ BSPListener(BuildingMap* map);
+ virtual bool visitNode(TCODBsp* node, void* user) override;
+
+};
+
+class BuildingMap
+{
+public:
+
+ bool organic;
+
+ std::vector monsters;
+
+ int min_size, max_size;
+
+ int spawn_x0, spawn_y0, spawn_x1, spawn_y1;
+
+ void dig(int x1, int y1, int x2, int y2);
+ void create_room(bool first, int x1, int y1, int x2, int y2);
+
+ int width, height;
+
+ TCODMap tcod_map;
+
+ std::vector tiles;
+
+ void draw(TCODConsole& target);
+
+ BuildingMap(int w, int h, bool organic = false);
+ ~BuildingMap();
+};
+
diff --git a/src/disembark/EmbarkScene.cpp b/src/disembark/EmbarkScene.cpp
new file mode 100644
index 0000000..d8ca0d5
--- /dev/null
+++ b/src/disembark/EmbarkScene.cpp
@@ -0,0 +1,1039 @@
+#include "EmbarkScene.h"
+#include "../flight/Gamemaster.h"
+#include "../flight/FlightScene.h"
+#include "../flight/entity/Buildings.h"
+
+void EmbarkScene::splatter(int x, int y, bool human)
+{
+ int bx = x + g_random->getInt(-2, 2);
+ int by = y + g_random->getInt(-2, 2);
+
+ if (bx > 0 && by > 0 && bx < map->width && by < map->height)
+ {
+ if (human)
+ {
+ map->tiles[by * map->width + bx].blood = true;
+ }
+ else
+ {
+ map->tiles[by * map->width + bx].alien_blood = true;
+ }
+
+ }
+}
+
+void EmbarkScene::update_entity(float dt, EmbarkMonster* monster)
+{
+ if (monster->health > 0.0f)
+ {
+ monster->walkt -= dt;
+ monster->t0 -= dt;
+ monster->t1 -= dt;
+ monster->t2 -= dt;
+ monster->attackt -= dt;
+
+ if (monster->walkt <= 0.0f)
+ {
+ int nx, ny;
+ if (monster->path->walk(&nx, &ny, false))
+ {
+ if (is_free_crew(nx, ny))
+ {
+ monster->x = nx;
+ monster->y = ny;
+ }
+ else
+ {
+ // Attack crew at location
+ if (monster->attackt < 0.0f)
+ {
+ for (EmbarkCrew& c : crew)
+ {
+ if (c.x == nx && c.y == ny && c.health >= 0.0f)
+ {
+ c.hurt(monster->damage, this);
+ break;
+ }
+ }
+ monster->attackt = monster->attack_interval;
+ }
+ }
+ }
+
+ monster->walkt = monster->walk_interval;
+ }
+
+ if (monster->t0 < 0.0f)
+ {
+ // Go towards players upon being seen
+ if (monster->seen)
+ {
+ int tx, ty;
+ int idx = g_random->getInt(0, crew.size() - 1);
+ tx = crew[idx].x;
+ ty = crew[idx].y;
+
+ monster->path->compute(monster->x, monster->y, tx, ty);
+ }
+
+ if (monster->type == M_ARACHNID || monster->type == M_ROBOT)
+ {
+ monster->t0 = 3.0f;
+ }
+ else if (monster->type == M_GOLEM || monster->type == M_GRUNT)
+ {
+ monster->t0 = 6.0f;
+ }
+ else if (monster->type == M_ANCIENT)
+ {
+ monster->t0 = 10.0f;
+ }
+ else
+ {
+ monster->t0 = 5.0f;
+ }
+
+ }
+
+ }
+}
+
+void EmbarkScene::spot(int x, int y, EmbarkCrew* crew)
+{
+ for (int i = 0; i < map->monsters.size(); i++)
+ {
+ if (map->monsters[i].x == x && map->monsters[i].y == y && !map->monsters[i].seen)
+ {
+ map->monsters[i].seen = true;
+ switch (map->monsters[i].type)
+ {
+ case M_ARACHNID:
+ crew->gc->speak("Spotted an arachnid.");
+ break;
+ case M_ANT:
+ crew->gc->speak("Spotted a huge ant.");
+ break;
+ case M_GOLEM:
+ crew->gc->speak("Spotted an alien golem.");
+ break;
+ case M_NEST:
+ crew->gc->speak("Spotted the alien nest!");
+ break;
+ case M_ROBOT:
+ if (!g_master->seen_robot)
+ {
+ crew->gc->speak("Wait! The hell is that robot doing here?");
+ g_master->seen_robot = true;
+ }
+ else
+ {
+ crew->gc->speak("Spotted a robot.");
+ }
+ break;
+ case M_GRUNT:
+ if (!g_master->seen_grunt)
+ {
+ crew->gc->speak("Fuck! We haven't seen this one before.");
+ g_master->seen_grunt = true;
+ }
+ else
+ {
+ crew->gc->speak("Spotted a humanoid.");
+
+ }
+ break;
+ case M_ANCIENT:
+ crew->gc->speak("Fucking hell, destroy that thing!");
+ break;
+ case M_COMPUTER:
+ crew->gc->speak("Spotted a computer! It's their commander!");
+ break;
+ default:
+ crew->gc->speak("Ayy lmao!");
+ break;
+ }
+
+ }
+ }
+}
+
+void EmbarkScene::handle_mouse()
+{
+ TCOD_mouse_t mouse = TCODMouse::getStatus();
+
+ bool found = false;
+
+ EmbarkCrew* n_select = nullptr;
+
+ for (int i = 0; i < crew.size(); i++)
+ {
+ if (mouse.cx == crew[i].x && mouse.cy == crew[i].y)
+ {
+ // Draw info
+ TCODConsole::root->printf(61, 61, crew[i].gc->name.c_str());
+ TCODConsole::root->printf(62, 63, "Health: %i%%", (int)round(crew[i].health * 100.0f));
+ TCODConsole::root->printf(62, 64, "Ammo: %i", crew[i].ammo);
+ TCODConsole::root->printf(62, 65, "Grenades: %i", crew[i].grenades);
+
+ if (crew[i].hold_fire)
+ {
+ TCODConsole::root->printf(62, 67, "Holding Fire", crew[i].ammo);
+ }
+ else
+ {
+ TCODConsole::root->printf(62, 67, "Firing automatically", crew[i].ammo);
+ }
+
+ n_select = &crew[i];
+
+ found = true;
+ }
+ }
+
+ if ((mouse.lbutton_pressed && !(g_key.lctrl || g_key.rctrl)))
+ {
+ selected = n_select;
+ }
+
+ if (selected && selected->health > 0.00001f)
+ {
+ bool was_set = false;
+
+ TCODPath n_path = TCODPath(&map->tcod_map, 0.0f);
+
+
+ // Draw the visibility map
+ for (int x = 0; x < map->width; x++)
+ {
+ for (int y = 0; y < map->height; y++)
+ {
+ if (selected->view_map[y * map->width + x])
+ {
+ TCODConsole::root->setCharBackground(x, y, TCODColor(50, 30, 30));
+ }
+
+ }
+ }
+
+ if (is_free(mouse.cx, mouse.cy) && map->tiles[mouse.cy * map->width + mouse.cx].seen_before)
+ {
+ // Draw path
+ n_path.compute(selected->x, selected->y, mouse.cx, mouse.cy);
+
+ int sx = selected->x;
+ int sy = selected->y;
+ while (n_path.walk(&sx, &sy, false))
+ {
+ TCODConsole::root->setCharBackground(sx, sy, TCODColor::lightGrey);
+ }
+
+ if (mouse.mbutton_pressed || (mouse.lbutton_pressed && (g_key.lctrl || g_key.rctrl)))
+ {
+ selected->gc->speak("Moving!");
+ was_set = true;
+ selected->to_walk = new TCODPath(&map->tcod_map, 0.0f);
+ selected->to_walk->compute(selected->x, selected->y, mouse.cx, mouse.cy);
+ }
+ }
+
+ if (mouse.rbutton_pressed)
+ {
+ for (int i = 0; i < map->monsters.size(); i++)
+ {
+ if (map->monsters[i].x == mouse.cx && map->monsters[i].y == mouse.cy)
+ {
+ if (selected->view_map[mouse.cy * map->width + mouse.cx])
+ {
+ selected->target = &map->monsters[i];
+ selected->gc->speak("Changed target!");
+ break;
+ }
+ }
+ }
+
+ }
+
+ if (mouse.rbutton_pressed && g_key.shift)
+ {
+ // Throw grenade
+ if (selected->grenades > 0 && selected->view_map[mouse.cy * map->width + mouse.cx])
+ {
+ selected->gc->speak("Grenade out!");
+ selected->grenades--;
+
+ // Spawn grenade
+ Grenade gnd = Grenade();
+ gnd.x = mouse.cx;
+ gnd.y = mouse.cy;
+ gnd.timer = 2.0f;
+ gnd.exploded = false;
+
+ g_soloud->play(grenade_land, 2.0f);
+
+ grenades.push_back(gnd);
+ }
+ }
+
+ }
+
+
+
+ if (found)
+ {
+ TCODConsole::root->setCharBackground(mouse.cx, mouse.cy, TCODColor::white);
+ TCODConsole::root->setCharForeground(mouse.cx, mouse.cy, TCODColor::black);
+ }
+}
+
+void EmbarkScene::update_crew(float dt, EmbarkCrew* crew)
+{
+ if (crew->health > 0.00001f)
+ {
+ crew->walk_t -= dt;
+ crew->fired_timer -= dt;
+ if (crew->fired_timer < 0.0f)
+ {
+ crew->fired_timer = 0.0f;
+ }
+
+ if (crew->walk_t < 0.0f)
+ {
+ crew->walk_t = 0.4f;
+ if (crew->to_walk != nullptr)
+ {
+ int nx, ny;
+ if (!crew->to_walk->walk(&nx, &ny, true))
+ {
+ delete crew->to_walk;
+ crew->to_walk = nullptr;
+ }
+ else
+ {
+ crew->x = nx;
+ crew->y = ny;
+ }
+ }
+ }
+
+ if (crew->target == nullptr && !crew->hold_fire)
+ {
+ // Find a new target in field of view
+ for (EmbarkMonster& monster : map->monsters)
+ {
+ if (crew->view_map[monster.y * map->width + monster.x] && monster.health > 0.0f)
+ {
+ crew->target = &monster;
+ //crew->gc->speak("Acquired new target");
+ }
+ }
+ }
+
+ if (crew->target != nullptr)
+ {
+ if (crew->target->health <= 0.0f)
+ {
+ crew->target = nullptr;
+ }
+ else if (!crew->view_map[crew->target->y * map->width + crew->target->x])
+ {
+ //crew->gc->speak("Target lost");
+ crew->target = nullptr;
+ }
+ else if (crew->fire(crew->target->x, crew->target->y, dt, &gunshot))
+ {
+ // Damage
+ if (crew->target->type == M_ROBOT || crew->target->type == M_GRUNT
+ || crew->target->type == M_ANCIENT || crew->target->type == M_COMPUTER)
+ {
+ }
+ else
+ {
+ splatter(crew->target->x, crew->target->y, false);
+ }
+
+ crew->target->health -= 0.25f;
+ if (crew->target->health <= 0.0f)
+ {
+ if (crew->target->type == M_ROBOT || crew->target->type == M_GRUNT
+ || crew->target->type == M_ANCIENT || crew->target->type == M_COMPUTER)
+ {
+ g_soloud->play(die_metal, 2.0f);
+ }
+ else
+ {
+ g_soloud->play(die_alien, 2.0f);
+ }
+
+ if (crew->target->type == M_NEST
+ || crew->target->type == M_COMPUTER
+ || crew->target->type == M_ANCIENT)
+ {
+ crew->gc->speak("Great job! Lets get out of here.");
+ }
+ else
+ {
+ crew->gc->speak("Target neutralized.");
+ }
+
+ }
+ else
+ {
+ if (crew->target->type == M_ROBOT || crew->target->type == M_GRUNT
+ || crew->target->type == M_ANCIENT || crew->target->type == M_COMPUTER)
+ {
+ g_soloud->play(hurt_metal, 2.0f);
+ }
+ else
+ {
+ g_soloud->play(hurt_alien, 1.0f);
+ }
+
+ }
+ }
+ }
+ }
+}
+
+void EmbarkScene::update(float dt)
+{
+ for (int i = 0; i < map->monsters.size(); i++)
+ {
+ if ((map->monsters[i].type == M_NEST ||
+ map->monsters[i].type == M_COMPUTER ||
+ map->monsters[i].type == M_ANCIENT) && map->monsters[i].health <= 0.0f)
+ {
+ if(map->monsters[i].type == M_COMPUTER && !mission_done)
+ {
+ g_popup->show("The computer had a databank inside which was uploaded automatically to the\n\
+submarine. It will be scanned by the fatherland. Good job!");
+
+ std::string coords = "";
+ for (FlightEntity* ent : g_master->flight_scene->map.entities)
+ {
+ if (ent->get_type() == E_STATION && !((Building*)ent)->is_explored())
+ {
+ coords += std::to_string((int)floor(ent->get_x()));
+ coords += ", ";
+ coords += std::to_string((int)floor(ent->get_x()));
+ break;
+ }
+ }
+
+ g_master->flight_scene->vehicle.radio->push_message("We decoded the data-bank and found\
+new coordinates: " + coords);
+ }
+
+ if (map->monsters[i].type == M_ANCIENT && !mission_done)
+ {
+ g_popup->show("Upon killing the huge robot, lights started flickering in all walls.\n\
+Shortly after, a strong blast was heard. Something weird was going on outside, as the event was reported\
+ by bases all over Jupiter-2. We had awoken something which was not meant to be perturbed.\
+\n\n\n\n\
+(To be continued, feel free to have fun exploring the world and fighting enemies!)");
+ }
+
+ mission_done = true;
+ }
+ }
+
+ t += dt;
+
+ /*if (g_key.vk == TCODK_F5)
+ {
+ finished = true;
+
+ /*crew[0].health = 0.0f;
+ crew[1].health = 0.0f;
+ crew[2].health = 0.0f;
+ crew[3].health = 0.0f;
+ }*/
+
+ if (!is_init)
+ {
+ if (!map->organic && g_master->clear_station_count == 3)
+ {
+
+ int it = 0;
+
+ float max_dist = -100.0f;
+ int max_x = 0;
+ int max_y = 0;
+
+ while (it < 1000)
+ {
+ int x = g_random->getInt(0, map->width - 1);
+ int y = g_random->getInt(0, map->height - 1);
+ float dx = x - map->spawn_x0;
+ float dy = y - map->spawn_y0;
+ float dist = sqrt(dx * dx + dy * dy);
+
+ if (dist > max_dist && is_free(x, y))
+ {
+ max_dist = dist;
+ max_x = x;
+ max_y = y;
+ }
+
+ it++;
+ }
+ std::cout << "Spawn " << max_x << ", " << max_y << std::endl;
+
+ EmbarkMonster m = EmbarkMonster(M_ANCIENT, &map->tcod_map);
+ m.x = max_x;
+ m.y = max_y;
+ map->monsters.push_back(m);
+
+ for (auto it = map->monsters.begin(); it != map->monsters.end(); it++)
+ {
+ if (it->type == M_COMPUTER)
+ {
+ map->monsters.erase(it);
+ break;
+ }
+ }
+ }
+
+ // Position crew
+ for (int i = 0; i < crew.size(); i++)
+ {
+ int its = 0;
+ int x = -1, y = -1;
+ while (!is_free(x, y) && its < 100)
+ {
+ x = g_random->getInt(map->spawn_x0, map->spawn_x1);
+ y = g_random->getInt(map->spawn_y0, map->spawn_y1);
+ its++;
+ }
+
+ crew[i].x = x;
+ crew[i].y = y;
+ crew[i].view_map.resize(map->width * map->height);
+ }
+
+ is_init = true;
+ }
+
+
+ if (map == nullptr)
+ {
+ return;
+ }
+
+ blinkt -= dt;
+ if (blinkt < 0.0f)
+ {
+ blink = !blink;
+ blinkt = 0.5f;
+ }
+
+ for (int i = 0; i < crew.size(); i++)
+ {
+ update_crew(dt, &crew[i]);
+ }
+
+ for (int i = 0; i < map->monsters.size(); i++)
+ {
+ update_entity(dt, &map->monsters[i]);
+ }
+
+ for (int i = 0; i < grenades.size(); i++)
+ {
+ grenades[i].timer -= dt;
+ if (grenades[i].timer <= 0.0f && !grenades[i].exploded)
+ {
+ // Damage around
+ grenades[i].exploded = true;
+ g_soloud->play(grenade, 3.0f);
+
+ for (EmbarkMonster& mnst : map->monsters)
+ {
+ float dx = mnst.x - grenades[i].x;
+ float dy = mnst.y - grenades[i].y;
+
+ float dist = sqrt(dx * dx + dy * dy);
+
+ if (dist < 3.0f)
+ {
+ mnst.health -= 1.0f;
+ }
+ }
+ }
+ }
+
+}
+
+void EmbarkScene::render()
+{
+ if (map == nullptr)
+ {
+ return;
+ }
+
+ for (int x = 0; x < map->width; x++)
+ {
+ for (int y = 0; y < map->height; y++)
+ {
+ map->tiles[y * map->width + x].seen_by_someone = false;
+ }
+ }
+
+ // Compute visibility
+ for (int i = 0; i < crew.size(); i++)
+ {
+ map->tcod_map.computeFov(crew[i].x, crew[i].y, 18, true, FOV_DIAMOND);
+
+ for (int x = 0; x < map->width; x++)
+ {
+ for (int y = 0; y < map->height; y++)
+ {
+ if (map->tcod_map.isInFov(x, y))
+ {
+ if (!map->tiles[y * map->width + x].seen_before)
+ {
+ spot(x, y, &crew[i]);
+ }
+
+ map->tiles[y * map->width + x].seen_by_someone = true;
+ map->tiles[y * map->width + x].seen_before = true;
+ crew[i].view_map[y * map->width + x] = true;
+ }
+ else
+ {
+ crew[i].view_map[y * map->width + x] = false;
+ }
+ }
+ }
+ }
+
+ for (int x = 0; x < map->width; x++)
+ {
+ for (int y = 0; y < map->height; y++)
+ {
+ if (map->tiles[y * map->width + x].seen_by_someone)
+ {
+ map->tiles[y * map->width + x].seen = true;
+ }
+ else
+ {
+ map->tiles[y * map->width + x].seen = false;
+ }
+ }
+ }
+
+ map->draw(*TCODConsole::root);
+
+ // Check for all alive crew to be in square
+ int alive = 0;
+ int alive_in_square = 0;
+ for (EmbarkCrew& c : crew)
+ {
+ if (c.health > 0.0001f)
+ {
+ alive++;
+
+ if (c.x >= map->spawn_x0 && c.y >= map->spawn_y0 && c.x <= map->spawn_x1 && c.y <= map->spawn_y1)
+ {
+ alive_in_square++;
+ }
+ }
+ }
+
+ if (alive == 0)
+ {
+ finished = true;
+ }
+
+
+ if (mission_done)
+ {
+ float gf = (sin(t) + 1.0f) * 0.5f;
+ int g = (int)round(gf * 255);
+ TCODColor col = TCODColor(0, g, 0);
+ for (int x = map->spawn_x0; x <= map->spawn_x1; x++)
+ {
+ for (int y = map->spawn_y0; y <= map->spawn_y1; y++)
+ {
+ TCODConsole::root->setCharBackground(x, y, col);
+ }
+ }
+
+
+ if (alive_in_square == alive)
+ {
+ finished = true;
+ }
+ else
+ {
+ finished = false;
+ }
+ }
+
+
+ handle_mouse();
+
+ // Draw crew
+ for (int i = 0; i < crew.size(); i++)
+ {
+ if (&crew[i] == selected && blink)
+ {
+ TCODConsole::root->setChar(crew[i].x, crew[i].y, 'X');
+ }
+ else
+ {
+ TCODConsole::root->setChar(crew[i].x, crew[i].y, crew[i].gc->is_captain ? 2 : 1);
+ }
+
+ if (crew[i].fired_timer > 0.0f)
+ {
+ //TCODConsole::root->setCharForeground(crew[i].x, crew[i].y, TCODColor::yellow);
+ // Draw bullet path in background color
+ TCODLine line = TCODLine();
+ line.init(crew[i].x, crew[i].y, crew[i].fx, crew[i].fy);
+ int x, y;
+ while (!line.step(&x, &y))
+ {
+ TCODConsole::root->setCharBackground(x, y, TCODColor::lightYellow);
+ }
+ }
+
+
+ TCODConsole::root->setCharForeground(crew[i].x, crew[i].y, TCODColor::white);
+
+ if (crew[i].health <= 0.4f && crew[i].health > 0.0f && !blink)
+ {
+ TCODConsole::root->setCharForeground(crew[i].x, crew[i].y, TCODColor::darkRed);
+ }
+
+ if (crew[i].health <= 0.0f)
+ {
+ TCODConsole::root->setCharForeground(crew[i].x, crew[i].y, TCODColor::red);
+ }
+
+
+
+
+ }
+
+ // Draw grenades
+ for (int i = 0; i < grenades.size(); i++)
+ {
+ if (grenades[i].timer <= 1.0f && !grenades[i].exploded)
+ {
+ TCODConsole::root->setChar(grenades[i].x, grenades[i].y, 147);
+ }
+
+ if (grenades[i].timer >= -1.0f && grenades[i].exploded)
+ {
+ float itim = 1.0f + grenades[i].timer;
+ // Draw explosion circle
+
+ for (int x = 0; x < map->width; x++)
+ {
+ for (int y = 0; y < map->height; y++)
+ {
+ float dx = x - grenades[i].x;
+ float dy = y - grenades[i].y;
+
+ float dist = sqrt(dx * dx + dy * dy);
+
+ if (dist < itim * 10.0f)
+ {
+ TCODConsole::root->setCharBackground(x, y, TCODColor::lightGrey);
+ TCODConsole::root->setChar(x, y, '+');
+ TCODConsole::root->setCharForeground(x, y, TCODColor::white);
+ }
+ }
+ }
+ }
+ }
+
+ // Draw the status box
+ for (int x = 0; x < WIDTH; x++)
+ {
+ TCODConsole::root->setChar(x, 60, 205);
+ TCODConsole::root->setCharForeground(x, 60, TCODColor::lightGrey);
+ }
+
+ g_status->draw(0, 60, WIDTH, HEIGHT);
+
+ // Draw the info box
+
+ for (int y = 60; y < HEIGHT; y++)
+ {
+ TCODConsole::root->setChar(60, y, 186);
+ TCODConsole::root->setCharForeground(60, y, TCODColor::lightGrey);
+ }
+
+ TCODConsole::root->setChar(60, 60, 203);
+ TCODConsole::root->setCharForeground(60, 60, TCODColor::lightGrey);
+
+
+
+}
+
+bool EmbarkScene::is_free(int x, int y, int ignore_ent)
+{
+ if (x < 0 || y < 0 || x >= map->width || y >= map->height)
+ {
+ return false;
+ }
+
+ if (map->tiles[y * map->width + x].is_wall)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < crew.size(); i++)
+ {
+ if (crew[i].x == x && crew[i].y == y && crew[i].health > 0.00001f)
+ {
+ return false;
+ }
+ }
+
+ for (int i = 0; i < map->monsters.size(); i++)
+ {
+ if (map->monsters[i].x == x && map->monsters[i].y == y && i != ignore_ent
+ && map->monsters[i].health > 0.00001f)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool EmbarkScene::is_free_crew(int x, int y)
+{
+ if (x < 0 || y < 0 || x >= map->width || y >= map->height)
+ {
+ return false;
+ }
+
+ if (map->tiles[y * map->width + x].is_wall)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < crew.size(); i++)
+ {
+ if (crew[i].x == x && crew[i].y == y && crew[i].health > 0.00001f)
+ {
+ return false;
+ }
+ }
+}
+
+void EmbarkScene::restart()
+{
+ crew.clear();
+ grenades.clear();
+ selected = nullptr;
+ finished = false;
+ t = 0.0f;
+ mission_done = false;
+ is_init = false;
+ map = nullptr;
+}
+
+EmbarkScene::EmbarkScene()
+{
+ finished = false;
+ mission_done = false;
+
+ map = nullptr;
+ is_init = false;
+ blinkt = 0.0f;
+
+ gunshot.load("gunshot.wav");
+ grenade.load("grenade.wav");
+ grenade_land.load("grenade_land.wav");
+ hurt.load("hurt.wav");
+ hurt_alien.load("hurt_alien.wav");
+ hurt_metal.load("hurt_metal.wav");
+
+ die_alien.load("alien_die.wav");
+ die_man.load("man_die.wav");
+ die_metal.load("metal_die.wav");
+}
+
+
+EmbarkScene::~EmbarkScene()
+{
+}
+
+EmbarkMonster::EmbarkMonster(MonsterType type, TCODMap* map)
+{
+ path = new TCODPath(map, 0.0f);
+
+ this->seen = false;
+ this->type = type;
+ this->walkt = 0.0f;
+ t0 = 0.0f;
+ t1 = 0.0f;
+ t2 = 0.0f;
+
+ switch (type)
+ {
+ case M_ARACHNID:
+ health = 0.5f;
+ walk_interval = 0.05f;
+ damage = 0.1f;
+ attack_interval = 0.8f;
+ break;
+ case M_ANT:
+ health = 0.8f;
+ walk_interval = 0.15f;
+ damage = 0.25f;
+ attack_interval = 0.8f;
+ break;
+ case M_GOLEM:
+ health = 5.0f;
+ walk_interval = 0.6f;
+ damage = 0.3f;
+ attack_interval = 2.0f;
+ break;
+ case M_NEST:
+ health = 8.0f;
+ walk_interval = 10000000000.0f;
+ damage = 0.0f;
+ break;
+ case M_ROBOT:
+ health = 1.5f;
+ walk_interval = 0.25f;
+ damage = 0.1f;
+ attack_interval = 0.5f;
+ break;
+ case M_GRUNT:
+ health = 3.0f;
+ walk_interval = 0.5f;
+ damage = 0.5f;
+ attack_interval = 1.5f;
+ break;
+ case M_ANCIENT:
+ health = 11.0f;
+ walk_interval = 0.7f;
+ damage = 0.8f;
+ attack_interval = 1.0f;
+ break;
+ case M_COMPUTER:
+ health = 8.0f;
+ walk_interval = 10000000000.0f;
+ damage = 0.0f;
+ break;
+ }
+}
+
+int EmbarkMonster::get_symbol()
+{
+ switch (type)
+ {
+ case M_ARACHNID:
+ return 'x';
+ case M_ANT:
+ return 12;
+ case M_GOLEM:
+ return 227;
+ case M_NEST:
+ return 15;
+ case M_ROBOT:
+ return 232;
+ case M_GRUNT:
+ return 228;
+ case M_ANCIENT:
+ return 234;
+ case M_COMPUTER:
+ return 241;
+ }
+}
+
+TCODColor EmbarkMonster::get_foreground()
+{
+ if (type == M_ROBOT || type == M_GRUNT || type == M_COMPUTER)
+ {
+ return TCODColor(102, 155, 255);
+ }
+ else if (type == M_ANCIENT)
+ {
+ return TCODColor(205, 133, 220);
+ }
+ else
+ {
+ return TCODColor(140, 102, 255);
+ }
+
+}
+
+void EmbarkCrew::hurt(float dmg, EmbarkScene* scene)
+{
+ // Attack
+ health -= dmg;
+ if (health < 0.0f)
+ {
+ health = 0.0f;
+
+ scene->splatter(x, y, true);
+ scene->splatter(x, y, true);
+ g_soloud->play(scene->die_man, 2.0f);
+ }
+ else
+ {
+ scene->splatter(x, y, true);
+ g_soloud->play(scene->hurt, 2.0f);
+ }
+
+ if (g_random->getFloat(0.0f, 1.0f) > 0.5f)
+ {
+ gc->speak("I'm Hurt!");
+ }
+}
+
+bool EmbarkCrew::fire(int tx, int ty, float dt, SoLoud::Wav * sound)
+{
+ float dx = tx - x;
+ float dy = ty - y;
+ float dist = sqrt(dx * dx + dy * dy);
+
+ float fail_chance = 0.6f;
+
+ if (dist <= 1.0f)
+ {
+ fail_chance = 0.1f;
+ }
+ else if (dist <= 3.0f)
+ {
+ fail_chance = 0.3f;
+ }
+
+
+ int rx = tx; int ry = ty;
+ if (g_random->getFloat(0.0f, 1.0f) < fail_chance)
+ {
+ // Miss (may actually not miss, but very low prob)
+ rx += g_random->getInt(-2, 2);
+ ry += g_random->getInt(-2, 2);
+ }
+
+
+ fire_timer -= dt;
+ if (fire_timer < 0.0f)
+ {
+ g_soloud->play(*sound, 1.0f);
+
+ TCODConsole::root->setCharForeground(x, y, TCODColor::yellow);
+
+ // Do the actual shot
+
+ fire_timer = g_random->getFloat(1.0f, 3.0f);
+ fired_timer = g_random->getFloat(0.3f, 0.5f);
+ fx = rx;
+ fy = ry;
+
+ if (rx == tx && ry == ty)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/disembark/EmbarkScene.h b/src/disembark/EmbarkScene.h
new file mode 100644
index 0000000..fd2036d
--- /dev/null
+++ b/src/disembark/EmbarkScene.h
@@ -0,0 +1,115 @@
+#pragma once
+#include "BuildingMap.h"
+#include "../Crewmember.h"
+
+class EmbarkScene;
+
+struct EmbarkCrew
+{
+ Crewmember* gc;
+ EmbarkMonster* target;
+
+ int x, y;
+
+ float health;
+
+ bool hold_fire;
+ int ammo;
+ int grenades;
+
+ float reload_timer;
+ float fire_timer;
+
+ TCODPath* to_walk;
+ float walk_t;
+
+ float fired_timer;
+ int fx, fy;
+
+ std::vector view_map;
+
+ void hurt(float dmg, EmbarkScene* scene);
+
+ EmbarkCrew()
+ {
+ health = 1.0f;
+ hold_fire = false;
+ ammo = 8;
+ grenades = g_random->getInt(1, 3);
+ reload_timer = 0.0f;
+ gc = nullptr;
+ walk_t = 0.0f;
+ to_walk = nullptr;
+ target = nullptr;
+ fire_timer = 0.0f;
+ fired_timer = 0.0f;
+ }
+
+ bool fire(int tx, int ty, float dt, SoLoud::Wav* sound);
+
+};
+
+struct Grenade
+{
+ int x, y;
+ float timer;
+ bool exploded;
+
+};
+
+class EmbarkScene
+{
+public:
+
+ bool finished;
+
+ float t;
+
+ bool mission_done;
+
+ void splatter(int x, int y, bool human);
+
+ void update_entity(float dt, EmbarkMonster* monster);
+
+ void spot(int x, int y, EmbarkCrew* crew);
+
+ bool blink;
+ float blinkt;
+
+
+ EmbarkCrew* selected;
+
+ void handle_mouse();
+ void update_crew(float dt, EmbarkCrew* crew);
+
+ SoLoud::Wav gunshot;
+ SoLoud::Wav grenade;
+ SoLoud::Wav grenade_land;
+ SoLoud::Wav hurt;
+ SoLoud::Wav hurt_alien;
+ SoLoud::Wav hurt_metal;
+ SoLoud::Wav die_alien;
+ SoLoud::Wav die_man;
+ SoLoud::Wav die_metal;
+
+
+ bool is_init;
+
+ std::vector crew;
+ std::vector grenades;
+
+ BuildingMap* map;
+
+ void update(float dt);
+ void render();
+
+ bool is_free(int x, int y, int ignore_ent = -1);
+
+ bool is_free_crew(int x, int y);
+
+ void restart();
+
+ EmbarkScene();
+ ~EmbarkScene();
+};
+
diff --git a/src/flight/FlightMap.cpp b/src/flight/FlightMap.cpp
index ca186b1..899ff84 100644
--- a/src/flight/FlightMap.cpp
+++ b/src/flight/FlightMap.cpp
@@ -1,5 +1,6 @@
#include "FlightMap.h"
-
+#include "entity/Buildings.h"
+#include "entity/Monsters.h"
static float grad_func(float x)
{
@@ -84,12 +85,52 @@ MapTile FlightMap::get_subtile(float x, float y)
return tile;
}
-std::vector& FlightMap::get_entities()
+std::vector& FlightMap::get_entities()
{
return entities;
}
-FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height), sub_tile_noise(2)
+/*
+int FlightMap::get_entity_symbol(EntityType type)
+{
+ switch (type)
+ {
+ case E_NEST: return 15;
+ case E_STATION: return 234;
+ case E_BASE: return 127;
+ case E_CRAWLER: return 157;
+ case E_WORM: return 126;
+ case E_GUARDIAN: return 236;
+ case E_SERPENT: return 21;
+ case E_BOMB: return 155;
+ case E_BITER: return 224;
+ case E_SUBMARINE: return '!';
+ default: return '?';
+ }
+}
+*/
+
+template
+void create_entities(std::vector* target, FlightMap* map, FlightScene* scene)
+{
+ int n = 0;
+ while (n < NUM)
+ {
+ int x = g_random->getInt(0, map->width - 1);
+ int y = g_random->getInt(0, map->height - 1);
+
+ if (map->tiles[y * map->width + x] != WALL)
+ {
+ T* crawler = new T();
+ crawler->set_position((float)x + 0.5f, (float)y + 0.5f);
+ crawler->init(map, scene);
+ target->push_back(crawler);
+ n++;
+ }
+ }
+}
+
+FlightMap::FlightMap(int width, int height, size_t seed, FlightScene* scene) : vmap(width, height), sub_tile_noise(2)
{
this->width = width;
this->height = height;
@@ -105,11 +146,10 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
}
}
- TCODRandom rng = TCODRandom(seed);
int dx = width / 2, dy = height / 2;
int open = 0;
- int target_open = rng.getFloat(0.3, 0.5) * width * height;
+ int target_open = g_random->getFloat(0.3, 0.5) * width * height;
int max_steps = 100000;
int steps = 0;
@@ -121,9 +161,9 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
open++;
}
- if (rng.getFloat(0.0, 1.0) >= 0.5)
+ if (g_random->getFloat(0.0, 1.0) >= 0.5)
{
- if (rng.getFloat(0.0, 1.0) >= 0.5)
+ if (g_random->getFloat(0.0, 1.0) >= 0.5)
{
dx++;
}
@@ -134,7 +174,7 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
}
else
{
- if (rng.getFloat(0.0, 1.0) >= 0.5)
+ if (g_random->getFloat(0.0, 1.0) >= 0.5)
{
dy++;
}
@@ -180,7 +220,7 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
}
}
- TCODNoise noise = TCODNoise(2, TCOD_NOISE_PERLIN);
+ TCODNoise noise = TCODNoise(2, g_random, TCOD_NOISE_PERLIN);
// Generate air map
for (int x = 0; x < width; x++)
@@ -200,11 +240,11 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
int stations = 0;
- while (stations < 4)
+ while (stations < 3)
{
// Place stations far away from the center
- int x = rng.getInt(0, width - 1);
- int y = rng.getInt(0, height - 1);
+ int x = g_random->getInt(0, width - 1);
+ int y = g_random->getInt(0, height - 1);
float xf = (float)(x - width / 2) / (float)(width / 2);
float yf = (float)(y - height / 2) / (float)(height / 2);
@@ -215,7 +255,10 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
{
tiles[y * width + x] = STATION;
stations++;
- entities.push_back(Entity(E_STATION, (float)x + 0.5f, (float)x + 0.5f));
+ EntityStation* n = new EntityStation();
+ n->set_position((float)x + 0.5f, (float)y + 0.5f);
+ n->init(this, scene);
+ entities.push_back(n);
}
}
@@ -226,14 +269,17 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
while (nests < 15)
{
// Place stations far away from the center
- int x = rng.getInt(0, width - 1);
- int y = rng.getInt(0, height - 1);
+ int x = g_random->getInt(0, width - 1);
+ int y = g_random->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));
+ EntityNest* n = new EntityNest();
+ n->set_position((float)x + 0.5f, (float)y + 0.5f);
+ n->init(this, scene);
+ entities.push_back(n);
}
}
@@ -243,15 +289,18 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
while (bases < 5)
{
// Place stations far away from the center
- int x = rng.getInt(0, width - 1);
- int y = rng.getInt(0, height - 1);
+ int x = g_random->getInt(0, width - 1);
+ int y = g_random->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));
+ EntityBase* n = new EntityBase();
+ n->set_position((float)x + 0.5f, (float)y + 0.5f);
+ n->init(this, scene);
+ entities.push_back(n);
}
}
@@ -259,9 +308,63 @@ FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height),
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));
+ EntityBase* n = new EntityBase();
+ n->set_position((float)(width / 2) + 0.5f, (float)(height / 2) + 0.5f);
+ n->init(this, scene);
+ entities.push_back(n);
+
+ // Spawn some entities across the map
+ if (width > 20)
+ {
+ create_entities(&entities, this, scene);
+ create_entities(&entities, this, scene);
+ create_entities(&entities, this, scene);
+ }
+
+
+ for (FlightEntity* ent : entities)
+ {
+ if (ent->get_type() == E_NEST)
+ {
+ // Spawn a guardian near it
+ int guardians = g_random->getInt(-1, 2);
+ int g = 0;
+ while (g < guardians)
+ {
+ // Place stations far away from the center
+ float ox = g_random->getFloat(-3.0f, 3.0f);
+ float oy = g_random->getInt(-3.0f, 3.0f);
+
+ ox += ent->get_x();
+ oy += ent->get_y();
+
+ if (get_tile((int)floor(ox), (int)floor(oy)) != WALL)
+ {
+ EntityGuardian* ent = new EntityGuardian();
+ ent->init(this, scene);
+ ent->set_position(ox, oy);
+ entities.push_back(ent);
+
+ g++;
+ }
+
+ }
+ }
+ }
+
+ /*
+ REMEMBER TO ADD SOUND!!!
+ EntityBomb* ent = new EntityBomb();
+ ent->init(this, scene);
+ ent->set_position(51.0f, 50.5f);
+ entities.push_back(ent);*/
+
+ /*EntitySerpent* ent = new EntitySerpent();
+ ent->init(this, scene);
+ ent->set_position(51.0f, 50.5f);
+ entities.push_back(ent);
+ */
- // Create all default entities
}
FlightMap::~FlightMap()
diff --git a/src/flight/FlightMap.h b/src/flight/FlightMap.h
index e0272ae..b52ea81 100644
--- a/src/flight/FlightMap.h
+++ b/src/flight/FlightMap.h
@@ -1,6 +1,9 @@
#pragma once
#include
#include
+#include "entity/FlightEntity.h"
+
+class FlightScene;
enum MapTile
{
@@ -12,31 +15,12 @@ enum MapTile
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;
+ std::vector entities;
TCODNoise sub_tile_noise;
@@ -62,9 +46,9 @@ class FlightMap
MapTile get_subtile(float x, float y);
- std::vector& get_entities();
+ std::vector& get_entities();
- FlightMap(int width, int height, size_t seed);
+ FlightMap(int width, int height, size_t seed, FlightScene* scene);
~FlightMap();
};
diff --git a/src/flight/FlightScene.cpp b/src/flight/FlightScene.cpp
index 42271f2..c1c4cac 100644
--- a/src/flight/FlightScene.cpp
+++ b/src/flight/FlightScene.cpp
@@ -1,15 +1,34 @@
#include "FlightScene.h"
-
+#include "entity/Buildings.h"
void FlightScene::update(float dt)
{
+ underwater.setVolume(0.6f);
+
t += dt;
- if (g_key.vk == TCODK_F11)
+ for (int i = 0; i < map.entities.size(); i++)
{
- help_open = !help_open;
+ if (map.entities[i]->is_alive())
+ {
+ map.entities[i]->update(dt);
+ }
+ }
+
+ for (auto it = expl_effects.begin(); it != expl_effects.end();)
+ {
+ it->t -= dt;
+
+ if (it->t < 0.0f)
+ {
+ it = expl_effects.erase(it);
+ }
+ else
+ {
+ it++;
+ }
}
@@ -35,46 +54,58 @@ We are now going to send the location of all our outposts.\n";
sent_start_message = true;
}
- if (g_key.vk == TCODK_KP8)
+ if (g_key.vk == TCODK_KP8 || g_key.c == 'k')
{
vehicle.move_order(N);
}
- if (g_key.vk == TCODK_KP2)
+ if (g_key.vk == TCODK_KP2 || g_key.c == 'j')
{
vehicle.move_order(S);
}
- if (g_key.vk == TCODK_KP6)
+ if (g_key.vk == TCODK_KP6 || g_key.c == 'l')
{
vehicle.move_order(E);
}
- if (g_key.vk == TCODK_KP4)
+ if (g_key.vk == TCODK_KP4 || g_key.c == 'h')
{
vehicle.move_order(W);
}
- if (g_key.vk == TCODK_KP1)
+ if (g_key.vk == TCODK_KP1 || g_key.c == 'b')
{
vehicle.move_order(SW);
}
- if (g_key.vk == TCODK_KP7)
+ if (g_key.vk == TCODK_KP7 || g_key.c == 'y')
{
vehicle.move_order(NW);
}
- if (g_key.vk == TCODK_KP9)
+ if (g_key.vk == TCODK_KP9 || g_key.c == 'u')
{
vehicle.move_order(NE);
}
- if (g_key.vk == TCODK_KP3)
+ if (g_key.vk == TCODK_KP3 || g_key.c == 'n')
{
vehicle.move_order(SE);
}
+ if (g_key.c == 'r')
+ {
+ for (int i = 0; i < vehicle.crew.size(); i++)
+ {
+ vehicle.crew[i].path_to(vehicle.crew[i].cx, vehicle.crew[i].cy, *vehicle.tcod_map);
+
+ if (vehicle.crew[i].gc->is_captain)
+ {
+ vehicle.crew[i].gc->speak("Everybody return to positions!");
+ }
+ }
+ }
if (g_key.vk == TCODK_1)
{
@@ -102,14 +133,68 @@ We are now going to send the location of all our outposts.\n";
}
+ if (g_key.c == 'f')
+ {
+ vehicle.targeting->fire_torpedo();
+ }
+
+ if (g_key.c == 'e')
+ {
+ // Electric shock
+ vehicle.electric_shock();
+ }
+
+ for (FlightEntity* ent : map.entities)
+ {
+ if (ent->get_type() == E_NEST || ent->get_type() == E_STATION)
+ {
+ float dx = vehicle.x - ent->get_x();
+ float dy = vehicle.y - ent->get_y();
+ float dist = sqrt(dx * dx + dy * dy);
+
+ if (dist < 0.2f && !((Building*)ent)->is_explored())
+ {
+ possible_embark = ent;
+ break;
+ }
+ }
+ /*if (ent->get_type() == E_NEST && !((Building*)ent)->is_explored())
+ {
+ possible_embark = ent;
+ }*/
+ }
+ if (possible_embark != nullptr)
+ {
+ if (g_key.c == 'g')
+ {
+ embark_target = possible_embark;
+ ((Building*)possible_embark)->explore();
+ }
+ }
}
void FlightScene::render()
{
- TCODConsole::root->setDefaultBackground(TCODColor(4, 24, 30));
+ float expl_power = 0.0f;
+ for (int i = 0; i < expl_effects.size(); i++)
+ {
+ if (expl_effects[i].t >= 0.0f)
+ {
+ expl_power += expl_effects[i].t / expl_effects[i].dist;
+ }
+ }
+
+ if (expl_power > 1.0f)
+ {
+ expl_power = 1.0f;
+ }
+
+ int expl_poweri = (int)(expl_power * 100.0f);
+
+ TCODConsole::root->setDefaultBackground(TCODColor(4 + expl_poweri, 24 + expl_poweri, 30 + expl_poweri));
TCODConsole::root->clear();
TCODConsole::root->setDefaultBackground(TCODColor::black);
@@ -130,32 +215,101 @@ void FlightScene::render()
// Draw window
vehicle.draw_window(TCODConsole::root);
-
- if (help_open)
+
+ if (possible_embark != nullptr && vehicle.blink)
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor::lightYellow);
+ TCODConsole::root->printf(0, 0, "Embark is possible");
+ TCODConsole::root->printf(0, 1, "Press 'g' to embark!");
+ TCODConsole::root->setDefaultForeground(TCODColor::white);
+ }
+
+}
+
+void FlightScene::explosion(float x, float y, float power)
+{
+ for (int i = 0; i < map.entities.size(); i++)
{
- Drawing::draw_window(TCODConsole::root, 0, 0, 40, HEIGHT - 1, "Help", true);
+ float dx = map.entities[i]->get_x() - x;
+ float dy = map.entities[i]->get_y() - y;
+
+ float dist = sqrt(dx * dx + dy * dy);
- TCODConsole::root->setDefaultForeground(TCODColor::lightGrey);
- TCODConsole::root->printRect(1, 1, 39, HEIGHT - 2, help_str.c_str());
+ if (dist < 0.1f)
+ {
+ dist = 0.1f;
+ }
+
+ float damage = power / dist;
+
+ if (damage > 1.0f && map.entities[i]->is_alive())
+ {
+ map.entities[i]->damage(damage - 0.25f);
+ }
}
+ // Play sound
+ float dx = vehicle.x - x;
+ float dy = vehicle.y - y;
+ float dist = sqrt(dx * dx + dy * dy);
+ if (dist < 0.15f)
+ {
+ dist = 0.15f;
+ }
+ if (dist >= 1.5f)
+ {
+ g_soloud->play(explo_far_dist, 2.0f);
+ }
+ else if (dist >= 0.6f)
+ {
+ g_soloud->play(explo_med_dist, 2.0f);
+ }
+ else
+ {
+ g_soloud->play(explo_near_dist, 1.0f);
+ if (dist <= 0.4f)
+ {
+ g_soloud->play(explo_hit, 2.5f);
+ // Damage the vehicle
+ vehicle.damage(power * 25.0f / (dist / 0.15f));
+ }
+ }
+
+ ExplosionEffect fx;
+ fx.x = x;
+ fx.y = y;
+ fx.t = power * 2.0f;
+ fx.dist = dist;
+ expl_effects.push_back(fx);
+}
+
+void FlightScene::shut_up()
+{
+ underwater.setVolume(0.0f);
}
-FlightScene::FlightScene() : map(map_size, map_size, 11234), vehicle(&map)
+FlightScene::FlightScene() : map(map_size, map_size, 11234, this), vehicle(&map)
{
+ embark_target = nullptr;
+
underwater.load("underwater.wav");
underwater.setLooping(true);
+ explo_far_dist.load("explo_far_dist.wav");
+ explo_med_dist.load("explo_med_dist.wav");
+ explo_near_dist.load("explo_near_dist.wav");
+ explo_hit.load("collide.wav");
+
this->soloud = g_soloud;
this->status = g_status;
soloud->play(underwater);
- help_str = Help::help_main;
+ vehicle.scene = this;
}
diff --git a/src/flight/FlightScene.h b/src/flight/FlightScene.h
index 3e1055d..3dbba68 100644
--- a/src/flight/FlightScene.h
+++ b/src/flight/FlightScene.h
@@ -10,13 +10,20 @@
#include "../Help.h"
#include "../vehicle/Vehicle.h"
-constexpr int map_size = 500;
+constexpr int map_size = 100;
+struct ExplosionEffect
+{
+ float x, y, t, dist;
+};
class FlightScene
{
public:
+ FlightEntity* embark_target;
+ FlightEntity* possible_embark;
+
float t = 0.0f;
bool sent_start_message = false;
@@ -24,20 +31,27 @@ class FlightScene
SoLoud::Soloud* soloud;
SoLoud::Wav underwater;
+ SoLoud::Wav explo_near_dist;
+ SoLoud::Wav explo_far_dist;
+ SoLoud::Wav explo_med_dist;
+ SoLoud::Wav explo_hit;
FlightMap map;
Vehicle vehicle;
bool help_open;
- std::string help_str;
+ std::vector expl_effects;
Status* status;
void update(float dt);
void render();
+ void explosion(float x, float y, float power);
+
+ void shut_up();
FlightScene();
~FlightScene();
diff --git a/src/flight/Gamemaster.cpp b/src/flight/Gamemaster.cpp
index a2e033f..f64544b 100644
--- a/src/flight/Gamemaster.cpp
+++ b/src/flight/Gamemaster.cpp
@@ -1,15 +1,261 @@
#include "Gamemaster.h"
+#include "FlightScene.h"
+#include "../disembark/EmbarkScene.h"
+#include "entity/Buildings.h"
+void Gamemaster::update(float dt)
+{
+ if (!gameover)
+ {
+ if (!embarked)
+ {
+ flight_scene->update(dt);
+ }
+ else
+ {
+ embark_scene->update(dt);
+ }
+ }
+ if (embarked)
+ {
+ flight_scene->shut_up();
-void Gamemaster::update(float dt)
+ if (embark_scene->finished)
+ {
+ if (g_master->clear_nest_count == 2 && g_master->clear_station_count == 0)
+ {
+ std::string coords = "";
+ for (FlightEntity* ent : g_master->flight_scene->map.entities)
+ {
+ if (ent->get_type() == E_STATION && !((Building*)ent)->is_explored())
+ {
+ coords += std::to_string((int)floor(ent->get_x()));
+ coords += ", ";
+ coords += std::to_string((int)floor(ent->get_x()));
+ break;
+ }
+ }
+
+ flight_scene->vehicle.radio->push_message("\
+Greetings from the fatherland. We have received information from other submarines about strange and massive\
+ structures. You may have already seen some of them. They look like a tower on the sonar.\n\n\
+One of these has been reported on coordinates: " + coords + "\n\n\
+You should continue clearing nests, but it's of our interest to get more information about these structures.\n\
+We don't know what dangers may lie there, be careful, commander.");
+
+ }
+ else if (g_master->clear_nest_count == 3)
+ {
+ flight_scene->vehicle.radio->push_message("\
+Greetings from the fatherland. Our leader has given orders to all submarines to explore these huge structures.\n\
+Keep your vehicle focused on this task.\n\
+Proceed with caution, commander.");
+ }
+
+ embarked = false;
+ int alive = 0;
+ bool reassign_captain = false;
+ std::vector dead;
+ // Handle crew changes (crew could have died)
+ for (EmbarkCrew& c : embark_scene->crew)
+ {
+ if (c.health <= 0.0f)
+ {
+ // Remove from vehicle
+ auto& vcrew = flight_scene->vehicle.crew;
+ dead.push_back(c.gc->name);
+ for (auto it = vcrew.begin(); it != vcrew.end(); )
+ {
+ if (it->gc == c.gc)
+ {
+ if (it->gc->is_captain)
+ {
+ reassign_captain = true;
+ }
+ it = vcrew.erase(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+
+ // Remove from crew
+ for (auto it = crew.begin(); it != crew.end(); )
+ {
+ if (*it == c.gc)
+ {
+ it = crew.erase(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+
+ }
+ else
+ {
+ alive++;
+ }
+ }
+
+ if (alive > 0)
+ {
+
+ std::string memory;
+
+ if (dead.size() == 0)
+ {
+ memory = "Thanksfully, all the expedition crew survived.";
+ }
+ else
+ {
+ std::string name = "expeditioners were";
+ if (dead.size() == 1)
+ {
+ name = "expeditioner was";
+ }
+
+ memory = "Sadly, " + std::to_string(dead.size()) + " " + name + " lost. May their names be remembered: \n\n";
+ for (int i = 0; i < dead.size(); i++)
+ {
+ memory += dead[i];
+ memory += '\n';
+ }
+ }
+
+ g_popup->show("The crew made its way back into the\
+ submarine through the hole they had originally blown up.\n\n" + memory);
+
+
+ }
+ else
+ {
+ g_popup->show("No response was received from the expedition crew, vitals were lost. May they rest in peace.");
+ }
+
+ if (flight_scene->vehicle.crew.size() == 0)
+ {
+ gameover = true;
+ }
+ else
+ {
+ if (reassign_captain)
+ {
+ flight_scene->vehicle.crew[0].gc->is_captain = true;
+ }
+ }
+
+
+ }
+ }
+ else
+ {
+ if (flight_scene->embark_target != nullptr)
+ {
+ if (flight_scene->embark_target->get_type() == E_NEST)
+ {
+ g_popup->show("Four of the crewmembers left the vehicle in a tiny auxiliary submarine, carrying\
+ a few hundred kilograms of explosives to breach a hole. They set the bomb and detonated it, opening a reasonably\
+ sized hole in the fleshy wall of the nest. The crew quickly inserted a clamp as the hole was closing rapidly.\n\
+As the flesh around the hole healed, they prepared to jump inside.\n\n\
+The explosion has been intense, the aliens inside are probably not too happy!");
+
+ g_master->clear_nest_count++;
+ }
+ else if (flight_scene->embark_target->get_type() == E_STATION)
+ {
+ if (g_master->clear_station_count < 2)
+ {
+ g_popup->show("Four of the crewmembers left the vehicle in a tiny auxiliary submarine, and approached\
+ what seemed to be the entrance of the huge structure. It spanned a few kilometers vertically, reaching the\
+ topmost ice crust, and touching the rock layer below the ocean.\n\
+Upon reaching the entrance, they realized that it was clogged up, so they set a small\
+ breaching charge and blew a small hole. The interior of the structure, unlike its exterior, was perfectly clean.\
+ The walls were covered in instruments and lights, which illuminated the room with a faint blue colour.\n\n\
+The explosion was probably heard around, so prepare for action!");
+
+ }
+ else
+ {
+ g_popup->show("Four of the crewmembers left the vehicle in a tiny auxiliary submarine, and approached\
+ what seemed to be the entrance of the huge structure. It spanned a few kilometers vertically, reaching the\
+ topmost ice crust, and touching the rock layer below the ocean.\n\n\
+Unlike the previous stations they had explored, this one was way cleaner, runes engraved in the rock were clearly visible.\n\
+Upon reaching the entrance, they realized that it was clogged up, so they set a small\
+ breaching charge and blew a small hole. The interior of the structure, unlike its exterior, was perfectly clean.\
+ The walls were covered in instruments and lights, which illuminated the room with a faint blue colour.\n\n\
+The explosion was probably heard around, so prepare for action!");
+ }
+
+
+ g_master->clear_station_count++;
+ std::cout << "c = " << g_master->clear_station_count << std::endl;
+ }
+
+ embark_scene->restart();
+ Building* as_building = (Building*)flight_scene->embark_target;
+
+ embark_scene->map = &as_building->get_bmap();
+
+ // Obtain 4 expedition crew
+ int cnum = 1;
+ EmbarkCrew captain = EmbarkCrew();
+ captain.gc = flight_scene->vehicle.get_captain()->gc;
+ embark_scene->crew.push_back(captain);
+ for (int i = 0; i < crew.size(); i++)
+ {
+ bool found = false;
+
+ for (int j = 0; j < embark_scene->crew.size(); j++)
+ {
+ if (crew[i] == embark_scene->crew[j].gc)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+
+ cnum++;
+ if (cnum > 4)
+ {
+ break;
+ }
+
+ EmbarkCrew ec = EmbarkCrew();
+ ec.gc = crew[i];
+ embark_scene->crew.push_back(ec);
+ }
+ }
+
+ embarked = true;
+ flight_scene->embark_target = nullptr;
+
+ embark_scene->update(0.0f);
+ }
+ }
+}
+
+void Gamemaster::init()
{
+ // Load crew from the vehicle
+ for (int i = 0; i < flight_scene->vehicle.crew.size(); i++)
+ {
+ crew.push_back(flight_scene->vehicle.crew[i].gc);
+ }
}
-Gamemaster::Gamemaster(Vehicle* veh)
+Gamemaster::Gamemaster()
{
- vehicle = veh;
+ seen_robot = false;
+ seen_grunt = false;
+ clear_station_count = 0;
+ clear_nest_count = 0;
}
Gamemaster::~Gamemaster()
diff --git a/src/flight/Gamemaster.h b/src/flight/Gamemaster.h
index f6b77e8..4c5741a 100644
--- a/src/flight/Gamemaster.h
+++ b/src/flight/Gamemaster.h
@@ -1,5 +1,6 @@
#pragma once
#include "../vehicle/Vehicle.h"
+#include "../Popup.h"
// The gamemaster spawns enemies and generates orders from high command, driving the lore forward
// Lore:
@@ -13,21 +14,40 @@
// High command will show opposition to this, eventually turning you against the other
// submarines
+
+class FlightScene;
+class EmbarkScene;
+
class Gamemaster
{
private:
- Vehicle* vehicle;
-
public:
+ bool gameover = false;
+ bool gamewin = false;
+
+ std::vector crew;
+
+ bool embarked;
+
+ FlightScene* flight_scene;
+ EmbarkScene* embark_scene;
+
+ bool seen_robot;
+ bool seen_grunt;
+
+ // The ancient is never seen
+
// Activates once we discover intelligent life
bool is_fatherland_enemy = false;
int clear_station_count = 0;
+ int clear_nest_count = 0;
void update(float dt);
+ void init();
- Gamemaster(Vehicle* veh);
+ Gamemaster();
~Gamemaster();
};
diff --git a/src/flight/entity/Buildings.cpp b/src/flight/entity/Buildings.cpp
new file mode 100644
index 0000000..f14ada7
--- /dev/null
+++ b/src/flight/entity/Buildings.cpp
@@ -0,0 +1,60 @@
+#include "Buildings.h"
+#include "../FlightScene.h"
+
+void EntityNest::update(float dt)
+{
+ timer -= dt;
+ float px = get_scene()->vehicle.x;
+ float py = get_scene()->vehicle.y;
+
+ float dx = px - get_x();
+ float dy = py - get_y();
+
+ float dist = sqrt(dx * dx + dy * dy);
+ float thresold = 1.0f + get_scene()->vehicle.noise * 5.0f;
+
+ if (dist <= thresold)
+ {
+ // Send attackers
+ if (attackers > 0 && timer <= 0.0f && g_random->getFloat(0.0f, 1.0f) >= 0.98f)
+ {
+ attackers--;
+
+ if (g_random->getFloat(0.0f, 1.0f) >= 0.5f)
+ {
+ // Biter
+ }
+ else
+ {
+ if (g_random->getFloat(0.0f, 1.0f) > 0.5f)
+ {
+ // Bomb
+ }
+ else
+ {
+ if (g_random->getFloat(0.0f, 1.0f) > 0.5f)
+ {
+ // Crawler
+ }
+ else
+ {
+ // Worm
+ }
+ }
+ }
+
+ timer = 5.0f;
+
+ }
+ }
+}
+
+EntityType EntityNest::get_type()
+{
+ return E_NEST;
+}
+
+int EntityNest::get_symbol()
+{
+ return 15;
+}
diff --git a/src/flight/entity/Buildings.h b/src/flight/entity/Buildings.h
new file mode 100644
index 0000000..3d90e7c
--- /dev/null
+++ b/src/flight/entity/Buildings.h
@@ -0,0 +1,104 @@
+#pragma once
+
+#include "FlightEntity.h"
+#include "../../disembark/BuildingMap.h"
+
+
+class Building : public FlightEntity
+{
+public:
+
+ BuildingMap bmap;
+
+
+ bool explored;
+
+ bool is_explored()
+ {
+ return explored;
+ }
+
+ void explore()
+ {
+ explored = true;
+ }
+
+ BuildingMap& get_bmap()
+ {
+ return bmap;
+ }
+
+ Building(int x, int y, bool organic = false) : bmap(x, y, organic)
+ {
+ explored = false;
+ }
+};
+
+// A building does very little
+class EntityNest : public Building
+{
+public:
+
+ int attackers = 0;
+ float timer;
+
+ virtual void update(float dt) override;
+
+ virtual EntityType get_type() override;
+
+ virtual int get_symbol() override;
+
+
+ EntityNest() : Building(80, 50, true)
+ {
+ attackers = g_random->getInt(1, 4);
+ timer = 0.0f;
+ }
+};
+
+class EntityStation : public Building
+{
+public:
+ virtual void update(float dt) override
+ {
+
+ }
+
+ virtual EntityType get_type() override
+ {
+ return E_STATION;
+ }
+
+ virtual int get_symbol() override
+ {
+ return 234;
+ }
+
+ EntityStation() : Building(80, 50)
+ {
+
+ }
+
+};
+
+class EntityBase : public FlightEntity
+{
+public:
+ virtual void update(float dt) override
+ {
+
+ }
+
+ virtual EntityType get_type() override
+ {
+ return E_BASE;
+ }
+
+ virtual int get_symbol() override
+ {
+ return 127;
+ }
+
+
+};
+
diff --git a/src/flight/entity/EntityTorpedo.cpp b/src/flight/entity/EntityTorpedo.cpp
new file mode 100644
index 0000000..f252ba0
--- /dev/null
+++ b/src/flight/entity/EntityTorpedo.cpp
@@ -0,0 +1,124 @@
+#include "EntityTorpedo.h"
+#include "../FlightScene.h"
+
+void EntityTorpedo::damage(float power)
+{
+ if (is_alive())
+ {
+ alive = false;
+
+ // Explosion, damage all nearby entities and play sound
+ // depending on player distance (we use explosion function)
+ get_scene()->explosion(get_x(), get_y(), 0.5f);
+ }
+}
+
+EntityTorpedo::EntityTorpedo(FlightEntity* target)
+{
+ this->target = target;
+ alive = true;
+}
+
+EntityTorpedo::~EntityTorpedo()
+{
+}
+
+
+inline float wrap_angle(float angle)
+{
+ float two_pi = 2.0f * PI;
+ return angle - two_pi * floor(angle / two_pi);
+}
+
+void EntityTorpedo::update(float dt)
+{
+ lived += dt;
+ safety -= dt;
+
+ float tx, ty;
+ if(target == nullptr)
+ {
+ tx = get_scene()->vehicle.x;
+ ty = get_scene()->vehicle.y;
+ }
+ else
+ {
+ tx = target->get_x();
+ ty = target->get_y();
+ }
+
+ float x = get_x();
+ float y = get_y();
+
+ float dx = tx - x;
+ float dy = ty - y;
+
+ float heading_wanted = atan2(dy, dx) + PI / 2.0f;
+
+ float real_wanted = wrap_angle(heading_wanted);
+ float real_vehicle = wrap_angle(heading);
+
+ float dir = 1.0f;
+ float diff = real_wanted - real_vehicle;
+
+ if (fabs(diff) <= 0.005)
+ {
+ heading = heading_wanted;
+ }
+ else
+ {
+ if (fabs(diff) > PI)
+ {
+ dir = -1.0f;
+ }
+
+ if (diff < 0)
+ {
+ dir = -dir;
+ }
+
+
+ heading = real_vehicle + dir * dt * maneouver;
+ }
+
+ if (lived >= lifetime)
+ {
+ maneouver = 0.0f;
+ }
+
+ x += sin(heading) * dt * velocity;
+ y -= cos(heading) * dt * velocity;
+
+ set_position(x, y);
+
+ float dist = sqrt(dx * dx + dy * dy);
+
+ float thresold = 0.1f;
+ if (abs(diff) > 0.3f)
+ {
+ thresold = 0.2f;
+ }
+
+ if (dist <= thresold && safety <= 0.0f && g_random->getFloat(0.0f, 1.0f) >= 0.8f)
+ {
+ damage(1.0f);
+ }
+
+ // Check collisions against walls
+ if (get_map()->get_subtile(get_x(), get_y()) == WALL)
+ {
+ damage(1.0f);
+ }
+
+
+}
+
+EntityType EntityTorpedo::get_type()
+{
+ return E_TORPEDO;
+}
+
+int EntityTorpedo::get_symbol()
+{
+ return 94;
+}
diff --git a/src/flight/entity/EntityTorpedo.h b/src/flight/entity/EntityTorpedo.h
new file mode 100644
index 0000000..bd110e7
--- /dev/null
+++ b/src/flight/entity/EntityTorpedo.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "FlightEntity.h"
+
+// If no entity is given (nullptr)
+// the torpedo will go towards the player
+class EntityTorpedo : public FlightEntity
+{
+private:
+
+ FlightEntity* target;
+
+public:
+
+ float heading;
+ float velocity;
+ float maneouver;
+ float safety;
+ float lifetime;
+
+ float lived;
+
+ bool alive;
+
+ virtual bool is_alive()
+ {
+ return alive;
+ }
+
+
+ virtual void damage(float power);
+
+
+ EntityTorpedo()
+ {
+ target = nullptr;
+ alive = true;
+ }
+
+ EntityTorpedo(FlightEntity* target);
+ ~EntityTorpedo();
+
+ // Inherited via FlightEntity
+ virtual void update(float dt) override;
+ virtual EntityType get_type() override;
+ virtual int get_symbol() override;
+};
+
diff --git a/src/flight/entity/FlightEntity.h b/src/flight/entity/FlightEntity.h
new file mode 100644
index 0000000..9259a3a
--- /dev/null
+++ b/src/flight/entity/FlightEntity.h
@@ -0,0 +1,147 @@
+#pragma once
+
+#include "../../defines.h"
+#include
+
+class FlightMap;
+class FlightScene;
+
+enum EntityType
+{
+ E_NEST,
+ E_STATION,
+ E_BASE,
+
+ // Monsters
+
+ // The crawler is weak but moves pretty fast
+ // It can only live in high oxygen tiles, and cannot
+ // move out of them
+ // Bad ears
+ E_CRAWLER,
+ // Opposite of the crawler, strong as hell but slow
+ // Lives anywhere and has good ears. Your usual enemy
+ E_WORM,
+
+ // Medium speed, medium strength. Usually stays near nests
+ // Spawns bombs and biters
+ E_GUARDIAN,
+
+ // Slow and very strong
+ // Can spawn bombs, biters and crawlers
+ // Very very rare
+ E_SERPENT,
+ // Fast moving, very weak. Explodes once near the submarine or destroyed
+ E_BOMB,
+ // Fast moving, very weak. Gets close to the submarine, and tries to cause
+ // damage
+ E_BITER,
+ // Allied (or not) submarines. Spawn at bases and move to random points in the map
+ E_SUBMARINE,
+
+ // Torpedoes must be given a target by their creator, which they will try and hit
+ // then they will explode
+ E_TORPEDO,
+
+
+};
+
+class FlightEntity
+{
+private:
+
+ FlightMap* map;
+ FlightScene* scene;
+
+ float x, y;
+ bool visible;
+
+ bool identified;
+
+public:
+
+
+
+ virtual void update(float dt) = 0;
+ virtual EntityType get_type() = 0;
+ virtual int get_symbol() = 0;
+
+ virtual bool is_alive()
+ {
+ return true;
+ }
+
+ // By default is does nothing
+ virtual void damage(float power)
+ {
+
+ }
+
+ std::pair get_position()
+ {
+ return std::make_pair(x, y);
+ }
+
+ void set_position(float x, float y)
+ {
+ this->x = x;
+ this->y = y;
+ }
+
+ float get_x()
+ {
+ return this->x;
+ }
+
+ float get_y()
+ {
+ return this->y;
+ }
+
+ FlightMap* get_map()
+ {
+ return map;
+ }
+
+ FlightScene* get_scene()
+ {
+ return scene;
+ }
+
+ void init(FlightMap* map, FlightScene* scene)
+ {
+ this->map = map;
+ this->scene = scene;
+ }
+
+ bool is_visible()
+ {
+ return visible;
+ }
+
+ void set_visible(bool v)
+ {
+ visible = v;
+
+ if (v == false)
+ {
+ identified = false;
+ }
+ }
+
+ void identify()
+ {
+ identified = true;
+ }
+
+ bool is_identified()
+ {
+ return identified;
+ }
+
+ virtual std::string get_name()
+ {
+ return "Forgot name";
+ }
+
+};
\ No newline at end of file
diff --git a/src/flight/entity/Monsters.cpp b/src/flight/entity/Monsters.cpp
new file mode 100644
index 0000000..72762e3
--- /dev/null
+++ b/src/flight/entity/Monsters.cpp
@@ -0,0 +1,495 @@
+#include "Monsters.h"
+#include "../FlightScene.h"
+#include "../FlightMap.h"
+
+
+
+void EntityCrawler::update(float dt)
+{
+ Monster::update(dt);
+
+ if (c_x == -1)
+ {
+ c_x = (int)floor(get_x());
+ c_y = (int)floor(get_x());
+ }
+
+ speed_penalty -= dt * get_speed_penalty_rate();
+ if (speed_penalty < 0.0f)
+ {
+ speed_penalty = 0.0f;
+ }
+
+ if (persecuting)
+ {
+ c_x = (int)floor(get_x());
+ c_y = (int)floor(get_x());
+
+ auto player = get_player_pos();
+ go_to(player.first, player.second);
+
+ velocity = max(0.25f - speed_penalty * 10.0f, 0.0f);
+
+ if (distance_to_player() <= 0.05f && speed_penalty == 0.0f)
+ {
+ // Attack and slowdown
+ g_soloud->play(attack, 2.0f);
+
+ do_damage();
+
+ speed_penalty = 1.0f;
+ }
+ }
+ else
+ {
+ velocity = 0.1f;
+
+ // Move idly, if we hear the player we have a chance to attack
+ if (in_destination())
+ {
+ idle(5, c_x, c_y);
+ }
+
+ if (distance_to_player() <= 1.0f + get_player_noise() * 4.0f && g_random->getFloat(0.0f, 1.0f) >= 0.98f
+ || autofind())
+ {
+ persecuting = true;
+ g_soloud->play(persecute, 0.6f);
+ }
+ }
+}
+
+void EntityCrawler::do_damage()
+{
+ if (g_random->getFloat(0.0f, 1.0f) >= 0.6f)
+ {
+ // Damage
+ g_soloud->play(impact, 2.0f);
+ get_player()->damage(g_random->getFloat(1.3f, 2.0f));
+ }
+}
+
+
+void EntityWorm::update(float dt)
+{
+ Monster::update(dt);
+
+ auto player = get_player_pos();
+
+ if (dive)
+ {
+ velocity = 0.5f;
+ go_to(player.first, player.second);
+
+ if (distance_to_player() <= 0.15f)
+ {
+ dive = false;
+ g_soloud->play(attack_sound, 2.0f);
+ get_player()->damage(g_random->getFloat(1.8f, 3.2f));
+
+ }
+ }
+ else
+ {
+ if (persecuting)
+ {
+ // Worms are free to go through walls!
+ rand_timer -= dt;
+ if (rand_timer <= 0.0f)
+ {
+ float offset_x = g_random->getFloat(-1.8f, 1.8f);
+ float offset_y = g_random->getFloat(-1.8f, 1.8f);
+ move_to(player.first + offset_x, player.second + offset_y);
+ rand_timer = 3.0f;
+ }
+
+
+
+
+ velocity = 0.1f;
+
+ if (distance_to_player() <= 1.3f && distance_to_player() >= 0.6f
+ && g_random->getFloat(0.0f, 1.0f) >= 0.994f)
+ {
+ dive = true;
+ g_soloud->play(dive_sound, 2.0f);
+ }
+ }
+ else
+ {
+ velocity = 0.1f;
+
+ // Move idly, if we hear the player we have a chance to attack
+ if (in_destination())
+ {
+ idle(5, (int)get_x(), (int)get_y());
+ }
+
+ if (distance_to_player() <= 1.0f + get_player_noise() * 7.0f && g_random->getFloat(0.0f, 1.0f) >= 0.95f)
+ {
+ persecuting = true;
+ //g_soloud->play(persecute, 0.6f);
+ }
+ }
+ }
+}
+
+
+
+void Monster::path_to(int x, int y)
+{
+ if (current_path != nullptr)
+ {
+ int dx, dy;
+ current_path->getDestination(&dx, &dy);
+
+ if (dx == x && dy == y)
+ {
+ return;
+ }
+ }
+
+ TCODPath* n_path = new TCODPath(&get_map()->vmap, 0.0f);
+
+ auto t = get_map_position();
+
+ if (n_path->compute(t.first, t.second, x, y))
+ {
+ current_path = n_path;
+ just_started = true;
+ }
+ else
+ {
+ x_target = -1.0f;
+ y_target = -1.0f;
+ delete n_path;
+ }
+
+
+}
+
+
+void Monster::update(float dt)
+{
+ // Update basic movement
+ if (!in_destination())
+ {
+ float x = get_x();
+ float y = get_y();
+
+ float dx = x_target - x;
+ float dy = y_target - y;
+ float l = sqrt(dx * dx + dy * dy);
+ float xn = dx / l;
+ float yn = dy / l;
+
+ if (l <= get_speed() * 0.01f)
+ {
+ set_position(x_target, y_target);
+ }
+ else
+ {
+ set_position(x + xn * get_speed() * dt, y + yn * get_speed() * dt);
+ }
+ }
+
+
+ if (current_path != nullptr)
+ {
+ auto t = get_map_position();
+
+ if (just_started)
+ {
+ if (std::abs(get_x() - t.first - 0.5f) <= 0.2f &&
+ std::abs(get_y() - t.second - 0.5f) <= 0.2f)
+ {
+ // We are sufficiently close to the center of the tile
+ just_started = false;
+ step();
+ }
+ else
+ {
+ // Move to center of tile
+ move_to((float)t.first + 0.5f, (float)t.second + 0.5f);
+
+ if (in_destination())
+ {
+ just_started = false;
+ step();
+ }
+ }
+ }
+ else
+ {
+ // Move to center of next tile
+ if (in_destination())
+ {
+ step();
+ }
+ }
+
+ }
+}
+
+std::pair Monster::get_map_position()
+{
+ std::pair pos = get_position();
+
+ return std::make_pair((int)floor(pos.first), (int)floor(pos.second));
+}
+
+void Monster::step()
+{
+ if (current_path != nullptr)
+ {
+ int nx, ny;
+ if (!current_path->walk(&nx, &ny, true))
+ {
+ delete current_path;
+ current_path = nullptr;
+ return;
+ }
+
+ move_to((float)nx + 0.5f, (float)ny + 0.5f);
+ }
+}
+
+void Monster::move_to(float x, float y)
+{
+ x_target = x;
+ y_target = y;
+}
+
+bool Monster::in_destination()
+{
+ if (x_target == -1.0f)
+ {
+ return true;
+ }
+ else
+ {
+ return x_target == get_x() && y_target == get_y();
+ }
+}
+
+float Monster::go_to(float px, float py)
+{
+ float x = get_x();
+ float y = get_y();
+
+ if (floor(px) == floor(x) && floor(py) == floor(y))
+ {
+ // Same tile, simple path
+ move_to(px, py);
+ }
+ else
+ {
+ // Other tile, path
+ path_to((int)floor(px), (int)floor(py));
+ }
+
+ float dx = px - x;
+ float dy = py - y;
+ return sqrt(dx * dx + dy * dy);
+}
+
+void Monster::idle(int radius, int cx, int cy)
+{
+ int t_x = g_random->getInt(cx - radius, cx + radius);
+ int t_y = g_random->getInt(cy - radius, cy + radius);
+
+ path_to(t_x, t_y);
+}
+
+float Monster::distance_to(float x, float y)
+{
+ float dx = get_x() - x;
+ float dy = get_y() - y;
+ return sqrt(dx * dx + dy * dy);
+}
+
+float Monster::distance_to_player()
+{
+ float px = get_scene()->vehicle.x;
+ float py = get_scene()->vehicle.y;
+ return distance_to(px, py);
+}
+
+std::pair Monster::get_player_pos()
+{
+ return std::make_pair(get_scene()->vehicle.x, get_scene()->vehicle.y);
+}
+
+float Monster::get_player_noise()
+{
+ return get_scene()->vehicle.noise;
+}
+
+Vehicle* Monster::get_player()
+{
+ return &get_scene()->vehicle;
+}
+
+void EntityBiter::do_damage()
+{
+ if (g_random->getFloat(0.0f, 1.0f) >= 0.3f)
+ {
+ // Damage
+ g_soloud->play(impact, 1.6f);
+ get_player()->damage(1.2f);
+ }
+}
+
+void EntityBomb::do_damage()
+{
+ damage(1.0f);
+ get_scene()->explosion(get_x(), get_y(), 0.1f);
+}
+
+void EntityGuardian::update(float dt)
+{
+ Monster::update(dt);
+
+ velocity = 0.02f;
+
+ if (c_x == -1.0f)
+ {
+ c_x = get_x();
+ c_y = get_y();
+ }
+
+ float r = 3.0f;
+
+ move_timer -= dt;
+ if (move_timer <= 0.0f)
+ {
+ float ox = g_random->getFloat(-r, r);
+ float oy = g_random->getFloat(-r, r);
+ move_to(c_x + ox, c_y + oy);
+ move_timer = 10.0f;
+ }
+
+ // Difference to base
+ float dxb = get_player()->x - c_x;
+ float dyb = get_player()->y - c_y;
+
+ float base_dist = sqrt(dxb * dxb + dyb * dyb);
+
+ if (base_dist <= r)
+ {
+ // Send hell
+ spawn_timer -= dt;
+
+ if (spawn_timer <= 0.0f)
+ {
+ FlightEntity* ent;
+ if (g_random->getFloat(0.0f, 1.0f) >= 0.45f)
+ {
+ // Biter
+ ent = new EntityBiter();
+ }
+ else
+ {
+ // Bomb
+ ent = new EntityBomb();
+ }
+
+ ent->set_position(get_x(), get_y());
+ ent->init(get_map(), get_scene());
+ get_map()->entities.push_back(ent);
+
+ spawn_timer = g_random->getFloat(10.0f, 35.0f);
+ }
+ }
+}
+
+void EntitySerpent::update(float dt)
+{
+ Monster::update(dt);
+
+ engage_timer -= dt;
+
+ if (engaging)
+ {
+ velocity = 0.25f;
+
+ fight_timer -= dt;
+ if (fight_timer < 0.0f)
+ {
+ float px = get_player()->x;
+ float py = get_player()->y;
+ float ox = g_random->getFloat(-1.7f, 1.7f);
+ float oy = g_random->getFloat(-1.7f, 1.7f);
+
+ if (std::abs(ox) < 0.4f)
+ {
+ ox = ox > 0.0f ? 0.35f : -0.4f;
+ }
+
+ if (std::abs(oy) < 0.4f)
+ {
+ oy = oy > 0.0f ? 0.4f : -0.4f;
+ }
+
+ move_to(px + ox, py + oy);
+ fight_timer = 4.0f;
+ }
+
+ spawn_timer -= dt;
+ if (spawn_timer < 0.0f)
+ {
+ FlightEntity* ent;
+ if (g_random->getFloat(0.0f, 1.0f) >= 0.5f)
+ {
+ if (g_random->getFloat(0.0f, 1.0f) >= 0.5f)
+ {
+ // Biter
+ ent = new EntityBiter();
+ }
+ else
+ {
+ // Bomb
+ ent = new EntityBomb();
+ }
+
+ }
+ else
+ {
+ // Crawler
+ ent = new EntityCrawler();
+ }
+
+ ent->set_position(get_x(), get_y());
+ ent->init(get_map(), get_scene());
+ get_map()->entities.push_back(ent);
+
+ spawn_timer = g_random->getFloat(5.0f, 30.0f);
+ }
+
+ if (engage_timer < 0.0f)
+ {
+ engaging = false;
+ }
+ }
+ else
+ {
+ velocity = 0.15f;
+
+ // Move idly, if we hear the player we have a chance to attack
+ if (in_destination())
+ {
+ idle(5, (int)get_x(), (int)get_y());
+ }
+
+ if (engage_timer <= -60.0f)
+ {
+ if (distance_to_player() <= 1.0f + get_player_noise() * 7.0f && g_random->getFloat(0.0f, 1.0f) >= 0.95f)
+ {
+ engaging = true;
+ //g_soloud->play(persecute, 0.6f);
+ engage_timer = 60.0f;
+ }
+ }
+
+ }
+}
diff --git a/src/flight/entity/Monsters.h b/src/flight/entity/Monsters.h
new file mode 100644
index 0000000..8f0543a
--- /dev/null
+++ b/src/flight/entity/Monsters.h
@@ -0,0 +1,365 @@
+#pragma once
+
+#include "FlightEntity.h"
+#include
+
+class Vehicle;
+
+// Implements basic stuff for the others
+class Monster : public FlightEntity
+{
+public:
+
+ float health = 1.0f;
+
+ TCODPath* current_path;
+
+ bool just_started;
+
+ float x_target, y_target;
+
+ // Map coordinates, moves through the center
+ // of tiles and never diagonally to avoid going into walls
+ void path_to(int x, int y);
+
+ virtual void update(float dt) override;
+
+ virtual EntityType get_type() = 0;
+
+ virtual int get_symbol() = 0;
+
+ std::pair get_map_position();
+
+ virtual float get_speed() = 0;
+
+ void step();
+
+ // Use only within a tile, it does no pathing and
+ // will happily go through walls!
+ void move_to(float x, float y);
+ bool in_destination();
+
+ // Returns distance to point
+ float go_to(float x, float y);
+
+ // Moves randomly in points within radius of a given point
+ void idle(int radius, int cx, int cy);
+
+ float distance_to(float x, float y);
+
+ float distance_to_player();
+
+ std::pair get_player_pos();
+
+ float get_player_noise();
+
+
+ Vehicle* get_player();
+
+ Monster()
+ {
+ current_path = nullptr;
+ x_target = -1.0f;
+ y_target = -1.0f;
+ }
+
+
+ virtual bool is_alive() override
+ {
+ return health > 0.0f;
+ }
+
+ virtual void damage(float power) override
+ {
+ health -= power;
+ }
+
+};
+
+
+class EntityCrawler : public Monster
+{
+public:
+
+ SoLoud::Wav attack;
+ SoLoud::Wav impact;
+ SoLoud::Wav persecute;
+
+ float velocity;
+
+ bool persecuting;
+
+ float speed_penalty;
+
+ int c_x = -1, c_y = -1;
+
+ virtual void update(float dt) override;
+
+ virtual float get_speed_penalty_rate()
+ {
+ return 0.1f;
+ }
+
+ virtual bool autofind()
+ {
+ return false;
+ }
+
+ virtual void do_damage();
+
+
+ virtual std::string get_name() override
+ {
+ return "Crawler";
+ }
+
+ virtual EntityType get_type() override
+ {
+ return E_CRAWLER;
+ }
+
+ virtual int get_symbol() override
+ {
+ return 157;
+ }
+
+ virtual float get_speed() override
+ {
+ return velocity;
+ }
+
+ EntityCrawler()
+ {
+ persecuting = false;
+ speed_penalty = 0.0f;
+ attack.load("crawler/attack.wav");
+ impact.load("collide2.wav");
+ persecute.load("crawler/idle.wav");
+ health = 1.5f;
+ };
+
+};
+
+class EntityBiter : public EntityCrawler
+{
+public:
+
+ virtual std::string get_name() override
+ {
+ return "Biter";
+ }
+
+ virtual EntityType get_type() override
+ {
+ return E_BITER;
+ }
+
+ virtual int get_symbol() override
+ {
+ return 224;
+ }
+
+ virtual float get_speed_penalty_rate()
+ {
+ return 0.05f;
+ }
+
+ virtual bool autofind()
+ {
+ return true;
+ }
+
+ virtual void do_damage() override;
+
+ EntityBiter()
+ {
+ health = 0.5f;
+ attack.load("biter/attack.wav");
+ persecute.load("biter/idle.wav");
+ }
+};
+
+// TODO: Custom sounds to the BOMB
+class EntityBomb : public EntityCrawler
+{
+public:
+
+ virtual std::string get_name() override
+ {
+ return "Bomb";
+ }
+
+ virtual EntityType get_type() override
+ {
+ return E_BOMB;
+ }
+
+ virtual int get_symbol() override
+ {
+ return 155;
+ }
+
+ virtual float get_speed_penalty_rate()
+ {
+ return 0.05f;
+ }
+
+ virtual bool autofind()
+ {
+ return true;
+ }
+
+ virtual void do_damage() override;
+
+ EntityBomb()
+ {
+ health = 0.5f;
+ attack.load("biter/attack.wav");
+ persecute.load("biter/idle.wav");
+ }
+};
+
+// The worm moves randomly across the whole map
+// and attacks, it's strong
+class EntityWorm : public Monster
+{
+public:
+
+ float velocity;
+ bool persecuting;
+ bool dive;
+
+ SoLoud::Wav dive_sound;
+ SoLoud::Wav attack_sound;
+
+ float rand_timer = 0.0f;
+
+ virtual std::string get_name() override
+ {
+ return "Worm";
+ }
+
+ virtual int get_symbol()
+ {
+ return 126;
+ }
+ virtual EntityType get_type() override
+ {
+ return E_WORM;
+ }
+
+ virtual float get_speed() override
+ {
+ return velocity;
+ }
+
+ virtual void update(float dt) override;
+
+ EntityWorm()
+ {
+ health = 2.0f;
+ attack_sound.load("worm/attack.wav");
+ dive_sound.load("worm/dive.wav");
+ rand_timer = 0.0f;
+ }
+
+};
+
+// Guardians refuse to go very far away from their nest
+class EntityGuardian : public Monster
+{
+public:
+
+ float velocity;
+ float move_timer;
+ float spawn_timer;
+
+ float c_x, c_y;
+
+ virtual void update(float dt) override;
+
+ virtual std::string get_name() override
+ {
+ return "Guardian";
+ }
+
+ virtual EntityType get_type() override
+ {
+ return E_GUARDIAN;
+ }
+
+ virtual int get_symbol() override
+ {
+ return 236;
+ }
+
+ virtual float get_speed() override
+ {
+ return velocity;
+ }
+
+ EntityGuardian()
+ {
+ health = 4.0f;
+ c_x = -1.0f;
+ c_y = -1.0f;
+ move_timer = 0.0f;
+ }
+};
+
+// Serpents move across the map
+class EntitySerpent : public Monster
+{
+public:
+
+ float velocity;
+ float spawn_timer;
+ float engage_timer;
+ float fight_timer;
+ bool engaging;
+
+ virtual void update(float dt) override;
+
+
+ virtual std::string get_name() override
+ {
+ return "Giant Serpent";
+ }
+
+ virtual EntityType get_type() override
+ {
+ return E_SERPENT;
+ }
+
+ virtual int get_symbol() override
+ {
+ return 21;
+ }
+
+ virtual float get_speed() override
+ {
+ return velocity;
+ }
+
+ virtual void damage(float pow) override
+ {
+ Monster::damage(pow);
+
+ if (!engaging)
+ {
+ engaging = true;
+ engage_timer = 60.0f;
+ }
+
+ }
+
+ EntitySerpent()
+ {
+ health = 10.0f;
+ engaging = false;
+ spawn_timer = 0.0f;
+ engage_timer = -1000.0f;
+ fight_timer = 0.0f;
+ }
+
+};
\ No newline at end of file
diff --git a/src/vehicle/Crewmember.cpp b/src/vehicle/Crewmember.cpp
deleted file mode 100644
index 84e20f1..0000000
--- a/src/vehicle/Crewmember.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-#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/FlightCrew.cpp b/src/vehicle/FlightCrew.cpp
new file mode 100644
index 0000000..4940d4e
--- /dev/null
+++ b/src/vehicle/FlightCrew.cpp
@@ -0,0 +1,45 @@
+#include "FlightCrew.h"
+#include "workbench/Workbench.h"
+
+
+
+FlightCrew::FlightCrew(Crewmember* c)
+{
+ this->gc = c;
+ repair_order = false;
+ path = nullptr;
+ path_t = 0.0f;
+ // Procedural
+
+}
+
+bool FlightCrew::can_work_in(Workbench * bench)
+{
+ /*for (size_t i = 0; i < assigned.size(); i++)
+ {
+ if (bench == assigned[i])
+ {
+ return true;
+ }
+ }
+
+ return false;
+ */
+
+ return true;
+}
+
+void FlightCrew::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/FlightCrew.h
similarity index 56%
rename from src/vehicle/Crewmember.h
rename to src/vehicle/FlightCrew.h
index 8d19e6f..554c608 100644
--- a/src/vehicle/Crewmember.h
+++ b/src/vehicle/FlightCrew.h
@@ -4,7 +4,7 @@
#include "soloud_speech.h"
#include "soloud_wav.h"
#include "../defines.h"
-
+#include "../Crewmember.h"
#include
#include
@@ -12,27 +12,23 @@
class Workbench;
-
-
-class Crewmember
+class FlightCrew
{
public:
- SoLoud::Speech voice;
-
- std::string name;
+ Crewmember* gc;
int x, y;
- bool is_captain;
+ // Where we go when called to return to positions
+ int cx, cy;
// 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();
+
+ FlightCrew(Crewmember* crew);
bool can_work_in(Workbench* bench);
@@ -40,4 +36,14 @@ class Crewmember
TCODPath* path;
float path_t = 0.0f;
+
+ bool has_torpedo = false;
+ bool wants_torpedo = false;
+
+ int repair_x = -1, repair_y = -1;
+
+ bool is_repairing = false;
+ bool is_pumping = false;
+
+ bool repair_order;
};
\ No newline at end of file
diff --git a/src/vehicle/Vehicle.cpp b/src/vehicle/Vehicle.cpp
index 98d161e..8012f52 100644
--- a/src/vehicle/Vehicle.cpp
+++ b/src/vehicle/Vehicle.cpp
@@ -1,9 +1,568 @@
#include "Vehicle.h"
+#include "../flight/FlightScene.h"
+VehicleTile& Vehicle::get_tile_for_water(int x, int y)
+{
+ if (x < 0 || y < 0 || x >= width || y >= height)
+ {
+ return dummy_tile;
+ }
+ else
+ {
+ return tiles[y * width + x];
+ }
+}
+
+void Vehicle::update_water_flow(float dt)
+{
+ water_tick -= dt;
+ if (water_tick < 0.0f)
+ {
+ water_level = 0.0f;
+ water_flow = 0.0f;
+ // Update water flow
+ // We set flowing here
+ 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].health = 1.0f;
+ }
+
+ water_level += tiles[y * width + x].water;
+
+ tiles[y * width + x].really_blocks_water = tiles[y * width + x].blocks_water;
+
+ if (!tiles[y * width + x].is_outside)
+ {
+ tiles[y * width + x].prev_water = tiles[y * width + x].water;
+ }
+
+ if (tiles[y * width + x].blocks_water)
+ {
+ if (tiles[y * width + x].health <= 0.5f)
+ {
+ tiles[y * width + x].really_blocks_water = false;
+ }
+
+ bool found = false;
+
+ for (int i = 0; i < crew.size(); i++)
+ {
+ if (crew[i].x == x && crew[i].y == y)
+ {
+ tiles[y * width + x].really_blocks_water = false;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found && tiles[y * width + x].health >= 0.5f)
+ {
+ tiles[y * width + x].water = 0.0f;
+ }
+ }
+ }
+ }
+
+ for (int x = 0; x < width; x++)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ VehicleTile& c = get_tile_for_water(x + 0, y + 0);
+ VehicleTile& u = get_tile_for_water(x + 0, y - 1);
+ VehicleTile& r = get_tile_for_water(x + 1, y + 0);
+ VehicleTile& d = get_tile_for_water(x + 0, y + 1);
+ VehicleTile& l = get_tile_for_water(x - 1, y + 0);
+
+ if (c.health <= 0.5f)
+ {
+ water_flow += 1.0f - c.health;
+ if (g_random->getFloat(0.0f, 0.5f) > c.health)
+ {
+ c.water += g_random->getFloat(0.001f, 1.0f - c.health) * dt * 0.5f;
+ }
+ }
+
+ if (!c.is_outside && !c.really_blocks_water)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if (g_random->getFloat(0.0f, 1.0f) > 0.5f)
+ {
+ if (g_random->getFloat(0.0f, 1.0f) > 0.5f)
+ {
+ if (!(u.really_blocks_water && u.health >= 0.5f) && u.prev_water < c.prev_water)
+ {
+ u.water += min(WATER_FLOW_RATE * dt, c.water);
+ c.water -= min(WATER_FLOW_RATE * dt, c.water);
+ }
+ }
+ else
+ {
+ if (!(l.really_blocks_water && l.health >= 0.5f) && l.prev_water < c.prev_water)
+ {
+ l.water += min(WATER_FLOW_RATE * dt, c.water);
+ c.water -= min(WATER_FLOW_RATE * dt, c.water);
+ }
+ }
+ }
+ else
+ {
+ if (g_random->getFloat(0.0f, 1.0f) > 0.5f)
+ {
+ if (!(r.really_blocks_water && r.health >= 0.5f) && r.prev_water < c.prev_water)
+ {
+ r.water += min(WATER_FLOW_RATE * dt, c.water);
+ c.water -= min(WATER_FLOW_RATE * dt, c.water);
+ }
+ }
+ else
+ {
+ if (!(d.really_blocks_water && d.health >= 0.5f) && d.prev_water < c.prev_water)
+ {
+ d.water += min(WATER_FLOW_RATE * dt, c.water);
+ c.water -= min(WATER_FLOW_RATE * dt, c.water);
+ }
+ }
+ }
+
+ }
+
+
+ }
+ }
+ }
+
+ water_tick = 0.05f;
+ g_soloud->setVolume(waterflow_h, min(water_flow / 10.0f, 2.0f));
+ }
+
+
+}
+
+void Vehicle::update_crew(float dt)
+{
+ 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;
+ }
+
+ if (crew[i].has_torpedo)
+ {
+ // Find free torpedo slot
+ int slot = -1;
+ for (int j = 0; j < torpedo_slots.size(); j++)
+ {
+ if (torpedo_slots[j].has_torpedo == false &&
+ (torpedo_slots[j].crew_coming == i || torpedo_slots[j].crew_coming == -1))
+ {
+ slot = j;
+ break;
+ }
+ }
+
+ if (slot != -1)
+ {
+ torpedo_slots[slot].crew_coming = i;
+
+ crew[i].path_to(torpedo_slots[slot].x, torpedo_slots[slot].y, *tcod_map);
+
+ if (crew[i].x == torpedo_slots[slot].x && crew[i].y == torpedo_slots[slot].y)
+ {
+ torpedo_slots[slot].crew_coming = -1;
+ torpedo_slots[slot].has_torpedo = true;
+ crew[i].has_torpedo = false;
+
+ crew[i].gc->speak("Torpedo loaded, sir!");
+ }
+ }
+ else
+ {
+ // Drop the torpedo
+ Torpedo tp = Torpedo();
+ tp.x = crew[i].x;
+ tp.y = crew[i].y;
+ crew[i].has_torpedo = false;
+ torpedoes.push_back(tp);
+ }
+ }
+ else
+ {
+ if (crew[i].repair_order && !crew[i].is_repairing)
+ {
+ bool found = false;
+
+ int tries = 0;
+
+ while(!found && tries < 30)
+ {
+ int x = g_random->getInt(0, width - 1);
+ int y = g_random->getInt(0, height - 1);
+
+ if (tiles[y * width + x].health < 1.0f)
+ {
+ crew[i].repair_x = x;
+ crew[i].repair_y = y;
+ found = true;
+ }
+
+ tries++;
+ }
+ }
+
+ if (crew[i].wants_torpedo)
+ {
+ int to_remove = -1;
+ for (int j = 0; j < torpedoes.size(); j++)
+ {
+ if (crew[i].x >= torpedoes[j].x && crew[i].x <= torpedoes[j].x + 4 && crew[i].y == torpedoes[j].y)
+ {
+ to_remove = j;
+ crew[i].wants_torpedo = false;
+ crew[i].has_torpedo = true;
+ break;
+ }
+ }
+
+ if (to_remove != -1)
+ {
+ torpedoes.erase(torpedoes.begin() + to_remove);
+ }
+ }
+
+ if (tiles[crew[i].y * width + crew[i].x].ch == 235)
+ {
+ crew[i].is_pumping = true;
+
+ tiles[crew[i].y * width + crew[i].x].water -= WATER_PUMP_RATE * dt;
+ if (tiles[crew[i].y * width + crew[i].x].water < 0.0f)
+ {
+ tiles[crew[i].y * width + crew[i].x].water = 0.0f;
+ crew[i].is_pumping = false;
+ }
+ }
+ else
+ {
+ crew[i].is_pumping = false;
+ }
+
+ if (crew[i].repair_x != -1)
+ {
+ if (crew[i].x >= crew[i].repair_x - 2
+ && crew[i].y >= crew[i].repair_y - 2
+ && crew[i].x <= crew[i].repair_x + 2
+ && crew[i].y <= crew[i].repair_y + 2)
+ {
+ crew[i].is_repairing = true;
+
+ // Repair
+ VehicleTile& to_repair = tiles[crew[i].repair_y * width + crew[i].repair_x];
+
+ to_repair.health += REPAIR_RATE * dt;
+
+ if (to_repair.health >= 1.0f)
+ {
+ to_repair.health = 1.0f;
+ crew[i].repair_x = -1;
+ crew[i].repair_y = -1;
+ if (!crew[i].repair_order)
+ {
+ crew[i].gc->speak("Tile repaired!");
+ }
+
+ crew[i].is_repairing = false;
+
+ if (crew[i].repair_order)
+ {
+ // Go to workstation
+ crew[i].path_to(crew[i].cx, crew[i].cy, *tcod_map);
+ }
+ }
+ }
+ else
+ {
+ TCODPath t_path = TCODPath(tcod_map);
+ // Path to any free tile
+ for (int ox = -2; ox < 2; ox++)
+ {
+ for (int oy = -2; oy < 2; oy++)
+ {
+ if (t_path.compute(crew[i].x, crew[i].y, crew[i].repair_x + ox, crew[i].repair_y + oy))
+ {
+ crew[i].path_to(crew[i].repair_x + ox, crew[i].repair_y + oy, *tcod_map);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void Vehicle::update_bubbles(float dt)
+{
+
+
+ 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++;
+ }
+ }
+}
+
+void Vehicle::do_context_menu(TCODConsole* target, int ox, int oy, TCOD_mouse_t pos, int torpedo_hlight)
+{
+ 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 || (pos.lbutton_pressed && (g_key.lctrl || g_key.rctrl)))
+ {
+ selected->path_to(pos.cx - ox, pos.cy - oy, *tcod_map);
+ selected->is_repairing = false;
+ selected->is_pumping = false;
+ }
+ }
+
+ 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("Assign Location");
+ }
+
+ if (torpedo_hlight != -1)
+ {
+ menu_items.push_back("Load Torpedo");
+ }
+
+ if (tiles[ctx_y * width + ctx_x].ch == 235)
+ {
+ menu_items.push_back("Pump Water Out");
+ }
+
+ // We can repair tiles in a 2x2 range
+ if (tiles[ctx_y * width + ctx_x].health < 1.0f)
+ {
+ menu_items.push_back("Repair Tile");
+ }
+
+ if (selected->repair_order)
+ {
+ menu_items.push_back("Stop Auto-Repairing");
+ }
+ else
+ {
+ menu_items.push_back("Auto-Repair");
+ }
+
+
+
+ menu_items.push_back(selected->gc->name);
+
+
+
+ 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 && !(g_key.lctrl || g_key.rctrl)))
+ {
+ if (menu_items[i] == "Move Here")
+ {
+ selected->path_to(ctx_x, ctx_y, *tcod_map);
+ selected->is_repairing = false;
+ selected->is_pumping = false;
+ }
+ else if (menu_items[i] == "Load Torpedo")
+ {
+ selected->path_to(ctx_x, ctx_y, *tcod_map);
+ selected->wants_torpedo = true;
+ selected->is_repairing = false;
+ selected->is_pumping = false;
+ }
+ else if (menu_items[i] == "Pump Water Out")
+ {
+ selected->path_to(ctx_x, ctx_y, *tcod_map);
+ }
+ else if (menu_items[i] == "Repair Tile")
+ {
+ selected->repair_x = ctx_x;
+ selected->repair_y = ctx_y;
+ }
+ else if (menu_items[i] == "Auto-Repair")
+ {
+ selected->repair_order = true;
+ }
+ else if (menu_items[i] == "Stop Auto-Repairing")
+ {
+ selected->repair_order = false;
+ }
+ else if (menu_items[i] == "Assign Location")
+ {
+ selected->cx = ctx_x;
+ selected->cy = ctx_y;
+ }
+ }
+
+ }
+
+ if ((pos.lbutton_pressed && !(g_key.lctrl || g_key.rctrl)))
+ {
+ in_context_menu = false;
+ }
+
+ target->setAlignment(TCOD_LEFT);
+ }
+}
+
+
+
+FlightCrew * Vehicle::get_captain()
+{
+ for (int i = 0; i < crew.size(); i++)
+ {
+ if (crew[i].gc->is_captain)
+ {
+ return &crew[i];
+ }
+ }
+
+ return nullptr;
+}
void Vehicle::update(float dt)
{
+
for (size_t i = 0; i < workbenches.size(); i++)
{
if (workbenches[i]->update(dt))
@@ -16,8 +575,36 @@ void Vehicle::update(float dt)
auto old = get_tile();
bool was_breathing = breathing;
- x += sin(angle) * velocity * dt * 0.15f;
- y -= cos(angle) * velocity * dt * 0.15f;
+ float slowdown = 1.0f - min(water_level * 0.025f, 1.0f);
+
+ //std::cout << slowdown << std::endl;
+
+ float dx = sin(angle) * velocity * dt * 0.2f * possible_speed;
+ float dy = -cos(angle) * velocity * dt * 0.2f * possible_speed;
+
+ if (in_map->get_subtile(x + dx, y + dy) == WALL)
+ {
+ if (velocity > 0.2f)
+ {
+ damage(1.5f);
+ }
+
+ velocity = 0.0f;
+ }
+ else
+ {
+ x += dx;
+ y += dy;
+ }
+
+ noise = 0.0f;
+
+ // Collect noise
+ noise += velocity * 0.6f;
+ noise += machines->eng0_running ? 0.6f : 0.0f;
+ noise += machines->eng1_running ? 0.6f : 0.0f;
+ noise += sonar->sonar_active ? 0.3f : 0.0f;
+ noise /= 1.5f;
if (get_tile() != old)
{
@@ -30,7 +617,7 @@ void Vehicle::update(float dt)
{
if (maneouver->is_crewed())
{
- maneouver->get_crewman()->speak("We have entered an oxygen rich zone.");
+ maneouver->get_crewman()->gc->speak("We have entered an oxygen rich zone.");
}
}
}
@@ -41,67 +628,30 @@ void Vehicle::update(float dt)
{
if (maneouver->is_crewed())
{
- maneouver->get_crewman()->speak("We have exited the oxygen rich zone.");
+ maneouver->get_crewman()->gc->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;
+ g_soloud->setVolume(moving_fast_h, velocity * base_vol);
- if (g_random->getFloat(0.0f, 1.0f) >= 0.99f)
- {
- it->y += g_random->getInt(-1, 1);
- }
+ flip_timer -= velocity * dt * 10.0f;
- it->b = (sin(it->t * it->f) + 1.0f) * 30.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;
- if (it->x >= WIDTH)
- {
- it = bubbles.erase(it);
- }
- else
- {
- it++;
- }
+ flip_timer = 1.0f;
}
blinkt -= dt;
@@ -111,16 +661,30 @@ void Vehicle::update(float dt)
blink = !blink;
}
- for (int i = 0; i < crew.size(); i++)
- {
- crew[i].path_t -= dt;
+
+ update_bubbles(dt);
+ update_crew(dt);
+ update_water_flow(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 draw_torpedo(TCODConsole* target, int x, int y, int ox, int oy, bool hlight)
+{
+ target->setChar(x + ox, y + oy, 238);
+ target->setChar(x + 1 + ox, y + oy, TCOD_CHAR_DHLINE);
+ target->setChar(x + 2 + ox, y + oy, TCOD_CHAR_DHLINE);
+ target->setChar(x + 3 + ox, y + oy, TCOD_CHAR_DHLINE);
+ target->setChar(x + 4 + ox, y + oy, 232);
+ target->setCharForeground(x + ox, y + oy, TCODColor(140, 70, 0));
+ target->setCharForeground(x + 1 + ox, y + oy, TCODColor(158, 158, 158));
+ target->setCharForeground(x + 2 + ox, y + oy, TCODColor(158, 158, 158));
+ target->setCharForeground(x + 3 + ox, y + oy, TCODColor(158, 158, 158));
+ target->setCharForeground(x + 4 + ox, y + oy, TCODColor(100, 100, 100));
+ if (hlight)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ target->setCharBackground(x + ox + i, y + oy, TCODColor::white);
}
}
}
@@ -143,37 +707,117 @@ void Vehicle::draw(TCODConsole* target, int ox, int oy)
{
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);
+ if (tiles[y * width + x].water >= 0.4f && tiles[y * width + x].ch != 235)
+ {
+ target->setChar(x + ox, y + oy, 247);
+ target->setCharForeground(x + ox, y + oy, TCODColor(0, 89, 178));
+ target->setCharBackground(x + ox, y + oy, TCODColor(0, 32, 64));
+ }
+ else if (tiles[y * width + x].water > 0.25f && tiles[y * width + x].ch != 235)
+ {
+ target->setChar(x + ox, y + oy, 126);
+ target->setCharForeground(x + ox, y + oy, TCODColor(0, 89, 178));
+ target->setCharBackground(x + ox, y + oy, tiles[y * width + x].back);
+ }
+ else if (tiles[y * width + x].water > 0.0f && tiles[y * width + x].ch != 235
+ && tiles[y * width + x].health == 1.0f)
+ {
+ target->setChar(x + ox, y + oy, 126);
+ target->setCharForeground(x + ox, y + oy, TCODColor(51, 153, 255));
+ target->setCharBackground(x + ox, y + oy, tiles[y * width + x].back);
+ }
+ else
+ {
+ 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);
+
+ if (tiles[y * width + x].health <= 0.05f)
+ {
+ target->setChar(x + ox, y + oy, tiles[y * width + x].blocks_player ? 8 : ',');
+ target->setCharForeground(x + ox, y + oy, TCODColor(158, 158, 158));
+ target->setCharBackground(x + ox, y + oy, TCODColor(80, 80, 80));
+ }
+ else if (tiles[y * width + x].health <= 0.25f)
+ {
+ target->setChar(x + ox, y + oy, tiles[y * width + x].blocks_player ? 10 : '%');
+ target->setCharForeground(x + ox, y + oy, TCODColor(158, 158, 158));
+ }
+ else if (tiles[y * width + x].health <= 0.65f)
+ {
+ target->setChar(x + ox, y + oy, tiles[y * width + x].blocks_player ? 15 : '+');
+ target->setCharForeground(x + ox, y + oy, TCODColor(158, 158, 158));
+ }
+ }
}
}
}
+ int torpedo_hlight = -1;
+
// 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));
+
+ bool hlight = false;
+ if (selected != nullptr)
+ {
+ int x = ctx_x;
+ int y = ctx_y;
+
+ if (!in_context_menu)
+ {
+ x = pos.cx - ox;
+ y = pos.cy - oy;
+ }
+
+ if (x >= tp.x && x <= tp.x + 4 && y == tp.y)
+ {
+ hlight = true;
+ torpedo_hlight = i;
+ }
+
+
+ }
+
+
+ draw_torpedo(target, tp.x, tp.y, ox, oy, hlight);
+
+ }
+
+ for (int i = 0; i < torpedo_slots.size(); i++)
+ {
+ if (torpedo_slots[i].has_torpedo)
+ {
+ target->setChar(torpedo_slots[i].x + ox, torpedo_slots[i].y + oy, 238);
+ target->setCharForeground(torpedo_slots[i].x + ox, torpedo_slots[i].y + oy, TCODColor(140, 70, 0));
+ }
}
// Draw crew
bool selected_crew = false;
for (int i = 0; i < crew.size(); i++)
{
- Crewmember& c = crew[i];
+ FlightCrew& c = crew[i];
- target->setChar(c.x + ox, c.y + oy, c.is_captain ? 2 : 1);
+ int ch = c.gc->is_captain ? 2 : 1;
+
+ if (blink)
+ {
+ if (c.is_pumping)
+ {
+ ch = 'P';
+ }
+
+ if (c.is_repairing)
+ {
+ ch = 'R';
+ }
+ }
+
+ target->setChar(c.x + ox, c.y + oy, ch);
target->setCharForeground(c.x + ox, c.y + oy, TCODColor(240, 240, 240));
if (pos.cx - ox == c.x && pos.cy - oy == c.y)
@@ -181,7 +825,7 @@ void Vehicle::draw(TCODConsole* target, int ox, int oy)
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)
+ if ((pos.lbutton_pressed && !(g_key.lctrl || g_key.rctrl)) && !in_context_menu)
{
selected = &crew[i];
selected_crew = true;
@@ -197,7 +841,7 @@ void Vehicle::draw(TCODConsole* target, int ox, int oy)
}
- if (pos.lbutton_pressed && !selected_crew && !in_context_menu)
+ if ((pos.lbutton_pressed && !(g_key.lctrl || g_key.rctrl)) && !selected_crew && !in_context_menu)
{
selected = nullptr;
}
@@ -224,7 +868,7 @@ void Vehicle::draw(TCODConsole* target, int ox, int oy)
target->setCharBackground(v2.first + ox, v2.second + oy, TCODColor(200, 200, 200));
}
- if (pos.lbutton_pressed && !selected_crew && !in_context_menu)
+ if ((pos.lbutton_pressed && !(g_key.lctrl || g_key.rctrl)) && !selected_crew && !in_context_menu)
{
if (workbenches[i]->is_crewed())
{
@@ -242,136 +886,7 @@ void Vehicle::draw(TCODConsole* target, int ox, int oy)
}
// 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);
- }
+ do_context_menu(target, ox, oy, pos, torpedo_hlight);
}
@@ -441,7 +956,7 @@ void Vehicle::move_order(Direction dir)
- maneouver->get_crewman()->speak(Speech::positive() + Speech::heading() + dir_name);
+ get_captain()->gc->speak("Go " + dir_name + "!");
}
}
@@ -452,22 +967,22 @@ void Vehicle::speed_order(Speed speed)
std::string name;
if (speed == STOP)
{
- name = "Stopping!";
+ name = "Stop!";
maneouver->wanted_velocity = 0.0f;
}
else if (speed == SLOW)
{
- name = "Moving slowly!";
+ name = "Move slow!";
maneouver->wanted_velocity = 0.25f;
}
else if (speed == MEDIUM)
{
- name = "Moving normally!";
+ name = "Move normally!";
maneouver->wanted_velocity = 0.5f;
}
else if (speed == FAST)
{
- name = "Moving fast!";
+ name = "Move fast!";
maneouver->wanted_velocity = 0.75f;
}
else
@@ -476,14 +991,105 @@ void Vehicle::speed_order(Speed speed)
maneouver->wanted_velocity = 1.0f;
}
- maneouver->get_crewman()->speak(name);
+ get_captain()->gc->speak(name);
+ }
+}
+
+void Vehicle::damage(float power, int x, int y)
+{
+ if (power < 1.0f)
+ {
+ power = 1.0f;
+ }
+
+ if (x == -1)
+ {
+ x = g_random->getInt(0, width);
+ }
+
+ if (y == -1)
+ {
+ y = g_random->getInt(0, height);
+ }
+
+ int poweri = (int)ceil(power);
+
+ float xf = (float)x;
+ float yf = (float)y;
+
+ for (int x0 = x - poweri; x0 < x + poweri; x0++)
+ {
+ for (int y0 = y - poweri; y0 < y + poweri; y0++)
+ {
+ float x0f = (float)x0;
+ float y0f = (float)y0;
+
+ float dx = x0f - xf;
+ float dy = y0f - yf;
+
+ float dist = dx * dx + dy * dy;
+
+ float chance = power / dist;
+
+ if (g_random->getFloat(0.0f, 1.0f) <= chance)
+ {
+ if (x0 >= 0 && y0 >= 0 && x0 < width - 1 && y0 < height - 1)
+ {
+ tiles[y0 * width + x0].health -= 1.0f / sqrt(dist);
+ if (tiles[y0 * width + x0].health < 0.0f)
+ {
+ tiles[y0 * width + x0].health = 0.0f;
+ }
+ }
+ }
+ }
+ }
+}
+
+void Vehicle::electric_shock()
+{
+ if (battery_aux >= 0.25f / 3.0f)
+ {
+ battery_aux -= 0.25f / 3.0f - 0.0001f;
+ g_soloud->play(shock, 2.0f);
+
+ ExplosionEffect fx = ExplosionEffect();
+ fx.x = x;
+ fx.y = y;
+ fx.t = 0.3f;
+ fx.dist = 1.0f;
+
+ scene->expl_effects.push_back(fx);
+
+ for (int i = 0; i < in_map->entities.size(); i++)
+ {
+ float dx = in_map->entities[i]->get_x() - x;
+ float dy = in_map->entities[i]->get_y() - y;
+
+ float dist = sqrt(dx * dx + dy * dy);
+
+ if (dist < 0.35f)
+ {
+ in_map->entities[i]->damage(0.5f);
+ }
+ }
}
}
Vehicle::Vehicle(FlightMap* map)
{
+ water_tick = 0.25f;
+
+ torpedo_0 = false;
+ torpedo_1 = false;
+ torpedo_2 = false;
+ battery_a = 1.0f;
+ battery_b = 1.0f;
+ battery_aux = 0.25f;
+ diesel = 1.0f;
+ engines_on_diesel = false;
blink = true;
blinkt = 0.5f;
@@ -513,11 +1119,15 @@ Vehicle::Vehicle(FlightMap* map)
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);
+ waterflow.load("waterflow.wav"); waterflow.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);
+ waterflow_h = g_soloud->play(waterflow);
+
+ shock.load("shock.wav");
this->in_map = map;
@@ -591,14 +1201,15 @@ Vehicle::Vehicle(FlightMap* map)
// Load crewmember in chairs and assign them the occupation
if (tile->ch == 239)
{
- Crewmember c = Crewmember();
+ FlightCrew c = FlightCrew(new Crewmember());
c.x = tx; c.y = ty;
- c.is_captain = tile->fore == TCODColor(222, 211, 195);
+ c.gc->is_captain = tile->fore == TCODColor(222, 211, 195);
+ c.cx = tx; c.cy = ty;
Workbench* work = nullptr;
- if (c.is_captain)
+ if (c.gc->is_captain)
{
// Captain seat
}
@@ -642,14 +1253,14 @@ Vehicle::Vehicle(FlightMap* map)
}
else
{
- work = new Listening();
+ work = new Targeting();
// 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;
+ targeting = (Targeting*)work;
}
}
else if (loaded_sub.getChar(x - 1, y) == 155)
@@ -670,7 +1281,7 @@ Vehicle::Vehicle(FlightMap* map)
work->tiles.push_back(std::make_pair(tx - 1, ty + 0));
work->tiles.push_back(std::make_pair(tx - 1, ty + 1));
- machines = work;
+ machines = (Machines*)work;
}
else if(loaded_sub.getChar(x, y + 1) == '{')
{
@@ -723,6 +1334,8 @@ Vehicle::Vehicle(FlightMap* map)
{
m1x = tx; m1y = ty;
}
+
+ tile->health = 1.0f;
if (loaded_sub.getCharBackground(x, y) == TCODColor(4, 24, 30))
{
@@ -738,6 +1351,7 @@ Vehicle::Vehicle(FlightMap* map)
tile->blocks_player = false;
tile->blocks_light = false;
tile->blocks_water = false;
+
// Load properties
if ((tile->ch >= 179 && tile->ch <= 218) || tile->ch == '=')
@@ -746,7 +1360,19 @@ Vehicle::Vehicle(FlightMap* map)
tile->blocks_player = true;
tile->blocks_light = true;
tile->blocks_water = true;
- tile->health = 100.0f;
+ }
+
+ if (tile->ch == '=')
+ {
+ tile->blocks_player = false;
+
+ TorpedoSlot slot = TorpedoSlot();
+ slot.x = tx;
+ slot.y = ty;
+ slot.has_torpedo = false;
+ slot.crew_coming = -1;
+
+ torpedo_slots.push_back(slot);
}
if (tile->ch == 'o')
@@ -755,7 +1381,6 @@ Vehicle::Vehicle(FlightMap* map)
tile->blocks_player = false;
tile->blocks_light = true;
tile->blocks_water = true;
- tile->health = 60.0f;
}
}
}
@@ -774,6 +1399,8 @@ Vehicle::Vehicle(FlightMap* map)
tcod_map->setProperties(x, y, transparent, walkable);
}
}
+
+ dummy_tile.blocks_water = true;
}
diff --git a/src/vehicle/Vehicle.h b/src/vehicle/Vehicle.h
index 889cd51..e9e3f73 100644
--- a/src/vehicle/Vehicle.h
+++ b/src/vehicle/Vehicle.h
@@ -2,28 +2,33 @@
#include
#include "libtcod.h"
#include "../defines.h"
-#include "Crewmember.h"
+#include "FlightCrew.h"
#include "../Speech.h"
#include "../flight/FlightMap.h"
#include "workbench/Sonar.h"
#include "workbench/Radio.h"
-#include "workbench/Listening.h"
+#include "workbench/Targeting.h"
#include "workbench/Machines.h"
#include "workbench/Maneouver.h"
#include "workbench/Battery.h"
#include "workbench/Periscope.h"
+class FlightScene;
struct VehicleTile
{
+ // Health always goes from 0-1
float health;
float water;
+ float prev_water;
+
int ch;
TCODColor fore;
TCODColor back;
+ bool really_blocks_water;
bool blocks_water;
bool blocks_player;
bool blocks_light;
@@ -36,15 +41,61 @@ struct Torpedo
int x, y;
};
+struct TorpedoSlot
+{
+ int x, y;
+ bool has_torpedo;
+ int crew_coming;
+};
class Vehicle
{
+private:
+
+ VehicleTile dummy_tile;
+ VehicleTile& get_tile_for_water(int x, int y);
+
+ void update_water_flow(float dt);
+ void update_crew(float dt);
+ void update_bubbles(float dt);
+
+ void do_context_menu(TCODConsole* target, int ox, int oy, TCOD_mouse_t pos, int torpedo_hlight);
+
public:
+ FlightScene* scene;
+
+ static constexpr float WATER_FLOW_RATE = 4.0f;
+ static constexpr float WATER_PUMP_RATE = 2.0f;
+ static constexpr float REPAIR_RATE = 0.25f;
+
+ // Total added water level, having too much water slows the submarine
+ // considerably
+ float water_level;
+ // Water added per second
+ float water_flow;
+
+ float noise;
+
+ float water_tick;
+
+ float battery_a;
+ float battery_b;
+ float battery_aux;
+ float diesel;
+
+ bool engines_on_diesel;
+
+ float possible_speed;
+
+ bool torpedo_0;
+ bool torpedo_1;
+ bool torpedo_2;
+
TCODMap* tcod_map;
- Crewmember* selected;
+ FlightCrew* selected;
bool in_context_menu;
int ctx_x, ctx_y;
@@ -80,17 +131,23 @@ class Vehicle
std::vector bubbles;
+ std::vector torpedo_slots;
+
float flip_timer;
SoLoud::Wav engines_high;
SoLoud::Wav engines_low;
SoLoud::Wav moving_fast;
SoLoud::Wav moving_slow;
+ SoLoud::Wav waterflow;
+
+ SoLoud::Wav shock;
SoLoud::handle engines_high_h;
SoLoud::handle engines_low_h;
SoLoud::handle moving_fast_h;
SoLoud::handle moving_slow_h;
+ SoLoud::handle waterflow_h;
Workbench* workbench_open;
@@ -106,13 +163,17 @@ class Vehicle
// they only have visual and physical (water) effects
std::vector tiles;
std::vector torpedoes;
- std::vector crew;
+ std::vector crew;
std::vector workbenches;
- Workbench *periscope, *machines, *battery, *listening;
+ Workbench *periscope, *battery;
Sonar* sonar;
Radio* radio;
Maneouver* maneouver;
+ Machines* machines;
+ Targeting* targeting;
+
+ FlightCrew* get_captain();
int m0x, m0y, m1x, m1y;
@@ -123,6 +184,11 @@ class Vehicle
void move_order(Direction dir);
void speed_order(Speed speed);
+ void damage(float power, int x = - 1, int y = - 1);
+
+ // Consumes half the SHK battery
+ void electric_shock();
+
std::pair get_tile()
{
return std::make_pair((int)floor(x), (int)floor(y));
diff --git a/src/vehicle/workbench/Battery.cpp b/src/vehicle/workbench/Battery.cpp
index 168b40b..2d27de7 100644
--- a/src/vehicle/workbench/Battery.cpp
+++ b/src/vehicle/workbench/Battery.cpp
@@ -1,9 +1,27 @@
#include "Battery.h"
+#include "../Vehicle.h"
+static void charge_meter(TCODConsole* target, int x, int y, float charge, float max = 1.0f)
+{
+ Drawing::draw_rectangle(target, x, y, 30, 2);
+
+ float v = charge / max;
+ for (int i = 0; i < v * 29; i++)
+ {
+ target->setChar(x + i + 1, y + 1, 254);
+ }
+}
-Battery::Battery()
+Battery::Battery() : console(50, 50)
{
+ ba_to_bb = false;
+ bb_to_ba = false;
+ ba_to_aux = false;
+ bb_to_aux = false;
+ ba_to_eng = false;
+ bb_to_eng = false;
+
}
@@ -13,14 +31,172 @@ Battery::~Battery()
bool Battery::update(float dt)
{
+ get_vehicle()->possible_speed = 0.0f;
+
+ float a_consume = 0.0f;
+ float b_consume = 0.0f;
+ float aux_consume = 0.0f;
+
+ if (get_vehicle()->battery_a > 0.0f)
+ {
+ if (ba_to_eng)
+ {
+ get_vehicle()->possible_speed += 0.75f;
+ a_consume += ENGINE_RATE * get_vehicle()->velocity;
+ }
+
+ if (ba_to_bb)
+ {
+ if (get_vehicle()->battery_b < 1.0f - CHARGE_RATE * dt)
+ {
+ a_consume += CHARGE_RATE;
+ b_consume -= CHARGE_RATE;
+ }
+ }
+
+ if (ba_to_aux)
+ {
+ if (get_vehicle()->battery_aux < 0.25f - CHARGE_RATE * dt)
+ {
+ a_consume += CHARGE_RATE;
+ aux_consume -= CHARGE_RATE;
+ }
+ }
+ }
+ else
+ {
+ get_vehicle()->battery_a = 0.0f;
+ }
+
+ if (get_vehicle()->battery_b > 0.0f)
+ {
+ if (bb_to_eng)
+ {
+ get_vehicle()->possible_speed += 0.75f;
+ b_consume += ENGINE_RATE * get_vehicle()->velocity;
+ }
+
+ if (bb_to_ba)
+ {
+ if (get_vehicle()->battery_a < 1.0f - CHARGE_RATE * dt)
+ {
+ b_consume += CHARGE_RATE;
+ a_consume -= CHARGE_RATE;
+ }
+ }
+
+ if (bb_to_aux)
+ {
+ if (get_vehicle()->battery_aux < 0.25f - CHARGE_RATE * dt)
+ {
+ b_consume += CHARGE_RATE;
+ aux_consume -= CHARGE_RATE;
+ }
+ }
+ }
+ else
+ {
+ get_vehicle()->battery_b = 0.0f;
+ }
+
+ if (get_vehicle()->battery_aux > 0.0f)
+ {
+ if (aux_to_a)
+ {
+ if (get_vehicle()->battery_a < 1.0f - CHARGE_RATE * dt)
+ {
+ aux_consume += CHARGE_RATE;
+ a_consume -= CHARGE_RATE;
+ }
+ }
+
+ if (aux_to_b)
+ {
+ if (get_vehicle()->battery_b < 1.0f - CHARGE_RATE * dt)
+ {
+ aux_consume += CHARGE_RATE;
+ b_consume -= CHARGE_RATE;
+ }
+ }
+
+ if (aux_to_eng)
+ {
+ get_vehicle()->possible_speed += 0.25f;
+ aux_consume += ENGINE_RATE * 0.5f * get_vehicle()->velocity;
+ }
+ }
+ else
+ {
+ get_vehicle()->battery_aux = 0.0f;
+ }
+
+ get_vehicle()->battery_a -= a_consume * dt;
+ get_vehicle()->battery_b -= b_consume * dt;
+ get_vehicle()->battery_aux -= aux_consume * dt;
+
+ if (get_vehicle()->battery_a >= 1.0f)
+ {
+ get_vehicle()->battery_a = 1.0f;
+ }
+
+ if (get_vehicle()->battery_b >= 1.0f)
+ {
+ get_vehicle()->battery_b = 1.0f;
+ }
+
+ if (get_vehicle()->battery_aux >= 0.25f)
+ {
+ get_vehicle()->battery_aux = 0.25f;
+ }
+
+ if (get_vehicle()->possible_speed >= 1.0f)
+ {
+ get_vehicle()->possible_speed = 1.0f;
+ }
+
+ get_vehicle()->possible_speed = get_vehicle()->engines_on_diesel ? 1.0f : get_vehicle()->possible_speed;
+
return false;
}
void Battery::draw(int rx, int ry)
{
-}
+ console.clear();
-TCODConsole * Battery::get_console()
-{
- return nullptr;
+ int by = 34;
+
+ Drawing::draw_switch(&console, 7, by + 0, rx, ry, &ba_to_bb, "BAT.A > BAT.B");
+ Drawing::draw_switch(&console, 7, by + 5, rx, ry, &ba_to_aux, "BAT.A > B.SHK");
+ Drawing::draw_switch(&console, 7, by + 10, rx, ry, &ba_to_eng, "BAT.A > MOTOR");
+
+ Drawing::draw_switch(&console, 23, by + 0, rx, ry, &bb_to_ba, "BAT.B > BAT.A");
+ Drawing::draw_switch(&console, 23, by + 5, rx, ry, &bb_to_aux, "BAT.B > B.SHK");
+ Drawing::draw_switch(&console, 23, by + 10, rx, ry, &bb_to_eng, "BAT.B > MOTOR");
+
+ Drawing::draw_switch(&console, 39, by + 0, rx, ry, &aux_to_a, "B.SHK > BAT.A");
+ Drawing::draw_switch(&console, 39, by + 5, rx, ry, &aux_to_b, "B.SHK > BAT.B");
+ Drawing::draw_switch(&console, 39, by + 10, rx, ry, &aux_to_eng, "B.SHK > MOTOR");
+
+ console.hline(0, by - 4, 50);
+ console.vline(16, by - 4, 20);
+ console.vline(32, by - 4, 20);
+
+ console.setChar(16, by - 4, 194);
+ console.setChar(32, by - 4, 194);
+
+ // Draw indicators
+
+ console.printf(2, 2, "BAT. ID");
+ console.printf(20, 2, "CHARGE");
+ console.printf(43, 2, "TEMP");
+
+ console.printf(2, 6, "BAT.A");
+ charge_meter(&console, 9, 5, get_vehicle()->battery_a);
+
+ console.printf(2, 10, "BAT.B");
+ charge_meter(&console, 9, 9, get_vehicle()->battery_b);
+
+ console.printf(2, 14, "B.SHK");
+ charge_meter(&console, 9, 13, get_vehicle()->battery_aux, 0.25f);
}
+
diff --git a/src/vehicle/workbench/Battery.h b/src/vehicle/workbench/Battery.h
index fba0ad8..4165c5e 100644
--- a/src/vehicle/workbench/Battery.h
+++ b/src/vehicle/workbench/Battery.h
@@ -6,21 +6,45 @@
#include "libtcod.h"
#include "Workbench.h"
+#include "../../Drawing.h"
+
class Battery : public Workbench
{
+private:
+
public:
+
+ static constexpr float ENGINE_RATE = 0.007f;
+ static constexpr float CHARGE_RATE = 0.050f;
+
+ bool ba_to_bb;
+ bool bb_to_ba;
+ bool ba_to_aux;
+ bool bb_to_aux;
+ bool ba_to_eng;
+ bool bb_to_eng;
+
+ bool aux_to_a;
+ bool aux_to_b;
+ bool aux_to_eng;
+
+ TCODConsole console;
+
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 TCODConsole* get_console() override
+ {
+ return &console;
+ }
virtual std::string get_name() override
{
- return "Battery Control Panel";
+ return "Battery Station";
}
};
diff --git a/src/vehicle/workbench/Listening.cpp b/src/vehicle/workbench/Listening.cpp
deleted file mode 100644
index 2969ad6..0000000
--- a/src/vehicle/workbench/Listening.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#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
deleted file mode 100644
index 1981963..0000000
--- a/src/vehicle/workbench/Listening.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#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
index ca58e92..d40c3ec 100644
--- a/src/vehicle/workbench/Machines.cpp
+++ b/src/vehicle/workbench/Machines.cpp
@@ -1,9 +1,20 @@
#include "Machines.h"
+#include "../Vehicle.h"
-
-Machines::Machines()
+Machines::Machines() : console(40, 50)
{
+ eng0_running = false;
+ eng1_running = false;
+ eng0_out = BOTH_BATTERIES;
+ eng1_out = ENGINES;
+
+ diesel.load("diesel.wav");
+ diesel.setLooping(true);
+
+ diesel_off.load("diesel_off.wav");
+
+ diesel_h = g_soloud->play(diesel, 0.0f);
}
@@ -13,14 +24,142 @@ Machines::~Machines()
bool Machines::update(float dt)
{
+ if (!get_vehicle()->breathing)
+ {
+ if (eng0_running || eng1_running)
+ {
+ // Play starving sound
+ g_soloud->play(diesel_off, 2.0f);
+ }
+
+ eng0_running = false;
+ eng1_running = false;
+ }
+
+ if (eng0_running || eng1_running)
+ {
+ g_soloud->setVolume(diesel_h, 1.0f);
+ }
+ else
+ {
+ g_soloud->setVolume(diesel_h, 0.0f);
+ }
+
+ float to_bat_a = 0.0f;
+ float to_bat_b = 0.0f;
+ float to_engines = 0.0f;
+
+ if (eng0_running)
+ {
+ if (eng0_out == BATTERY_A)
+ {
+ to_bat_a += 0.5f;
+ }
+ else if (eng0_out == BATTERY_B)
+ {
+ to_bat_b += 0.5f;
+ }
+ else if (eng0_out == BOTH_BATTERIES)
+ {
+ to_bat_a += 0.25f;
+ to_bat_b += 0.25f;
+ }
+ else
+ {
+ to_engines += 0.5f;
+ }
+ }
+
+ if (eng1_running)
+ {
+ if (eng1_out == BATTERY_A)
+ {
+ to_bat_a += 0.5f;
+ }
+ else if (eng1_out == BATTERY_B)
+ {
+ to_bat_b += 0.5f;
+ }
+ else if (eng1_out == BOTH_BATTERIES)
+ {
+ to_bat_a += 0.25f;
+ to_bat_b += 0.25f;
+ }
+ else
+ {
+ to_engines += 0.5f;
+ }
+ }
+
+ get_vehicle()->battery_a += to_bat_a * dt * RATE;
+ get_vehicle()->battery_b += to_bat_b * dt * RATE;
+
+ if (get_vehicle()->battery_a > 1.0f)
+ {
+ get_vehicle()->battery_a = 1.0f;
+ }
+
+ if (get_vehicle()->battery_b > 1.0f)
+ {
+ get_vehicle()->battery_b = 1.0f;
+ }
+
+
+ get_vehicle()->engines_on_diesel = to_engines != 0.0f;
+
return false;
}
void Machines::draw(int rx, int ry)
{
-}
+ console.clear();
-TCODConsole * Machines::get_console()
-{
- return nullptr;
+ console.setAlignment(TCOD_CENTER);
+
+ if (get_vehicle()->breathing)
+ {
+ console.setDefaultForeground(TCODColor::lightGreen);
+ console.printf(console.getWidth() / 2 - 1, 2, "OXYGEN AVAILABLE");
+ console.printf(console.getWidth() / 2 - 1, 4, "ENGINES READY");
+ }
+ else
+ {
+ console.setDefaultForeground(TCODColor::lightRed);
+ console.printf(console.getWidth() / 2 - 1, 2, "OXYGEN UNAVAILABLE");
+ console.printf(console.getWidth() / 2 - 1, 4, "ENGINES CANNOT RUN");
+ }
+
+ console.setDefaultForeground(TCODColor::white);
+
+ console.setAlignment(TCOD_LEFT);
+
+ console.hline(0, 6, console.getWidth());
+
+ int y0 = 12;
+ console.setAlignment(TCOD_CENTER);
+ console.printf(console.getWidth() / 2 - 1, y0 - 5, "Generator A");
+ console.setAlignment(TCOD_LEFT);
+
+ if (Drawing::draw_switch(&console, 9, y0 + 3, rx, ry, &eng0_running, "Generator Active") && eng0_running == false)
+ {
+ g_soloud->play(diesel_off, 2.0f);
+ }
+
+ Drawing::draw_four_choice(&console, 26, y0, rx, ry, (int*)&eng0_out, "BAT A", "BATTS", "BAT B", "MOTOR");
+
+ y0 = console.getHeight() / 2 + 6;
+
+ console.setAlignment(TCOD_CENTER);
+ console.printf(console.getWidth() / 2 - 1, y0 - 5, "Generator B");
+ console.setAlignment(TCOD_LEFT);
+
+ if (Drawing::draw_switch(&console, 9, y0 + 3, rx, ry, &eng1_running, "Generator Active") && eng0_running == false)
+ {
+ g_soloud->play(diesel_off, 2.0f);
+ }
+
+ Drawing::draw_four_choice(&console, 26, y0, rx, ry, (int*)&eng1_out, "BAT A", "BATTS", "BAT B", "MOTOR");
+
+
+ console.hline(0, console.getHeight() / 2, console.getWidth());
}
diff --git a/src/vehicle/workbench/Machines.h b/src/vehicle/workbench/Machines.h
index b68641a..c3d6e3a 100644
--- a/src/vehicle/workbench/Machines.h
+++ b/src/vehicle/workbench/Machines.h
@@ -6,21 +6,55 @@
#include "libtcod.h"
#include "Workbench.h"
+#include "../../Drawing.h"
+
+
+// If you unload a battery while it is loading
+// it heats up and could cause a failure
+// so strategic loading is neccesary
+enum MachineOutput
+{
+ BATTERY_A,
+ BOTH_BATTERIES,
+ BATTERY_B,
+ ENGINES
+};
class Machines : public Workbench
{
+private:
+
public:
+
+ constexpr static float RATE = 0.04f;
+
+ SoLoud::Wav diesel;
+ SoLoud::handle diesel_h;
+
+ SoLoud::Wav diesel_off;
+
+ bool eng0_running;
+ bool eng1_running;
+
+ MachineOutput eng0_out;
+ MachineOutput eng1_out;
+
+ TCODConsole console;
+
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 TCODConsole* get_console() override
+ {
+ return &console;
+ }
virtual std::string get_name() override
{
- return "Machine Control";
+ return "Diesel Engines";
}
};
diff --git a/src/vehicle/workbench/Maneouver.cpp b/src/vehicle/workbench/Maneouver.cpp
index 8ae32ce..ae3277b 100644
--- a/src/vehicle/workbench/Maneouver.cpp
+++ b/src/vehicle/workbench/Maneouver.cpp
@@ -47,7 +47,7 @@ bool Maneouver::update(float dt)
{
if (is_crewed())
{
- get_crewman()->speak(Speech::speed_done());
+ get_crewman()->gc->speak(Speech::speed_done());
}
said_speed = true;
@@ -76,7 +76,7 @@ bool Maneouver::update(float dt)
{
if (is_crewed())
{
- get_crewman()->speak(Speech::heading_done());
+ get_crewman()->gc->speak(Speech::heading_done());
}
said = true;
@@ -97,8 +97,34 @@ bool Maneouver::update(float dt)
}
- get_vehicle()->angle = real_vehicle + dir * dt * 1.0f;
+ float possible_maneouver = 1.0f - get_vehicle()->water_level / 50.0f;
+ if (possible_maneouver < 0.2f)
+ {
+ possible_maneouver = 0.2f;
+ }
+
+ get_vehicle()->angle = real_vehicle + dir * dt * 0.7f * possible_maneouver;
+
+ }
+
+ if (get_vehicle()->velocity > get_vehicle()->possible_speed && wanted_velocity > get_vehicle()->possible_speed
+ && get_vehicle()->velocity != 0.0)
+ {
+ get_vehicle()->velocity = get_vehicle()->possible_speed + 0.0000001f;
+ if (!said_max)
+ {
+ if (is_crewed())
+ {
+ get_crewman()->gc->speak("Engine power too low!");
+ said_max = true;
+ }
+
+ }
+ }
+ else
+ {
+ said_max = false;
}
return false;
diff --git a/src/vehicle/workbench/Maneouver.h b/src/vehicle/workbench/Maneouver.h
index 017aa44..5f26950 100644
--- a/src/vehicle/workbench/Maneouver.h
+++ b/src/vehicle/workbench/Maneouver.h
@@ -22,6 +22,7 @@ class Maneouver : public Workbench
bool said;
bool said_speed;
+ bool said_max;
TCODConsole console;
diff --git a/src/vehicle/workbench/Periscope.cpp b/src/vehicle/workbench/Periscope.cpp
index b7b3aec..c073dce 100644
--- a/src/vehicle/workbench/Periscope.cpp
+++ b/src/vehicle/workbench/Periscope.cpp
@@ -1,6 +1,6 @@
#include "Periscope.h"
#include "../Vehicle.h"
-
+#include "../../flight/FlightScene.h"
Periscope::Periscope() : console(49, 49)
{
@@ -36,7 +36,7 @@ void Periscope::draw(int rx, int ry)
TCODMap view_map = TCODMap(w, h);
- std::vector& entities = get_vehicle()->in_map->get_entities();
+ std::vector& entities = get_vehicle()->in_map->get_entities();
float vx = get_vehicle()->x;
float vy = get_vehicle()->y;
@@ -81,30 +81,18 @@ void Periscope::draw(int rx, int ry)
// Draw entities and features
for (int i = 0; i < entities.size(); i++)
- {
-
-
- float exf = entities[i].x - vx;
- float eyf = entities[i].y - vy;
+ {
+ if (entities[i]->is_alive())
+ {
+ float exf = entities[i]->get_position().first - vx;
+ float eyf = entities[i]->get_position().second - vy;
- int ex = (int)floor(exf * (float)w * 0.5f + 24.0f);
- int ey = (int)floor(eyf * (float)h * 0.5f + 24.0f);
+ 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)
+ if (ex >= 0 && ex < w && ey >= 0 && ey < h)
{
- 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;
+ console.setChar(ex, ey, entities[i]->get_symbol());
}
}
}
@@ -120,7 +108,8 @@ void Periscope::draw(int rx, int ry)
yf = (yf - 0.5f) * 2.0f;
float dist = sqrt(xf * xf + yf * yf) * 7.0f;
- float bright = 1.0f / (dist * dist);
+ float bright = 3.0f / (dist * dist);
+
TCODColor lit = TCODColor(207.0f, 1.0f / bright, bright);
@@ -198,4 +187,46 @@ void Periscope::draw(int rx, int ry)
}
}
+ // Draw explosions
+ for (int i = 0; i < get_vehicle()->scene->expl_effects.size(); i++)
+ {
+ ExplosionEffect fx = get_vehicle()->scene->expl_effects[i];
+
+
+ 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;
+
+ float dx = tx - fx.x;
+ float dy = ty - fx.y;
+
+ float dist = sqrt(dx * dx + dy * dy);
+
+ if (dist <= fx.t * 2.0f)
+ {
+ int n = min(fx.t * 255.0f, 255);
+ console.setCharForeground(x, y, TCODColor(n, n, n));
+ if (dist <= fx.t)
+ {
+ console.setChar(x, y, 178);
+ }
+ else
+ {
+ console.setChar(x, y, '.');
+ }
+
+ }
+ }
+ }
+ }
+
}
diff --git a/src/vehicle/workbench/Periscope.h b/src/vehicle/workbench/Periscope.h
index a45b9c4..0b16773 100644
--- a/src/vehicle/workbench/Periscope.h
+++ b/src/vehicle/workbench/Periscope.h
@@ -19,10 +19,6 @@ class Periscope : public Workbench
bool blink = false;
float blinkt = 0.5f;
- int msg;
-
- std::vector messages;
-
TCODConsole console;
Periscope();
diff --git a/src/vehicle/workbench/Radio.cpp b/src/vehicle/workbench/Radio.cpp
index c17c9d3..2e7e398 100644
--- a/src/vehicle/workbench/Radio.cpp
+++ b/src/vehicle/workbench/Radio.cpp
@@ -43,9 +43,9 @@ void Radio::draw(int rx, int ry)
if (Drawing::draw_button(&console, console.getWidth() - 9, console.getHeight() - 5, rx, ry, '>'))
{
msg++;
- if (msg > 0)
+ if (msg > messages.size() - 1)
{
- msg = 0;
+ msg = messages.size() - 1;
}
}
@@ -96,7 +96,7 @@ void Radio::push_message(std::string nmsg)
// If anybody is on the radio, say new message received
if (is_crewed() && !is_open())
{
- get_crewman()->speak("Captain, new radio message");
+ get_crewman()->gc->speak("Captain, new radio message");
}
msg = messages.size() - 1;
diff --git a/src/vehicle/workbench/Sonar.cpp b/src/vehicle/workbench/Sonar.cpp
index 7757c33..64cf0c2 100644
--- a/src/vehicle/workbench/Sonar.cpp
+++ b/src/vehicle/workbench/Sonar.cpp
@@ -14,7 +14,7 @@ bool Sonar::update(float dt)
if (is_open())
{
- draw_world(get_vehicle()->get_tile().first, get_vehicle()->get_tile().second, *get_vehicle()->in_map);
+ //draw_world(get_vehicle()->get_tile().first, get_vehicle()->get_tile().second, *get_vehicle()->in_map);
}
}
@@ -27,6 +27,8 @@ bool Sonar::update(float dt)
{
sonar_radius = 0.0f;
g_soloud->play(ping);
+
+ draw_world((int)floor(get_vehicle()->x), (int)floor(get_vehicle()->y), *get_vehicle()->in_map);
}
@@ -37,8 +39,6 @@ bool Sonar::update(float dt)
{
return true;
}
-
- draw_world((int)floor(get_vehicle()->x), (int)floor(get_vehicle()->y), *get_vehicle()->in_map);
}
return false;
@@ -46,6 +46,11 @@ bool Sonar::update(float dt)
void Sonar::draw(int rx, int ry)
{
+ if (!sonar_active)
+ {
+ draw_world((int)floor(get_vehicle()->x), (int)floor(get_vehicle()->y), *get_vehicle()->in_map);
+ }
+
// Clear lower area
console.rect(0, 49, 49, 60, true);
@@ -151,8 +156,8 @@ void Sonar::draw(int rx, int ry)
if (sonar_active)
{
g_soloud->play(ping);
+ draw_world((int)floor(get_vehicle()->x), (int)floor(get_vehicle()->y), *get_vehicle()->in_map);
}
- draw_world(lpx, lpy, *last_map);
}
console.setDefaultForeground(TCODColor::white);
@@ -213,7 +218,7 @@ void Sonar::draw_world(int px, int py, FlightMap& map)
lpy = py;
last_map = ↦
// Run a visibility algorithm and draw
- map.vmap.computeFov(px, py, sonar_active ? 24 : 1, true, FOV_BASIC);
+ map.vmap.computeFov(px, py, sonar_active ? 24 : 1, true, FOV_DIAMOND);
for (int x = -24; x < 24; x++)
{
@@ -277,6 +282,63 @@ void Sonar::draw_world(int px, int py, FlightMap& map)
}
}
+
+ // Draw entities
+ for (int i = 0; i < map.entities.size(); i++)
+ {
+ if (map.entities[i]->is_alive())
+ {
+ auto type = map.entities[i]->get_type();
+ if (type != E_BASE && type != E_STATION && type != E_NEST)
+ {
+ int ch = '*';
+
+ if (type == E_SUBMARINE)
+ {
+ ch = '!';
+ }
+ else if (type == E_TORPEDO)
+ {
+ ch = '+';
+ }
+
+
+ if (map.entities[i]->is_identified())
+ {
+ ch = map.entities[i]->get_symbol();
+ }
+
+ auto fpos = map.entities[i]->get_position();
+ int x = (int)floor(fpos.first);
+ int y = (int)floor(fpos.second);
+
+ int mx = x - px;
+ int my = y - py;
+
+ int sx = mx + 24;
+ int sy = my + 24;
+
+ bool seen = true;
+
+
+ if (!map.vmap.isInFov(x, y))
+ {
+ seen = false;
+ }
+
+ if (seen)
+ {
+ console.setChar(sx, sy, ch);
+ map.entities[i]->set_visible(true);
+ }
+ else
+ {
+ map.entities[i]->set_visible(false);
+ }
+
+ }
+ }
+ }
}
diff --git a/src/vehicle/workbench/Targeting.cpp b/src/vehicle/workbench/Targeting.cpp
new file mode 100644
index 0000000..da6de26
--- /dev/null
+++ b/src/vehicle/workbench/Targeting.cpp
@@ -0,0 +1,363 @@
+#include "Targeting.h"
+#include "../../flight/entity/EntityTorpedo.h"
+#include "../Vehicle.h"
+#include "../../flight/FlightScene.h"
+
+void Targeting::fire_torpedo()
+{
+ if (is_crewed())
+ {
+ if (target == nullptr)
+ {
+ get_crewman()->gc->speak("No target assigned!");
+ }
+ else
+ {
+ if (has_torpedo())
+ {
+ if (torpedo_timer < 0.0f)
+ {
+ get_crewman()->gc->speak("Firing torpedo!");
+ torpedo_timer = 4.0f;
+ g_soloud->play(torpedo_out, 6.0f);
+ firing = true;
+ }
+ }
+ else
+ {
+ get_crewman()->gc->speak("No torpedoes loaded!");
+ }
+ }
+ }
+}
+
+bool Targeting::has_torpedo()
+{
+ for (int i = 0; i < get_vehicle()->torpedo_slots.size(); i++)
+ {
+ if (get_vehicle()->torpedo_slots[i].has_torpedo)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Targeting::Targeting() : console(50, 50)
+{
+ torpedo_timer = 0.0f;
+ torpedo_out.load("torpedo_out.wav");
+ target = nullptr;
+
+ identify = nullptr;
+ identify_timer = 0.0f;
+}
+
+
+Targeting::~Targeting()
+{
+}
+
+std::vector Targeting::get_visible_entities()
+{
+ std::vector out;
+
+ for (int i = 0; i < get_vehicle()->in_map->entities.size(); i++)
+ {
+ FlightEntity* ent = get_vehicle()->in_map->entities[i];
+ if (ent->is_visible() &&
+ ent->get_type() != E_BASE && ent->get_type() != E_NEST &&
+ ent->get_type() != E_STATION && ent->get_type() != E_TORPEDO
+ && ent->is_alive())
+ {
+ out.push_back(get_vehicle()->in_map->entities[i]);
+ }
+ }
+
+ return out;
+}
+
+bool Targeting::update(float dt)
+{
+
+ torpedo_timer -= dt;
+
+ if (torpedo_timer < 1.8f)
+ {
+ if (firing)
+ {
+ // Fire the boy
+ EntityTorpedo* torpedo = new EntityTorpedo(target);
+ torpedo->velocity = 0.35f;
+ torpedo->heading = get_vehicle()->angle;
+ torpedo->safety = 1.0f;
+ torpedo->maneouver = 0.74f;
+ torpedo->lifetime = 25.0f;
+
+ torpedo->set_position(get_vehicle()->x, get_vehicle()->y);
+ torpedo->init(get_vehicle()->in_map, get_vehicle()->scene);
+
+
+ get_vehicle()->in_map->entities.push_back(torpedo);
+
+ for (int i = 0; i < get_vehicle()->torpedo_slots.size(); i++)
+ {
+ if (get_vehicle()->torpedo_slots[i].has_torpedo)
+ {
+ get_vehicle()->torpedo_slots[i].has_torpedo = false;
+ break;
+ }
+ }
+
+ firing = false;
+ }
+ }
+
+ if (torpedo_timer < 0.0f)
+ {
+ if (!said)
+ {
+ if (is_crewed() && has_torpedo())
+ {
+ get_crewman()->gc->speak("Weapons ready!");
+ said = true;
+ }
+ }
+ }
+ else
+ {
+ said = false;
+ }
+
+ if (is_crewed())
+ {
+ identify_timer -= dt;
+
+ auto visibles = get_visible_entities();
+
+ if (identify != nullptr)
+ {
+ bool found = false;
+ for (int i = 0; i < visibles.size(); i++)
+ {
+ if (visibles[i] == identify)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ get_crewman()->gc->speak("Sonar target lost");
+ identify = nullptr;
+ identify_timer = 0.0f;
+ }
+ }
+
+ if (identify_timer <= 0.0f)
+ {
+ if (identify != nullptr)
+ {
+ identify->identify();
+
+ std::string name = identify->get_name();
+
+ get_crewman()->gc->speak("Identified a " + name);
+ identify = nullptr;
+ }
+
+ // Automatically start finding a new one
+ for (int i = 0; i < visibles.size(); i++)
+ {
+ if (!visibles[i]->is_identified())
+ {
+ identify = visibles[i];
+ identify_timer = IDENTIFY_TIME;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void Targeting::draw(int rx, int ry)
+{
+ console.clear();
+
+ // Draw a list of visible entities, and show a identification
+ // progress meter. The user can click entities to target
+ // and it shows a little preview of the entity in a sonar
+
+ auto visible = get_visible_entities();
+
+ std::vector sorted;
+
+ // We first sort
+ for (int i = 0; i < visible.size(); i++)
+ {
+ if (visible[i]->is_identified())
+ {
+ sorted.push_back(visible[i]);
+ }
+ }
+
+ for (int i = 0; i < visible.size(); i++)
+ {
+ if (!visible[i]->is_identified())
+ {
+ sorted.push_back(visible[i]);
+ }
+ }
+
+ int selected = -1;
+ int highlighted = -1;
+
+ TCOD_mouse_t mouse = TCODMouse::getStatus();
+
+ // Now draw the list
+ for (int i = 0; i < sorted.size(); i++)
+ {
+ if (mouse.cy - ry == i + 2)
+ {
+ highlighted = i;
+ }
+
+ if (highlighted == i)
+ {
+ console.putChar(2, i + 2, '>');
+ if (mouse.lbutton_pressed)
+ {
+ if (target != sorted[i])
+ {
+ target = sorted[i];
+ get_crewman()->gc->speak("Changed weapon's target");
+ }
+
+ }
+
+ if (mouse.rbutton_pressed)
+ {
+ if (identify != sorted[i] && !sorted[i]->is_identified())
+ {
+ identify = sorted[i];
+ identify_timer = IDENTIFY_TIME;
+ get_crewman()->gc->speak("Identifying sonar signal");
+ }
+ }
+ }
+
+ if (identify == sorted[i])
+ {
+ console.setChar(0, i + 2, 'I');
+ console.setCharForeground(0, i + 2, TCODColor(255, 191, 0));
+ }
+
+ if (target == sorted[i])
+ {
+ console.setChar(1, i + 2, 'T');
+ console.setCharForeground(1, i + 2, TCODColor(255, 50, 0));
+ }
+
+ if (sorted[i]->is_identified())
+ {
+ if (sorted[i]->get_type() != E_SUBMARINE)
+ {
+ console.setDefaultForeground(TCODColor(255, 128, 128));
+ }
+ else
+ {
+ console.setDefaultForeground(TCODColor(128, 255, 128));
+ }
+ }
+
+
+ if (sorted[i]->is_identified())
+ {
+ console.putChar(3, i + 2, sorted[i]->get_symbol());
+ }
+ else
+ {
+ console.putChar(3, i + 2, '?');
+ }
+
+ int xo = 3;
+
+ console.setDefaultForeground(TCODColor::white);
+
+
+
+ if (sorted[i]->is_identified())
+ {
+ console.printf(2 + xo, i + 2, sorted[i]->get_name().c_str());
+ }
+ else
+ {
+ console.printf(2 + xo, i + 2, "Unknown Signal");
+ }
+
+ xo = 19;
+
+ // Distance
+ float dx = sorted[i]->get_x() - get_vehicle()->x;
+ float dy = sorted[i]->get_y() - get_vehicle()->y;
+
+ float dist = sqrt(dx * dx + dy * dy);
+
+ std::string str = "";
+ str += std::to_string((int)round(dist * 100.0f));
+ str += "m";
+
+ console.printf(2 + xo, i + 2, str.c_str());
+
+
+
+ }
+
+ console.setDefaultForeground(TCODColor::grey);
+ // Draw small sonar
+ Drawing::draw_rectangle(&console, 32, -1, 32, 17, true);
+
+ console.setDefaultForeground(TCODColor::white);
+
+
+ int sx, sy;
+ if (highlighted == -1)
+ {
+ sx = 16;
+ sy = 16;
+ }
+ else
+ {
+ float ex = sorted[highlighted]->get_x();
+ float ey = sorted[highlighted]->get_y();
+
+ // Relative to the player
+ float rx = ex - get_vehicle()->x;
+ float ry = ey - get_vehicle()->y;
+
+ // Relative to the sonar upper left corner
+ float ssx = rx + 24.0f;
+ float ssy = ry + 24.0f;
+
+ sx = (int)ssx - 7;
+ sy = (int)ssy - 7;
+
+ }
+
+ if (get_vehicle()->sonar->is_crewed())
+ {
+ get_vehicle()->sonar->draw(0, 0);
+ TCODConsole::blit(&get_vehicle()->sonar->console, sx, sy, 16, 16, &console, 33, 0);
+ }
+
+
+
+}
+
+TCODConsole* Targeting::get_console()
+{
+ return &console;
+}
diff --git a/src/vehicle/workbench/Targeting.h b/src/vehicle/workbench/Targeting.h
new file mode 100644
index 0000000..9a2b3a7
--- /dev/null
+++ b/src/vehicle/workbench/Targeting.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include
+#include
+#include "../../Date.h"
+#include "libtcod.h"
+
+#include "Workbench.h"
+
+class FlightEntity;
+
+class Targeting : public Workbench
+{
+public:
+
+ FlightEntity* identify;
+ float identify_timer;
+
+ constexpr static float IDENTIFY_TIME = 10.0f;
+
+ FlightEntity* target;
+
+ TCODConsole console;
+
+ SoLoud::Wav torpedo_out;
+
+ bool firing;
+ float torpedo_timer;
+ bool said;
+
+ void fire_torpedo();
+ bool has_torpedo();
+
+ Targeting();
+ ~Targeting();
+
+ // Ignores torpedoes, bases, nests and stations
+ std::vector get_visible_entities();
+
+ // 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 "Targeting Station";
+ }
+};
+
diff --git a/src/vehicle/workbench/Workbench.h b/src/vehicle/workbench/Workbench.h
index 4a1be6c..baa6a5e 100644
--- a/src/vehicle/workbench/Workbench.h
+++ b/src/vehicle/workbench/Workbench.h
@@ -5,7 +5,7 @@
#include
#include
#include
-#include "../Crewmember.h"
+#include "../FlightCrew.h"
class Vehicle;
@@ -18,7 +18,7 @@ enum Window
PERISCOPE,
MACHINES,
BATTERY,
- LISTENING
+ TARGETING,
};
class Workbench
@@ -34,7 +34,7 @@ class Workbench
public:
- std::vector* crew;
+ std::vector* crew;
// Chair x and y
@@ -79,9 +79,9 @@ class Workbench
virtual std::string get_name() = 0;
- Crewmember* get_crewman()
+ FlightCrew* get_crewman()
{
- for (Crewmember& cm : *crew)
+ for (FlightCrew& cm : *crew)
{
if (cm.can_work_in(this) && cm.x == cx && cm.y == cy)
{
@@ -94,7 +94,7 @@ class Workbench
bool is_crewed()
{
- for (Crewmember& cm : *crew)
+ for (FlightCrew& cm : *crew)
{
if (cm.can_work_in(this) && cm.x == cx && cm.y == cy)
{
diff --git a/torpedo_out.wav b/torpedo_out.wav
new file mode 100644
index 0000000..cefc1da
Binary files /dev/null and b/torpedo_out.wav differ
diff --git a/waterflow.wav b/waterflow.wav
new file mode 100644
index 0000000..d8f9da3
Binary files /dev/null and b/waterflow.wav differ
diff --git a/worm/attack.wav b/worm/attack.wav
new file mode 100644
index 0000000..d340395
Binary files /dev/null and b/worm/attack.wav differ
diff --git a/worm/dive.wav b/worm/dive.wav
new file mode 100644
index 0000000..c799b95
Binary files /dev/null and b/worm/dive.wav differ