File indexing completed on 2024-04-28 04:04:39

0001 /*
0002     This file is part of the game 'KTron'
0003 
0004     SPDX-FileCopyrightText: 1998-2000 Matthias Kiefer <matthias.kiefer@gmx.de>
0005     SPDX-FileCopyrightText: 2005 Benjamin C. Meyer <ben at meyerhome dot net>
0006     SPDX-FileCopyrightText: 2008-2009 Stas Verberkt <legolas at legolasweb dot nl>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 
0010 */
0011 
0012 #include "player.h"
0013 
0014 #include "tron.h"
0015 #include "settings.h"
0016 
0017 #include "ksnakeduel_debug.h"
0018 #include <KLocalizedString>
0019 #include <KUser>
0020 
0021 Player::Player(PlayField &pf, int playerNr) : QObject()
0022 {
0023     m_playField = &pf;
0024     m_playerNumber = playerNr;
0025     m_computer = false;
0026 
0027     // Calling setName() with an empty string will cause the defaults
0028     setName(QString());
0029 
0030     m_score = 0;
0031     m_dir = PlayerDirections::Up;
0032     m_enlarge = 0;
0033     m_blockSwitchDir = false;
0034     
0035     reset();
0036 }
0037 
0038 //
0039 // Get / Set
0040 //
0041 
0042 int Player::getPlayerNumber()
0043 {
0044     return m_playerNumber;
0045 }
0046 
0047 int Player::getX()
0048 {
0049     if (m_snakeParts.isEmpty())
0050     {
0051         qCDebug(KSNAKEDUEL_LOG) << "Requested coordinate of nonexistent snake";
0052         return 0;
0053     }
0054     
0055     return m_snakeParts.last().getX();
0056 }
0057 
0058 int Player::getY()
0059 {
0060     if (m_snakeParts.isEmpty())
0061     {
0062         qCDebug(KSNAKEDUEL_LOG) << "Requested coordinate of nonexistent snake";
0063         return 0;
0064     }
0065     
0066     return m_snakeParts.last().getY();
0067 }
0068 
0069 int Player::getScore()
0070 {
0071     return m_score;
0072 }
0073 
0074 //
0075 // Player name
0076 //
0077 
0078 QString Player::getName() const
0079 {
0080     if (isComputer())
0081     {
0082         return i18n("KSnakeDuel");
0083     }
0084     else
0085     {
0086         return m_name;
0087     }
0088 }
0089 
0090 void Player::setName(const QString &name)
0091 {
0092     if (name.isEmpty())
0093     {
0094         // If first player, name it after the user
0095         KUser thisUser = KUser();
0096 
0097         if (m_playerNumber == 0 && thisUser.property(KUser::FullName).isValid())
0098         {
0099             this->m_name = thisUser.property(KUser::FullName).toString();
0100         }
0101         else
0102         {
0103             this->m_name = i18n("Player %1", m_playerNumber + 1);
0104         }
0105     }
0106     else
0107     {
0108         this->m_name = name;
0109     }
0110 }
0111 
0112 //
0113 // Snake growing
0114 //
0115 
0116 void Player::setEnlargement(int enlargement)
0117 {
0118     if (enlargement < 0)
0119     {
0120         return;
0121     }
0122 
0123     m_enlarge += enlargement;
0124 }
0125 
0126 //
0127 // Directions
0128 //
0129 
0130 PlayerDirections::Direction Player::getDirection()
0131 {
0132     return m_dir;
0133 }
0134 
0135 void Player::setDirection(PlayerDirections::Direction direction)
0136 {
0137     if (m_blockSwitchDir)
0138     {
0139         return;
0140     }
0141     else if (direction == PlayerDirections::Up && m_dir == PlayerDirections::Down)
0142     {
0143         return;
0144     }
0145     else if (direction == PlayerDirections::Down && m_dir == PlayerDirections::Up)
0146     {
0147         return;
0148     }
0149     else if (direction == PlayerDirections::Left && m_dir == PlayerDirections::Right)
0150     {
0151         return;
0152     }
0153     else if (direction == PlayerDirections::Right && m_dir == PlayerDirections::Left)
0154     {
0155         return;
0156     }
0157     
0158     m_dir = direction;
0159     m_blockSwitchDir = true;
0160 }
0161 
0162 //
0163 // Live Control
0164 //
0165 
0166 bool Player::isAlive()
0167 {
0168     return m_alive;
0169 }
0170 
0171 void Player::die()
0172 {
0173     m_alive = false;
0174 }
0175 
0176 //
0177 // Score updating
0178 //
0179 
0180 void Player::addScore(int increment)
0181 {
0182     if (increment < 0)
0183     {
0184         return;
0185     }
0186 
0187     m_score += increment;
0188 }
0189 
0190 void Player::resetScore()
0191 {
0192     m_score = 0;
0193 }
0194 
0195 //
0196 // Start
0197 //
0198 
0199 void Player::setStartPosition()
0200 {
0201     while (!m_snakeParts.isEmpty())
0202     {
0203         m_snakeParts.dequeue();
0204     }
0205     
0206     int x = (2 - getPlayerNumber()) * m_playField->getWidth() / 3;
0207     int y = m_playField->getHeight() / 2;
0208     
0209     SnakePart head(getPlayerNumber());
0210     head.setPartTop(true);
0211     head.setPartLeft(true);
0212     head.setPartRight(true);
0213     head.setPartType(SnakePartType::Head);
0214     head.generateSVGName();
0215     
0216     SnakePart tail(getPlayerNumber());
0217     tail.setPartBottom(true);
0218     tail.setPartLeft(true);
0219     tail.setPartRight(true);
0220     if (Settings::gameType() == Settings::EnumGameType::Snake)
0221     {
0222         tail.setPartType(SnakePartType::Tail);
0223     }
0224     else
0225     {
0226         tail.setPartType(SnakePartType::Hole);
0227     }
0228     tail.generateSVGName();
0229     
0230     m_playField->setObjectAt(x, y, head);
0231     m_playField->setObjectAt(x, y + 1, tail);
0232     
0233     m_snakeParts.enqueue(tail);
0234     m_snakeParts.enqueue(head);
0235     
0236     // Make the computer player start some random direction
0237     if (isComputer())
0238     {
0239         switch ((int)(rand() % 3))
0240         {
0241             default:
0242             case 0:
0243                 m_dir = PlayerDirections::Up;
0244                 break;
0245             case 1:
0246                 m_dir = PlayerDirections::Left;
0247                 break;
0248             case 2:
0249                 m_dir = PlayerDirections::Right;
0250                 break;
0251         }
0252         
0253         m_blockSwitchDir = true;
0254     }
0255     else
0256     {
0257         m_dir = PlayerDirections::Up;
0258     }
0259 }
0260 
0261 //
0262 // Movement
0263 //
0264 
0265 void Player::movePlayer()
0266 {
0267     int oldX = m_snakeParts.last().getX();
0268     int oldY = m_snakeParts.last().getY();
0269     SnakePart newHead(m_playerNumber);
0270 
0271     int newX = oldX;
0272     int newY = oldY;
0273 
0274     newHead.setPartType(SnakePartType::Head);
0275 
0276     switch (m_dir)
0277     {
0278         case PlayerDirections::Up:
0279             newY--;
0280             newHead.setPartTop(true);
0281             newHead.setPartLeft(true);
0282             newHead.setPartRight(true);
0283             break;
0284         case PlayerDirections::Down:
0285             newY++;
0286             newHead.setPartBottom(true);
0287             newHead.setPartLeft(true);
0288             newHead.setPartRight(true);
0289             break;
0290         case PlayerDirections::Left:
0291             newX--;
0292             newHead.setPartTop(true);
0293             newHead.setPartBottom(true);
0294             newHead.setPartLeft(true);
0295             break;
0296         case PlayerDirections::Right:
0297             newX++;
0298             newHead.setPartTop(true);
0299             newHead.setPartBottom(true);
0300             newHead.setPartRight(true);
0301             break;
0302         default:
0303             break;
0304     }
0305     
0306     if (crashed(newX, newY))
0307     {
0308         //qCDebug(KSNAKEDUEL_LOG) << "Crashed at: (" << newX << ", " << newY << ")";
0309         m_alive = false;
0310     }
0311     
0312     if (m_alive)
0313     {
0314         switch (m_dir)
0315         {
0316             // unset drawing flags in the moving direction
0317             case PlayerDirections::Up:
0318                 m_snakeParts.last().setPartTop(false);
0319                 break;
0320             case PlayerDirections::Down:
0321                 m_snakeParts.last().setPartBottom(false);
0322                 break;
0323             case PlayerDirections::Right:
0324                 m_snakeParts.last().setPartRight(false);
0325                 break;
0326             case PlayerDirections::Left:
0327                 m_snakeParts.last().setPartLeft(false);
0328                 break;
0329             default:
0330                 break;
0331         }
0332         m_snakeParts.last().setPartType(SnakePartType::Body);
0333         m_snakeParts.last().generateSVGName();
0334         m_playField->setObjectAt(m_snakeParts.last().getX(), m_snakeParts.last().getY(), m_snakeParts.last());
0335         
0336         if (m_playField->getObjectAt(newX, newY)->getObjectType() == ObjectType::Item)
0337         {
0338             //qCDebug(KSNAKEDUEL_LOG) << "Boom!";
0339             Q_EMIT fetchedItem(m_playerNumber, newX, newY);
0340         }
0341 
0342         newHead.generateSVGName();
0343         m_playField->setObjectAt(newX, newY, newHead);
0344         m_snakeParts.enqueue(newHead);
0345     }
0346 
0347     if (m_alive && m_enlarge == 0 && Settings::gameType() == Settings::EnumGameType::Snake)
0348     {
0349         SnakePart oldTail = m_snakeParts.dequeue();
0350         
0351         if (!oldTail.getPartTop())
0352         {
0353             m_snakeParts.head().setPartBottom(true);
0354         }
0355         else if (!oldTail.getPartBottom())
0356         {
0357             m_snakeParts.head().setPartTop(true);
0358         }
0359         else if (!oldTail.getPartLeft())
0360         {
0361             m_snakeParts.head().setPartRight(true);
0362         }
0363         else if (!oldTail.getPartRight())
0364         {
0365             m_snakeParts.head().setPartLeft(true);
0366         }
0367         
0368         m_snakeParts.head().setPartType(SnakePartType::Tail);
0369         m_snakeParts.head().generateSVGName();
0370         m_playField->setObjectAt(m_snakeParts.head().getX(), m_snakeParts.head().getY(), m_snakeParts.head());
0371         
0372         Object emptyObject = Object();
0373         m_playField->setObjectAt(oldTail.getX(), oldTail.getY(), emptyObject);
0374     }
0375     else if (m_enlarge > 0)
0376     {
0377         m_enlarge--;
0378     }
0379     
0380     m_blockSwitchDir = false;
0381 }
0382 
0383 //
0384 // Crash check
0385 //
0386 
0387 bool Player::crashed(int x, int y)
0388 {
0389     if (x < 0 || y < 0 || x >= m_playField->getWidth() || y >= m_playField->getHeight())
0390     {
0391         return true;
0392     }
0393     else
0394     {
0395         ObjectType::Type objType = m_playField->getObjectAt(x, y)->getObjectType();
0396         return (objType != ObjectType::Item && objType != ObjectType::Object);
0397     }
0398 }
0399 
0400 //
0401 // Reset
0402 //
0403 
0404 void Player::reset()
0405 {
0406     m_alive = true;
0407     m_accelerated = false;
0408     m_enlarge = 0;
0409     m_keyPressed = m_computer;
0410     m_blockSwitchDir = false;
0411 }
0412 
0413 //
0414 // Computer powered
0415 //
0416 
0417 bool Player::isComputer() const
0418 {
0419     return m_computer;
0420 }
0421 
0422 void Player::setComputer(bool isComputer)
0423 {
0424     m_computer = isComputer;
0425     m_keyPressed = m_computer;
0426 }
0427 
0428 //
0429 // Acceleration
0430 //
0431 
0432 bool Player::isAccelerated()
0433 {
0434     return m_accelerated;
0435 }
0436 
0437 void Player::setAccelerated(bool value)
0438 {
0439     m_accelerated = value;
0440 }
0441 
0442 
0443 //
0444 // Key pressed
0445 //
0446 
0447 bool Player::hasKeyPressed()
0448 {
0449     return m_keyPressed;
0450 }
0451 
0452 void Player::setKeyPressed(bool value)
0453 {
0454     m_keyPressed = value;
0455 }
0456 
0457 #include "moc_player.cpp"