1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-10-31 00:07:39 +02:00

Merge pull request #5030 from vcmi/timed_events_objects_removal

Timed events objects removal
This commit is contained in:
Ivan Savenko
2024-12-06 16:07:21 +02:00
committed by GitHub
17 changed files with 198 additions and 17 deletions

View File

@@ -403,6 +403,18 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
localState->erasePath(hero); localState->erasePath(hero);
} }
void CPlayerInterface::townRemoved(const CGTownInstance* town)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(town->tempOwner == playerID)
{
localState->removeOwnedTown(town);
adventureInt->onTownChanged(town);
}
}
void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start)
{ {
EVENT_HANDLER_CALLED_BY_CLIENT; EVENT_HANDLER_CALLED_BY_CLIENT;
@@ -1424,6 +1436,12 @@ void CPlayerInterface::objectRemoved(const CGObjectInstance * obj, const PlayerC
const CGHeroInstance * h = static_cast<const CGHeroInstance *>(obj); const CGHeroInstance * h = static_cast<const CGHeroInstance *>(obj);
heroKilled(h); heroKilled(h);
} }
if(obj->ID == Obj::TOWN && obj->tempOwner == playerID)
{
const CGTownInstance * t = static_cast<const CGTownInstance *>(obj);
townRemoved(t);
}
GH.fakeMouseMove(); GH.fakeMouseMove();
} }

View File

@@ -220,6 +220,7 @@ private:
}; };
void heroKilled(const CGHeroInstance* hero); void heroKilled(const CGHeroInstance* hero);
void townRemoved(const CGTownInstance* town);
void garrisonsChanged(std::vector<const CArmedInstance *> objs); void garrisonsChanged(std::vector<const CArmedInstance *> objs);
void requestReturningToMainMenu(bool won); void requestReturningToMainMenu(bool won);
void acceptTurn(QueryID queryID, bool hotseatWait); //used during hot seat after your turn message is close void acceptTurn(QueryID queryID, bool hotseatWait); //used during hot seat after your turn message is close

View File

@@ -378,6 +378,9 @@ void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const
std::string CGTownInstance::getObjectName() const std::string CGTownInstance::getObjectName() const
{ {
if(ID == Obj::RANDOM_TOWN )
return CGObjectInstance::getObjectName();
return getNameTranslated() + ", " + getTown()->faction->getNameTranslated(); return getNameTranslated() + ", " + getTown()->faction->getNameTranslated();
} }

View File

@@ -51,6 +51,12 @@ void FlaggableMapObject::onHeroVisit( const CGHeroInstance * h ) const
giveBonusTo(h->getOwner()); giveBonusTo(h->getOwner());
} }
void FlaggableMapObject::markAsDeleted() const
{
if(getOwner().isValidPlayer())
takeBonusFrom(getOwner());
}
void FlaggableMapObject::initObj(vstd::RNG & rand) void FlaggableMapObject::initObj(vstd::RNG & rand)
{ {
if(getOwner().isValidPlayer()) if(getOwner().isValidPlayer())

View File

@@ -28,6 +28,7 @@ public:
using CGObjectInstance::CGObjectInstance; using CGObjectInstance::CGObjectInstance;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void markAsDeleted() const;
void initObj(vstd::RNG & rand) override; void initObj(vstd::RNG & rand) override;
const IOwnableObject * asOwnable() const final; const IOwnableObject * asOwnable() const final;

View File

@@ -103,6 +103,9 @@ void CMapEvent::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt("firstOccurrence", firstOccurrence); handler.serializeInt("firstOccurrence", firstOccurrence);
handler.serializeInt("nextOccurrence", nextOccurrence); handler.serializeInt("nextOccurrence", nextOccurrence);
resources.serializeJson(handler, "resources"); resources.serializeJson(handler, "resources");
auto deletedObjects = handler.enterArray("deletedObjectsInstances");
deletedObjects.serializeArray(deletedObjectsInstances);
} }
void CCastleEvent::serializeJson(JsonSerializeFormat & handler) void CCastleEvent::serializeJson(JsonSerializeFormat & handler)

View File

@@ -12,6 +12,7 @@
#include "../ResourceSet.h" #include "../ResourceSet.h"
#include "../texts/MetaString.h" #include "../texts/MetaString.h"
#include "../int3.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@@ -42,6 +43,8 @@ public:
ui32 firstOccurrence; ui32 firstOccurrence;
ui32 nextOccurrence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time ui32 nextOccurrence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time
std::vector<ObjectInstanceID> deletedObjectsInstances;
template <typename Handler> template <typename Handler>
void serialize(Handler & h) void serialize(Handler & h)
{ {
@@ -64,6 +67,10 @@ public:
h & computerAffected; h & computerAffected;
h & firstOccurrence; h & firstOccurrence;
h & nextOccurrence; h & nextOccurrence;
if(h.version >= Handler::Version::EVENT_OBJECTS_DELETION)
{
h & deletedObjectsInstances;
}
} }
virtual void serializeJson(JsonSerializeFormat & handler); virtual void serializeJson(JsonSerializeFormat & handler);

