1 /*
2  * Copyright (C) 2004 Ivo Danihelka (ivo@danihelka.net)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 #include "Controls.h"
10 
11 #include "Unit.h"
12 #include "PhaseLocker.h"
13 
14 #include "KeyStroke.h"
15 #include "MouseStroke.h"
16 
17 //-----------------------------------------------------------------
18 /**
19  * Create list of drivers.
20  * @param locker shared locker for anim
21  */
Controls(PhaseLocker * locker)22 Controls::Controls(PhaseLocker *locker)
23     : m_units(), m_moves()
24 {
25     m_locker = locker;
26     m_active = m_units.begin();
27     m_speedup = 0;
28     m_switch = true;
29     m_strokeSymbol = ControlSym::SYM_NONE;
30 }
31 //-----------------------------------------------------------------
32 /**
33  * Delete drivers.
34  */
~Controls()35 Controls::~Controls()
36 {
37     t_units::iterator end = m_units.end();
38     for (t_units::iterator i = m_units.begin(); i != end; ++i) {
39         delete (*i);
40     }
41 }
42 //-----------------------------------------------------------------
43 /**
44  * Add unit under our control.
45  * @return model index
46  */
47     void
addUnit(Unit * unit)48 Controls::addUnit(Unit *unit)
49 {
50     m_units.push_back(unit);
51     //NOTE: insertion invalidates m_active
52     t_units::iterator end = m_units.end();
53     for (t_units::iterator i = m_units.begin(); i != end; ++i) {
54         if ((*i)->startActive()) {
55             setActive(i);
56             return;
57         }
58     }
59     setActive(m_units.begin());
60 }
61 //-----------------------------------------------------------------
62 /**
63  * Returns active unit or NULL.
64  */
65 const Unit *
getActive()66 Controls::getActive()
67 {
68     Unit *result = NULL;
69     if (m_active != m_units.end()) {
70         result = *m_active;
71     }
72     return result;
73 }
74 //-----------------------------------------------------------------
75 /**
76  * Let drivers to drive.
77  * Only one driver can drive at the same time.
78  * @param input wrapped input
79  * @return true when a fish has moved (switch does not count)
80  */
81     bool
driving(const InputProvider * input)82 Controls::driving(const InputProvider *input)
83 {
84     bool moved = false;
85     if (!useSwitch()) {
86         if (!useStroke()) {
87             moved = driveUnit(input);
88         }
89         else {
90             moved = true;
91         }
92     }
93     return moved;
94 }
95 //-----------------------------------------------------------------
96 /**
97  * Returns true when a switch was done.
98  */
99 bool
useSwitch()100 Controls::useSwitch()
101 {
102     bool result = false;
103     if (m_active != m_units.end()) {
104         if (!(*m_active)->willMove()) {
105             checkActive();
106         }
107 
108         if (m_switch && m_active != m_units.end()) {
109             m_locker->ensurePhases(3);
110             (*m_active)->activate();
111             result = true;
112         }
113     }
114     m_switch = false;
115     return result;
116 }
117 //-----------------------------------------------------------------
118 /**
119  * Use gathered stroke.
120  * NOTE: returns true even for bad move (not used)
121  * @return true for used stroke
122  */
123 bool
useStroke()124 Controls::useStroke()
125 {
126     bool result = false;
127     if (m_strokeSymbol != ControlSym::SYM_NONE) {
128        makeMove(m_strokeSymbol);
129        m_strokeSymbol = ControlSym::SYM_NONE;
130        result = true;
131     }
132     return result;
133 }
134 //-----------------------------------------------------------------
135     bool
driveUnit(const InputProvider * input)136 Controls::driveUnit(const InputProvider *input)
137 {
138     char moved = ControlSym::SYM_NONE;
139     if (m_active != m_units.end()) {
140         moved = (*m_active)->driveBorrowed(input, m_arrows);
141     }
142 
143     if (ControlSym::SYM_NONE == moved) {
144         t_units::iterator end = m_units.end();
145         for (t_units::iterator i = m_units.begin(); i != end; ++i) {
146             moved = (*i)->drive(input);
147             if (moved != ControlSym::SYM_NONE) {
148                 setActive(i);
149                 break;
150             }
151         }
152     }
153 
154     if (moved != ControlSym::SYM_NONE) {
155         m_moves.append(1, moved);
156     }
157     return (moved != ControlSym::SYM_NONE);
158 }
159 //-----------------------------------------------------------------
160     void
lockPhases()161 Controls::lockPhases()
162 {
163     if (m_active != m_units.end() && (*m_active)->isMoving()) {
164         if ((*m_active)->isPushing()) {
165             m_speedup = 0;
166         }
167         else if (!(*m_active)->isTurning()) {
168             m_speedup++;
169         }
170 
171         m_locker->ensurePhases(getNeededPhases(m_speedup));
172     }
173     else {
174         m_speedup = 0;
175     }
176 }
177 //-----------------------------------------------------------------
178 int
getNeededPhases(int speedup) const179 Controls::getNeededPhases(int speedup) const
180 {
181     static const int SPEED_WARP1 = 6;
182     static const int SPEED_WARP2 = 10;
183 
184     int phases = 3;
185     if (m_active != m_units.end()) {
186         if ((*m_active)->isTurning()) {
187             phases = (*m_active)->countAnimPhases("turn");
188         }
189         else if (speedup > SPEED_WARP2) {
190             phases = (*m_active)->countAnimPhases("swam") / 6;
191         }
192         else if (speedup > SPEED_WARP1) {
193             phases = (*m_active)->countAnimPhases("swam") / 3;
194         }
195         else {
196             phases = (*m_active)->countAnimPhases("swam") / 2;
197         }
198     }
199     return phases;
200 }
201 //-----------------------------------------------------------------
202 /**
203  * Check whether active unit can still drive,
204  * otherwise make switch.
205  */
206 void
checkActive()207 Controls::checkActive()
208 {
209     if (m_active == m_units.end() || !(*m_active)->canDrive()) {
210         switchActive();
211     }
212 }
213 //-----------------------------------------------------------------
214 /**
215  * Switch active unit.
216  * Activate next driveable unit.
217  */
218 void
switchActive()219 Controls::switchActive()
220 {
221     if (!m_units.empty()) {
222         t_units::iterator start = m_active;
223 
224         do {
225             if (m_active == m_units.end() || m_active + 1 == m_units.end()) {
226                 m_active = m_units.begin();
227             }
228             else {
229                 ++m_active;
230             }
231         } while (m_active != start && !(*m_active)->canDrive());
232 
233         if (start != m_active) {
234             m_speedup = 0;
235             m_switch = true;
236         }
237     }
238 }
239 //-----------------------------------------------------------------
240 /**
241  * Obtain first control symbol from keyboard events.
242  */
243 void
controlEvent(const KeyStroke & stroke)244 Controls::controlEvent(const KeyStroke &stroke)
245 {
246     SDLKey key = stroke.getKey();
247 
248     if (m_strokeSymbol == ControlSym::SYM_NONE) {
249         if (m_active != m_units.end()) {
250             m_strokeSymbol = (*m_active)->mySymbolBorrowed(key, m_arrows);
251         }
252 
253         if (m_strokeSymbol == ControlSym::SYM_NONE) {
254             t_units::iterator end = m_units.end();
255             for (t_units::iterator i = m_units.begin(); i != end; ++i) {
256                 m_strokeSymbol = (*i)->mySymbol(key);
257                 if (m_strokeSymbol != ControlSym::SYM_NONE) {
258                     return;
259                 }
260             }
261         }
262     }
263 }
264 //-----------------------------------------------------------------
265 /**
266  * Activate fish under cursor.
267  * @param occupant model to activate
268  * @return true when fish was selected
269  */
270 bool
activateSelected(const Cube * occupant)271 Controls::activateSelected(const Cube *occupant)
272 {
273     t_units::iterator end = m_units.end();
274     for (t_units::iterator i = m_units.begin(); i != end; ++i) {
275         if ((*i)->equalsModel(occupant)) {
276             m_active = i;
277             m_switch = true;
278             return true;
279         }
280     }
281     return false;
282 }
283 //-----------------------------------------------------------------
284     void
setMoves(const std::string & moves)285 Controls::setMoves(const std::string &moves)
286 {
287     m_moves = moves;
288     if (!m_moves.empty()) {
289         activateDriven(m_moves[m_moves.size() - 1]);
290     }
291 }
292 //-----------------------------------------------------------------
293 /**
294  * Activate fish driven by given symbol.
295  * @param symbol one of fish symbols
296  * @return true when fish was selected
297  */
298 bool
activateDriven(char symbol)299 Controls::activateDriven(char symbol)
300 {
301     t_units::iterator end = m_units.end();
302     for (t_units::iterator i = m_units.begin(); i != end; ++i) {
303         if ((*i)->isDrivenBy(symbol)) {
304             m_active = i;
305             m_switch = true;
306             return true;
307         }
308     }
309     return false;
310 }
311 //-----------------------------------------------------------------
312 /**
313  * Change active unit.
314  * NOTE: change is without switch animation
315  */
316 void
setActive(t_units::iterator active)317 Controls::setActive(t_units::iterator active)
318 {
319     if (m_active != active) {
320         m_speedup = 0;
321         m_active = active;
322     }
323 }
324 //-----------------------------------------------------------------
325 /**
326  * Make this move.
327  * @return false for bad move
328  */
329 bool
makeMove(char move)330 Controls::makeMove(char move)
331 {
332     t_units::iterator end = m_units.end();
333     for (t_units::iterator i = m_units.begin(); i != end; ++i) {
334         if ((*i)->driveOrder(move) == move) {
335             setActive(i);
336             m_moves.append(1, move);
337             return true;
338         }
339     }
340     return false;
341 }
342 //-----------------------------------------------------------------
343 /**
344  * Returns true when there is no unit which will be able to move.
345  */
346 bool
cannotMove() const347 Controls::cannotMove() const
348 {
349     t_units::const_iterator end = m_units.end();
350     for (t_units::const_iterator i = m_units.begin(); i != end; ++i) {
351         if ((*i)->willMove()) {
352             return false;
353         }
354     }
355     return true;
356 }
357 //-----------------------------------------------------------------
358 /**
359  * Returns true when active fish is powerful.
360  */
361 bool
isPowerful() const362 Controls::isPowerful() const
363 {
364     bool result = false;
365     if (m_active != m_units.end()) {
366         result = (*m_active)->isPowerful();
367     }
368     return result;
369 }
370 //-----------------------------------------------------------------
371 /**
372  * Returns true when the active fish is doing a dangerous move.
373  */
374 bool
isDangerousMove() const375 Controls::isDangerousMove() const
376 {
377     bool result = false;
378     if (m_active != m_units.end()) {
379         result = (*m_active)->isPushing();
380     }
381     return result;
382 }
383 
384 
385