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 }