1 | /********************************************************************************** |
2 | This file is part of the game 'KTron' |
3 | |
4 | Copyright (C) 1998-2000 by Matthias Kiefer <matthias.kiefer@gmx.de> |
5 | Copyright (C) 2005 Benjamin C. Meyer <ben at meyerhome dot net> |
6 | Copyright (C) 2008-2009 Stas Verberkt <legolas at legolasweb dot nl> |
7 | |
8 | This program is free software; you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation; either version 2 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
21 | |
22 | *******************************************************************************/ |
23 | |
24 | #include "player.h" |
25 | |
26 | #include "tron.h" |
27 | #include "snakepart.h" |
28 | #include "settings.h" |
29 | |
30 | #include <KDebug> |
31 | #include <KLocale> |
32 | #include <KUser> |
33 | |
34 | Player::Player(PlayField &pf, int playerNr) : QObject() |
35 | { |
36 | m_playField = &pf; |
37 | m_playerNumber = playerNr; |
38 | m_computer = false; |
39 | |
40 | // Calling setName() with an empty string will cause the defaults |
41 | setName(QString()); |
42 | |
43 | m_score = 0; |
44 | m_dir = PlayerDirections::Up; |
45 | m_enlarge = 0; |
46 | m_blockSwitchDir = false; |
47 | |
48 | reset(); |
49 | } |
50 | |
51 | // |
52 | // Get / Set |
53 | // |
54 | |
55 | int Player::getPlayerNumber() |
56 | { |
57 | return m_playerNumber; |
58 | } |
59 | |
60 | int Player::getX() |
61 | { |
62 | if (m_snakeParts.isEmpty()) |
63 | { |
64 | kDebug() << "Requested coordinate of nonexistent snake" ; |
65 | return 0; |
66 | } |
67 | |
68 | return m_snakeParts.last().getX(); |
69 | } |
70 | |
71 | int Player::getY() |
72 | { |
73 | if (m_snakeParts.isEmpty()) |
74 | { |
75 | kDebug() << "Requested coordinate of nonexistent snake" ; |
76 | return 0; |
77 | } |
78 | |
79 | return m_snakeParts.last().getY(); |
80 | } |
81 | |
82 | int Player::getScore() |
83 | { |
84 | return m_score; |
85 | } |
86 | |
87 | // |
88 | // Player name |
89 | // |
90 | |
91 | QString Player::getName() |
92 | { |
93 | if (isComputer()) |
94 | { |
95 | return i18n("KSnakeDuel" ); |
96 | } |
97 | else |
98 | { |
99 | return m_name; |
100 | } |
101 | } |
102 | |
103 | void Player::setName(QString name) |
104 | { |
105 | if (name.isEmpty()) |
106 | { |
107 | // If first player, name it after the user |
108 | KUser thisUser = KUser(); |
109 | |
110 | if (m_playerNumber == 0 && thisUser.property(KUser::FullName).isValid()) |
111 | { |
112 | this->m_name = thisUser.property(KUser::FullName).toString(); |
113 | } |
114 | else |
115 | { |
116 | this->m_name = i18n("Player %1" , m_playerNumber + 1); |
117 | } |
118 | } |
119 | else |
120 | { |
121 | this->m_name = name; |
122 | } |
123 | } |
124 | |
125 | // |
126 | // Snake growing |
127 | // |
128 | |
129 | void Player::setEnlargement(int enlargement) |
130 | { |
131 | if (enlargement < 0) |
132 | { |
133 | return; |
134 | } |
135 | |
136 | m_enlarge += enlargement; |
137 | } |
138 | |
139 | // |
140 | // Directions |
141 | // |
142 | |
143 | PlayerDirections::Direction Player::getDirection() |
144 | { |
145 | return m_dir; |
146 | } |
147 | |
148 | void Player::setDirection(PlayerDirections::Direction direction) |
149 | { |
150 | if (m_blockSwitchDir) |
151 | { |
152 | return; |
153 | } |
154 | else if (direction == PlayerDirections::Up && m_dir == PlayerDirections::Down) |
155 | { |
156 | return; |
157 | } |
158 | else if (direction == PlayerDirections::Down && m_dir == PlayerDirections::Up) |
159 | { |
160 | return; |
161 | } |
162 | else if (direction == PlayerDirections::Left && m_dir == PlayerDirections::Right) |
163 | { |
164 | return; |
165 | } |
166 | else if (direction == PlayerDirections::Right && m_dir == PlayerDirections::Left) |
167 | { |
168 | return; |
169 | } |
170 | |
171 | m_dir = direction; |
172 | m_blockSwitchDir = true; |
173 | } |
174 | |
175 | // |
176 | // Live Control |
177 | // |
178 | |
179 | bool Player::isAlive() |
180 | { |
181 | return m_alive; |
182 | } |
183 | |
184 | void Player::die() |
185 | { |
186 | m_alive = false; |
187 | } |
188 | |
189 | // |
190 | // Score updating |
191 | // |
192 | |
193 | void Player::addScore(int increment) |
194 | { |
195 | if (increment < 0) |
196 | { |
197 | return; |
198 | } |
199 | |
200 | m_score += increment; |
201 | } |
202 | |
203 | void Player::resetScore() |
204 | { |
205 | m_score = 0; |
206 | } |
207 | |
208 | // |
209 | // Start |
210 | // |
211 | |
212 | void Player::setStartPosition() |
213 | { |
214 | while (!m_snakeParts.isEmpty()) |
215 | { |
216 | m_snakeParts.dequeue(); |
217 | } |
218 | |
219 | int x = (2 - getPlayerNumber()) * m_playField->getWidth() / 3; |
220 | int y = m_playField->getHeight() / 2; |
221 | |
222 | SnakePart head(getPlayerNumber()); |
223 | head.setPartTop(true); |
224 | head.setPartLeft(true); |
225 | head.setPartRight(true); |
226 | head.setPartType(SnakePartType::Head); |
227 | head.generateSVGName(); |
228 | |
229 | SnakePart tail(getPlayerNumber()); |
230 | tail.setPartBottom(true); |
231 | tail.setPartLeft(true); |
232 | tail.setPartRight(true); |
233 | if (Settings::gameType() == Settings::EnumGameType::Snake) |
234 | { |
235 | tail.setPartType(SnakePartType::Tail); |
236 | } |
237 | else |
238 | { |
239 | tail.setPartType(SnakePartType::Hole); |
240 | } |
241 | tail.generateSVGName(); |
242 | |
243 | m_playField->setObjectAt(x, y, head); |
244 | m_playField->setObjectAt(x, y + 1, tail); |
245 | |
246 | m_snakeParts.enqueue(tail); |
247 | m_snakeParts.enqueue(head); |
248 | |
249 | // Make the computer player start some random direction |
250 | if (isComputer()) |
251 | { |
252 | switch ((int)(rand() % 3)) |
253 | { |
254 | default: |
255 | case 0: |
256 | m_dir = PlayerDirections::Up; |
257 | break; |
258 | case 1: |
259 | m_dir = PlayerDirections::Left; |
260 | break; |
261 | case 2: |
262 | m_dir = PlayerDirections::Right; |
263 | break; |
264 | } |
265 | |
266 | m_blockSwitchDir = true; |
267 | } |
268 | else |
269 | { |
270 | m_dir = PlayerDirections::Up; |
271 | } |
272 | } |
273 | |
274 | // |
275 | // Movement |
276 | // |
277 | |
278 | void Player::movePlayer() |
279 | { |
280 | int oldX = m_snakeParts.last().getX(); |
281 | int oldY = m_snakeParts.last().getY(); |
282 | SnakePart newHead(m_playerNumber); |
283 | |
284 | int newX = oldX; |
285 | int newY = oldY; |
286 | |
287 | newHead.setPartType(SnakePartType::Head); |
288 | |
289 | switch (m_dir) |
290 | { |
291 | case PlayerDirections::Up: |
292 | newY--; |
293 | newHead.setPartTop(true); |
294 | newHead.setPartLeft(true); |
295 | newHead.setPartRight(true); |
296 | break; |
297 | case PlayerDirections::Down: |
298 | newY++; |
299 | newHead.setPartBottom(true); |
300 | newHead.setPartLeft(true); |
301 | newHead.setPartRight(true); |
302 | break; |
303 | case PlayerDirections::Left: |
304 | newX--; |
305 | newHead.setPartTop(true); |
306 | newHead.setPartBottom(true); |
307 | newHead.setPartLeft(true); |
308 | break; |
309 | case PlayerDirections::Right: |
310 | newX++; |
311 | newHead.setPartTop(true); |
312 | newHead.setPartBottom(true); |
313 | newHead.setPartRight(true); |
314 | break; |
315 | default: |
316 | break; |
317 | } |
318 | |
319 | if (crashed(newX, newY)) |
320 | { |
321 | //kDebug() << "Crashed at: (" << newX << ", " << newY << ")"; |
322 | m_alive = false; |
323 | } |
324 | |
325 | if (m_alive) |
326 | { |
327 | switch (m_dir) |
328 | { |
329 | // unset drawing flags in the moving direction |
330 | case PlayerDirections::Up: |
331 | m_snakeParts.last().setPartTop(false); |
332 | break; |
333 | case PlayerDirections::Down: |
334 | m_snakeParts.last().setPartBottom(false); |
335 | break; |
336 | case PlayerDirections::Right: |
337 | m_snakeParts.last().setPartRight(false); |
338 | break; |
339 | case PlayerDirections::Left: |
340 | m_snakeParts.last().setPartLeft(false); |
341 | break; |
342 | default: |
343 | break; |
344 | } |
345 | m_snakeParts.last().setPartType(SnakePartType::Body); |
346 | m_snakeParts.last().generateSVGName(); |
347 | m_playField->setObjectAt(m_snakeParts.last().getX(), m_snakeParts.last().getY(), m_snakeParts.last()); |
348 | |
349 | if (m_playField->getObjectAt(newX, newY)->getObjectType() == ObjectType::Item) |
350 | { |
351 | //kDebug() << "Boom!"; |
352 | emit fetchedItem(m_playerNumber, newX, newY); |
353 | } |
354 | |
355 | newHead.generateSVGName(); |
356 | m_playField->setObjectAt(newX, newY, newHead); |
357 | m_snakeParts.enqueue(newHead); |
358 | } |
359 | |
360 | if (m_alive && m_enlarge == 0 && Settings::gameType() == Settings::EnumGameType::Snake) |
361 | { |
362 | SnakePart oldTail = m_snakeParts.dequeue(); |
363 | |
364 | if (!oldTail.getPartTop()) |
365 | { |
366 | m_snakeParts.head().setPartBottom(true); |
367 | } |
368 | else if (!oldTail.getPartBottom()) |
369 | { |
370 | m_snakeParts.head().setPartTop(true); |
371 | } |
372 | else if (!oldTail.getPartLeft()) |
373 | { |
374 | m_snakeParts.head().setPartRight(true); |
375 | } |
376 | else if (!oldTail.getPartRight()) |
377 | { |
378 | m_snakeParts.head().setPartLeft(true); |
379 | } |
380 | |
381 | m_snakeParts.head().setPartType(SnakePartType::Tail); |
382 | m_snakeParts.head().generateSVGName(); |
383 | m_playField->setObjectAt(m_snakeParts.head().getX(), m_snakeParts.head().getY(), m_snakeParts.head()); |
384 | |
385 | Object emptyObject = Object(); |
386 | m_playField->setObjectAt(oldTail.getX(), oldTail.getY(), emptyObject); |
387 | } |
388 | else if (m_enlarge > 0) |
389 | { |
390 | m_enlarge--; |
391 | } |
392 | |
393 | m_blockSwitchDir = false; |
394 | } |
395 | |
396 | // |
397 | // Crash check |
398 | // |
399 | |
400 | bool Player::crashed(int x, int y) |
401 | { |
402 | if (x < 0 || y < 0 || x >= m_playField->getWidth() || y >= m_playField->getHeight()) |
403 | { |
404 | return true; |
405 | } |
406 | else |
407 | { |
408 | ObjectType::Type objType = m_playField->getObjectAt(x, y)->getObjectType(); |
409 | return (objType != ObjectType::Item && objType != ObjectType::Object); |
410 | } |
411 | } |
412 | |
413 | // |
414 | // Reset |
415 | // |
416 | |
417 | void Player::reset() |
418 | { |
419 | m_alive = true; |
420 | m_accelerated = false; |
421 | m_enlarge = 0; |
422 | m_keyPressed = m_computer; |
423 | m_blockSwitchDir = false; |
424 | } |
425 | |
426 | // |
427 | // Computer powered |
428 | // |
429 | |
430 | bool Player::isComputer() |
431 | { |
432 | return m_computer; |
433 | } |
434 | |
435 | void Player::setComputer(bool isComputer) |
436 | { |
437 | m_computer = isComputer; |
438 | m_keyPressed = m_computer; |
439 | } |
440 | |
441 | // |
442 | // Acceleration |
443 | // |
444 | |
445 | bool Player::isAccelerated() |
446 | { |
447 | return m_accelerated; |
448 | } |
449 | |
450 | void Player::setAccelerated(bool value) |
451 | { |
452 | m_accelerated = value; |
453 | } |
454 | |
455 | |
456 | // |
457 | // Key pressed |
458 | // |
459 | |
460 | bool Player::hasKeyPressed() |
461 | { |
462 | return m_keyPressed; |
463 | } |
464 | |
465 | void Player::setKeyPressed(bool value) |
466 | { |
467 | m_keyPressed = value; |
468 | } |
469 | |
470 | |