File indexing completed on 2024-05-05 05:40:29
0001 /*************************************************************************** 0002 * Copyright (C) 2021 by Renaud Guezennec * 0003 * http://www.rolisteam.org/contact * 0004 * * 0005 * This software is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 0019 ***************************************************************************/ 0020 #include "model/nonplayablecharactermodel.h" 0021 #include "worker/iohelper.h" 0022 namespace campaign 0023 { 0024 NonPlayableCharacter::NonPlayableCharacter(QObject* parent) : Character(parent) 0025 { 0026 setNpc(true); 0027 } 0028 0029 QStringList NonPlayableCharacter::tags() const 0030 { 0031 return m_tags; 0032 } 0033 0034 QString NonPlayableCharacter::avatarPath() const 0035 { 0036 return m_avatarPath; 0037 } 0038 0039 int NonPlayableCharacter::size() const 0040 { 0041 return m_size; 0042 } 0043 0044 QString NonPlayableCharacter::description() const 0045 { 0046 return m_description; 0047 } 0048 0049 void NonPlayableCharacter::setTags(const QStringList& list) 0050 { 0051 if(list == m_tags) 0052 return; 0053 m_tags= list; 0054 emit tagsChanged(); 0055 } 0056 0057 void NonPlayableCharacter::setAvatarPath(const QString& path) 0058 { 0059 if(path == m_avatarPath) 0060 return; 0061 m_avatarPath= path; 0062 emit avatarPathChanged(); 0063 } 0064 0065 void NonPlayableCharacter::setSize(int size) 0066 { 0067 if(size == m_size) 0068 return; 0069 m_size= size; 0070 emit sizeChanged(); 0071 } 0072 0073 void NonPlayableCharacter::setDescription(const QString& desc) 0074 { 0075 if(desc == m_description) 0076 return; 0077 m_description= desc; 0078 emit descriptionChanged(); 0079 } 0080 0081 NonPlayableCharacterModel::NonPlayableCharacterModel(CharacterStateModel* states, QObject* parent) 0082 : QAbstractListModel(parent), m_states(states) 0083 { 0084 m_header << tr("Avatar") << tr("Name") << tr("Description") << tr("GM Details") << tr("Tags") << tr("Color") 0085 << tr("Min HP") << tr("Current HP") << tr("Max HP") << tr("Initiative") << tr("Distance Per turn") 0086 << tr("State") << tr("Life Color") << tr("Init Command"); 0087 } 0088 0089 QVariant NonPlayableCharacterModel::headerData(int section, Qt::Orientation orientation, int role) const 0090 { 0091 if(orientation == Qt::Vertical) 0092 return {}; 0093 0094 if(Qt::DisplayRole == role) 0095 { 0096 return m_header[section]; 0097 } 0098 0099 return {}; 0100 } 0101 0102 int NonPlayableCharacterModel::rowCount(const QModelIndex& parent) const 0103 { 0104 if(parent.isValid()) 0105 return 0; 0106 0107 return m_data.size(); 0108 } 0109 0110 int NonPlayableCharacterModel::columnCount(const QModelIndex&) const 0111 { 0112 return m_header.size(); 0113 } 0114 0115 QVariant NonPlayableCharacterModel::data(const QModelIndex& index, int role) const 0116 { 0117 static QSet<int> acceptedRole({Qt::DecorationRole, 0118 Qt::DisplayRole, 0119 Qt::TextAlignmentRole, 0120 Qt::BackgroundRole, 0121 Qt::EditRole, 0122 RoleUuid, 0123 RoleAvatar, 0124 RoleName, 0125 RoleDescription, 0126 RoleGmDetails, 0127 RoleTags, 0128 RoleColor, 0129 RoleMinHp, 0130 RoleCurrentHp, 0131 RoleMaxHp, 0132 RoleInitiative, 0133 RoleDistancePerTurn, 0134 RoleState, 0135 RoleLifeColor, 0136 RoleInitCommand, 0137 RoleUuid, 0138 RoleAvatarPath, 0139 RoleHasInitiative, 0140 RoleActionCount, 0141 RoleShapeCount, 0142 RolePropertiesCount}); 0143 if(!index.isValid() || !acceptedRole.contains(role)) 0144 return QVariant(); 0145 0146 if(role == Qt::TextAlignmentRole) 0147 return Qt::AlignCenter; 0148 0149 auto customRole= role > Qt::UserRole ? role : Qt::UserRole + 1 + index.column(); 0150 0151 if(role == Qt::BackgroundRole && (customRole != RoleColor && customRole != RoleLifeColor)) 0152 return {}; 0153 0154 if(role == Qt::DecorationRole && (customRole != RoleAvatar)) 0155 return {}; 0156 0157 auto const& character= m_data[index.row()]; 0158 QVariant res; 0159 switch(customRole) 0160 { 0161 case RoleAvatar: 0162 if(role == Qt::EditRole) 0163 res= QString{}; 0164 else 0165 res= QVariant::fromValue(IOHelper::dataToPixmap(character->avatar())); 0166 break; 0167 case RoleUuid: 0168 res= character->uuid(); 0169 break; 0170 case RoleName: 0171 res= character->name(); 0172 break; 0173 case RoleDescription: 0174 res= character->description(); 0175 break; 0176 case RoleColor: 0177 res= character->getColor(); 0178 break; 0179 case RoleMinHp: 0180 res= character->getHealthPointsMin(); 0181 break; 0182 case RoleCurrentHp: 0183 res= character->getHealthPointsCurrent(); 0184 break; 0185 case RoleMaxHp: 0186 res= character->getHealthPointsMax(); 0187 break; 0188 case RoleInitiative: 0189 res= character->getInitiativeScore(); 0190 break; 0191 case RoleDistancePerTurn: 0192 res= character->getDistancePerTurn(); 0193 break; 0194 case RoleState: 0195 if(role == Qt::DisplayRole) 0196 res= stateName(character->stateId()); 0197 else if(role == Qt::EditRole || role == RoleState) 0198 res= character->stateId(); 0199 break; 0200 case RoleLifeColor: 0201 res= character->getLifeColor(); 0202 break; 0203 case RoleInitCommand: 0204 res= character->initCommand(); 0205 break; 0206 case RoleTags: 0207 if(role == Qt::DisplayRole || role == RoleTags) 0208 res= character->tags(); 0209 else if(role == Qt::EditRole) 0210 res= character->tags().join(";"); 0211 break; 0212 case RoleAvatarPath: 0213 res= character->avatarPath(); 0214 break; 0215 case RoleHasInitiative: 0216 res= character->hasInitScore(); 0217 break; 0218 case RoleActionCount: 0219 res= character->actionList().size(); 0220 break; 0221 case RoleShapeCount: 0222 res= character->shapeList().size(); 0223 break; 0224 case RolePropertiesCount: 0225 res= character->propertiesList().size(); 0226 break; 0227 case RoleGmDetails: 0228 res= character->gameMasterDesc(); 0229 break; 0230 } 0231 0232 return res; 0233 } 0234 0235 bool NonPlayableCharacterModel::setData(const QModelIndex& index, const QVariant& value, int role) 0236 { 0237 if(!index.isValid()) 0238 return false; 0239 0240 auto customRole= Qt::UserRole + 1 + index.column(); 0241 if(role > Qt::UserRole) 0242 customRole= role; 0243 0244 if(data(index, role) != value) 0245 { 0246 0247 bool res= true; 0248 auto const& character= m_data[index.row()]; 0249 switch(customRole) 0250 { 0251 case RoleName: 0252 character->setName(value.toString()); 0253 break; 0254 case RoleDescription: 0255 character->setDescription(value.toString()); 0256 break; 0257 case RoleColor: 0258 character->setColor(value.value<QColor>()); 0259 break; 0260 case RoleMinHp: 0261 character->setHealthPointsMin(value.toInt()); 0262 break; 0263 case RoleCurrentHp: 0264 character->setHealthPointsCurrent(value.toInt()); 0265 break; 0266 case RoleMaxHp: 0267 character->setHealthPointsMax(value.toInt()); 0268 break; 0269 case RoleInitiative: 0270 character->setInitiativeScore(value.toInt()); 0271 break; 0272 case RoleDistancePerTurn: 0273 character->setDistancePerTurn(value.toInt()); 0274 break; 0275 case RoleState: 0276 character->setStateId(value.toString()); 0277 break; 0278 case RoleLifeColor: 0279 character->setLifeColor(value.value<QColor>()); 0280 break; 0281 case RoleInitCommand: 0282 character->setInitCommand(value.toString()); 0283 break; 0284 case RoleTags: 0285 character->setTags(value.toString().split(';')); 0286 break; 0287 case RoleAvatar: 0288 if(value.typeId() == qMetaTypeId<QByteArray>()) 0289 { 0290 character->setAvatar(value.toByteArray()); 0291 } 0292 else if(value.typeId() == qMetaTypeId<QString>()) 0293 { 0294 auto str= value.toString(); 0295 if(str.isEmpty()) 0296 return false; 0297 auto path= IOHelper::copyImageFileIntoCampaign(str, m_npcRoot); 0298 character->setAvatarPath(path); 0299 character->setAvatar(IOHelper::imageToData(IOHelper::readImageFromURL(QUrl::fromUserInput(path)))); 0300 } 0301 else if(value.typeId() == qMetaTypeId<QUrl>()) 0302 { 0303 character->setAvatar(IOHelper::imageToData(IOHelper::readImageFromURL(value.toUrl()))); 0304 } 0305 break; 0306 case RoleAvatarPath: 0307 character->setAvatarPath(value.toString()); 0308 break; 0309 default: 0310 res= false; 0311 break; 0312 } 0313 if(res) 0314 emit dataChanged(index, index, QVector<int>() << role); 0315 return res; 0316 } 0317 return false; 0318 } 0319 0320 QString NonPlayableCharacterModel::stateName(const QString& id) const 0321 { 0322 if(!m_states) 0323 return id; 0324 0325 return m_states->stateLabelFromId(id); 0326 } 0327 0328 Qt::DropActions NonPlayableCharacterModel::supportedDropActions() const 0329 { 0330 return Qt::MoveAction | Qt::CopyAction | Qt::TargetMoveAction | Qt::ActionMask ; 0331 } 0332 0333 bool NonPlayableCharacterModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) 0334 { 0335 if(column != NonPlayableCharacterModel::ColAvatar || action == Qt::IgnoreAction) 0336 return false; 0337 0338 bool added= false; 0339 0340 if(data->hasImage()) 0341 { 0342 setData(index(row, column), IOHelper::imageToData(data->imageData().value<QImage>()),NonPlayableCharacterModel::RoleAvatar); 0343 } 0344 else if(data->hasUrls()) 0345 { 0346 auto urls = data->urls(); 0347 if(urls.size() > 1 || urls.isEmpty()) 0348 return false; 0349 setData(index(row, column), urls[0],NonPlayableCharacterModel::RoleAvatar); 0350 setData(index(row, column), urls[0],NonPlayableCharacterModel::RoleAvatarPath); 0351 } 0352 return added; 0353 } 0354 0355 Qt::ItemFlags NonPlayableCharacterModel::flags(const QModelIndex& index) const 0356 { 0357 if(!index.isValid()) 0358 return Qt::NoItemFlags; 0359 0360 if(index.column() == NonPlayableCharacterModel::ColAvatar) 0361 return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled; 0362 else 0363 return Qt::ItemIsDragEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled; 0364 } 0365 0366 void NonPlayableCharacterModel::append() 0367 { 0368 addCharacter(new NonPlayableCharacter); 0369 } 0370 0371 void NonPlayableCharacterModel::addCharacter(NonPlayableCharacter* character) 0372 { 0373 auto size= static_cast<int>(m_data.size()); 0374 beginInsertRows(QModelIndex(), size, size); 0375 std::unique_ptr<NonPlayableCharacter> uni{character}; 0376 m_data.push_back(std::move(uni)); 0377 endInsertRows(); 0378 emit characterAdded(); 0379 } 0380 0381 void NonPlayableCharacterModel::setModelData(const std::vector<NonPlayableCharacter*>& data) 0382 { 0383 beginResetModel(); 0384 m_data.clear(); 0385 std::for_each(std::begin(data), std::end(data), 0386 [this](NonPlayableCharacter* npc) 0387 { 0388 std::unique_ptr<NonPlayableCharacter> unique(npc); 0389 m_data.push_back(std::move(unique)); 0390 }); 0391 endResetModel(); 0392 } 0393 0394 void NonPlayableCharacterModel::removeNpc(const QString& uuid) 0395 { 0396 auto index= indexFromUuid(uuid); 0397 if(!index.isValid()) 0398 return; 0399 0400 beginRemoveRows(QModelIndex(), index.row(), index.row()); 0401 auto const& it= m_data.begin() + index.row(); 0402 m_data.erase(it); 0403 endRemoveRows(); 0404 0405 emit characterRemoved(uuid); 0406 } 0407 0408 void NonPlayableCharacterModel::refresh(const QString& uuid) 0409 { 0410 auto index= indexFromUuid(uuid); 0411 if(!index.isValid()) 0412 return; 0413 0414 emit dataChanged(index, index); 0415 } 0416 0417 QModelIndex NonPlayableCharacterModel::indexFromUuid(const QString& id) 0418 { 0419 auto it= std::find_if(std::begin(m_data), std::end(m_data), 0420 [id](const std::unique_ptr<NonPlayableCharacter>& character) 0421 { return character->uuid() == id; }); 0422 0423 if(it == std::end(m_data)) 0424 return {}; 0425 else 0426 return index(std::distance(std::begin(m_data), it), 0); 0427 } 0428 0429 NonPlayableCharacter* NonPlayableCharacterModel::characterFromUuid(const QString& id) 0430 { 0431 auto it= std::find_if(std::begin(m_data), std::end(m_data), 0432 [id](const std::unique_ptr<NonPlayableCharacter>& character) 0433 { return character->uuid() == id; }); 0434 0435 if(it != std::end(m_data)) 0436 return it->get(); 0437 0438 return nullptr; 0439 } 0440 0441 const std::vector<std::unique_ptr<NonPlayableCharacter>>& NonPlayableCharacterModel::npcList() const 0442 { 0443 return m_data; 0444 } 0445 0446 QStringList NonPlayableCharacterModel::headers() 0447 { 0448 return m_header; 0449 } 0450 0451 const QString& NonPlayableCharacterModel::npcRoot() const 0452 { 0453 return m_npcRoot; 0454 } 0455 0456 void NonPlayableCharacterModel::setNpcRoot(const QString& newNpcRoot) 0457 { 0458 if(m_npcRoot == newNpcRoot) 0459 return; 0460 m_npcRoot= newNpcRoot; 0461 emit npcRootChanged(); 0462 } 0463 0464 const QString& NonPlayableCharacter::gameMasterDesc() const 0465 { 0466 return m_gameMasterDesc; 0467 } 0468 0469 void NonPlayableCharacter::setGameMasterDesc(const QString& newGameMasterDesc) 0470 { 0471 if(m_gameMasterDesc == newGameMasterDesc) 0472 return; 0473 m_gameMasterDesc= newGameMasterDesc; 0474 emit gameMasterDescChanged(); 0475 } 0476 0477 } // namespace campaign