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