mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
Classes under "rulesets" have been moved to "rules", and split up into futher subdirectories matching their library. As a result we can now better separate the python bindings, so that things that belongs to the simulation are separeted from things that belongs to the ai.
228 lines
9.8 KiB
C++
228 lines
9.8 KiB
C++
#include <memory>
|
|
|
|
/*
|
|
Copyright (C) 2018 Erik Ogenvik
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "AttachmentsProperty.h"
|
|
#include "BaseWorld.h"
|
|
#include "common/operations/Update.h"
|
|
#include "rules/LocatedEntity.h"
|
|
#include "PlantedOnProperty.h"
|
|
#include <Atlas/Objects/Operation.h>
|
|
#include <rules/entityfilter/Providers.h>
|
|
#include <common/Inheritance.h>
|
|
|
|
AttachmentsProperty::AttachmentsProperty(uint32_t flags)
|
|
: PropertyBase(flags)
|
|
{
|
|
|
|
}
|
|
|
|
void AttachmentsProperty::install(LocatedEntity* entity, const std::string& name)
|
|
{
|
|
entity->installDelegate(Atlas::Objects::Operation::WIELD_NO, name);
|
|
}
|
|
|
|
void AttachmentsProperty::remove(LocatedEntity* entity, const std::string& name)
|
|
{
|
|
entity->removeDelegate(Atlas::Objects::Operation::WIELD_NO, name);
|
|
}
|
|
|
|
HandlerResult AttachmentsProperty::operation(LocatedEntity* entity, const Operation& op, OpVector& res)
|
|
{
|
|
if (!op->getArgs().empty()) {
|
|
auto& arg = op->getArgs().front();
|
|
//Check if it's this attach point that's being affected
|
|
Atlas::Message::Element attachment_name;
|
|
if (arg->copyAttr("attachment", attachment_name) == 0 && attachment_name.isString()) {
|
|
auto attachmentI = m_data.find(attachment_name.String());
|
|
|
|
if (attachmentI != m_data.end()) {
|
|
auto& attachment = attachmentI->second;
|
|
std::string entity_id;
|
|
if (!arg->isDefaultId()) {
|
|
entity_id = arg->getId();
|
|
}
|
|
|
|
|
|
//std::set<const LocatedEntity*> prevObserving, newObserving;
|
|
|
|
//Check if something already is attached, and detach it if so
|
|
|
|
std::string attached_prop_name = std::string("attached_") + attachment_name.String();
|
|
|
|
Ref<LocatedEntity> existing_entity;
|
|
|
|
auto attachedProp = entity->getPropertyClass<SoftProperty>(attached_prop_name);
|
|
if (attachedProp) {
|
|
existing_entity = extractEntityRef(attachedProp->data());
|
|
}
|
|
|
|
//If we're attaching an entity which is already attached at the same attachment just bail now.
|
|
if (existing_entity) {
|
|
if (entity_id == existing_entity->getId()) {
|
|
//Entity is already attached, do nothing
|
|
return OPERATION_BLOCKED;
|
|
}
|
|
}
|
|
//Get the entities currently observing the attached entity
|
|
// existing_entity->collectObservers(prevObserving);
|
|
|
|
auto resetExistingEntityPlantedOn = [&]() {
|
|
if (existing_entity) {
|
|
auto existing_entity_planted_on_prop = existing_entity->modPropertyClassFixed<PlantedOnProperty>();
|
|
if (existing_entity_planted_on_prop) {
|
|
existing_entity_planted_on_prop->data() = PlantedOnProperty::Data{};
|
|
existing_entity->applyProperty(PlantedOnProperty::property_name, existing_entity_planted_on_prop);
|
|
{
|
|
Atlas::Objects::Operation::Update update;
|
|
update->setTo(existing_entity->getId());
|
|
existing_entity->sendWorld(update);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
if (entity_id.empty()) {
|
|
//Unwielding
|
|
|
|
entity->setAttr(attached_prop_name, "");
|
|
|
|
//Check if there was another entity attached to the attachment, and if so reset it's attachment.
|
|
resetExistingEntityPlantedOn();
|
|
|
|
Atlas::Objects::Operation::Update update;
|
|
update->setTo(entity->getId());
|
|
entity->sendWorld(update);
|
|
|
|
} else {
|
|
|
|
auto new_entity = BaseWorld::instance().getEntity(entity_id);
|
|
if (new_entity) {
|
|
//Check that the attached entity matches the constraint filter
|
|
if (attachment.filter) {
|
|
EntityFilter::QueryContext queryContext{*entity, entity, new_entity.get()};
|
|
queryContext.entity_lookup_fn = [](const std::string& id) { return BaseWorld::instance().getEntity(id); };
|
|
queryContext.type_lookup_fn = [](const std::string& id) { return Inheritance::instance().getType(id); };
|
|
if (!attachment.filter->match(queryContext)) {
|
|
entity->clientError(op, String::compose("Attached entity failed the constraint '%1'.", attachment.contraint), res, entity->getId());
|
|
return OPERATION_BLOCKED;
|
|
}
|
|
}
|
|
//Check if the entity already is attached, and if so abort. The client needs to first send a detach/unwield op in this case.
|
|
auto plantedOnProp = new_entity->requirePropertyClassFixed<PlantedOnProperty>();
|
|
if (plantedOnProp->data().entity) {
|
|
//Check if the entity is attached to ourselves; if so we can just detach it from ourselves.
|
|
//Otherwise we need to abort, since we don't allow ourselves to detach it from another entity.
|
|
if (plantedOnProp->data().entity.get() == entity) {
|
|
if (plantedOnProp->data().attachment) {
|
|
//We need to reset the old attached value for the attached entity
|
|
auto old_attached_prop_name = std::string("attached_") + *plantedOnProp->data().attachment;
|
|
auto oldAttachedProp = entity->modPropertyClass<SoftProperty>(old_attached_prop_name);
|
|
if (oldAttachedProp) {
|
|
oldAttachedProp->data() = Atlas::Message::Element();
|
|
entity->applyProperty(old_attached_prop_name, oldAttachedProp);
|
|
}
|
|
}
|
|
} else {
|
|
entity->clientError(op, "The entity is already attached to another entity.", res, entity->getId());
|
|
return OPERATION_BLOCKED;
|
|
}
|
|
}
|
|
|
|
plantedOnProp->data().entity = WeakEntityRef(entity);
|
|
plantedOnProp->data().attachment = attachment_name.String();
|
|
|
|
new_entity->applyProperty(PlantedOnProperty::property_name, plantedOnProp);
|
|
{
|
|
Atlas::Objects::Operation::Update update;
|
|
update->setTo(new_entity->getId());
|
|
new_entity->sendWorld(update);
|
|
}
|
|
|
|
entity->setAttr(attached_prop_name, Atlas::Message::MapType{{"$eid", new_entity->getId()}});
|
|
|
|
//Check if there was another entity attached to the attachment, and if so reset it's attachment.
|
|
resetExistingEntityPlantedOn();
|
|
|
|
Atlas::Objects::Operation::Update update;
|
|
update->setTo(entity->getId());
|
|
entity->sendWorld(update);
|
|
|
|
} else {
|
|
entity->clientError(op, "Could not find wielded entity.", res, entity->getId());
|
|
}
|
|
}
|
|
return OPERATION_BLOCKED;
|
|
}
|
|
}
|
|
}
|
|
return OPERATION_IGNORED;
|
|
}
|
|
|
|
AttachmentsProperty* AttachmentsProperty::copy() const
|
|
{
|
|
return new AttachmentsProperty();
|
|
}
|
|
|
|
int AttachmentsProperty::get(Atlas::Message::Element& val) const
|
|
{
|
|
Atlas::Message::MapType map;
|
|
for (auto& entry : m_data) {
|
|
map.emplace(entry.first, entry.second.contraint);
|
|
}
|
|
val = std::move(map);
|
|
return 0;
|
|
}
|
|
|
|
void AttachmentsProperty::set(const Atlas::Message::Element& val)
|
|
{
|
|
m_data.clear();
|
|
if (val.isMap()) {
|
|
for (auto& entry : val.Map()) {
|
|
if (entry.second.isString() && !entry.second.String().empty()) {
|
|
try {
|
|
EntityFilter::ProviderFactory factory{};
|
|
Attachment attachment{
|
|
entry.second.String(),
|
|
std::make_unique<EntityFilter::Filter>(entry.second.String(), factory)
|
|
};
|
|
m_data.emplace(entry.first, std::move(attachment));
|
|
} catch (const std::invalid_argument& e) {
|
|
log(WARNING, String::compose(
|
|
"Error when creating entity filter for attachment with constraint '%1'.: \n%2",
|
|
entry.second.String(),
|
|
e.what())
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Ref<LocatedEntity> AttachmentsProperty::extractEntityRef(const Atlas::Message::Element& val)
|
|
{
|
|
if (val.isMap()) {
|
|
auto I = val.Map().find("$eid");
|
|
if (I != val.Map().end() && I->second.isString()) {
|
|
return BaseWorld::instance().getEntity(I->second.String());
|
|
}
|
|
}
|
|
return Ref<LocatedEntity>();
|
|
}
|