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:
		| @@ -403,6 +403,18 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* 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) | ||||
| { | ||||
| 	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); | ||||
| 		heroKilled(h); | ||||
| 	} | ||||
|  | ||||
| 	if(obj->ID == Obj::TOWN && obj->tempOwner == playerID) | ||||
| 	{ | ||||
| 		const CGTownInstance * t = static_cast<const CGTownInstance *>(obj); | ||||
| 		townRemoved(t); | ||||
| 	} | ||||
| 	GH.fakeMouseMove(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -220,6 +220,7 @@ private: | ||||
| 	}; | ||||
|  | ||||
| 	void heroKilled(const CGHeroInstance* hero); | ||||
| 	void townRemoved(const CGTownInstance* town); | ||||
| 	void garrisonsChanged(std::vector<const CArmedInstance *> objs); | ||||
| 	void requestReturningToMainMenu(bool won); | ||||
| 	void acceptTurn(QueryID queryID, bool hotseatWait); //used during hot seat after your turn message is close | ||||
|   | ||||
| @@ -378,6 +378,9 @@ void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const | ||||
|  | ||||
| std::string CGTownInstance::getObjectName() const | ||||
| { | ||||
| 	if(ID == Obj::RANDOM_TOWN ) | ||||
| 		return CGObjectInstance::getObjectName(); | ||||
|  | ||||
| 	return getNameTranslated() + ", " + getTown()->faction->getNameTranslated(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -51,6 +51,12 @@ void FlaggableMapObject::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 	giveBonusTo(h->getOwner()); | ||||
| } | ||||
|  | ||||
| void FlaggableMapObject::markAsDeleted() const | ||||
| { | ||||
| 	if(getOwner().isValidPlayer()) | ||||
| 		takeBonusFrom(getOwner()); | ||||
| } | ||||
|  | ||||
| void FlaggableMapObject::initObj(vstd::RNG & rand) | ||||
| { | ||||
| 	if(getOwner().isValidPlayer()) | ||||
|   | ||||
| @@ -28,6 +28,7 @@ public: | ||||
| 	using CGObjectInstance::CGObjectInstance; | ||||
|  | ||||
| 	void onHeroVisit(const CGHeroInstance * h) const override; | ||||
| 	void markAsDeleted() const; | ||||
| 	void initObj(vstd::RNG & rand) override; | ||||
|  | ||||
| 	const IOwnableObject * asOwnable() const final; | ||||
|   | ||||
| @@ -103,6 +103,9 @@ void CMapEvent::serializeJson(JsonSerializeFormat & handler) | ||||
| 	handler.serializeInt("firstOccurrence", firstOccurrence); | ||||
| 	handler.serializeInt("nextOccurrence", nextOccurrence); | ||||
| 	resources.serializeJson(handler, "resources"); | ||||
|  | ||||
| 	auto deletedObjects = handler.enterArray("deletedObjectsInstances"); | ||||
| 	deletedObjects.serializeArray(deletedObjectsInstances); | ||||
| } | ||||
|  | ||||
| void CCastleEvent::serializeJson(JsonSerializeFormat & handler) | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
|  | ||||
| #include "../ResourceSet.h" | ||||
| #include "../texts/MetaString.h" | ||||
| #include "../int3.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| @@ -42,6 +43,8 @@ public: | ||||
| 	ui32 firstOccurrence; | ||||
| 	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> | ||||
| 	void serialize(Handler & h) | ||||
| 	{ | ||||
| @@ -64,6 +67,10 @@ public: | ||||
| 		h & computerAffected; | ||||
| 		h & firstOccurrence; | ||||
| 		h & nextOccurrence; | ||||
| 		if(h.version >= Handler::Version::EVENT_OBJECTS_DELETION) | ||||
| 		{ | ||||
| 			h & deletedObjectsInstances; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	virtual void serializeJson(JsonSerializeFormat & handler); | ||||
|   | ||||
| @@ -45,6 +45,7 @@ | ||||
| #include "mapObjectConstructors/CObjectClassesHandler.h" | ||||
| #include "campaign/CampaignState.h" | ||||
| #include "IGameSettings.h" | ||||
| #include "mapObjects/FlaggableMapObject.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| @@ -1184,7 +1185,6 @@ void RemoveBonus::applyGs(CGameState *gs) | ||||
|  | ||||
| void RemoveObject::applyGs(CGameState *gs) | ||||
| { | ||||
|  | ||||
| 	CGObjectInstance *obj = gs->getObjInstance(objectID); | ||||
| 	logGlobal->debug("removing object id=%d; address=%x; name=%s", objectID, (intptr_t)obj, obj->getObjectName()); | ||||
| 	//unblock tiles | ||||
| @@ -1197,10 +1197,7 @@ void RemoveObject::applyGs(CGameState *gs) | ||||
| 	{ | ||||
| 		auto * beatenHero = dynamic_cast<CGHeroInstance *>(obj); | ||||
| 		assert(beatenHero); | ||||
| 		PlayerState * p = gs->getPlayerState(beatenHero->tempOwner); | ||||
| 		gs->map->heroesOnMap -= beatenHero; | ||||
| 		p->removeOwnedObject(beatenHero); | ||||
|  | ||||
|  | ||||
| 		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->objects[objectID.getNum()].dellNull(); | ||||
| 	gs->map->calculateGuardingGreaturePositions();//FIXME: excessive, update only affected tiles | ||||
|   | ||||
| @@ -69,6 +69,7 @@ enum class ESerializationVersion : int32_t | ||||
| 	FOLDER_NAME_REWORK, // 870 - rework foldername | ||||
| 	REWARDABLE_GUARDS, // 871 - fix missing serialization of guards in rewardable objects | ||||
| 	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 | ||||
| }; | ||||
|   | ||||
| @@ -17,6 +17,7 @@ enum MapEditorRoles | ||||
| 	PlayerIDRole, | ||||
| 	BuildingIDRole, | ||||
| 	SpellIDRole, | ||||
| 	ObjectInstanceIDRole, | ||||
| 	ArtifactIDRole, | ||||
| 	ArtifactSlotRole | ||||
| 	ArtifactSlotRole, | ||||
| }; | ||||
|   | ||||
| @@ -14,6 +14,8 @@ | ||||
| #include "../../lib/mapObjects/CGTownInstance.h" | ||||
| #include "../../lib/mapObjects/CGHeroInstance.h" | ||||
|  | ||||
| Q_DECLARE_METATYPE(int3) | ||||
|  | ||||
| //parses date for lose condition (1m 1w 1d) | ||||
| int expiredDate(const QString & date); | ||||
| QString expiredDate(int date); | ||||
|   | ||||
| @@ -55,6 +55,28 @@ TResources resourcesFromVariant(const QVariant & v) | ||||
| 	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) | ||||
| { | ||||
| 	QVariantMap result; | ||||
| @@ -66,6 +88,7 @@ QVariant toVariant(const CMapEvent & event) | ||||
| 	result["firstOccurrence"] = QVariant::fromValue(event.firstOccurrence); | ||||
| 	result["nextOccurrence"] = QVariant::fromValue(event.nextOccurrence); | ||||
| 	result["resources"] = toVariant(event.resources); | ||||
| 	result["deletedObjectsInstances"] = toVariant(event.deletedObjectsInstances); | ||||
| 	return QVariant(result); | ||||
| } | ||||
|  | ||||
| @@ -81,6 +104,7 @@ CMapEvent eventFromVariant(CMapHeader & mapHeader, const QVariant & variant) | ||||
| 	result.firstOccurrence = v.value("firstOccurrence").toInt(); | ||||
| 	result.nextOccurrence = v.value("nextOccurrence").toInt(); | ||||
| 	result.resources = resourcesFromVariant(v.value("resources")); | ||||
| 	result.deletedObjectsInstances = deletedObjectsIdsFromVariant(v.value("deletedObjectsInstances")); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| @@ -137,6 +161,6 @@ void EventSettings::on_timedEventRemove_clicked() | ||||
|  | ||||
| void EventSettings::on_eventsList_itemActivated(QListWidgetItem *item) | ||||
| { | ||||
| 	new TimedEvent(item, parentWidget()); | ||||
| 	new TimedEvent(*controller, item, parentWidget()); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,13 +11,15 @@ | ||||
| #include "timedevent.h" | ||||
| #include "ui_timedevent.h" | ||||
| #include "eventsettings.h" | ||||
| #include "../mapeditorroles.h" | ||||
| #include "../../lib/constants/EntityIdentifiers.h" | ||||
| #include "../../lib/constants/StringConstants.h" | ||||
|  | ||||
| TimedEvent::TimedEvent(QListWidgetItem * t, QWidget *parent) : | ||||
| TimedEvent::TimedEvent(MapController & c, QListWidgetItem * t, QWidget *parent) :  | ||||
| 	controller(c), | ||||
| 	QDialog(parent), | ||||
| 	target(t), | ||||
| 	ui(new Ui::TimedEvent) | ||||
| 	ui(new Ui::TimedEvent), | ||||
| 	target(t) | ||||
| { | ||||
| 	ui->setupUi(this); | ||||
|  | ||||
| @@ -51,7 +53,14 @@ TimedEvent::TimedEvent(QListWidgetItem * t, QWidget *parent) : | ||||
| 		nval->setFlags(nval->flags() | Qt::ItemIsEditable); | ||||
| 		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(); | ||||
| } | ||||
|  | ||||
| @@ -89,10 +98,63 @@ void TimedEvent::on_TimedEvent_finished(int result) | ||||
| 	} | ||||
| 	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->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() | ||||
| { | ||||
|   | ||||
| @@ -11,6 +11,8 @@ | ||||
|  | ||||
| #include <QDialog> | ||||
|  | ||||
| #include "mapcontroller.h" | ||||
|  | ||||
| namespace Ui { | ||||
| class TimedEvent; | ||||
| } | ||||
| @@ -20,18 +22,23 @@ class TimedEvent : public QDialog | ||||
| 	Q_OBJECT | ||||
|  | ||||
| public: | ||||
| 	explicit TimedEvent(QListWidgetItem *, QWidget *parent = nullptr); | ||||
| 	explicit TimedEvent(MapController & map, QListWidgetItem *, QWidget * parent = nullptr); | ||||
| 	~TimedEvent(); | ||||
|  | ||||
| private slots: | ||||
|  | ||||
| 	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_resources_itemDoubleClicked(QTableWidgetItem *item); | ||||
| 	void on_resources_itemDoubleClicked(QTableWidgetItem * item); | ||||
|  | ||||
| private: | ||||
| 	Ui::TimedEvent *ui; | ||||
| 	MapController & controller; | ||||
| 	Ui::TimedEvent * ui; | ||||
| 	QListWidgetItem * target; | ||||
| }; | ||||
|   | ||||
| @@ -9,8 +9,8 @@ | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>620</width> | ||||
|     <height>371</height> | ||||
|     <width>730</width> | ||||
|     <height>422</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
| @@ -201,6 +201,34 @@ | ||||
|        </column> | ||||
|       </widget> | ||||
|      </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> | ||||
|       <widget class="QPushButton" name="pushButton"> | ||||
|        <property name="text"> | ||||
|   | ||||
| @@ -787,7 +787,7 @@ bool CGameHandler::removeObject(const CGObjectInstance * obj, const PlayerColor | ||||
| 	ro.initiator = initiator; | ||||
| 	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; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -61,6 +61,14 @@ void NewTurnProcessor::handleTimeEvents(PlayerColor color) | ||||
| 				if (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 | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user