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
34Player::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
55int Player::getPlayerNumber()
56{
57 return m_playerNumber;
58}
59
60int 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
71int 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
82int Player::getScore()
83{
84 return m_score;
85}
86
87//
88// Player name
89//
90
91QString Player::getName()
92{
93 if (isComputer())
94 {
95 return i18n("KSnakeDuel");
96 }
97 else
98 {
99 return m_name;
100 }
101}
102
103void 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
129void 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
143PlayerDirections::Direction Player::getDirection()
144{
145 return m_dir;
146}
147
148void 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
179bool Player::isAlive()
180{
181 return m_alive;
182}
183
184void Player::die()
185{
186 m_alive = false;
187}
188
189//
190// Score updating
191//
192
193void Player::addScore(int increment)
194{
195 if (increment < 0)
196 {
197 return;
198 }
199
200 m_score += increment;
201}
202
203void Player::resetScore()
204{
205 m_score = 0;
206}
207
208//
209// Start
210//
211
212void 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
278void 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
400bool 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
417void 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
430bool Player::isComputer()
431{
432 return m_computer;
433}
434
435void Player::setComputer(bool isComputer)
436{
437 m_computer = isComputer;
438 m_keyPressed = m_computer;
439}
440
441//
442// Acceleration
443//
444
445bool Player::isAccelerated()
446{
447 return m_accelerated;
448}
449
450void Player::setAccelerated(bool value)
451{
452 m_accelerated = value;
453}
454
455
456//
457// Key pressed
458//
459
460bool Player::hasKeyPressed()
461{
462 return m_keyPressed;
463}
464
465void Player::setKeyPressed(bool value)
466{
467 m_keyPressed = value;
468}
469
470