File indexing completed on 2025-04-20 13:31:31
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 }