1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include <Urho3D/Core/CoreEvents.h>
24 #include <Urho3D/Core/ProcessUtils.h>
25 #include <Urho3D/Core/Timer.h>
26 #include <Urho3D/Engine/Console.h>
27 #include <Urho3D/Engine/Engine.h>
28 #include <Urho3D/Engine/EngineEvents.h>
29 #include <Urho3D/Input/Input.h>
30 #include <Urho3D/IO/Log.h>
31 #include <Urho3D/UI/Button.h>
32
33 #include "ConsoleInput.h"
34
35 #include <Urho3D/DebugNew.h>
36
37 // Expands to this example's entry-point
38 URHO3D_DEFINE_APPLICATION_MAIN(ConsoleInput)
39
40 // Hunger level descriptions
41 String hungerLevels[] = {
42 "bursting",
43 "well-fed",
44 "fed",
45 "hungry",
46 "very hungry",
47 "starving"
48 };
49
50 // Urho threat level descriptions
51 String urhoThreatLevels[] = {
52 "Suddenly Urho appears from a dark corner of the fish tank",
53 "Urho seems to have his eyes set on you",
54 "Urho is homing in on you mercilessly"
55 };
56
ConsoleInput(Context * context)57 ConsoleInput::ConsoleInput(Context* context) :
58 Sample(context)
59 {
60 }
61
Start()62 void ConsoleInput::Start()
63 {
64 // Execute base class startup
65 Sample::Start();
66
67 // Subscribe to console commands and the frame update
68 SubscribeToEvent(E_CONSOLECOMMAND, URHO3D_HANDLER(ConsoleInput, HandleConsoleCommand));
69 SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(ConsoleInput, HandleUpdate));
70
71 // Subscribe key down event
72 SubscribeToEvent(E_KEYDOWN, URHO3D_HANDLER(ConsoleInput, HandleEscKeyDown));
73 UnsubscribeFromEvent(E_KEYUP);
74
75 // Hide logo to make room for the console
76 SetLogoVisible(false);
77
78 // Show the console by default, make it large. Console will show the text edit field when there is at least one
79 // subscriber for the console command event
80 Console* console = GetSubsystem<Console>();
81 console->SetNumRows(GetSubsystem<Graphics>()->GetHeight() / 16);
82 console->SetNumBufferedRows(2 * console->GetNumRows());
83 console->SetCommandInterpreter(GetTypeName());
84 console->SetVisible(true);
85 console->GetCloseButton()->SetVisible(false);
86 console->AddAutoComplete("help");
87 console->AddAutoComplete("eat");
88 console->AddAutoComplete("hide");
89 console->AddAutoComplete("wait");
90 console->AddAutoComplete("score");
91 console->AddAutoComplete("quit");
92
93 // Show OS mouse cursor
94 GetSubsystem<Input>()->SetMouseVisible(true);
95
96 // Set the mouse mode to use in the sample
97 Sample::InitMouseMode(MM_FREE);
98
99 // Open the operating system console window (for stdin / stdout) if not open yet
100 OpenConsoleWindow();
101
102 // Initialize game and print the welcome message
103 StartGame();
104
105 // Randomize from system clock
106 SetRandomSeed(Time::GetSystemTime());
107 }
108
HandleConsoleCommand(StringHash eventType,VariantMap & eventData)109 void ConsoleInput::HandleConsoleCommand(StringHash eventType, VariantMap& eventData)
110 {
111 using namespace ConsoleCommand;
112 if (eventData[P_ID].GetString() == GetTypeName())
113 HandleInput(eventData[P_COMMAND].GetString());
114 }
115
HandleUpdate(StringHash eventType,VariantMap & eventData)116 void ConsoleInput::HandleUpdate(StringHash eventType, VariantMap& eventData)
117 {
118 // Check if there is input from stdin
119 String input = GetConsoleInput();
120 if (input.Length())
121 HandleInput(input);
122 }
123
HandleEscKeyDown(StringHash eventType,VariantMap & eventData)124 void ConsoleInput::HandleEscKeyDown(StringHash eventType, VariantMap& eventData)
125 {
126 // Unlike the other samples, exiting the engine when ESC is pressed instead of just closing the console
127 if (eventData[KeyDown::P_KEY].GetInt() == KEY_ESCAPE && GetPlatform() != "Web")
128 engine_->Exit();
129 }
130
StartGame()131 void ConsoleInput::StartGame()
132 {
133 Print("Welcome to the Urho adventure game! You are the newest fish in the tank; your\n"
134 "objective is to survive as long as possible. Beware of hunger and the merciless\n"
135 "predator cichlid Urho, who appears from time to time. Evading Urho is easier\n"
136 "with an empty stomach. Type 'help' for available commands.");
137
138 gameOn_ = true;
139 foodAvailable_ = false;
140 eatenLastTurn_ = false;
141 numTurns_ = 0;
142 hunger_ = 2;
143 urhoThreat_ = 0;
144 }
145
EndGame(const String & message)146 void ConsoleInput::EndGame(const String& message)
147 {
148 Print(message);
149 Print("Game over! You survived " + String(numTurns_) + " turns.\n"
150 "Do you want to play again (Y/N)?");
151
152 gameOn_ = false;
153 }
154
Advance()155 void ConsoleInput::Advance()
156 {
157 if (urhoThreat_ > 0)
158 {
159 ++urhoThreat_;
160 if (urhoThreat_ > 3)
161 {
162 EndGame("Urho has eaten you!");
163 return;
164 }
165 }
166 else if (urhoThreat_ < 0)
167 ++urhoThreat_;
168 if (urhoThreat_ == 0 && Random() < 0.2f)
169 ++urhoThreat_;
170
171 if (urhoThreat_ > 0)
172 Print(urhoThreatLevels[urhoThreat_ - 1] + ".");
173
174 if ((numTurns_ & 3) == 0 && !eatenLastTurn_)
175 {
176 ++hunger_;
177 if (hunger_ > 5)
178 {
179 EndGame("You have died from starvation!");
180 return;
181 }
182 else
183 Print("You are " + hungerLevels[hunger_] + ".");
184 }
185
186 eatenLastTurn_ = false;
187
188 if (foodAvailable_)
189 {
190 Print("The floating pieces of fish food are quickly eaten by other fish in the tank.");
191 foodAvailable_ = false;
192 }
193 else if (Random() < 0.15f)
194 {
195 Print("The overhead dispenser drops pieces of delicious fish food to the water!");
196 foodAvailable_ = true;
197 }
198
199 ++numTurns_;
200 }
201
HandleInput(const String & input)202 void ConsoleInput::HandleInput(const String& input)
203 {
204 String inputLower = input.ToLower().Trimmed();
205 if (inputLower.Empty())
206 {
207 Print("Empty input given!");
208 return;
209 }
210
211 if (inputLower == "quit" || inputLower == "exit")
212 engine_->Exit();
213 else if (gameOn_)
214 {
215 // Game is on
216 if (inputLower == "help")
217 Print("The following commands are available: 'eat', 'hide', 'wait', 'score', 'quit'.");
218 else if (inputLower == "score")
219 Print("You have survived " + String(numTurns_) + " turns.");
220 else if (inputLower == "eat")
221 {
222 if (foodAvailable_)
223 {
224 Print("You eat several pieces of fish food.");
225 foodAvailable_ = false;
226 eatenLastTurn_ = true;
227 hunger_ -= (hunger_ > 3) ? 2 : 1;
228 if (hunger_ < 0)
229 {
230 EndGame("You have killed yourself by over-eating!");
231 return;
232 }
233 else
234 Print("You are now " + hungerLevels[hunger_] + ".");
235 }
236 else
237 Print("There is no food available.");
238
239 Advance();
240 }
241 else if (inputLower == "wait")
242 {
243 Print("Time passes...");
244 Advance();
245 }
246 else if (inputLower == "hide")
247 {
248 if (urhoThreat_ > 0)
249 {
250 bool evadeSuccess = hunger_ > 2 || Random() < 0.5f;
251 if (evadeSuccess)
252 {
253 Print("You hide behind the thick bottom vegetation, until Urho grows bored.");
254 urhoThreat_ = -2;
255 }
256 else
257 Print("Your movements are too slow; you are unable to hide from Urho.");
258 }
259 else
260 Print("There is nothing to hide from.");
261
262 Advance();
263 }
264 else
265 Print("Cannot understand the input '" + input + "'.");
266 }
267 else
268 {
269 // Game is over, wait for (y)es or (n)o reply
270 if (inputLower[0] == 'y')
271 StartGame();
272 else if (inputLower[0] == 'n')
273 engine_->Exit();
274 else
275 Print("Please answer 'y' or 'n'.");
276 }
277 }
278
Print(const String & output)279 void ConsoleInput::Print(const String& output)
280 {
281 // Logging appears both in the engine console and stdout
282 URHO3D_LOGRAW(output + "\n");
283 }
284