File indexing completed on 2024-05-19 05:40:34

0001 /***************************************************************************
0002  *  Copyright (C) 2019 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 "controller/item_controllers/characteritemcontroller.h"
0021 
0022 #include <QDebug>
0023 #include <QFontMetrics>
0024 #include <QImage>
0025 #include <QPainter>
0026 
0027 #include "controller/view_controller/vectorialmapcontroller.h"
0028 #include "utils/iohelper.h"
0029 #include "worker/utilshelper.h"
0030 #include "worker/vectorialmapmessagehelper.h"
0031 
0032 #define MARGING 1
0033 
0034 namespace vmap
0035 {
0036 
0037 CharacterItemController::CharacterItemController(const std::map<QString, QVariant>& params,
0038                                                  VectorialMapController* ctrl, QObject* parent)
0039     : VisualItemController(VisualItemController::CHARACTER, params, ctrl, parent)
0040     , m_mapCtrl(ctrl)
0041     , m_vision(new CharacterVision)
0042 {
0043     if(params.end() != params.find(Core::vmapkeys::KEY_TOOL))
0044         m_tool= params.at(Core::vmapkeys::KEY_TOOL).value<Core::SelectableTool>();
0045     else if(params.end() != params.find(Core::vmapkeys::KEY_PLAYABLECHARACTER))
0046         m_tool= params.at(Core::vmapkeys::KEY_PLAYABLECHARACTER).toBool() ? Core::SelectableTool::PlayableCharacter :
0047                                                                             Core::SelectableTool::NonPlayableCharacter;
0048 
0049     if(params.end() != params.find(Core::vmapkeys::KEY_CHARACTER))
0050     {
0051         m_character= params.at(Core::vmapkeys::KEY_CHARACTER).value<Character*>();
0052     }
0053     else
0054     {
0055         m_character= new Character();
0056         VectorialMapMessageHelper::fetchCharacter(params, m_character);
0057     }
0058 
0059     connect(&m_finder, &CharacterFinder::dataChanged, this, &CharacterItemController::findCharacter);
0060     m_finder.setUpConnect();
0061     // connect(model,&PlayerModel::playerJoin,this, &CharacterItemController::findCharacter);
0062 
0063     findCharacter();
0064 
0065     connect(m_character, &Character::stateIdChanged, this, &CharacterItemController::stateIdChanged);
0066     connect(m_character, &Character::npcChanged, this, &CharacterItemController::playableCharacterChanged);
0067     connect(m_character, &Character::npcChanged, this, &CharacterItemController::refreshTextRect);
0068     connect(m_character, &Character::colorChanged, this, [this]() { emit colorChanged(m_character->getColor()); });
0069     connect(m_character, &Character::avatarChanged, this, &CharacterItemController::computeThumbnail);
0070     connect(m_character, &Character::hasInitScoreChanged, this, &CharacterItemController::setModified);
0071     connect(m_character, &Character::initCommandChanged, this, &CharacterItemController::setModified);
0072     connect(m_character, &Character::initiativeChanged, this, &CharacterItemController::setModified);
0073     connect(m_character, &Character::currentHealthPointsChanged, this, &CharacterItemController::setModified);
0074     connect(m_character, &Character::maxHPChanged, this, &CharacterItemController::setModified);
0075     connect(m_character, &Character::minHPChanged, this, &CharacterItemController::setModified);
0076     connect(m_character, &Character::distancePerTurnChanged, this, &CharacterItemController::setModified);
0077     connect(m_character, &Character::lifeColorChanged, this, &CharacterItemController::setModified);
0078     connect(m_character, &Character::nameChanged, this, &CharacterItemController::setModified);
0079     connect(m_character, &Character::colorChanged, this, &CharacterItemController::setModified);
0080     connect(m_character, &Character::avatarChanged, this, &CharacterItemController::setModified);
0081 
0082     connect(ctrl, &VectorialMapController::npcNameVisibleChanged, this, &CharacterItemController::refreshTextRect);
0083     connect(ctrl, &VectorialMapController::pcNameVisibleChanged, this, &CharacterItemController::refreshTextRect);
0084     connect(ctrl, &VectorialMapController::npcNumberVisibleChanged, this, &CharacterItemController::refreshTextRect);
0085     connect(ctrl, &VectorialMapController::stateLabelVisibleChanged, this, &CharacterItemController::refreshTextRect);
0086     connect(ctrl, &VectorialMapController::healthBarVisibleChanged, this,
0087             &CharacterItemController::healthStatusVisibleChanged);
0088     connect(this, &CharacterItemController::sideChanged, this, &CharacterItemController::computeThumbnail);
0089     connect(this, &CharacterItemController::sideChanged, this, &CharacterItemController::refreshTextRect);
0090     connect(this, &CharacterItemController::thumnailRectChanged, this, &CharacterItemController::computeThumbnail);
0091     connect(this, &CharacterItemController::thumnailRectChanged, this, &CharacterItemController::refreshTextRect);
0092     connect(this, &CharacterItemController::textChanged, this, &CharacterItemController::refreshTextRect);
0093     connect(this, &CharacterItemController::radiusChanged, this, &CharacterItemController::refreshTextRect);
0094     connect(this, &CharacterItemController::rectEditFinished, this, &CharacterItemController::refreshTextRect);
0095     connect(this, &CharacterItemController::fontChanged, this, &CharacterItemController::refreshTextRect);
0096 
0097     if(!m_character->isNpc())
0098     {
0099         m_tool= Core::SelectableTool::PlayableCharacter;
0100         VectorialMapMessageHelper::fetchCharacterVision(params, m_vision.get());
0101         ctrl->addVision(m_vision.get());
0102 
0103         auto updatePos= [this]()
0104         {
0105             auto rect= thumnailRect();
0106             auto p= pos() + rect.center(); // ;
0107 
0108             m_vision->setPosition(p);
0109         };
0110 
0111         connect(this, &CharacterItemController::posChanged, m_vision.get(), updatePos);
0112         connect(this, &CharacterItemController::sideChanged, m_vision.get(), updatePos);
0113         connect(this, &CharacterItemController::rotationChanged, m_vision.get(), &CharacterVision::setRotation);
0114         updatePos();
0115     }
0116 
0117     {
0118 
0119         VectorialMapMessageHelper::fetchCharacterItem(params, this);
0120         setRect(QRectF(0.0, 0.0, m_side, m_side));
0121     }
0122 
0123     refreshTextRect();
0124     computeThumbnail();
0125 
0126     connect(this, &CharacterItemController::sideChanged, this, [this] { setModified(); });
0127     connect(this, &CharacterItemController::stateColorChanged, this, [this] { setModified(); });
0128     connect(this, &CharacterItemController::stateIdChanged, this, [this] { setModified(); });
0129     connect(this, &CharacterItemController::stateImageChanged, this, [this] { setModified(); });
0130     connect(this, &CharacterItemController::numberChanged, this, [this] { setModified(); });
0131     connect(this, &CharacterItemController::playableCharacterChanged, this, [this] { setModified(); });
0132     connect(this, &CharacterItemController::thumnailRectChanged, this, [this] { setModified(); });
0133     connect(this, &CharacterItemController::visionShapeChanged, this, [this] { setModified(); });
0134     connect(this, &CharacterItemController::textRectChanged, this, [this] { setModified(); });
0135     connect(this, &CharacterItemController::textChanged, this, [this] { setModified(); });
0136     connect(this, &CharacterItemController::hasAvatarChanged, this, [this] { setModified(); });
0137     connect(this, &CharacterItemController::avatarChanged, this, [this] { setModified(); });
0138     connect(this, &CharacterItemController::fontChanged, this, [this] { setModified(); });
0139     connect(this, &CharacterItemController::visionChanged, this, [this] { setModified(); });
0140     connect(this, &CharacterItemController::radiusChanged, this, [this] { setModified(); });
0141     connect(this, &CharacterItemController::healthStatusVisibleChanged, this, [this] { setModified(); });
0142     connect(this, &CharacterItemController::removedChanged, this, [this] { m_vision->setRemoved(removed()); });
0143 
0144     m_vision->setSide(m_side);
0145 }
0146 
0147 void CharacterItemController::aboutToBeRemoved()
0148 {
0149     emit removeItem();
0150 }
0151 
0152 void CharacterItemController::endGeometryChange()
0153 {
0154     VisualItemController::endGeometryChange();
0155     if(m_changes & ChangedProperty::RECT)
0156         emit rectEditFinished();
0157 
0158     if(m_changes & ChangedProperty::SIDE)
0159         emit sideEdited();
0160 
0161     m_changes= ChangedProperty::NONE;
0162     m_vision->endOfGeometryChanges();
0163 }
0164 
0165 void CharacterItemController::setThumnailRect(const QRectF& rect)
0166 {
0167     setRect(rect);
0168 }
0169 
0170 void CharacterItemController::setCorner(const QPointF& move, int corner, Core::TransformType tt)
0171 {
0172     if(move.isNull())
0173         return;
0174 
0175     auto rect= thumnailRect();
0176     qreal x2= rect.right();
0177     qreal y2= rect.bottom();
0178     qreal x= rect.x();
0179     qreal y= rect.y();
0180     qreal motion= 0.0;
0181 
0182     if(move.x() > 0 || move.y() > 0)
0183         motion= std::max(move.x(), move.y());
0184     else
0185         motion= std::min(move.x(), move.y());
0186 
0187     switch(corner)
0188     {
0189     case TopLeft:
0190         x+= motion;
0191         y+= motion;
0192         break;
0193     case TopRight:
0194         if(qFuzzyCompare(move.x(), motion))
0195         {
0196             x2+= motion;
0197             y-= motion;
0198         }
0199         else
0200         {
0201             x2-= motion;
0202             y+= motion;
0203         }
0204         break;
0205     case BottomRight:
0206         x2+= motion;
0207         y2+= motion;
0208         break;
0209     case BottomLeft:
0210         if(qFuzzyCompare(move.x(), motion))
0211         {
0212             x+= motion;
0213             y2-= motion;
0214         }
0215         else
0216         {
0217             x-= motion;
0218             y2+= motion;
0219         }
0220 
0221         break;
0222     case DirectionHandle:
0223     {
0224         auto xmove= move.x();
0225         auto r= m_vision->radius();
0226         if((xmove + r) < (m_rect.width() / 2))
0227         {
0228             xmove= (m_rect.width() / 2) - r;
0229         }
0230         m_vision->setRadius(xmove + m_vision->radius());
0231     }
0232     break;
0233     case AngleHandle:
0234     {
0235         m_vision->setAngle(std::min(std::max(m_vision->angle() + (move.y() * -1), 0.), 360.));
0236     }
0237     break;
0238     }
0239 
0240     rect.setCoords(x, y, x2, y2);
0241     if(!rect.isValid())
0242         rect= rect.normalized();
0243     setRect(rect);
0244     setSide(rect.width());
0245     refreshTextRect();
0246     computeThumbnail();
0247 }
0248 
0249 qreal CharacterItemController::side() const
0250 {
0251     return m_side;
0252 }
0253 
0254 QColor CharacterItemController::stateColor() const
0255 {
0256     return m_stateColor;
0257 }
0258 
0259 int CharacterItemController::number() const
0260 {
0261     return m_number;
0262 }
0263 
0264 bool CharacterItemController::playableCharacter() const
0265 {
0266     if(!m_character)
0267         return false;
0268     return !m_character->isNpc();
0269 }
0270 
0271 QRectF CharacterItemController::thumnailRect() const
0272 {
0273     return m_rect;
0274 }
0275 
0276 void CharacterItemController::setRect(const QRectF& rect)
0277 {
0278     if(rect == m_rect)
0279         return;
0280 
0281     m_rect= rect;
0282     emit thumnailRectChanged(m_rect);
0283     m_changes|= ChangedProperty::RECT;
0284 }
0285 
0286 void CharacterItemController::findCharacter()
0287 {
0288     if(!m_mapCtrl || !m_character)
0289         return;
0290 
0291     auto p= m_finder.find(m_character->uuid());
0292 
0293     if(p != nullptr && p != m_character)
0294     {
0295         if(m_character)
0296             m_character->deleteLater();
0297 
0298         m_character= p;
0299         emit characterChanged();
0300     }
0301 }
0302 
0303 CharacterVision::SHAPE CharacterItemController::visionShape() const
0304 {
0305     return m_vision ? m_vision->shape() : CharacterVision::ANGLE;
0306 }
0307 
0308 QRectF CharacterItemController::textRect() const
0309 {
0310     return m_textRect;
0311 }
0312 
0313 QString CharacterItemController::text() const
0314 {
0315     if(!m_character)
0316         return {};
0317 
0318     QStringList label;
0319     auto playableCharacter= !m_character->isNpc();
0320     auto name= playableCharacter ? QStringLiteral("PC") : QStringLiteral("NPC");
0321     if(m_character)
0322         name= m_character->name();
0323 
0324     if((!playableCharacter && m_ctrl->npcNameVisible()) || (playableCharacter && m_ctrl->pcNameVisible()))
0325         label << name;
0326 
0327     if(m_ctrl->npcNumberVisible() && !playableCharacter)
0328     {
0329         auto number= playableCharacter ? QStringLiteral("") : QString::number(m_number);
0330         label << number;
0331     }
0332 
0333     if(m_ctrl->stateLabelVisible())
0334         label << m_character->currentStateLabel();
0335 
0336     return label.join(QStringLiteral(" - "));
0337 }
0338 
0339 bool CharacterItemController::hasAvatar() const
0340 {
0341     if(nullptr == m_character)
0342         return false;
0343 
0344     return m_character->hasAvatar();
0345 }
0346 
0347 QImage* CharacterItemController::avatar() const
0348 {
0349     return m_thumb.get();
0350 }
0351 
0352 QColor CharacterItemController::color() const
0353 {
0354     if(nullptr == m_character)
0355         return {};
0356     return m_character->getColor();
0357 }
0358 
0359 QFont CharacterItemController::font() const
0360 {
0361     return m_font;
0362 }
0363 
0364 QPainterPath CharacterItemController::shape() const
0365 {
0366     QPainterPath path;
0367     path.moveTo(0, 0);
0368     hasAvatar() ? path.addEllipse(thumnailRect().united(textRect())) :
0369                   path.addRoundedRect(m_rect.x(), m_rect.y(), m_rect.width(), m_rect.height(), m_side / m_radius,
0370                                       m_side / m_radius);
0371     path.addRect(textRect());
0372     return path;
0373 }
0374 
0375 CharacterVision* CharacterItemController::vision() const
0376 {
0377     return m_vision.get();
0378 }
0379 
0380 qreal CharacterItemController::radius() const
0381 {
0382     return m_radius;
0383 }
0384 
0385 QRectF CharacterItemController::rect() const
0386 {
0387     return thumnailRect();
0388 }
0389 
0390 QString CharacterItemController::stateId() const
0391 {
0392     return m_character ? m_character->stateId() : QString();
0393 }
0394 
0395 QImage CharacterItemController::stateImage() const
0396 {
0397     return m_character ? m_character->currentStateImage() : QImage();
0398 }
0399 
0400 bool CharacterItemController::healthStatusVisible() const
0401 {
0402     return m_ctrl->healthBarVisible();
0403 }
0404 
0405 void CharacterItemController::refreshTextRect()
0406 {
0407     auto subtext= text();
0408     QFontMetrics metrics(m_font);
0409     QRectF rect(thumnailRect().center().x() - ((metrics.boundingRect(subtext).width() + MARGING) / 2),
0410                 thumnailRect().bottom(), metrics.boundingRect(subtext).width() + MARGING + MARGING, metrics.height());
0411     setTextRect(rect);
0412 }
0413 
0414 void CharacterItemController::setSide(qreal side)
0415 {
0416     if(qFuzzyCompare(m_side, side))
0417         return;
0418 
0419     m_side= side;
0420     emit sideChanged(m_side);
0421     m_vision->setSide(m_side);
0422     m_changes|= ChangedProperty::SIDE;
0423 }
0424 
0425 void CharacterItemController::setStateColor(QColor stateColor)
0426 {
0427     if(m_stateColor == stateColor)
0428         return;
0429 
0430     m_stateColor= stateColor;
0431     emit stateColorChanged(m_stateColor);
0432 }
0433 
0434 void CharacterItemController::setNumber(int number)
0435 {
0436     if(m_number == number)
0437         return;
0438 
0439     m_number= number;
0440     emit numberChanged(m_number);
0441 }
0442 
0443 void CharacterItemController::setPlayableCharacter(bool playableCharacter)
0444 {
0445     if(!m_character)
0446         return;
0447     m_character->setNpc(!playableCharacter);
0448 }
0449 
0450 void CharacterItemController::setVisionShape(CharacterVision::SHAPE visionShape)
0451 {
0452     if(!m_vision)
0453         return;
0454     if(m_vision->shape() == visionShape)
0455         return;
0456 
0457     m_vision->setShape(visionShape);
0458     emit visionShapeChanged(visionShape);
0459 }
0460 
0461 void CharacterItemController::setTextRect(QRectF textRect)
0462 {
0463     if(m_textRect == textRect)
0464         return;
0465 
0466     m_textRect= textRect;
0467     emit textRectChanged(m_textRect);
0468 }
0469 
0470 void CharacterItemController::setFont(const QFont& font)
0471 {
0472     if(m_font == font)
0473         return;
0474     m_font= font;
0475     emit fontChanged(m_font);
0476 }
0477 
0478 void CharacterItemController::setStateId(const QString& id)
0479 {
0480     if(m_character)
0481         m_character->setStateId(id);
0482 }
0483 
0484 void CharacterItemController::setRadius(qreal r)
0485 {
0486     if(qFuzzyCompare(r, m_radius))
0487         return;
0488     m_radius= r;
0489     emit radiusChanged(r);
0490 }
0491 
0492 void CharacterItemController::computeThumbnail()
0493 {
0494     int diam= static_cast<int>(m_side);
0495 
0496     m_thumb.reset(new QImage(diam, diam, QImage::Format_ARGB32));
0497     m_thumb->fill(Qt::transparent);
0498     QPainter painter(m_thumb.get());
0499     QBrush brush;
0500     if(m_character->avatar().isNull())
0501     {
0502         painter.setPen(m_character->getColor());
0503         brush.setColor(m_character->getColor());
0504         brush.setStyle(Qt::SolidPattern);
0505     }
0506     else
0507     {
0508         painter.setPen(Qt::NoPen);
0509         QImage img= utils::IOHelper::dataToImage(m_character->avatar());
0510         brush.setTextureImage(img.scaled(diam, diam));
0511     }
0512 
0513     painter.setBrush(brush);
0514     painter.drawRoundedRect(0, 0, diam, diam, m_side / m_radius, m_side / m_radius);
0515     emit avatarChanged();
0516 }
0517 
0518 Character* CharacterItemController::character() const
0519 {
0520     return m_character;
0521 }
0522 const QList<CharacterAction*> CharacterItemController::actionList() const
0523 {
0524     if(m_character)
0525         return m_character->actionList();
0526     return {};
0527 }
0528 const QList<CharacterShape*> CharacterItemController::shapeList() const
0529 {
0530     if(m_character)
0531         return m_character->shapeList();
0532     return {};
0533 }
0534 const QList<CharacterProperty*> CharacterItemController::propertiesList() const
0535 {
0536     if(m_character)
0537         return m_character->propertiesList();
0538     return {};
0539 }
0540 
0541 void CharacterItemController::runInit()
0542 {
0543     m_ctrl->rollInit({this});
0544 }
0545 void CharacterItemController::cleanInit()
0546 {
0547     m_ctrl->cleanUpInit({this});
0548 }
0549 void CharacterItemController::setShape(int index)
0550 {
0551     if(m_character)
0552         m_character->setCurrentShape(index);
0553 }
0554 void CharacterItemController::cleanShape()
0555 {
0556     if(m_character)
0557         m_character->setCurrentShape(nullptr);
0558 }
0559 
0560 void CharacterItemController::runCommand(int index)
0561 {
0562     if(!m_character)
0563         return;
0564 
0565     auto list= m_character->actionList();
0566 
0567     if(list.size() <= index)
0568         return;
0569 
0570     auto cmd= list[index];
0571     m_ctrl->runDiceCommand({this}, cmd->command());
0572 }
0573 } // namespace vmap