View File

@@ -45,6 +45,7 @@
#include "mapObjectConstructors/CObjectClassesHandler.h" #include "mapObjectConstructors/CObjectClassesHandler.h"
#include "campaign/CampaignState.h" #include "campaign/CampaignState.h"
#include "IGameSettings.h" #include "IGameSettings.h"
#include "mapObjects/FlaggableMapObject.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@@ -1184,7 +1185,6 @@ void RemoveBonus::applyGs(CGameState *gs)
void RemoveObject::applyGs(CGameState *gs) void RemoveObject::applyGs(CGameState *gs)
{ {
CGObjectInstance *obj = gs->getObjInstance(objectID); CGObjectInstance *obj = gs->getObjInstance(objectID);
logGlobal->debug("removing object id=%d; address=%x; name=%s", objectID, (intptr_t)obj, obj->getObjectName()); logGlobal->debug("removing object id=%d; address=%x; name=%s", objectID, (intptr_t)obj, obj->getObjectName());
//unblock tiles //unblock tiles
@@ -1197,10 +1197,7 @@ void RemoveObject::applyGs(CGameState *gs)
{ {
auto * beatenHero = dynamic_cast<CGHeroInstance *>(obj); auto * beatenHero = dynamic_cast<CGHeroInstance *>(obj);
assert(beatenHero); assert(beatenHero);
PlayerState * p = gs->getPlayerState(beatenHero->tempOwner);
gs->map->heroesOnMap -= beatenHero; gs->map->heroesOnMap -= beatenHero;
p->removeOwnedObject(beatenHero);
auto * siegeNode = beatenHero->whereShouldBeAttachedOnSiege(gs); auto * siegeNode = beatenHero->whereShouldBeAttachedOnSiege(gs);
@@ -1254,6 +1251,18 @@ void RemoveObject::applyGs(CGameState *gs)
} }
} }
if(obj->getOwner().isValidPlayer())
{
gs->getPlayerState(obj->getOwner())->removeOwnedObject(obj); //object removed via map event or hero got beaten
FlaggableMapObject* flaggableObject = dynamic_cast<FlaggableMapObject*>(obj);
if(flaggableObject)
{
flaggableObject->markAsDeleted();
}
}
gs->map->instanceNames.erase(obj->instanceName); gs->map->instanceNames.erase(obj->instanceName);
gs->map->objects[objectID.getNum()].dellNull(); gs->map->objects[objectID.getNum()].dellNull();
gs->map->calculateGuardingGreaturePositions();//FIXME: excessive, update only affected tiles gs->map->calculateGuardingGreaturePositions();//FIXME: excessive, update only affected tiles

View File

@@ -69,6 +69,7 @@ enum class ESerializationVersion : int32_t
FOLDER_NAME_REWORK, // 870 - rework foldername FOLDER_NAME_REWORK, // 870 - rework foldername
REWARDABLE_GUARDS, // 871 - fix missing serialization of guards in rewardable objects REWARDABLE_GUARDS, // 871 - fix missing serialization of guards in rewardable objects
MARKET_TRANSLATION_FIX, // 872 - remove serialization of markets translateable strings MARKET_TRANSLATION_FIX, // 872 - remove serialization of markets translateable strings
EVENT_OBJECTS_DELETION, //873 - allow events to remove map objects
CURRENT = MARKET_TRANSLATION_FIX CURRENT = EVENT_OBJECTS_DELETION
}; };

View File

@@ -17,6 +17,7 @@ enum MapEditorRoles
PlayerIDRole, PlayerIDRole,
BuildingIDRole, BuildingIDRole,
SpellIDRole, SpellIDRole,
ObjectInstanceIDRole,
ArtifactIDRole, ArtifactIDRole,
ArtifactSlotRole ArtifactSlotRole,
}; };

View File

@@ -14,6 +14,8 @@
#include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h"
Q_DECLARE_METATYPE(int3)
//parses date for lose condition (1m 1w 1d) //parses date for lose condition (1m 1w 1d)
int expiredDate(const QString & date); int expiredDate(const QString & date);
QString expiredDate(int date); QString expiredDate(int date);

View File

