File indexing completed on 2024-04-28 05:38:11
0001 /*************************************************************************** 0002 * Copyright (C) 2010 by Renaud Guezennec * 0003 * * 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 "characteritem.h" 0021 0022 #include <QDebug> 0023 #include <QGraphicsDropShadowEffect> 0024 #include <QGraphicsScene> 0025 #include <QGraphicsView> 0026 #include <QMenu> 0027 #include <QPainter> 0028 #include <QStyleOptionGraphicsItem> 0029 0030 #include "controller/item_controllers/characteritemcontroller.h" 0031 #include "controller/item_controllers/visualitemcontroller.h" 0032 #include "controller/view_controller/vectorialmapcontroller.h" 0033 #include "data/character.h" 0034 #include "diceparser/dicealias.h" 0035 #include "visualitem.h" 0036 0037 #define MARGING 1 0038 #define MINI_VALUE 25 0039 #define RADIUS_CORNER 10 0040 #define MAX_CORNER_ITEM 6 0041 #define DIRECTION_RADIUS_HANDLE 4 0042 #define ANGLE_HANDLE 5 0043 #define PEN_WIDTH 6 0044 #define PEN_RADIUS 3 0045 0046 void updateListAlias(QList<DiceAlias*>& list) 0047 { 0048 /*auto preferences= PreferencesManager::getInstance(); 0049 list.clear(); 0050 int size= preferences->value("DiceAliasNumber", 0).toInt(); 0051 for(int i= 0; i < size; ++i) 0052 { 0053 QString cmd= preferences->value(QString("DiceAlias_%1_command").arg(i), "").toString(); 0054 QString value= preferences->value(QString("DiceAlias_%1_value").arg(i), "").toString(); 0055 bool replace= preferences->value(QString("DiceAlias_%1_type").arg(i), true).toBool(); 0056 bool enable= preferences->value(QString("DiceAlias_%1_enable").arg(i), true).toBool(); 0057 //list.append(new DiceAlias(cmd, value, replace, enable)); 0058 }*/ 0059 } 0060 0061 QRect makeSquare(QRect rect) 0062 { 0063 if(rect.width() < rect.height()) 0064 rect.setWidth(rect.height()); 0065 else 0066 rect.setHeight(rect.width()); 0067 0068 return rect; 0069 } 0070 0071 QColor ContrastColor(QColor color) 0072 { 0073 int d= 0; 0074 0075 // Counting the perceptive luminance - human eye favors green color... 0076 double luminance= (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255; 0077 0078 if(luminance > 0.5) 0079 d= 0; // bright colors - black font 0080 else 0081 d= 255; // dark colors - white font 0082 0083 return QColor(d, d, d); 0084 } 0085 0086 ////////////////// 0087 // code of CharacterItem class. 0088 ///////////////// 0089 CharacterItem::CharacterItem(vmap::CharacterItemController* ctrl) 0090 : VisualItem(ctrl) 0091 , m_itemCtrl(ctrl) 0092 , m_visionShapeDisk(new QAction(tr("Disk"))) 0093 , m_visionShapeAngle(new QAction(tr("Angle"))) 0094 , m_reduceLife(new QAction(tr("Reduce Life"))) 0095 , m_increaseLife(new QAction(tr("Increase Life"))) 0096 { 0097 if(!m_itemCtrl) 0098 return; 0099 0100 m_mapCtrl = m_itemCtrl->mapController(); 0101 0102 m_visionShapeDisk->setCheckable(true); 0103 m_visionShapeAngle->setCheckable(true); 0104 0105 connect(m_visionShapeAngle.get(), &QAction::triggered, m_itemCtrl, 0106 [this]() { m_itemCtrl->setVisionShape(CharacterVision::ANGLE); }); 0107 connect(m_visionShapeDisk.get(), &QAction::triggered, m_itemCtrl, 0108 [this]() { m_itemCtrl->setVisionShape(CharacterVision::DISK); }); 0109 0110 m_itemCtrl->setFont(QFont()); 0111 0112 connect(m_itemCtrl, &vmap::CharacterItemController::thumnailRectChanged, this, &CharacterItem::updateChildPosition); 0113 0114 if(m_itemCtrl->playableCharacter()) 0115 { 0116 auto vision= m_itemCtrl->vision(); 0117 0118 connect(vision, &CharacterVision::radiusChanged, this, &CharacterItem::updateChildPosition); 0119 connect(vision, &CharacterVision::angleChanged, this, &CharacterItem::updateChildPosition); 0120 } 0121 0122 auto updateLambda= [this]() { update(); }; 0123 connect(m_itemCtrl, &vmap::CharacterItemController::sideChanged, this, updateLambda); 0124 connect(m_itemCtrl, &vmap::CharacterItemController::stateColorChanged, this, updateLambda); 0125 connect(m_itemCtrl, &vmap::CharacterItemController::numberChanged, this, updateLambda); 0126 connect(m_itemCtrl, &vmap::CharacterItemController::thumnailRectChanged, this, updateLambda); 0127 connect(m_itemCtrl, &vmap::CharacterItemController::visionChanged, this, updateLambda); 0128 connect(m_itemCtrl, &vmap::CharacterItemController::visionShapeChanged, this, updateLambda); 0129 connect(m_itemCtrl, &vmap::CharacterItemController::textRectChanged, this, updateLambda); 0130 connect(m_itemCtrl, &vmap::CharacterItemController::textChanged, this, updateLambda); 0131 connect(m_itemCtrl, &vmap::CharacterItemController::hasAvatarChanged, this, updateLambda); 0132 connect(m_itemCtrl, &vmap::CharacterItemController::avatarChanged, this, updateLambda); 0133 connect(m_itemCtrl, &vmap::CharacterItemController::fontChanged, this, updateLambda); 0134 connect(m_itemCtrl, &vmap::CharacterItemController::radiusChanged, this, updateLambda); 0135 connect(m_itemCtrl, &vmap::CharacterItemController::remoteChanged, this, updateLambda); 0136 connect(m_itemCtrl, &vmap::CharacterItemController::stateIdChanged, this, updateLambda); 0137 connect(m_itemCtrl, &vmap::CharacterItemController::stateImageChanged, this, updateLambda); 0138 connect(m_itemCtrl, &vmap::CharacterItemController::modifiedChanged, this, updateLambda); 0139 connect(m_itemCtrl, &vmap::CharacterItemController::visibleChanged, this, updateLambda); 0140 connect(m_itemCtrl, &vmap::CharacterItemController::healthStatusVisibleChanged, this, updateLambda); 0141 connect(m_mapCtrl, &VectorialMapController::initScoreVisibleChanged, this, updateLambda); 0142 connect(m_mapCtrl, &VectorialMapController::healthBarVisibleChanged, this, updateLambda); 0143 connect(m_mapCtrl, &VectorialMapController::npcNameVisibleChanged, this, updateLambda); 0144 connect(m_mapCtrl, &VectorialMapController::npcNameChanged, this, updateLambda); 0145 connect(m_mapCtrl, &VectorialMapController::npcNumberVisibleChanged, this, updateLambda); 0146 connect(m_mapCtrl, &VectorialMapController::pcNameVisibleChanged, this, updateLambda); 0147 connect(m_mapCtrl, &VectorialMapController::stateLabelVisibleChanged, this, updateLambda); 0148 connect(m_mapCtrl, &VectorialMapController::characterVisionChanged, this, updateLambda); 0149 0150 0151 // createActions(); 0152 for(int i= 0; i <= CharacterItem::SightLenght; ++i) 0153 { 0154 auto c= ChildPointItem::Control::Geometry; 0155 if(i == CharacterItem::SightAngle || i == CharacterItem::SightLenght) 0156 c= ChildPointItem::Control::Vision; 0157 ChildPointItem* tmp= new ChildPointItem(m_itemCtrl, i, this, c); 0158 tmp->setMotion(ChildPointItem::MOUSE); 0159 0160 m_children.append(tmp); 0161 } 0162 updateChildPosition(); 0163 } 0164 0165 CharacterItem::~CharacterItem() 0166 { 0167 qDebug() << "charcteritem destroyed"; 0168 } 0169 0170 QRectF CharacterItem::boundingRect() const 0171 { 0172 return m_itemCtrl ? m_itemCtrl->thumnailRect().united(m_itemCtrl->textRect()) : QRectF(); 0173 } 0174 QPainterPath CharacterItem::shape() const 0175 { 0176 return m_itemCtrl->shape(); 0177 } 0178 0179 void CharacterItem::setNewEnd(const QPointF&) {} 0180 0181 void CharacterItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) 0182 { 0183 Q_UNUSED(option) 0184 Q_UNUSED(widget) 0185 bool hasFocusOrChildren= hasFocusOrChild(); 0186 setChildrenVisible(hasFocusOrChildren); 0187 emit selectStateChange(hasFocusOrChildren); 0188 auto rect= m_itemCtrl->rect(); 0189 0190 QString textToShow= m_itemCtrl->text(); 0191 0192 painter->setRenderHint(QPainter::Antialiasing, true); 0193 painter->setRenderHint(QPainter::TextAntialiasing, true); 0194 painter->setRenderHint(QPainter::SmoothPixmapTransform, true); 0195 0196 painter->save(); 0197 // Avatar 0198 if(m_itemCtrl->hasAvatar()) 0199 { 0200 painter->drawImage(rect, *m_itemCtrl->avatar(), m_itemCtrl->avatar()->rect()); 0201 } 0202 else 0203 { 0204 painter->setPen(m_itemCtrl->color()); 0205 painter->setBrush(QBrush(m_itemCtrl->color(), Qt::SolidPattern)); 0206 painter->drawEllipse(rect); 0207 } 0208 0209 QPen pen= painter->pen(); 0210 pen.setWidth(PEN_WIDTH); 0211 auto character= m_itemCtrl->character(); 0212 if(nullptr != character) 0213 { 0214 auto stateImg= m_itemCtrl->stateImage(); 0215 if(!stateImg.isNull()) 0216 { 0217 painter->drawImage(rect, stateImg, stateImg.rect()); 0218 } 0219 else if(!character->hasAvatar()) 0220 { 0221 pen.setColor(m_itemCtrl->stateColor()); 0222 painter->setPen(pen); 0223 painter->drawEllipse(m_itemCtrl->thumnailRect().adjusted(PEN_RADIUS, PEN_RADIUS, -PEN_RADIUS, -PEN_RADIUS)); 0224 } 0225 else 0226 { 0227 pen.setWidth(PEN_WIDTH / 2); 0228 pen.setColor(m_itemCtrl->stateColor()); 0229 painter->setPen(pen); 0230 int diam= static_cast<int>(m_itemCtrl->side()); 0231 painter->drawRoundedRect(rect.x(), rect.y(), diam, diam, m_itemCtrl->side() / RADIUS_CORNER, 0232 m_itemCtrl->side() / RADIUS_CORNER); 0233 } 0234 0235 if(m_mapCtrl->initScoreVisible() && character->hasInitScore()) 0236 { 0237 painter->save(); 0238 auto init= QString("%1").arg(character->getInitiativeScore()); 0239 auto chColor= character->getColor(); 0240 auto color= ContrastColor(chColor); 0241 painter->setPen(color); 0242 auto font= painter->font(); 0243 font.setBold(true); 0244 font.setPointSizeF(font.pointSizeF() * 2); 0245 painter->setFont(font); 0246 auto tl= rect.topLeft().toPoint(); 0247 auto metric= painter->fontMetrics(); 0248 auto rect= metric.boundingRect(init); 0249 rect.moveCenter(tl); 0250 painter->save(); 0251 painter->setPen(Qt::NoPen); 0252 painter->setBrush(QBrush(chColor, Qt::SolidPattern)); 0253 auto square= makeSquare(rect); 0254 square.moveCenter(tl); 0255 painter->drawEllipse(square); 0256 painter->restore(); 0257 painter->drawText(rect, Qt::AlignCenter, init); 0258 painter->restore(); 0259 } 0260 0261 } 0262 if(!textToShow.isEmpty()) 0263 { 0264 setToolTip(textToShow); 0265 painter->setPen(m_itemCtrl->color()); 0266 painter->drawText(m_itemCtrl->textRect(), Qt::AlignCenter, textToShow); 0267 } 0268 painter->restore(); 0269 0270 if(canBeMoved() && (option->state & QStyle::State_MouseOver || isSelected())) 0271 { 0272 painter->save(); 0273 QPen pen= painter->pen(); 0274 pen.setColor(isSelected() ? m_selectedColor : m_highlightColor); 0275 pen.setWidth(m_highlightWidth); 0276 painter->setPen(pen); 0277 if(m_itemCtrl->hasAvatar()) 0278 painter->drawRect(rect); 0279 else 0280 painter->drawEllipse(rect); 0281 painter->restore(); 0282 } 0283 0284 if(m_itemCtrl->healthStatusVisible()) 0285 { 0286 auto character= m_itemCtrl->character(); 0287 if(nullptr == character) 0288 return; 0289 0290 auto max= character->getHealthPointsMax(); 0291 auto color= character->getLifeColor(); 0292 auto min= character->getHealthPointsMin(); 0293 auto current= character->getHealthPointsCurrent(); 0294 QPen pen= painter->pen(); 0295 pen.setColor(color); 0296 0297 if(min < max) 0298 { 0299 QRectF bar(rect.x(), rect.height() + rect.y() - PEN_WIDTH, rect.width(), PEN_WIDTH); 0300 painter->save(); 0301 auto newWidth= (current - min) * bar.width() / (max - min); 0302 painter->drawRect(bar); 0303 QRectF value(rect.x(), rect.height() + rect.y() - PEN_WIDTH, newWidth, PEN_WIDTH); 0304 painter->fillRect(value, color); 0305 painter->restore(); 0306 } 0307 } 0308 } 0309 0310 void CharacterItem::updateChildPosition() 0311 { 0312 auto rect= m_itemCtrl->thumnailRect(); //(0, 0, m_itemCtrl->side(), m_itemCtrl->side()); 0313 0314 m_children.value(0)->setPos(rect.topLeft()); 0315 m_children.value(0)->setPlacement(ChildPointItem::TopLeft); 0316 m_children.value(1)->setPos(rect.topRight()); 0317 m_children.value(1)->setPlacement(ChildPointItem::TopRight); 0318 m_children.value(2)->setPos(rect.bottomRight()); 0319 m_children.value(2)->setPlacement(ChildPointItem::ButtomRight); 0320 m_children.value(3)->setPos(rect.bottomLeft()); 0321 m_children.value(3)->setPlacement(ChildPointItem::ButtomLeft); 0322 0323 // setTransformOriginPoint(rect.center()); 0324 if(m_itemCtrl->playableCharacter()) 0325 { 0326 auto vision= m_itemCtrl->vision(); 0327 if(vision) 0328 { 0329 m_children.value(DIRECTION_RADIUS_HANDLE) 0330 ->setPos(vision->radius() + m_itemCtrl->radius() 0331 + m_children[DIRECTION_RADIUS_HANDLE]->boundingRect().width(), 0332 m_itemCtrl->thumnailRect().height() / 2 0333 - m_children[DIRECTION_RADIUS_HANDLE]->boundingRect().height() / 2); 0334 0335 m_children[ANGLE_HANDLE]->setPos((vision->radius() + m_itemCtrl->radius()) / 2, -vision->angle()); 0336 } 0337 } 0338 else 0339 { 0340 m_children[DIRECTION_RADIUS_HANDLE]->setVisible(false); 0341 m_children[ANGLE_HANDLE]->setVisible(false); 0342 } 0343 if(!m_itemCtrl->localIsGM()) 0344 { 0345 setTransformOriginPoint(m_itemCtrl->thumnailRect().center()); 0346 } 0347 update(); 0348 } 0349 void CharacterItem::addActionContextMenu(QMenu& menu) 0350 { 0351 QMenu* stateMenu= menu.addMenu(tr("Change State")); 0352 QList<CharacterState*>* listOfState= Character::getCharacterStateList(); 0353 if(listOfState) 0354 { 0355 for(auto& state : *listOfState) 0356 { 0357 auto act= new QAction(QIcon(state->pixmap()), state->label(), this); 0358 act->setCheckable(true); 0359 0360 if(m_itemCtrl->stateId() == state->id()) 0361 act->setChecked(true); 0362 0363 connect(act, &QAction::triggered, this, 0364 [this, state](bool checked) { m_itemCtrl->setStateId(checked ? state->id() : QString()); }); 0365 stateMenu->addAction(act); 0366 } 0367 } 0368 0369 /*QMenu* user= menu.addMenu(tr("Transform into")); 0370 for(auto& character : PlayerModel::instance()->getCharacterList()) 0371 { 0372 QAction* act= user->addAction(character->name()); 0373 act->setData(character->getUuid()); 0374 0375 connect(act, &QAction::triggered, this, &CharacterItem::changeCharacter); 0376 }*/ 0377 QMenu* shape= menu.addMenu(tr("Vision Shape")); 0378 shape->addAction(m_visionShapeDisk.get()); 0379 shape->addAction(m_visionShapeAngle.get()); 0380 0381 m_visionShapeDisk->setChecked(CharacterVision::DISK == m_itemCtrl->visionShape()); 0382 m_visionShapeAngle->setChecked(CharacterVision::ANGLE == m_itemCtrl->visionShape()); 0383 0384 if(m_ctrl->localIsGM()) 0385 { 0386 // Actions 0387 auto actionlist= m_itemCtrl->actionList(); 0388 if(!actionlist.isEmpty()) 0389 { 0390 QMenu* actions= menu.addMenu(tr("Actions")); 0391 auto act= actions->addAction(tr("Initiative")); 0392 connect(act, &QAction::triggered, m_itemCtrl, &vmap::CharacterItemController::runInit); 0393 act= actions->addAction(tr("Clean Initiative")); 0394 connect(act, &QAction::triggered, m_itemCtrl, &vmap::CharacterItemController::cleanInit); 0395 0396 int i= 0; 0397 std::for_each(std::begin(actionlist), std::end(actionlist), 0398 [this, actions, &i](CharacterAction* charAction) 0399 { 0400 auto act= actions->addAction(charAction->name()); 0401 connect(act, &QAction::triggered, m_itemCtrl, [this, i]() { m_itemCtrl->runCommand(i); }); 0402 i++; 0403 }); 0404 } 0405 0406 // Shapes 0407 auto shapeList= m_itemCtrl->shapeList(); 0408 if(!shapeList.isEmpty()) 0409 { 0410 QMenu* shapeMenu= menu.addMenu(tr("Shapes")); 0411 int i= 0; 0412 for(auto& charShape : shapeList) 0413 { 0414 auto act= shapeMenu->addAction(charShape->name()); 0415 connect(act, &QAction::triggered, this, [i, this]() { m_itemCtrl->setShape(i); }); 0416 } 0417 auto action= shapeMenu->addAction(tr("Clean Shape")); 0418 connect(action, &QAction::triggered, m_itemCtrl, &vmap::CharacterItemController::cleanShape); 0419 } 0420 } 0421 0422 VisualItem::addActionContextMenu(menu); 0423 } 0424 0425 void CharacterItem::changeCharacter() 0426 { 0427 /* QAction* act= qobject_cast<QAction*>(sender()); 0428 QString uuid= act->data().toString();*/ 0429 0430 /*Character* tmp= PlayerModel::instance()->getCharacter(uuid); 0431 0432 Character* old= m_character; 0433 if(nullptr != tmp) 0434 { 0435 setCharacter(tmp); 0436 generatedThumbnail(); 0437 emit ownerChanged(old, this); 0438 emit itemGeometryChanged(this); 0439 }*/ 0440 } 0441 0442 void CharacterItem::addChildPoint(ChildPointItem* item) 0443 { 0444 m_children.append(item); 0445 } 0446 0447 void CharacterItem::wheelEvent(QGraphicsSceneWheelEvent* event) 0448 { 0449 /* if((nullptr != m_character) && (event->modifiers() & Qt::AltModifier)) 0450 { 0451 auto hp= m_character->getHealthPointsCurrent(); 0452 auto delta= event->delta(); 0453 if(delta > 0) 0454 ++hp; 0455 else 0456 --hp; 0457 m_character->setHealthPointsCurrent(hp); 0458 event->accept(); 0459 update(); 0460 } 0461 else 0462 VisualItem::wheelEvent(event);*/ 0463 }