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