2023-09-08 01:43:01 +02:00
|
|
|
/*
|
|
|
|
* loseconditions.cpp, part of VCMI engine
|
|
|
|
*
|
|
|
|
* Authors: listed in file AUTHORS in main folder
|
|
|
|
*
|
|
|
|
* License: GNU General Public License v2.0 or later
|
|
|
|
* Full text of license available in license.txt file, in main folder
|
|
|
|
*
|
|
|
|
*/
|
2023-09-05 01:26:38 +02:00
|
|
|
#include "StdInc.h"
|
|
|
|
#include "loseconditions.h"
|
|
|
|
#include "ui_loseconditions.h"
|
2023-09-11 19:16:24 +02:00
|
|
|
#include "../mapcontroller.h"
|
2024-07-20 14:55:17 +02:00
|
|
|
#include "../lib/texts/CGeneralTextHandler.h"
|
2023-09-05 01:26:38 +02:00
|
|
|
|
|
|
|
LoseConditions::LoseConditions(QWidget *parent) :
|
|
|
|
AbstractSettings(parent),
|
|
|
|
ui(new Ui::LoseConditions)
|
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
LoseConditions::~LoseConditions()
|
|
|
|
{
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
2023-09-11 19:16:24 +02:00
|
|
|
void LoseConditions::initialize(MapController & c)
|
2023-09-05 01:26:38 +02:00
|
|
|
{
|
2023-09-11 19:16:24 +02:00
|
|
|
AbstractSettings::initialize(c);
|
2023-09-05 01:26:38 +02:00
|
|
|
|
|
|
|
//loss messages
|
2023-09-11 19:16:24 +02:00
|
|
|
ui->defeatMessageEdit->setText(QString::fromStdString(controller->map()->defeatMessage.toString()));
|
2023-09-05 01:26:38 +02:00
|
|
|
|
|
|
|
//loss conditions
|
|
|
|
const std::array<std::string, 5> conditionStringsLose = {
|
|
|
|
QT_TR_NOOP("No special loss"),
|
|
|
|
QT_TR_NOOP("Lose castle"),
|
|
|
|
QT_TR_NOOP("Lose hero"),
|
|
|
|
QT_TR_NOOP("Time expired"),
|
|
|
|
QT_TR_NOOP("Days without town")
|
|
|
|
};
|
|
|
|
|
|
|
|
for(auto & s : conditionStringsLose)
|
|
|
|
{
|
2024-12-09 22:51:35 +02:00
|
|
|
ui->loseComboBox->addItem(tr(s.c_str()));
|
2023-09-05 01:26:38 +02:00
|
|
|
}
|
|
|
|
ui->standardLoseCheck->setChecked(false);
|
|
|
|
|
2023-09-11 19:16:24 +02:00
|
|
|
for(auto & ev : controller->map()->triggeredEvents)
|
2023-09-05 01:26:38 +02:00
|
|
|
{
|
|
|
|
if(ev.effect.type == EventEffect::DEFEAT)
|
|
|
|
{
|
|
|
|
if(ev.identifier == "standardDefeat")
|
|
|
|
ui->standardLoseCheck->setChecked(true);
|
|
|
|
|
|
|
|
if(ev.identifier == "specialDefeat")
|
|
|
|
{
|
|
|
|
auto readjson = ev.trigger.toJson(AbstractSettings::conditionToJson);
|
|
|
|
auto linearNodes = linearJsonArray(readjson);
|
|
|
|
|
|
|
|
for(auto & json : linearNodes)
|
|
|
|
{
|
|
|
|
switch(json["condition"].Integer())
|
|
|
|
{
|
|
|
|
case EventCondition::CONTROL: {
|
2024-05-26 22:50:26 +02:00
|
|
|
auto objectType = MapObjectID::decode(json["objectType"].String());
|
|
|
|
if(objectType == Obj::TOWN)
|
2023-09-05 01:26:38 +02:00
|
|
|
{
|
|
|
|
ui->loseComboBox->setCurrentIndex(1);
|
|
|
|
assert(loseTypeWidget);
|
2023-09-11 19:16:24 +02:00
|
|
|
int townIdx = getObjectByPos<const CGTownInstance>(*controller->map(), posFromJson(json["position"]));
|
2023-09-05 01:26:38 +02:00
|
|
|
if(townIdx >= 0)
|
|
|
|
{
|
|
|
|
auto idx = loseTypeWidget->findData(townIdx);
|
|
|
|
loseTypeWidget->setCurrentIndex(idx);
|
|
|
|
}
|
|
|
|
}
|
2024-05-26 22:50:26 +02:00
|
|
|
if(objectType == Obj::HERO)
|
2023-09-05 01:26:38 +02:00
|
|
|
{
|
|
|
|
ui->loseComboBox->setCurrentIndex(2);
|
|
|
|
assert(loseTypeWidget);
|
2023-09-11 19:16:24 +02:00
|
|
|
int heroIdx = getObjectByPos<const CGHeroInstance>(*controller->map(), posFromJson(json["position"]));
|
2023-09-05 01:26:38 +02:00
|
|
|
if(heroIdx >= 0)
|
|
|
|
{
|
|
|
|
auto idx = loseTypeWidget->findData(heroIdx);
|
|
|
|
loseTypeWidget->setCurrentIndex(idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case EventCondition::DAYS_PASSED: {
|
|
|
|
ui->loseComboBox->setCurrentIndex(3);
|
|
|
|
assert(loseValueWidget);
|
|
|
|
loseValueWidget->setText(expiredDate(json["value"].Integer()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case EventCondition::DAYS_WITHOUT_TOWN: {
|
|
|
|
ui->loseComboBox->setCurrentIndex(4);
|
|
|
|
assert(loseValueWidget);
|
|
|
|
loseValueWidget->setText(QString::number(json["value"].Integer()));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EventCondition::IS_HUMAN:
|
|
|
|
break; //ignore because always applicable for defeat conditions
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-11 19:16:24 +02:00
|
|
|
void LoseConditions::update()
|
2023-09-05 01:26:38 +02:00
|
|
|
{
|
|
|
|
//loss messages
|
2023-10-05 23:34:29 +02:00
|
|
|
bool customMessage = true;
|
2023-09-05 01:26:38 +02:00
|
|
|
|
|
|
|
//loss conditions
|
|
|
|
EventCondition defeatCondition(EventCondition::DAYS_WITHOUT_TOWN);
|
|
|
|
defeatCondition.value = 7;
|
|
|
|
|
|
|
|
//Loss condition - 7 days without town
|
|
|
|
TriggeredEvent standardDefeat;
|
|
|
|
standardDefeat.effect.type = EventEffect::DEFEAT;
|
|
|
|
standardDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.8");
|
|
|
|
standardDefeat.identifier = "standardDefeat";
|
|
|
|
standardDefeat.description.clear(); // TODO: display in quest window
|
|
|
|
standardDefeat.onFulfill.appendTextID("core.genrltxt.7");
|
|
|
|
standardDefeat.trigger = EventExpression(defeatCondition);
|
|
|
|
|
|
|
|
//DEFEAT
|
|
|
|
if(ui->loseComboBox->currentIndex() == 0)
|
|
|
|
{
|
2023-09-11 19:16:24 +02:00
|
|
|
controller->map()->triggeredEvents.push_back(standardDefeat);
|
|
|
|
controller->map()->defeatIconIndex = 3;
|
|
|
|
controller->map()->defeatMessage = MetaString::createFromTextID("core.lcdesc.0");
|
2023-10-05 23:34:29 +02:00
|
|
|
customMessage = false;
|
2023-09-05 01:26:38 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int lossCondition = ui->loseComboBox->currentIndex() - 1;
|
|
|
|
|
|
|
|
TriggeredEvent specialDefeat;
|
|
|
|
specialDefeat.effect.type = EventEffect::DEFEAT;
|
|
|
|
specialDefeat.identifier = "specialDefeat";
|
|
|
|
specialDefeat.description.clear(); // TODO: display in quest window
|
|
|
|
|
2023-09-11 19:16:24 +02:00
|
|
|
controller->map()->defeatIconIndex = lossCondition;
|
2023-09-05 01:26:38 +02:00
|
|
|
|
|
|
|
switch(lossCondition)
|
|
|
|
{
|
|
|
|
case 0: {
|
|
|
|
EventExpression::OperatorNone noneOf;
|
|
|
|
EventCondition cond(EventCondition::CONTROL);
|
2023-11-08 18:50:37 +02:00
|
|
|
cond.objectType = Obj(Obj::TOWN);
|
2023-09-05 01:26:38 +02:00
|
|
|
assert(loseTypeWidget);
|
|
|
|
int townIdx = loseTypeWidget->currentData().toInt();
|
2023-09-11 19:16:24 +02:00
|
|
|
cond.position = controller->map()->objects[townIdx]->pos;
|
2023-09-05 01:26:38 +02:00
|
|
|
noneOf.expressions.push_back(cond);
|
|
|
|
specialDefeat.onFulfill.appendTextID("core.genrltxt.251");
|
|
|
|
specialDefeat.trigger = EventExpression(noneOf);
|
2023-09-11 19:16:24 +02:00
|
|
|
controller->map()->defeatMessage = MetaString::createFromTextID("core.lcdesc.1");
|
2023-10-05 23:34:29 +02:00
|
|
|
customMessage = false;
|
2023-09-05 01:26:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 1: {
|
|
|
|
EventExpression::OperatorNone noneOf;
|
|
|
|
EventCondition cond(EventCondition::CONTROL);
|
2023-11-08 18:50:37 +02:00
|
|
|
cond.objectType = Obj(Obj::HERO);
|
2023-09-05 01:26:38 +02:00
|
|
|
assert(loseTypeWidget);
|
|
|
|
int townIdx = loseTypeWidget->currentData().toInt();
|
2023-09-11 19:16:24 +02:00
|
|
|
cond.position = controller->map()->objects[townIdx]->pos;
|
2023-09-05 01:26:38 +02:00
|
|
|
noneOf.expressions.push_back(cond);
|
|
|
|
specialDefeat.onFulfill.appendTextID("core.genrltxt.253");
|
|
|
|
specialDefeat.trigger = EventExpression(noneOf);
|
2023-09-11 19:16:24 +02:00
|
|
|
controller->map()->defeatMessage = MetaString::createFromTextID("core.lcdesc.2");
|
2023-10-05 23:34:29 +02:00
|
|
|
customMessage = false;
|
2023-09-05 01:26:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 2: {
|
|
|
|
EventCondition cond(EventCondition::DAYS_PASSED);
|
|
|
|
assert(loseValueWidget);
|
|
|
|
cond.value = expiredDate(loseValueWidget->text());
|
|
|
|
specialDefeat.onFulfill.appendTextID("core.genrltxt.254");
|
|
|
|
specialDefeat.trigger = EventExpression(cond);
|
2023-09-11 19:16:24 +02:00
|
|
|
controller->map()->defeatMessage = MetaString::createFromTextID("core.lcdesc.3");
|
2023-10-05 23:34:29 +02:00
|
|
|
customMessage = false;
|
2023-09-05 01:26:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 3: {
|
|
|
|
EventCondition cond(EventCondition::DAYS_WITHOUT_TOWN);
|
|
|
|
assert(loseValueWidget);
|
|
|
|
cond.value = loseValueWidget->text().toInt();
|
|
|
|
specialDefeat.onFulfill.appendTextID("core.genrltxt.7");
|
|
|
|
specialDefeat.trigger = EventExpression(cond);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EventExpression::OperatorAll allOf;
|
|
|
|
EventCondition isHuman(EventCondition::IS_HUMAN);
|
|
|
|
isHuman.value = 1;
|
|
|
|
|
|
|
|
allOf.expressions.push_back(specialDefeat.trigger.get());
|
|
|
|
allOf.expressions.push_back(isHuman);
|
|
|
|
specialDefeat.trigger = EventExpression(allOf);
|
|
|
|
|
|
|
|
if(ui->standardLoseCheck->isChecked())
|
|
|
|
{
|
2023-09-11 19:16:24 +02:00
|
|
|
controller->map()->triggeredEvents.push_back(standardDefeat);
|
2023-09-05 01:26:38 +02:00
|
|
|
}
|
2023-09-11 19:16:24 +02:00
|
|
|
controller->map()->triggeredEvents.push_back(specialDefeat);
|
2023-09-05 01:26:38 +02:00
|
|
|
}
|
|
|
|
|
2023-10-05 23:34:29 +02:00
|
|
|
if(customMessage)
|
|
|
|
{
|
|
|
|
controller->map()->defeatMessage = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller->map(), TextIdentifier("header", "defeatMessage"), ui->defeatMessageEdit->text().toStdString()));
|
|
|
|
}
|
2023-09-05 01:26:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void LoseConditions::on_loseComboBox_currentIndexChanged(int index)
|
|
|
|
{
|
|
|
|
delete loseTypeWidget;
|
|
|
|
delete loseValueWidget;
|
|
|
|
delete loseSelectWidget;
|
2023-09-11 19:16:24 +02:00
|
|
|
delete pickObjectButton;
|
2023-09-05 01:26:38 +02:00
|
|
|
loseTypeWidget = nullptr;
|
|
|
|
loseValueWidget = nullptr;
|
|
|
|
loseSelectWidget = nullptr;
|
2023-09-11 19:16:24 +02:00
|
|
|
pickObjectButton = nullptr;
|
2023-09-05 01:26:38 +02:00
|
|
|
|
|
|
|
if(index == 0)
|
|
|
|
{
|
|
|
|
ui->standardLoseCheck->setChecked(true);
|
|
|
|
ui->standardLoseCheck->setEnabled(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ui->standardLoseCheck->setEnabled(true);
|
|
|
|
|
|
|
|
int loseCondition = index - 1;
|
|
|
|
switch(loseCondition)
|
|
|
|
{
|
|
|
|
case 0: { //EventCondition::CONTROL (Obj::TOWN)
|
|
|
|
loseTypeWidget = new QComboBox;
|
|
|
|
ui->loseParamsLayout->addWidget(loseTypeWidget);
|
2023-09-11 19:16:24 +02:00
|
|
|
for(int i : getObjectIndexes<const CGTownInstance>(*controller->map()))
|
2023-09-28 14:49:47 +02:00
|
|
|
loseTypeWidget->addItem(QString::fromStdString(getTownName(*controller->map(), i).c_str()), QVariant::fromValue(i));
|
2023-09-11 19:16:24 +02:00
|
|
|
pickObjectButton = new QToolButton;
|
|
|
|
connect(pickObjectButton, &QToolButton::clicked, this, &LoseConditions::onObjectSelect);
|
|
|
|
ui->loseParamsLayout->addWidget(pickObjectButton);
|
2023-09-05 01:26:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 1: { //EventCondition::CONTROL (Obj::HERO)
|
|
|
|
loseTypeWidget = new QComboBox;
|
|
|
|
ui->loseParamsLayout->addWidget(loseTypeWidget);
|
2023-09-11 19:16:24 +02:00
|
|
|
for(int i : getObjectIndexes<const CGHeroInstance>(*controller->map()))
|
2023-09-28 14:49:47 +02:00
|
|
|
loseTypeWidget->addItem(QString::fromStdString(getHeroName(*controller->map(), i).c_str()), QVariant::fromValue(i));
|
2023-09-11 19:16:24 +02:00
|
|
|
pickObjectButton = new QToolButton;
|
|
|
|
connect(pickObjectButton, &QToolButton::clicked, this, &LoseConditions::onObjectSelect);
|
|
|
|
ui->loseParamsLayout->addWidget(pickObjectButton);
|
2023-09-05 01:26:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 2: { //EventCondition::DAYS_PASSED
|
|
|
|
loseValueWidget = new QLineEdit;
|
|
|
|
ui->loseParamsLayout->addWidget(loseValueWidget);
|
|
|
|
loseValueWidget->setText("2m 1w 1d");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 3: { //EventCondition::DAYS_WITHOUT_TOWN
|
|
|
|
loseValueWidget = new QLineEdit;
|
|
|
|
ui->loseParamsLayout->addWidget(loseValueWidget);
|
|
|
|
loseValueWidget->setText("7");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-11 19:16:24 +02:00
|
|
|
void LoseConditions::onObjectSelect()
|
|
|
|
{
|
2023-09-11 20:16:33 +02:00
|
|
|
int loseCondition = ui->loseComboBox->currentIndex() - 1;
|
|
|
|
for(int lvl : {0, 1})
|
|
|
|
{
|
|
|
|
auto & l = controller->scene(lvl)->objectPickerView;
|
|
|
|
switch(loseCondition)
|
|
|
|
{
|
|
|
|
case 0: { //EventCondition::CONTROL (Obj::TOWN)
|
|
|
|
l.highlight<const CGTownInstance>();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 1: { //EventCondition::CONTROL (Obj::HERO)
|
|
|
|
l.highlight<const CGHeroInstance>();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
l.update();
|
|
|
|
QObject::connect(&l, &ObjectPickerLayer::selectionMade, this, &LoseConditions::onObjectPicked);
|
|
|
|
}
|
2023-09-11 19:16:24 +02:00
|
|
|
|
2023-09-11 20:16:33 +02:00
|
|
|
dynamic_cast<QWidget*>(parent()->parent()->parent()->parent()->parent()->parent()->parent())->hide();
|
2023-09-11 19:16:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void LoseConditions::onObjectPicked(const CGObjectInstance * obj)
|
|
|
|
{
|
2023-09-11 20:16:33 +02:00
|
|
|
dynamic_cast<QWidget*>(parent()->parent()->parent()->parent()->parent()->parent()->parent())->show();
|
|
|
|
|
|
|
|
for(int lvl : {0, 1})
|
|
|
|
{
|
|
|
|
auto & l = controller->scene(lvl)->objectPickerView;
|
|
|
|
l.clear();
|
|
|
|
l.update();
|
|
|
|
QObject::disconnect(&l, &ObjectPickerLayer::selectionMade, this, &LoseConditions::onObjectPicked);
|
|
|
|
}
|
2023-09-11 19:16:24 +02:00
|
|
|
|
2023-09-11 20:16:33 +02:00
|
|
|
if(!obj) //discarded
|
|
|
|
return;
|
|
|
|
|
|
|
|
for(int i = 0; i < loseTypeWidget->count(); ++i)
|
|
|
|
{
|
|
|
|
auto data = controller->map()->objects.at(loseTypeWidget->itemData(i).toInt());
|
|
|
|
if(data == obj)
|
|
|
|
{
|
|
|
|
loseTypeWidget->setCurrentIndex(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-09-11 19:16:24 +02:00
|
|
|
}
|