File indexing completed on 2024-05-05 05:40:30

0001 /*************************************************************************
0002  *     Copyright (C) 2011 by Joseph Boudou                               *
0003  *     Copyright (C) 2014 by Renaud Guezennec                            *
0004  *                                                                       *
0005  *     https://rolisteam.org/                                         *
0006  *                                                                       *
0007  *   Rolisteam is free software; you can redistribute it and/or modify   *
0008  *   it under the terms of the GNU General Public License as published   *
0009  *   by the Free Software Foundation; either version 2 of the License,   *
0010  *   or (at your option) any later version.                              *
0011  *                                                                       *
0012  *   This program is distributed in the hope that it will be useful,     *
0013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of      *
0014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       *
0015  *   GNU General Public License for more details.                        *
0016  *                                                                       *
0017  *   You should have received a copy of the GNU General Public License   *
0018  *   along with this program; if not, write to the                       *
0019  *   Free Software Foundation, Inc.,                                     *
0020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           *
0021  *************************************************************************/
0022 
0023 #include <QDebug>
0024 #include <QLoggingCategory>
0025 #include <QPalette>
0026 
0027 #include "model/playermodel.h"
0028 
0029 #include "data/character.h"
0030 #include "data/person.h"
0031 #include "data/player.h"
0032 #include "network/networkmessagewriter.h"
0033 #include "utils/iohelper.h"
0034 
0035 QLoggingCategory rUser("rolisteam.user");
0036 
0037 template <typename T>
0038 void convertVariantToType(const T& val, NetworkMessageWriter& msg)
0039 {
0040     msg.string32(val);
0041 }
0042 
0043 template <>
0044 void convertVariantToType<QColor>(const QColor& val, NetworkMessageWriter& msg)
0045 {
0046     msg.rgb(val.rgb());
0047 }
0048 
0049 template <>
0050 void convertVariantToType<QImage>(const QImage& val, NetworkMessageWriter& msg)
0051 {
0052     QByteArray data;
0053     QDataStream in(&data, QIODevice::WriteOnly);
0054     in.setVersion(QDataStream::Qt_5_7);
0055     in << val;
0056     msg.byteArray32(data);
0057 }
0058 
0059 template <>
0060 void convertVariantToType<CharacterState*>(CharacterState* const& val, NetworkMessageWriter& msg)
0061 {
0062     if(val)
0063         msg.string32(val->label());
0064     else
0065     {
0066         msg.string32(QStringLiteral(""));
0067     }
0068 }
0069 
0070 Player* findPerson(const std::vector<std::unique_ptr<Player>>& data, Person* person)
0071 {
0072     auto it= std::find_if(data.begin(), data.end(),
0073                           [person](const std::unique_ptr<Player>& player)
0074                           {
0075                               bool val= ((player.get() == person) || (person->uuid() == player->uuid()));
0076 
0077                               if(!val)
0078                               {
0079                                   const auto& children= player->children();
0080                                   auto subIt= std::find_if(children.begin(), children.end(),
0081                                                            [person](const std::unique_ptr<Character>& character) {
0082                                                                return (character.get() == person)
0083                                                                       || (person->uuid() == character->uuid());
0084                                                            });
0085                                   val= (subIt != children.end());
0086                               }
0087                               return val;
0088                           });
0089     return it->get();
0090 }
0091 
0092 bool contains(const std::vector<std::unique_ptr<Player>>& data, Person* person)
0093 {
0094     auto it= std::find_if(data.begin(), data.end(),
0095                           [person](const std::unique_ptr<Player>& player)
0096                           {
0097                               bool val= ((player.get() == person) || (person->uuid() == player->uuid()));
0098 
0099                               if(!val)
0100                               {
0101                                   const auto& children= player->children();
0102                                   auto subIt= std::find_if(children.begin(), children.end(),
0103                                                            [person](const std::unique_ptr<Character>& character) {
0104                                                                return (character.get() == person)
0105                                                                       || (person->uuid() == character->uuid());
0106                                                            });
0107                                   val= (subIt != children.end());
0108                               }
0109                               return val;
0110                           });
0111     return it == data.end();
0112 }
0113 
0114 bool isLocal(Person* person, const QString& uuid)
0115 {
0116     if(!person)
0117         return false;
0118 
0119     if(person->isLeaf())
0120         person= person->parentPerson();
0121 
0122     if(!person)
0123         return false;
0124 
0125     return (uuid == person->uuid());
0126 }
0127 
0128 PlayerModel::PlayerModel(QObject* parent) : QAbstractItemModel(parent) {}
0129 
0130 PlayerModel::~PlayerModel()= default;
0131 
0132 /*************
0133  * ItemModel *
0134  *************/
0135 
0136 QVariant PlayerModel::data(const QModelIndex& index, int role) const
0137 {
0138     if(!index.isValid() || index.column() != 0)
0139         return QVariant();
0140 
0141     Person* person= static_cast<Person*>(index.internalPointer());
0142 
0143     if(!person)
0144         return {};
0145 
0146     Character* character= dynamic_cast<Character*>(person);
0147     Player* player= dynamic_cast<Player*>(person);
0148     bool isGM= false;
0149     bool isCharacter= (character != nullptr);
0150     bool isNPC= false;
0151 
0152     if(isCharacter)
0153         isNPC= character->isNpc();
0154 
0155     if((player != nullptr))
0156     {
0157         isGM= player->isGM();
0158     }
0159     if(isGM && (role == Qt::BackgroundRole))
0160     {
0161         QPalette pal;
0162         return QVariant(pal.color(QPalette::Active, QPalette::Dark));
0163     }
0164     QVariant var;
0165     switch(role)
0166     {
0167     case Qt::DisplayRole:
0168     case Qt::EditRole:
0169     {
0170         auto name= person->name();
0171         if((nullptr != character) && (character->hasInitScore()))
0172         {
0173             name= QStringLiteral("%1 (%2)").arg(name).arg(character->getInitiativeScore());
0174         }
0175         var= name;
0176         break;
0177     }
0178     case Qt::ToolTipRole:
0179     {
0180         if(nullptr != character)
0181         {
0182             QString stateName(tr("Not defined"));
0183             stateName= character->stateId();
0184 
0185             var= tr("%1:\n"
0186                     "HP: %2/%3\n"
0187                     "State: %4\n"
0188                     "Initiative Score: %5\n"
0189                     "Distance Per Turn: %6\n"
0190                     "type: %7\n")
0191                      .arg(character->name())
0192                      .arg(character->getHealthPointsCurrent())
0193                      .arg(character->getHealthPointsMax())
0194                      .arg(stateName)
0195                      .arg(character->getInitiativeScore())
0196                      .arg(character->getDistancePerTurn())
0197                      .arg(character->isNpc() ? tr("NPC") : tr("PC"));
0198         }
0199         break;
0200     }
0201     case PlayerModel::ColorRole:
0202     case Qt::DecorationRole:
0203     {
0204         if(!person->avatar().isNull()) // (person->isLeaf()) &&
0205         {
0206             var= utils::IOHelper::dataToImage(person->avatar())
0207                      .scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation);
0208         }
0209         else
0210             var= QVariant(person->getColor());
0211         break;
0212     }
0213     case PlayerModel::IdentifierRole:
0214         var= person->uuid();
0215         break;
0216     case PlayerModel::PersonPtrRole:
0217         var= QVariant::fromValue(person);
0218         break;
0219     case PlayerModel::CharacterRole:
0220         var= person->isLeaf();
0221         break;
0222     case PlayerModel::NpcRole:
0223         var= isNPC;
0224         break;
0225     case PlayerModel::NameRole:
0226         var= person->name();
0227         break;
0228     case PlayerModel::CharacterStateIdRole:
0229     {
0230         if(nullptr != character)
0231             var= character->stateId();
0232     }
0233     break;
0234     case PlayerModel::LocalRole:
0235         var= isLocal(person, m_localPlayerId);
0236         break;
0237     case PlayerModel::GmRole:
0238         var= isGM;
0239         break;
0240     case PlayerModel::AvatarRole:
0241         var= utils::IOHelper::dataToImage(person->avatar());
0242         break;
0243     }
0244 
0245     return var;
0246 }
0247 
0248 Qt::ItemFlags PlayerModel::flags(const QModelIndex& index) const
0249 {
0250     if(!index.isValid())
0251         return Qt::NoItemFlags;
0252 
0253     if(index.row() == 0 || gameMasterId() == localPlayerId())
0254         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
0255     else
0256         return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0257 }
0258 
0259 QVariant PlayerModel::headerData(int section, Qt::Orientation orientation, int role) const
0260 {
0261     Q_UNUSED(section)
0262     if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
0263         return QVariant(tr("Players List"));
0264     return QVariant();
0265 }
0266 
0267 QModelIndex PlayerModel::index(int row, int column, const QModelIndex& parent) const
0268 {
0269     if(column != 0 || row < 0)
0270         return QModelIndex();
0271     Person* childItem= nullptr;
0272     auto row_t= static_cast<std::size_t>(row);
0273     if(parent.isValid())
0274     {
0275         auto parentPerson= static_cast<Person*>(parent.internalPointer());
0276         auto parentPlayer= dynamic_cast<Player*>(parentPerson);
0277         if(nullptr == parentPlayer)
0278             return {};
0279         childItem= parentPlayer->getCharacterByIndex(row);
0280     }
0281     else if(m_players.size() > row_t)
0282     {
0283         childItem= m_players[row_t].get();
0284     }
0285 
0286     if(childItem)
0287         return createIndex(row, column, childItem);
0288     else
0289         return QModelIndex();
0290 }
0291 
0292 QModelIndex PlayerModel::parent(const QModelIndex& index) const
0293 {
0294     if(!index.isValid())
0295         return QModelIndex();
0296 
0297     auto childItem= static_cast<Person*>(index.internalPointer());
0298     auto parentPerson= childItem->parentPerson();
0299 
0300     if(nullptr == parentPerson)
0301     {
0302         return QModelIndex();
0303     }
0304     auto it
0305         = std::find_if(m_players.begin(), m_players.end(),
0306                        [parentPerson](const std::unique_ptr<Player>& person) { return parentPerson == person.get(); });
0307 
0308     return createIndex(static_cast<int>(std::distance(m_players.begin(), it)), 0, parentPerson);
0309 }
0310 
0311 int PlayerModel::rowCount(const QModelIndex& parent) const
0312 {
0313     int result= 0;
0314     if(!parent.isValid())
0315     {
0316         result= static_cast<int>(m_players.size());
0317     }
0318     else
0319     {
0320         auto parentItem= static_cast<Person*>(parent.internalPointer());
0321         auto player= dynamic_cast<Player*>(parentItem);
0322         if(player)
0323         {
0324             result= player->characterCount();
0325         }
0326     }
0327     return result;
0328 }
0329 
0330 int PlayerModel::columnCount(const QModelIndex& parent) const
0331 {
0332     Q_UNUSED(parent);
0333     return 1;
0334 }
0335 
0336 bool PlayerModel::setData(const QModelIndex& index, const QVariant& value, int role)
0337 {
0338     bool val= false;
0339     auto childItem= static_cast<Person*>(index.internalPointer());
0340     if(nullptr == childItem)
0341         return val;
0342 
0343     QVector<int> roles;
0344     switch(role)
0345     {
0346     case Qt::DisplayRole:
0347     case Qt::EditRole:
0348     case NameRole:
0349         childItem->setName(value.toString());
0350         roles << role << Qt::DisplayRole << Qt::EditRole << NameRole;
0351         val= true;
0352         break;
0353     case GmRole:
0354         break;
0355     case CharacterRole:
0356         break;
0357     case CharacterStateIdRole:
0358         break;
0359     case NpcRole:
0360         break;
0361     case AvatarRole:
0362         break;
0363     case Qt::DecorationRole:
0364     case PlayerModel::ColorRole:
0365         childItem->setColor(value.value<QColor>());
0366         val= true;
0367         roles << PlayerModel::ColorRole << Qt::DecorationRole;
0368         break;
0369     case LocalRole:
0370     case IdentifierRole:
0371     case PersonPtrRole:
0372     default:
0373         break;
0374     }
0375     if(val)
0376         emit dataChanged(index, index, roles);
0377     return val;
0378 }
0379 
0380 QModelIndex PlayerModel::personToIndex(Person* person) const
0381 {
0382     QModelIndex parent;
0383     int row= -1;
0384     if(person->isLeaf())
0385     {
0386         auto player= dynamic_cast<Player*>(person->parentPerson());
0387         auto posIt= std::find_if(m_players.begin(), m_players.end(),
0388                                  [player](const std::unique_ptr<Player>& person) { return player == person.get(); });
0389         if(posIt != m_players.end())
0390         {
0391             parent= index(static_cast<int>(std::distance(m_players.begin(), posIt)), 0, QModelIndex());
0392             row= player->indexOf(dynamic_cast<Character*>(person));
0393         }
0394     }
0395     else
0396     {
0397         auto player= dynamic_cast<Player*>(person);
0398         auto posIt= std::find_if(m_players.begin(), m_players.end(),
0399                                  [player](const std::unique_ptr<Player>& person) { return player == person.get(); });
0400         if(posIt != m_players.end())
0401             row= static_cast<int>(std::distance(m_players.begin(), posIt));
0402     }
0403     return index(row, 0, parent);
0404 }
0405 
0406 QString PlayerModel::gameMasterId() const
0407 {
0408     return m_gameMasterId;
0409 }
0410 
0411 QString PlayerModel::localPlayerId() const
0412 {
0413     return m_localPlayerId;
0414 }
0415 
0416 QHash<int, QByteArray> PlayerModel::roleNames() const
0417 {
0418     static QHash<int, QByteArray> role({{IdentifierRole, "uuid"},
0419                                         {PersonPtrRole, "person"},
0420                                         {NameRole, "name"},
0421                                         {LocalRole, "local"},
0422                                         {GmRole, "gm"},
0423                                         {CharacterRole, "character"},
0424                                         {CharacterStateIdRole, "characterState"},
0425                                         {NpcRole, "npc"},
0426                                         {AvatarRole, "avatar"}});
0427 
0428     return role;
0429 }
0430 
0431 Player* PlayerModel::playerById(const QString& id) const
0432 {
0433     auto it= std::find_if(m_players.begin(), m_players.end(),
0434                           [id](const std::unique_ptr<Player>& player) { return id == player->uuid(); });
0435     if(it == m_players.end())
0436         return nullptr;
0437     return (*it).get();
0438 }
0439 
0440 Person* PlayerModel::personById(const QString& id) const
0441 {
0442     auto player= playerById(id);
0443 
0444     if(nullptr != player)
0445         return player;
0446 
0447     return characterById(id);
0448 }
0449 
0450 Character* PlayerModel::characterById(const QString& id)const
0451 {
0452     const auto& it
0453         = std::find_if(m_players.begin(), m_players.end(),
0454                        [id](const std::unique_ptr<Player>& player) { return (nullptr != player->characterById(id)); });
0455 
0456     if(it != m_players.end())
0457         return it->get()->characterById(id);
0458     else
0459         return nullptr;
0460 }
0461 
0462 void PlayerModel::addPlayer(Player* player)
0463 {
0464     if(nullptr == player)
0465         return;
0466 
0467     auto it= std::find_if(m_players.begin(), m_players.end(),
0468                           [player](const std::unique_ptr<Player>& t)
0469                           { return (t.get() == player || t->uuid() == player->uuid()); });
0470 
0471     if(it != m_players.end())
0472     {
0473         qWarning() << tr("Duplicated player or uuid") << player->name();
0474         return;
0475     }
0476 
0477     if(player->isGM())
0478     {
0479         setGameMasterId(player->uuid());
0480     }
0481 
0482     int size= static_cast<int>(m_players.size());
0483 
0484     beginInsertRows(QModelIndex(), size, size);
0485     m_players.push_back(std::unique_ptr<Player>(player));
0486     endInsertRows();
0487 
0488     qCInfo(rUser) << QString("Player %1 just arrived").arg(player->name());
0489     emit playerJoin(player);
0490 }
0491 
0492 void PlayerModel::addCharacter(const QModelIndex& parent, Character* character, int pos)
0493 {
0494     if(!parent.isValid())
0495         return;
0496 
0497     int size= pos;
0498     if(size < 0)
0499         size= rowCount(parent.parent());
0500 
0501     auto person= static_cast<Person*>(parent.internalPointer());
0502     auto player= dynamic_cast<Player*>(person);
0503 
0504     character->setNpc(player->isGM());
0505 
0506      beginInsertRows(parent, size, size);
0507     player->addCharacter(character);
0508      endInsertRows();
0509 }
0510 
0511 void PlayerModel::removeCharacter(Character* character)
0512 {
0513     if(nullptr == character)
0514         return;
0515 
0516     auto player= character->getParentPlayer();
0517 
0518     if(nullptr == player)
0519         return;
0520 
0521     auto parent= personToIndex(player);
0522     auto idx= player->indexOf(character);
0523 
0524     beginRemoveRows(parent, idx, idx);
0525     player->removeChild(character);
0526     endRemoveRows();
0527 }
0528 
0529 void PlayerModel::setLocalPlayerId(const QString& uuid)
0530 {
0531     if(uuid == m_localPlayerId)
0532         return;
0533     m_localPlayerId= uuid;
0534     emit localPlayerIdChanged(m_localPlayerId);
0535 }
0536 
0537 void PlayerModel::removePlayer(Player* player)
0538 {
0539     if(!player)
0540         return;
0541 
0542     auto itPlayer= std::find_if(m_players.begin(), m_players.end(),
0543                                 [player](const std::unique_ptr<Player>& person) { return person.get() == player; });
0544     if(itPlayer == m_players.end())
0545         return;
0546 
0547     //    int charactersCount= player->get->childrenCount();
0548     /*    for(int i= 0; i < charactersCount; i++)
0549         {
0550             delCharacter(player, 0);
0551         }*/
0552 
0553     auto index= static_cast<int>(std::distance(m_players.begin(), itPlayer));
0554     qCInfo(rUser) << QString("Player %s left").arg(player->name());
0555 
0556     emit playerLeft(player);
0557 
0558     if(player->isGM())
0559     {
0560         setGameMasterId("");
0561     }
0562 
0563     beginRemoveRows(QModelIndex(), index, index);
0564     m_players.erase(itPlayer);
0565     endRemoveRows();
0566 }
0567 
0568 void PlayerModel::setGameMasterId(const QString& id)
0569 {
0570     if(id == m_gameMasterId)
0571         return;
0572     m_gameMasterId= id;
0573     emit gameMasterIdChanged(m_gameMasterId);
0574 }
0575 
0576 void PlayerModel::clear()
0577 {
0578     beginResetModel();
0579     m_players.clear();
0580     endResetModel();
0581     setGameMasterId("");
0582 }