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

0001 /***************************************************************************
0002  *   Copyright (C) 2015 by Renaud Guezennec                                *
0003  *   https://rolisteam.org/contact                   *
0004  *                                                                         *
0005  *   rolisteam 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 <QBuffer>
0021 #include <QColor>
0022 #include <QDebug>
0023 #include <QFileInfo>
0024 #include <QIcon>
0025 #include <QJsonArray>
0026 #include <QJsonObject>
0027 #include <QJsonValue>
0028 #include <QNetworkAccessManager>
0029 #include <QNetworkReply>
0030 #include <QPixmap>
0031 #include <QUuid>
0032 
0033 #include "charactersheet/rolisteamimageprovider.h"
0034 
0035 #include "worker/iohelper.h"
0036 
0037 #include "data/character.h"
0038 #ifndef UNIT_TEST
0039 #include "charactersheet.h"
0040 #endif
0041 #include "data/player.h"
0042 
0043 //  Begin character field
0044 CharacterField::CharacterField() {}
0045 
0046 CharacterField::~CharacterField() {}
0047 
0048 CharacterShape::CharacterShape() {}
0049 
0050 QString CharacterShape::name() const
0051 {
0052     return m_name;
0053 }
0054 
0055 void CharacterShape::setName(const QString& name)
0056 {
0057     m_name= name;
0058 }
0059 
0060 const QImage& CharacterShape::image() const
0061 {
0062     return m_image;
0063 }
0064 
0065 void CharacterShape::setImage(const QImage& image)
0066 {
0067     m_image= image;
0068 }
0069 
0070 const QMovie& CharacterShape::movie() const
0071 {
0072     return m_movie;
0073 }
0074 
0075 void CharacterShape::setMovie(const QMovie& movie)
0076 {
0077     m_movie.setFileName(movie.fileName());
0078 }
0079 
0080 QString CharacterShape::uri() const
0081 {
0082     return m_uri;
0083 }
0084 
0085 void CharacterShape::setUri(const QString& uri)
0086 {
0087     if(m_uri == uri)
0088         return;
0089 
0090     m_uri= uri;
0091     updateImage();
0092 }
0093 
0094 QVariant CharacterShape::getData(int col, int role)
0095 {
0096     if(Qt::DisplayRole == role || Qt::EditRole == role)
0097     {
0098         if(col == 0)
0099         {
0100             return name();
0101         }
0102         else if(col == 1)
0103         {
0104             return uri();
0105         }
0106     }
0107     else if(Qt::DecorationRole == role && col == 0 && !m_image.isNull())
0108     {
0109         return QPixmap::fromImage(m_image).scaled(m_size, m_size, Qt::KeepAspectRatio);
0110     }
0111     return QVariant();
0112 }
0113 
0114 bool CharacterShape::setData(int col, QVariant value, int role)
0115 {
0116     bool set= false;
0117     if(Qt::DisplayRole == role || Qt::EditRole == role)
0118     {
0119         if(col == 0)
0120         {
0121             setName(value.toString());
0122             set= true;
0123         }
0124         else if(col == 1)
0125         {
0126             setUri(value.toString());
0127             set= true;
0128         }
0129     }
0130     return set;
0131 }
0132 
0133 void CharacterShape::updateImage()
0134 {
0135     m_image= QImage(m_uri);
0136 }
0137 
0138 void CharacterShape::setSize(int size)
0139 {
0140     if(size == m_size)
0141         return;
0142     m_size= size;
0143 }
0144 
0145 ///////////////////////////////////
0146 ///
0147 /// Start CharacterAction
0148 ///
0149 ///////////////////////////////////
0150 
0151 CharacterAction::CharacterAction() {}
0152 
0153 QString CharacterAction::name() const
0154 {
0155     return m_name;
0156 }
0157 
0158 void CharacterAction::setName(const QString& name)
0159 {
0160     m_name= name;
0161 }
0162 
0163 QString CharacterAction::command() const
0164 {
0165     return m_command;
0166 }
0167 
0168 void CharacterAction::setCommand(const QString& command)
0169 {
0170     m_command= command;
0171 }
0172 
0173 QVariant CharacterAction::getData(int col, int role)
0174 {
0175     if(Qt::DisplayRole == role || Qt::EditRole == role)
0176     {
0177         if(col == 0)
0178         {
0179             return name();
0180         }
0181         else if(col == 1)
0182         {
0183             return command();
0184         }
0185     }
0186     return QVariant();
0187 }
0188 
0189 bool CharacterAction::setData(int col, QVariant value, int role)
0190 {
0191     bool set= false;
0192     if(Qt::DisplayRole == role || Qt::EditRole == role)
0193     {
0194         if(col == 0)
0195         {
0196             setName(value.toString());
0197             set= true;
0198         }
0199         else if(col == 1)
0200         {
0201             setCommand(value.toString());
0202             set= true;
0203         }
0204     }
0205     return set;
0206 }
0207 
0208 CharacterProperty::CharacterProperty() {}
0209 
0210 QString CharacterProperty::name() const
0211 {
0212     return m_name;
0213 }
0214 
0215 void CharacterProperty::setName(const QString& name)
0216 {
0217     m_name= name;
0218 }
0219 
0220 QString CharacterProperty::value() const
0221 {
0222     return m_value;
0223 }
0224 
0225 void CharacterProperty::setValue(const QString& value)
0226 {
0227     m_value= value;
0228 }
0229 
0230 QVariant CharacterProperty::getData(int col, int role)
0231 {
0232     if(Qt::DisplayRole == role || Qt::EditRole == role)
0233     {
0234         if(col == 0)
0235         {
0236             return name();
0237         }
0238         else if(col == 1)
0239         {
0240             return value();
0241         }
0242     }
0243     return QVariant();
0244 }
0245 
0246 bool CharacterProperty::setData(int col, QVariant value, int role)
0247 {
0248     bool set= false;
0249     if(Qt::DisplayRole == role || Qt::EditRole == role)
0250     {
0251         if(col == 0)
0252         {
0253             setName(value.toString());
0254             set= true;
0255         }
0256         else if(col == 1)
0257         {
0258             setValue(value.toString());
0259             set= true;
0260         }
0261     }
0262     return set;
0263 }
0264 
0265 // end character field
0266 
0267 QList<CharacterState*>* Character::m_stateList= nullptr;
0268 
0269 Character::Character(QObject* parent) : Person(parent), m_sheet(nullptr) {}
0270 
0271 Character::Character(const QString& name, const QColor& color, bool npc, int number)
0272     : Person(name, color), m_isNpc(npc), m_number(number), m_sheet(nullptr)
0273 {
0274 }
0275 
0276 Character::Character(const QString& uuid, const QString& name, const QColor& color, bool npc, int number)
0277     : Person(name, color, uuid), m_isNpc(npc), m_number(number), m_sheet(nullptr)
0278 {
0279 }
0280 
0281 /*Character::Character(NetworkMessageReader& data) : Person(), m_sheet(nullptr)
0282 {
0283     read(data);
0284 }*/
0285 
0286 Character::~Character() {}
0287 
0288 QList<CharacterShape*> Character::shapeList() const
0289 {
0290     return m_shapeList;
0291 }
0292 
0293 QList<CharacterAction*> Character::actionList() const
0294 {
0295     return m_actionList;
0296 }
0297 
0298 QList<CharacterProperty*> Character::propertiesList() const
0299 {
0300     return m_propertyList;
0301 }
0302 
0303 void Character::defineActionList(const QList<CharacterAction*>& actions)
0304 {
0305     m_actionList.clear();
0306     m_actionList << actions;
0307 }
0308 
0309 void Character::defineShapeList(const QList<CharacterShape*>& shape)
0310 {
0311     m_shapeList.clear();
0312     m_shapeList << shape;
0313 }
0314 void Character::definePropertiesList(const QList<CharacterProperty*>& props)
0315 {
0316     m_propertyList.clear();
0317     m_propertyList << props;
0318 }
0319 
0320 RolisteamImageProvider* Character::getImageProvider() const
0321 {
0322     return m_imageProvider;
0323 }
0324 
0325 void Character::setImageProvider(RolisteamImageProvider* imageProvider)
0326 {
0327     m_imageProvider= imageProvider;
0328 }
0329 
0330 qreal Character::getDistancePerTurn() const
0331 {
0332     return m_distancePerTurn;
0333 }
0334 
0335 void Character::setDistancePerTurn(const qreal& distancePerTurn)
0336 {
0337     if(qFuzzyCompare(m_distancePerTurn, distancePerTurn))
0338         return;
0339     m_distancePerTurn= distancePerTurn;
0340     emit distancePerTurnChanged();
0341 }
0342 
0343 int Character::getInitiativeScore() const
0344 {
0345     return m_initiativeScore;
0346 }
0347 
0348 void Character::setInitiativeScore(int intiativeScore)
0349 {
0350     if(m_initiativeScore == intiativeScore)
0351         return;
0352     m_initiativeScore= intiativeScore;
0353     setHasInitiative(true);
0354     emit initiativeChanged();
0355 }
0356 
0357 int Character::getHealthPointsCurrent() const
0358 {
0359     return m_healthPointsCurrent;
0360 }
0361 
0362 void Character::setHealthPointsCurrent(int hpCurrent)
0363 {
0364     if(hpCurrent == m_healthPointsCurrent)
0365         return;
0366     m_healthPointsCurrent= qBound(m_healthPointsMin, hpCurrent, m_healthPointsMax);
0367     emit currentHealthPointsChanged();
0368 }
0369 
0370 int Character::getHealthPointsMin() const
0371 {
0372     return m_healthPointsMin;
0373 }
0374 
0375 void Character::setHealthPointsMin(int hpMin)
0376 {
0377     if(hpMin == m_healthPointsMin)
0378         return;
0379     m_healthPointsMin= hpMin;
0380     emit minHPChanged();
0381 }
0382 
0383 int Character::getHealthPointsMax() const
0384 {
0385     return m_healthPointsMax;
0386 }
0387 
0388 void Character::setHealthPointsMax(int hpMax)
0389 {
0390     if(hpMax == m_healthPointsMax)
0391         return;
0392     m_healthPointsMax= hpMax;
0393     emit maxHPChanged();
0394 }
0395 
0396 QString Character::getParentId() const
0397 {
0398     if(nullptr != m_parentPerson)
0399     {
0400         return m_parentPerson->uuid();
0401     }
0402     return QString();
0403 }
0404 
0405 QHash<QString, QString> Character::getVariableDictionnary()
0406 {
0407     QHash<QString, QString> variables;
0408     if(nullptr != m_sheet)
0409     {
0410 #ifndef UNIT_TEST
0411         variables= m_sheet->getVariableDictionnary();
0412 #endif
0413     }
0414     else if(!m_propertyList.isEmpty())
0415     {
0416         for(auto& property : m_propertyList)
0417         {
0418             if(property != nullptr)
0419                 variables.insert(property->name(), property->value());
0420         }
0421     }
0422     return variables;
0423 }
0424 
0425 CharacterSheet* Character::getSheet() const
0426 {
0427     return m_sheet;
0428 }
0429 
0430 void Character::setSheet(CharacterSheet* sheet)
0431 {
0432     m_sheet= sheet;
0433 }
0434 void Character::setListOfCharacterState(QList<CharacterState*>* list)
0435 {
0436     m_stateList= list;
0437 }
0438 int Character::indexOfState(CharacterState* state)
0439 {
0440     if(nullptr != m_stateList)
0441     {
0442         return m_stateList->indexOf(state);
0443     }
0444     else
0445     {
0446         return -1;
0447     }
0448 }
0449 
0450 void Character::addShape(const QString& name, const QString& path)
0451 {
0452     auto shape= new CharacterShape();
0453 
0454     if(path.startsWith(QStringLiteral("image://rcs/")))
0455     {
0456 #ifdef QT_QUICK_LIB
0457         auto key= path;
0458         key.replace(QStringLiteral("image://rcs/"), "");
0459         QSize size;
0460         QSize resquestedSize;
0461         auto img= m_imageProvider->requestPixmap(key, &size, resquestedSize);
0462         shape->setImage(img.toImage());
0463 #endif
0464     }
0465     else if(path.startsWith("http"))
0466     {
0467 #ifdef HAVE_QT_NETWORK
0468         QNetworkAccessManager* manager= new QNetworkAccessManager(this);
0469         connect(manager, &QNetworkAccessManager::finished, this, [shape, manager](QNetworkReply* reply) {
0470             QByteArray data= reply->readAll();
0471             QPixmap map;
0472             bool ok= map.loadFromData(data);
0473             if(ok)
0474                 shape->setImage(map.toImage());
0475             if(manager != nullptr)
0476                 delete manager;
0477         });
0478 
0479         manager->get(QNetworkRequest(QUrl(path)));
0480 #endif
0481     }
0482     shape->setName(name);
0483     shape->setUri(path);
0484     m_shapeList.append(shape);
0485 }
0486 
0487 CharacterState* Character::getStateFromLabel(QString label)
0488 {
0489     for(auto& state : *m_stateList)
0490     {
0491         if(state->label() == label)
0492         {
0493             return state;
0494         }
0495     }
0496     return nullptr;
0497 }
0498 
0499 CharacterState* Character::getStateFromIndex(int i)
0500 {
0501     if(nullptr == m_stateList)
0502         return nullptr;
0503 
0504     if(m_stateList->empty())
0505         return nullptr;
0506     if(m_stateList->size() > i)
0507     {
0508         return m_stateList->at(i);
0509     }
0510     return nullptr;
0511 }
0512 
0513 /*QString Character::read(NetworkMessageReader& msg)
0514 {
0515     if(!msg.isValid())
0516         return {};
0517     QString parentId= msg.string8();
0518     m_uuid= msg.string8();
0519     setName(msg.string16());
0520     setStateId(msg.string16());
0521     setNpc(static_cast<bool>(msg.uint8()));
0522     setNumber(msg.int32());
0523     setColor(QColor(msg.rgb()));
0524     setHealthPointsCurrent(msg.int32());
0525     setHealthPointsMin(msg.int32());
0526     setHealthPointsMax(msg.int32());
0527     setInitiativeScore(msg.int32());
0528     m_initiativeRoll.setCommand(msg.string32());
0529     setDistancePerTurn(msg.real());
0530     m_hasInitScore= static_cast<bool>(msg.uint8());
0531 
0532     bool hasAvatar= static_cast<bool>(msg.uint8());
0533 
0534     if(hasAvatar)
0535     {
0536         auto avatar= QImage::fromData(msg.byteArray32());
0537         setAvatar(avatar);
0538     }
0539 
0540     return parentId;
0541 }*/
0542 
0543 Player* Character::getParentPlayer() const
0544 {
0545     return dynamic_cast<Player*>(m_parentPerson);
0546 }
0547 
0548 int Character::number() const
0549 {
0550     return m_number;
0551 }
0552 
0553 void Character::setNumber(int n)
0554 {
0555     m_number= n;
0556 }
0557 QColor Character::getLifeColor() const
0558 {
0559     return m_lifeColor;
0560 }
0561 void Character::setLifeColor(QColor color)
0562 {
0563     if(m_lifeColor == color)
0564         return;
0565 
0566     m_lifeColor= color;
0567     emit lifeColorChanged();
0568 }
0569 
0570 void Character::setInitCommand(const QString& init)
0571 {
0572     if(m_initiativeRoll.command() == init)
0573         return;
0574 
0575     m_initiativeRoll.setCommand(init);
0576     emit initCommandChanged();
0577 }
0578 QString Character::initCommand() const
0579 {
0580     return m_initiativeRoll.command();
0581 }
0582 
0583 bool Character::isNpc() const
0584 {
0585     return m_isNpc;
0586 }
0587 void Character::setStateId(const QString& stateId)
0588 {
0589     if(stateId == m_stateId)
0590         return;
0591     m_stateId= stateId;
0592     emit stateIdChanged(m_stateId);
0593 }
0594 
0595 QString Character::currentStateLabel() const
0596 {
0597     if(!m_stateList)
0598         return {};
0599     auto it= std::find_if(m_stateList->begin(), m_stateList->end(), [this](const CharacterState* state) {
0600         if(nullptr == state)
0601             return false;
0602         return state->id() == m_stateId;
0603     });
0604 
0605     if(it == m_stateList->end())
0606         return {};
0607     return (*it)->label();
0608 }
0609 
0610 QImage Character::currentStateImage() const
0611 {
0612     if(!m_stateList)
0613         return {};
0614 
0615     auto it= std::find_if(m_stateList->begin(), m_stateList->end(), [this](const CharacterState* state) {
0616         if(nullptr == state)
0617             return false;
0618         return state->id() == m_stateId;
0619     });
0620     if(it == m_stateList->end())
0621         return {};
0622     return QImage((*it)->imagePath());
0623 }
0624 
0625 bool Character::hasInitScore() const
0626 {
0627     return m_hasInitScore;
0628 }
0629 
0630 void Character::setHasInitiative(bool b)
0631 {
0632     if(m_hasInitScore == b)
0633         return;
0634     m_hasInitScore= b;
0635     emit hasInitScoreChanged();
0636 }
0637 
0638 QString Character::stateId() const
0639 {
0640     return m_stateId;
0641 }
0642 /*void Character::writeData(QDataStream& out) const
0643 {
0644     out << m_uuid;
0645     out << m_name;
0646     out << m_stateId;
0647     out << m_isNpc;
0648     out << m_number;
0649     out << m_color;
0650     out << m_avatar;
0651 }
0652 void Character::readData(QDataStream& in)
0653 {
0654     in >> m_uuid;
0655     in >> m_name;
0656     in >> m_stateId;
0657     in >> m_isNpc;
0658     in >> m_number;
0659     in >> m_color;
0660     in >> m_avatar;
0661 }*/
0662 
0663 QList<CharacterState*>* Character::getCharacterStateList()
0664 {
0665     return m_stateList;
0666 }
0667 void Character::setNpc(bool b)
0668 {
0669     if(m_isNpc == b)
0670         return;
0671 
0672     m_isNpc= b;
0673     emit npcChanged();
0674 }
0675 /*void Character::write(QDataStream& out, bool tag, bool) const
0676 {
0677     if(tag)
0678     {
0679         out << QStringLiteral("Character");
0680     }
0681     out << m_isNpc;
0682     out << m_number;
0683     out << m_name;
0684     out << m_uuid;
0685     out << m_color;
0686     if(nullptr != m_parentPerson)
0687         out << m_parentPerson->uuid();
0688     else
0689         out << QString();
0690 
0691     out << static_cast<int>(m_checkState);
0692 
0693     QByteArray array;
0694     QBuffer buffer(&array);
0695     buffer.open(QIODevice::WriteOnly);
0696     m_avatar.save(&buffer, "PNG");
0697     out << array;
0698 }
0699 void Character::read(QDataStream& in)
0700 {
0701     in >> m_isNpc;
0702     in >> m_number;
0703     in >> m_name;
0704     in >> m_uuid;
0705     in >> m_color;
0706     QString parentId;
0707     in >> parentId;
0708     int checkState;
0709     in >> checkState;
0710     m_checkState= static_cast<Qt::CheckState>(checkState);
0711     QByteArray array;
0712     in >> array;
0713     m_avatar= QImage::fromData(array);
0714 }*/
0715 
0716 void Character::setCurrentShape(int index)
0717 {
0718     if(index < 0)
0719     {
0720         setCurrentShape(nullptr);
0721         return;
0722     }
0723 
0724     if(qBound(0, index, m_shapeList.size()) != index)
0725         return;
0726 
0727     auto shape= m_shapeList[index];
0728     setCurrentShape(shape);
0729 }
0730 
0731 void Character::setCurrentShape(CharacterShape* shape)
0732 {
0733     if(shape == m_currentShape)
0734         return;
0735     m_currentShape= shape;
0736     emit avatarChanged();
0737 }
0738 
0739 void Character::addShape(CharacterShape* shape)
0740 {
0741     m_shapeList.append(shape);
0742     // TODO emit signal ?
0743 }
0744 
0745 void Character::addAction(CharacterAction* action)
0746 {
0747     m_actionList.append(action);
0748     // TODO emit signal ?
0749 }
0750 
0751 void Character::addProperty(CharacterProperty* property)
0752 {
0753     m_propertyList.append(property);
0754     // TODO emit signal ?
0755 }
0756 
0757 QByteArray Character::avatar() const
0758 {
0759     if(nullptr == m_currentShape)
0760         return Person::avatar();
0761     // else
0762     //        return IOHelper::imageToData(m_currentShape->image());
0763     // TODO change API of shapes
0764     return {};
0765 }
0766 
0767 CharacterShape* Character::currentShape() const
0768 {
0769     return m_currentShape;
0770 }