@@ -55,6 +55,28 @@ TResources resourcesFromVariant(const QVariant & v)
return TResources(vJson); return TResources(vJson);
} }
QVariant toVariant(std::vector<ObjectInstanceID> objects)
{
QVariantList result;
for(auto obj : objects)
{
result.push_back(QVariant::fromValue(obj.num));
}
return result;
}
std::vector<ObjectInstanceID> deletedObjectsIdsFromVariant(const QVariant & v)
{
std::vector<ObjectInstanceID> result;
for(auto idAsVariant : v.toList())
{
auto id = idAsVariant.value<int>();
result.push_back(ObjectInstanceID(id));
}
return result;
}
QVariant toVariant(const CMapEvent & event) QVariant toVariant(const CMapEvent & event)
{ {
QVariantMap result; QVariantMap result;
@@ -66,6 +88,7 @@ QVariant toVariant(const CMapEvent & event)
result["firstOccurrence"] = QVariant::fromValue(event.firstOccurrence); result["firstOccurrence"] = QVariant::fromValue(event.firstOccurrence);
result["nextOccurrence"] = QVariant::fromValue(event.nextOccurrence); result["nextOccurrence"] = QVariant::fromValue(event.nextOccurrence);
result["resources"] = toVariant(event.resources); result["resources"] = toVariant(event.resources);
result["deletedObjectsInstances"] = toVariant(event.deletedObjectsInstances);
return QVariant(result); return QVariant(result);
} }
@@ -81,6 +104,7 @@ CMapEvent eventFromVariant(CMapHeader & mapHeader, const QVariant & variant)
result.firstOccurrence = v.value("firstOccurrence").toInt(); result.firstOccurrence = v.value("firstOccurrence").toInt();
result.nextOccurrence = v.value("nextOccurrence").toInt(); result.nextOccurrence = v.value("nextOccurrence").toInt();
result.resources = resourcesFromVariant(v.value("resources")); result.resources = resourcesFromVariant(v.value("resources"));
result.deletedObjectsInstances = deletedObjectsIdsFromVariant(v.value("deletedObjectsInstances"));
return result; return result;
} }
@@ -137,6 +161,6 @@ void EventSettings::on_timedEventRemove_clicked()
void EventSettings::on_eventsList_itemActivated(QListWidgetItem *item) void EventSettings::on_eventsList_itemActivated(QListWidgetItem *item)
{ {
new TimedEvent(item, parentWidget()); new TimedEvent(*controller, item, parentWidget());
} }

View File

@@ -11,13 +11,15 @@
#include "timedevent.h" #include "timedevent.h"
#include "ui_timedevent.h" #include "ui_timedevent.h"
#include "eventsettings.h" #include "eventsettings.h"
#include "../mapeditorroles.h"
#include "../../lib/constants/EntityIdentifiers.h" #include "../../lib/constants/EntityIdentifiers.h"
#include "../../lib/constants/StringConstants.h" #include "../../lib/constants/StringConstants.h"
TimedEvent::TimedEvent(QListWidgetItem * t, QWidget *parent) : TimedEvent::TimedEvent(MapController & c, QListWidgetItem * t, QWidget *parent) :
controller(c),
QDialog(parent), QDialog(parent),
target(t), ui(new Ui::TimedEvent),
ui(new Ui::TimedEvent) target(t)
{ {
ui->setupUi(this); ui->setupUi(this);
@@ -51,7 +53,14 @@ TimedEvent::TimedEvent(QListWidgetItem * t, QWidget *parent) :
nval->setFlags(nval->flags() | Qt::ItemIsEditable); nval->setFlags(nval->flags() | Qt::ItemIsEditable);
ui->resources->setItem(i, 1, nval); ui->resources->setItem(i, 1, nval);
} }
auto deletedObjectInstances = params.value("deletedObjectsInstances").toList();
for(auto const & idAsVariant : deletedObjectInstances)
{
auto id = ObjectInstanceID(idAsVariant.toInt());
auto obj = controller.map()->objects[id];
if(obj)
insertObjectToDelete(obj);
}
show(); show();
} }
@@ -89,10 +98,63 @@ void TimedEvent::on_TimedEvent_finished(int result)
} }
descriptor["resources"] = res; descriptor["resources"] = res;
QVariantList deletedObjects;
for(int i = 0; i < ui->deletedObjects->count(); ++i)
{
auto const & item = ui->deletedObjects->item(i);
auto data = item->data(MapEditorRoles::ObjectInstanceIDRole);
auto id = ObjectInstanceID(data.value<int>());
deletedObjects.push_back(QVariant::fromValue(id.num));
}
descriptor["deletedObjectsInstances"] = QVariant::fromValue(deletedObjects);
target->setData(Qt::UserRole, descriptor); target->setData(Qt::UserRole, descriptor);
target->setText(ui->eventNameText->text()); target->setText(ui->eventNameText->text());
} }
void TimedEvent::on_addObjectToDelete_clicked()
{
for(int lvl : {0, 1})
{
auto & l = controller.scene(lvl)->objectPickerView;
l.highlight<const CGObjectInstance>();
l.update();
QObject::connect(&l, &ObjectPickerLayer::selectionMade, this, &TimedEvent::onObjectPicked);
}
hide();
dynamic_cast<QWidget *>(parent()->parent()->parent()->parent()->parent()->parent()->parent())->hide();
}
void TimedEvent::on_removeObjectToDelete_clicked()
{
delete ui->deletedObjects->takeItem(ui->deletedObjects->currentRow());
}
void TimedEvent::onObjectPicked(const CGObjectInstance * obj)
{
show();
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, &TimedEvent::onObjectPicked);
}
if(!obj)
return;
insertObjectToDelete(obj);
}
void TimedEvent::insertObjectToDelete(const CGObjectInstance * obj)
{
QString objectLabel = QString("%1, x: %2, y: %3, z: %4").arg(QString::fromStdString(obj->getObjectName())).arg(obj->pos.x).arg(obj->pos.y).arg(obj->pos.z);
auto * item = new QListWidgetItem(objectLabel);
item->setData(MapEditorRoles::ObjectInstanceIDRole, QVariant::fromValue(obj->id.num));
ui->deletedObjects->addItem(item);
}
void TimedEvent::on_pushButton_clicked() void TimedEvent::on_pushButton_clicked()
{ {

View File

@@ -11,6 +11,8 @@
#include <QDialog> #include <QDialog>
#include "mapcontroller.h"
namespace Ui { namespace Ui {
class TimedEvent; class TimedEvent;
} }
@@ -20,18 +22,23 @@ class TimedEvent : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit TimedEvent(QListWidgetItem *, QWidget *parent = nullptr); explicit TimedEvent(MapController & map, QListWidgetItem *, QWidget * parent = nullptr);
~TimedEvent(); ~TimedEvent();
private slots: private slots:
void on_TimedEvent_finished(int result); void on_TimedEvent_finished(int result);
void on_addObjectToDelete_clicked();
void on_removeObjectToDelete_clicked();
void onObjectPicked(const CGObjectInstance * obj);
void insertObjectToDelete(const CGObjectInstance * obj);
void on_pushButton_clicked(); void on_pushButton_clicked();
void on_resources_itemDoubleClicked(QTableWidgetItem *item); void on_resources_itemDoubleClicked(QTableWidgetItem * item);
private: private:
Ui::TimedEvent *ui; MapController & controller;
Ui::TimedEvent * ui;
QListWidgetItem * target; QListWidgetItem * target;
}; };

View File

@@ -9,8 +9,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>620</width> <width>730</width>
<height>371</height> <height>422</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -201,6 +201,34 @@
</column> </column>
</widget> </widget>
</item> </item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="deletedObjectsLabel">
<property name="text">
<string>Objects to delete</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addObjectToDelete">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeObjectToDelete">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="deletedObjects"/>
</item>
<item> <item>
<widget class="QPushButton" name="pushButton"> <widget class="QPushButton" name="pushButton">
<property name="text"> <property name="text">

View File

@@ -787,7 +787,7 @@ bool CGameHandler::removeObject(const CGObjectInstance * obj, const PlayerColor
ro.initiator = initiator; ro.initiator = initiator;
sendAndApply(ro); sendAndApply(ro);
checkVictoryLossConditionsForAll(); //eg if monster escaped (removing objs after battle is done dircetly by endBattle, not this function) checkVictoryLossConditionsForAll(); //e.g. if monster escaped (removing objs after battle is done directly by endBattle, not this function)
return true; return true;
} }

View File

@@ -61,6 +61,14 @@ void NewTurnProcessor::handleTimeEvents(PlayerColor color)
if (event.resources[i]) if (event.resources[i])
iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]); iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
} }
//remove objects specified by event
for(const ObjectInstanceID objectIdToRemove : event.deletedObjectsInstances)
{
auto objectInstance = gameHandler->getObj(objectIdToRemove, false);
if(objectInstance != nullptr)
gameHandler->removeObject(objectInstance, PlayerColor::NEUTRAL);
}
gameHandler->sendAndApply(iw); //show dialog gameHandler->sendAndApply(iw); //show dialog
} }
} }