mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
* rulesets/Movement.h, rulesets/Pedestrian.h: Update movement interface to reflect the different jobs done by genMoveOperation(). * rulesets/Movement.cpp: Fix badly named argument. * rulesets/Pedestrian.cpp: Shift code around a little to ensure it is clear that genMoveUpdate() only produces an op if one is necessary, but genMoveOperation() should always return one. * rulesets/Character.cpp: Get rid of confusing logic now That we know genMoveOperation() never returns NULL.
1053 lines
30 KiB
C++
1053 lines
30 KiB
C++
// This file may be redistributed and modified only under the terms of
|
|
// the GNU General Public License (See COPYING for details).
|
|
// Copyright (C) 2000,2001 Alistair Riddoch
|
|
|
|
#include "Character.h"
|
|
|
|
#include "Pedestrian.h"
|
|
#include "MindFactory.h"
|
|
#include "BaseMind.h"
|
|
#include "Script.h"
|
|
#include "World.h"
|
|
|
|
#include "common/BaseWorld.h"
|
|
#include "common/op_switch.h"
|
|
#include "common/const.h"
|
|
#include "common/debug.h"
|
|
#include "common/globals.h"
|
|
#include "common/log.h"
|
|
|
|
#include "common/Setup.h"
|
|
#include "common/Tick.h"
|
|
#include "common/Cut.h"
|
|
#include "common/Eat.h"
|
|
#include "common/Nourish.h"
|
|
|
|
#include <wfmath/atlasconv.h>
|
|
|
|
#include <Atlas/Objects/Operation/Action.h>
|
|
#include <Atlas/Objects/Operation/Sound.h>
|
|
#include <Atlas/Objects/Operation/Set.h>
|
|
#include <Atlas/Objects/Operation/Delete.h>
|
|
#include <Atlas/Objects/Operation/Imaginary.h>
|
|
#include <Atlas/Objects/Operation/Look.h>
|
|
#include <Atlas/Objects/Operation/Talk.h>
|
|
#include <Atlas/Objects/Operation/Touch.h>
|
|
#include <Atlas/Objects/Operation/Sight.h>
|
|
#include <Atlas/Objects/Operation/Move.h>
|
|
#include <Atlas/Objects/Operation/Error.h>
|
|
#include <Atlas/Objects/Operation/Appearance.h>
|
|
#include <Atlas/Objects/Operation/Disappearance.h>
|
|
|
|
static const bool debug_flag = false;
|
|
|
|
OpVector Character::metabolise(double ammount)
|
|
{
|
|
// Currently handles energy
|
|
// We should probably call this whenever the entity performs a movement.
|
|
Element::MapType ent;
|
|
ent["id"] = getId();
|
|
if ((m_status > (1.5 + energyLoss)) && (m_mass < m_maxMass)) {
|
|
m_status = m_status - energyLoss;
|
|
ent["mass"] = m_mass + weightGain;
|
|
}
|
|
double energyUsed = energyConsumption * ammount;
|
|
if ((m_status <= energyUsed) && (m_mass > weightConsumption)) {
|
|
ent["status"] = m_status - energyUsed + energyGain;
|
|
ent["mass"] = m_mass - weightConsumption;
|
|
} else {
|
|
ent["status"] = m_status - energyUsed;
|
|
}
|
|
if (m_drunkness > 0) {
|
|
ent["drunkness"] = m_drunkness - 0.1;
|
|
}
|
|
|
|
Set * s = new Set(Set::Instantiate());
|
|
s->setTo(getId());
|
|
s->setArgs(Element::ListType(1,ent));
|
|
|
|
return OpVector(1,s);
|
|
}
|
|
|
|
Character::Character(const std::string & id) : Character_parent(id),
|
|
m_movement(*new Pedestrian(*this)),
|
|
m_drunkness(0.0), m_sex("female"),
|
|
m_food(0), m_maxMass(100),
|
|
m_isAlive(true), m_mind(NULL),
|
|
m_externalMind(NULL)
|
|
{
|
|
m_mass = 60;
|
|
m_location.m_bBox = BBox(WFMath::Point<3>(-0.25, -0.25, 0),
|
|
WFMath::Point<3>(0.25, 0.25, 2));
|
|
m_attributes["mode"] = "birth";
|
|
|
|
subscribe("imaginary", OP_IMAGINARY);
|
|
subscribe("tick", OP_TICK);
|
|
subscribe("talk", OP_TALK);
|
|
subscribe("eat", OP_EAT);
|
|
subscribe("nourish", OP_NOURISH);
|
|
|
|
// subscribe to ops from the mind
|
|
mindSubscribe("action", OP_ACTION);
|
|
mindSubscribe("setup", OP_SETUP);
|
|
mindSubscribe("tick", OP_TICK);
|
|
mindSubscribe("move", OP_MOVE);
|
|
mindSubscribe("set", OP_SET);
|
|
mindSubscribe("create", OP_CREATE);
|
|
mindSubscribe("delete", OP_DELETE);
|
|
mindSubscribe("imaginary", OP_IMAGINARY);
|
|
mindSubscribe("talk", OP_TALK);
|
|
mindSubscribe("look", OP_LOOK);
|
|
mindSubscribe("cut", OP_CUT);
|
|
mindSubscribe("eat", OP_EAT);
|
|
mindSubscribe("touch", OP_TOUCH);
|
|
mindSubscribe("shoot", OP_OTHER);
|
|
|
|
// subscribe to ops for the mind
|
|
w2mSubscribe("appearance", OP_APPEARANCE);
|
|
w2mSubscribe("disappearance", OP_DISAPPEARANCE);
|
|
w2mSubscribe("error", OP_ERROR);
|
|
w2mSubscribe("setup", OP_SETUP);
|
|
w2mSubscribe("tick", OP_TICK);
|
|
w2mSubscribe("sight", OP_SIGHT);
|
|
w2mSubscribe("sound", OP_SOUND);
|
|
w2mSubscribe("touch", OP_TOUCH);
|
|
}
|
|
|
|
Character::~Character()
|
|
{
|
|
delete &m_movement;
|
|
if (m_mind != NULL) {
|
|
delete m_mind;
|
|
}
|
|
if (m_externalMind != NULL) {
|
|
delete m_externalMind;
|
|
}
|
|
}
|
|
|
|
bool Character::get(const std::string & aname, Element & attr) const
|
|
{
|
|
if (aname == "drunkness") {
|
|
attr = m_drunkness;
|
|
return true;
|
|
} else if (aname == "sex") {
|
|
attr = m_sex;
|
|
return true;
|
|
}
|
|
return Character_parent::get(aname, attr);
|
|
}
|
|
|
|
void Character::set(const std::string & aname, const Element & attr)
|
|
{
|
|
if ((aname == "drunkness") && attr.isFloat()) {
|
|
m_drunkness = attr.asFloat();
|
|
m_update_flags |= a_drunk;
|
|
} else if ((aname == "sex") && attr.isString()) {
|
|
m_sex = attr.asString();
|
|
m_update_flags |= a_sex;
|
|
} else {
|
|
Character_parent::set(aname, attr);
|
|
}
|
|
}
|
|
|
|
void Character::addToObject(Element::MapType & omap) const
|
|
{
|
|
omap["sex"] = m_sex;
|
|
Character_parent::addToObject(omap);
|
|
}
|
|
|
|
OpVector Character::ImaginaryOperation(const Imaginary & op)
|
|
{
|
|
Sight * s = new Sight(Sight::Instantiate());
|
|
s->setArgs(Element::ListType(1,op.asObject()));
|
|
return OpVector(1,s);
|
|
}
|
|
|
|
OpVector Character::SetupOperation(const Setup & op)
|
|
{
|
|
debug( std::cout << "Character::tick" << std::endl << std::flush;);
|
|
OpVector res;
|
|
debug( std::cout << "CHaracter::Operation(setup)" << std::endl
|
|
<< std::flush;);
|
|
if (m_script->Operation("setup", op, res) != 0) {
|
|
return res;
|
|
}
|
|
if (op.hasAttr("sub_to")) {
|
|
debug( std::cout << "Has sub_to" << std::endl << std::flush;);
|
|
return res;
|
|
}
|
|
|
|
//mind = new BaseMind(getId(), m_name);
|
|
//std::string mind_class("NPCMind"), mind_package("mind.NPCMind");
|
|
//if (global_conf->findItem("mind", m_type)) {
|
|
//mind_package = global_conf->getItem("mind", m_type);
|
|
//mind_class = m_type + "Mind";
|
|
//}
|
|
//Create_PyMind(mind, mind_package, mind_class);
|
|
m_mind = MindFactory::instance()->newMind(getId(), m_name, m_type);
|
|
|
|
OpVector res2(2);
|
|
Setup * s = new Setup(op);
|
|
// THis is so not the right thing to do
|
|
s->setAttr("sub_to", "mind");
|
|
res2[0] = s;
|
|
Look * l = new Look(Look::Instantiate());
|
|
l->setTo(m_world->getId());
|
|
res2[1] = l;
|
|
if (m_location.m_loc != &m_world->m_gameWorld) {
|
|
l = new Look(Look::Instantiate());
|
|
l->setTo(m_location.m_loc->getId());
|
|
res2.push_back(l);
|
|
}
|
|
l = new Look(Look::Instantiate());
|
|
l->setTo(getId());
|
|
res2.push_back(l);
|
|
Tick * tick = new Tick(Tick::Instantiate());
|
|
tick->setTo(getId());
|
|
res2.push_back(tick);
|
|
return res2;
|
|
}
|
|
|
|
OpVector Character::TickOperation(const Tick & op)
|
|
{
|
|
if (op.hasAttr("sub_to")) {
|
|
debug( std::cout << "Has sub_to" << std::endl << std::flush;);
|
|
return OpVector();
|
|
}
|
|
debug(std::cout << "================================" << std::endl
|
|
<< std::flush;);
|
|
const Element::ListType & args = op.getArgs();
|
|
if ((!args.empty()) && (args.front().isMap())) {
|
|
// Deal with movement.
|
|
const Element::MapType & arg1 = args.front().asMap();
|
|
Element::MapType::const_iterator I = arg1.find("serialno");
|
|
if ((I != arg1.end()) && (I->second.isInt())) {
|
|
if (I->second.asInt() < m_movement.m_serialno) {
|
|
debug(std::cout << "Old tick" << std::endl << std::flush;);
|
|
return OpVector();
|
|
}
|
|
}
|
|
Location ret_loc;
|
|
Move * moveOp = m_movement.genMoveUpdate(&ret_loc);
|
|
if (moveOp) {
|
|
if (!m_movement.moving()) {
|
|
return OpVector (1,moveOp);
|
|
}
|
|
OpVector res(2);
|
|
Element::MapType entmap;
|
|
entmap["name"] = "move";
|
|
entmap["serialno"] = m_movement.m_serialno;
|
|
Tick * tickOp = new Tick(Tick::Instantiate());
|
|
tickOp->setTo(getId());
|
|
tickOp->setFutureSeconds(m_movement.getTickAddition(ret_loc.m_pos));
|
|
tickOp->setArgs(Element::ListType(1,entmap));
|
|
res[0] = tickOp;
|
|
res[1] = moveOp;
|
|
return res;
|
|
}
|
|
} else {
|
|
OpVector res;
|
|
m_script->Operation("tick", op, res);
|
|
|
|
// DIGEST
|
|
if ((m_food >= foodConsumption) && (m_status < 2)) {
|
|
// It is important that the metabolise bit is done next, as this
|
|
// handles the status change
|
|
m_status = m_status + foodConsumption;
|
|
m_food = m_food - foodConsumption;
|
|
|
|
Element::MapType food_ent;
|
|
food_ent["id"] = getId();
|
|
food_ent["food"] = m_food;
|
|
Set s = Set::Instantiate();
|
|
s.setTo(getId());
|
|
s.setArgs(Element::ListType(1,food_ent));
|
|
|
|
Sight * si = new Sight(Sight::Instantiate());
|
|
si->setTo(getId());
|
|
si->setArgs(Element::ListType(1,s.asObject()));
|
|
res.push_back(si);
|
|
}
|
|
|
|
// METABOLISE
|
|
OpVector mres = metabolise();
|
|
for(OpVector::const_iterator I = mres.begin(); I != mres.end(); I++) {
|
|
res.push_back(*I);
|
|
}
|
|
|
|
// TICK
|
|
Tick * tickOp = new Tick(Tick::Instantiate());
|
|
tickOp->setTo(getId());
|
|
tickOp->setFutureSeconds(consts::basic_tick * 30);
|
|
res.push_back(tickOp);
|
|
return res;
|
|
}
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::TalkOperation(const Talk & op)
|
|
{
|
|
debug( std::cout << "Character::OPeration(Talk)" << std::endl<<std::flush;);
|
|
Sound * s = new Sound(Sound::Instantiate());
|
|
s->setArgs(Element::ListType(1,op.asObject()));
|
|
return OpVector(1,s);
|
|
}
|
|
|
|
OpVector Character::EatOperation(const Eat & op)
|
|
{
|
|
// This is identical to Foof::Operation(Eat &)
|
|
// Perhaps animal should inherit from Food?
|
|
OpVector res;
|
|
if (m_script->Operation("eat", op, res) != 0) {
|
|
return res;
|
|
}
|
|
Element::MapType self_ent;
|
|
self_ent["id"] = getId();
|
|
self_ent["status"] = -1;
|
|
|
|
Set * s = new Set(Set::Instantiate());
|
|
s->setTo(getId());
|
|
s->setArgs(Element::ListType(1,self_ent));
|
|
|
|
const std::string & to = op.getFrom();
|
|
Element::MapType nour_ent;
|
|
nour_ent["id"] = to;
|
|
nour_ent["mass"] = m_mass;
|
|
Nourish * n = new Nourish(Nourish::Instantiate());
|
|
n->setTo(to);
|
|
n->setArgs(Element::ListType(1,nour_ent));
|
|
|
|
OpVector res2(2);
|
|
res2[0] = s;
|
|
res2[1] = n;
|
|
return res2;
|
|
}
|
|
|
|
OpVector Character::NourishOperation(const Nourish & op)
|
|
{
|
|
if (op.getArgs().empty()) {
|
|
return error(op, "Nourish has no argument", getId());
|
|
}
|
|
if (!op.getArgs().front().isMap()) {
|
|
return error(op, "Nourish arg is malformed", getId());
|
|
}
|
|
const Element::MapType & nent = op.getArgs().front().asMap();
|
|
Element::MapType::const_iterator I = nent.find("mass");
|
|
if ((I == nent.end()) || !I->second.isNum()) { return OpVector(); }
|
|
m_food = m_food + I->second.asNum();
|
|
|
|
Element::MapType food_ent;
|
|
food_ent["id"] = getId();
|
|
food_ent["food"] = m_food;
|
|
if (((I = nent.find("alcohol")) != nent.end()) && I->second.isNum()) {
|
|
m_drunkness += I->second.asNum() / m_mass;
|
|
food_ent["drunkness"] = m_drunkness;
|
|
}
|
|
Set s = Set::Instantiate();
|
|
s.setArgs(Element::ListType(1,food_ent));
|
|
|
|
Sight * si = new Sight(Sight::Instantiate());
|
|
si->setTo(getId());
|
|
si->setArgs(Element::ListType(1,s.asObject()));
|
|
return OpVector(1,si);
|
|
}
|
|
|
|
OpVector Character::mindLoginOperation(const Login & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindLogoutOperation(const Logout & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindActionOperation(const Action & op)
|
|
{
|
|
Action *a = new Action(op);
|
|
a->setTo(getId());
|
|
return OpVector(1,a);
|
|
}
|
|
|
|
OpVector Character::mindSetupOperation(const Setup & op)
|
|
{
|
|
Setup *s = new Setup(op);
|
|
s->setTo(getId());
|
|
s->setAttr("sub_to", "mind");
|
|
return OpVector(1,s);
|
|
}
|
|
|
|
OpVector Character::mindTickOperation(const Tick & op)
|
|
{
|
|
Tick *t = new Tick(op);
|
|
t->setTo(getId());
|
|
t->setAttr("sub_to", "mind");
|
|
return OpVector(1,t);
|
|
}
|
|
|
|
OpVector Character::mindMoveOperation(const Move & op)
|
|
{
|
|
debug( std::cout << "Character::mind_move_op" << std::endl << std::flush;);
|
|
const Element::ListType & args = op.getArgs();
|
|
if ((args.empty()) || (!args.front().isMap())) {
|
|
log(ERROR, "mindMoveOperation: move op has no argument");
|
|
return OpVector();
|
|
}
|
|
const Element::MapType & arg1 = args.front().asMap();
|
|
Element::MapType::const_iterator I = arg1.find("id");
|
|
if ((I == arg1.end()) || !I->second.isString()) {
|
|
log(ERROR, "mindMoveOperation: Args has got no id");
|
|
}
|
|
const std::string & oname = I->second.asString();
|
|
EntityDict::const_iterator J = m_world->getObjects().find(oname);
|
|
if (J == m_world->getObjects().end()) {
|
|
log(ERROR, "mindMoveOperation: This move op is for a phoney id");
|
|
return OpVector();
|
|
}
|
|
Entity * obj = J->second;
|
|
if (obj != this) {
|
|
debug( std::cout << "Moving something else. " << oname << std::endl << std::flush;);
|
|
if ((obj->getMass() < 0) || (obj->getMass() > m_mass)) {
|
|
debug( std::cout << "We can't move this. Just too heavy" << std::endl << std::flush;);
|
|
return OpVector();
|
|
}
|
|
Move * newop = new Move(op);
|
|
newop->setTo(oname);
|
|
return OpVector(1,newop);
|
|
}
|
|
std::string location_ref;
|
|
I = arg1.find("loc");
|
|
if ((I != arg1.end()) && (I->second.isString())) {
|
|
location_ref = I->second.asString();
|
|
} else {
|
|
debug( std::cout << "Parent not set" << std::endl << std::flush;);
|
|
}
|
|
Vector3D location_coords, location_vel;
|
|
Quaternion location_orientation;
|
|
try {
|
|
I = arg1.find("pos");
|
|
if (I != arg1.end()) {
|
|
location_coords.fromAtlas(I->second.asList());
|
|
debug( std::cout << "pos set to " << location_coords << std::endl << std::flush;);
|
|
}
|
|
|
|
I = arg1.find("velocity");
|
|
if (I != arg1.end()) {
|
|
location_vel.fromAtlas(I->second.asList());
|
|
debug( std::cout << "vel set to " << location_vel << std::endl << std::flush;);
|
|
}
|
|
|
|
I = arg1.find("orientation");
|
|
if (I != arg1.end()) {
|
|
location_orientation.fromAtlas(I->second.asList());
|
|
debug( std::cout << "ori set to " << location_orientation << std::endl << std::flush;);
|
|
}
|
|
}
|
|
catch (Atlas::Message::WrongTypeException) {
|
|
log(ERROR, "EXCEPTION: mindMoveOperation: Malformed move operation");
|
|
}
|
|
|
|
double futureSeconds = op.getFutureSeconds();
|
|
if (!location_coords.isValid()) {
|
|
if (futureSeconds < 0.) {
|
|
futureSeconds = 0.;
|
|
}
|
|
} else {
|
|
location_coords +=
|
|
(Vector3D(((double)rand())/RAND_MAX, ((double)rand())/RAND_MAX, 0)
|
|
* (m_drunkness * 10));
|
|
}
|
|
debug( std::cout << ":" << location_ref << ":" << m_location.m_loc->getId()
|
|
<< ":" << std::endl << std::flush;);
|
|
// At the moment we can't deal if the movement changes ref, or occurs
|
|
// in the past, so we just let it by unchanged.
|
|
if ((futureSeconds < 0.) ||
|
|
((location_ref != m_location.m_loc->getId()) &&
|
|
(!location_ref.empty())) ) {
|
|
Move * newop = new Move(op);
|
|
newop->setTo(getId());
|
|
newop->setFutureSeconds(futureSeconds);
|
|
return OpVector(1,newop);
|
|
}
|
|
// Movement within current ref. Work out the speed and stuff and
|
|
// use movement object to track movement.
|
|
//
|
|
double vel_mag;
|
|
if (!location_vel.isValid()) {
|
|
debug( std::cout << "\tVelocity default" << std::endl<<std::flush;);
|
|
vel_mag = consts::base_velocity;
|
|
} else {
|
|
debug( std::cout << "\tVelocity: " << location_vel
|
|
<< std::endl << std::flush;);
|
|
vel_mag = location_vel.mag();
|
|
if (vel_mag > consts::base_velocity) {
|
|
vel_mag = consts::base_velocity;
|
|
}
|
|
}
|
|
|
|
Vector3D direction;
|
|
// If the position is given, and it is about right, don't bother to
|
|
// use it.
|
|
if (location_coords.isValid() &&
|
|
(squareDistance(location_coords, m_location.m_pos) < 0.01)) {
|
|
location_coords = Vector3D();
|
|
}
|
|
if (!location_coords.isValid()) {
|
|
if (!location_vel.isValid() || isZero(location_vel)) {
|
|
debug( std::cout << "\tUsing orientation for direction"
|
|
<< std::endl << std::flush;);
|
|
// If velocity is not given, and target is not given,
|
|
// then we are not moving at all, so direction must
|
|
// remain invalid.
|
|
} else {
|
|
debug( std::cout << "\tUsing velocity for direction"
|
|
<< std::endl << std::flush;);
|
|
direction = location_vel;
|
|
}
|
|
} else {
|
|
debug( std::cout << "\tUsing destination for direction"
|
|
<< std::endl << std::flush;);
|
|
direction = Vector3D(location_coords) - m_location.m_pos;
|
|
}
|
|
if (direction.isValid()) {
|
|
direction.normalize();
|
|
debug( std::cout << "Direction: " << direction << std::endl
|
|
<< std::flush;);
|
|
if (!location_orientation.isValid()) {
|
|
// This is a character walking, so it should stap upright
|
|
Vector3D uprightDirection = direction;
|
|
uprightDirection[cZ] = 0;
|
|
uprightDirection.normalize();
|
|
location_orientation = quaternionFromTo(Vector3D(1,0,0),
|
|
uprightDirection);
|
|
debug( std::cout << "Orientation: " << location_orientation
|
|
<< std::endl << std::flush;);
|
|
}
|
|
}
|
|
Location ret_location;
|
|
Move * moveOp = m_movement.genMoveUpdate(&ret_location);
|
|
const Location & current_location = (NULL!=moveOp) ? ret_location : m_location;
|
|
m_movement.reset();
|
|
if ((vel_mag == 0) || !direction.isValid()) {
|
|
debug( std::cout << "\tMovement stopped" << std::endl
|
|
<< std::flush;);
|
|
if (NULL != moveOp) {
|
|
debug( std::cout << "Stop!" << std::endl << std::flush;);
|
|
Element::ListType & args = moveOp->getArgs();
|
|
Element::MapType & ent = args.front().asMap();
|
|
ent["velocity"] = Vector3D(0,0,0).toAtlas();
|
|
ent["mode"] = "standing";
|
|
if (location_orientation.isValid()) {
|
|
ent["orientation"] = location_orientation.toAtlas();
|
|
}
|
|
} else {
|
|
debug( std::cout << "Turn!" << std::endl << std::flush;);
|
|
m_movement.m_orientation = location_orientation;
|
|
moveOp = m_movement.genFaceOperation();
|
|
}
|
|
if (NULL != moveOp) {
|
|
return OpVector(1,moveOp);
|
|
}
|
|
return OpVector();
|
|
}
|
|
if (NULL != moveOp) {
|
|
delete moveOp;
|
|
}
|
|
|
|
Tick * tickOp = new Tick(Tick::Instantiate());
|
|
Element::MapType ent;
|
|
ent["serialno"] = m_movement.m_serialno;
|
|
ent["name"] = "move";
|
|
Element::ListType new_args(1,ent);
|
|
tickOp->setArgs(new_args);
|
|
tickOp->setTo(getId());
|
|
// Need to add the arguments to this op before we return it
|
|
// direction is already a unit vector
|
|
debug( if (location_coords.isValid()) { std::cout<<"\tUsing target"
|
|
<< std::endl
|
|
<< std::flush; } );
|
|
m_movement.m_targetPos = location_coords;
|
|
m_movement.m_velocity = direction;
|
|
m_movement.m_velocity *= vel_mag;
|
|
m_movement.m_orientation = location_orientation;
|
|
debug( std::cout << "Velocity " << vel_mag << std::endl << std::flush;);
|
|
moveOp = m_movement.genMoveOperation(NULL, current_location);
|
|
tickOp->setFutureSeconds(m_movement.getTickAddition(m_location.m_pos));
|
|
debug( std::cout << "Next tick " << tickOp->getFutureSeconds()
|
|
<< std::endl << std::flush;);
|
|
if (moveOp == NULL) {
|
|
// FIXME migrate this to being an assert
|
|
log(ERROR, "No move operation generated in mindMoveOp");
|
|
return OpVector();
|
|
}
|
|
// return moveOp and tickOp;
|
|
OpVector res(2);
|
|
res[0] = moveOp;
|
|
res[1] = tickOp;
|
|
return res;
|
|
}
|
|
|
|
OpVector Character::mindSetOperation(const Set & op)
|
|
{
|
|
const Element::ListType & args = op.getArgs();
|
|
if (args.front().isMap()) {
|
|
Set * s = new Set(op);
|
|
const Element::MapType & amap = args.front().asMap();
|
|
Element::MapType::const_iterator I = amap.find("id");
|
|
if (I != amap.end() && I->second.isString()) {
|
|
const std::string & opid = I->second.asString();
|
|
s->setTo(opid);
|
|
} else {
|
|
if (op.getTo().empty()) {
|
|
s->setTo(getId());
|
|
}
|
|
}
|
|
return OpVector(1,s);
|
|
}
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindSightOperation(const Sight & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindSoundOperation(const Sound & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindChopOperation(const Chop & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindCombineOperation(const Combine & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindCreateOperation(const Create & op)
|
|
{
|
|
Create * c = new Create(op);
|
|
c->setTo(getId());
|
|
return OpVector(1,c);
|
|
}
|
|
|
|
OpVector Character::mindDeleteOperation(const Delete & op)
|
|
{
|
|
Delete * d = new Delete(op);
|
|
d->setTo(getId());
|
|
return OpVector(1,d);
|
|
}
|
|
|
|
OpVector Character::mindDivideOperation(const Divide & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindBurnOperation(const Burn & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindGetOperation(const Get & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindImaginaryOperation(const Imaginary & op)
|
|
{
|
|
Imaginary * i = new Imaginary(op);
|
|
i->setTo(getId());
|
|
return OpVector(1,i);
|
|
}
|
|
|
|
OpVector Character::mindInfoOperation(const Info & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindNourishOperation(const Nourish & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindTalkOperation(const Talk & op)
|
|
{
|
|
debug( std::cout << "Character::mindOPeration(Talk)"
|
|
<< std::endl << std::flush;);
|
|
Talk * t = new Talk(op);
|
|
t->setTo(getId());
|
|
return OpVector(1,t);
|
|
}
|
|
|
|
OpVector Character::mindLookOperation(const Look & op)
|
|
{
|
|
debug(std::cout << "Got look up from mind from [" << op.getFrom()
|
|
<< "] to [" << op.getTo() << "]" << std::endl << std::flush;);
|
|
m_perceptive = true;
|
|
Look * l = new Look(op);
|
|
if (op.getTo().empty()) {
|
|
const Element::ListType & args = op.getArgs();
|
|
if (args.empty()) {
|
|
l->setTo(m_world->getId());
|
|
} else {
|
|
if (args.front().isMap()) {
|
|
const Element::MapType & amap = args.front().asMap();
|
|
Element::MapType::const_iterator I = amap.find("id");
|
|
if (I != amap.end() && I->second.isString()) {
|
|
l->setTo(I->second.asString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
debug( std::cout <<" now to ["<<l->getTo()<<"]"<<std::endl<<std::flush;);
|
|
return OpVector(1,l);
|
|
}
|
|
|
|
OpVector Character::mindCutOperation(const Cut & op)
|
|
{
|
|
Cut * c = new Cut(op);
|
|
if (op.getTo().empty()) {
|
|
c->setTo(getId());
|
|
}
|
|
return OpVector(1,c);
|
|
}
|
|
|
|
OpVector Character::mindEatOperation(const Eat & op)
|
|
{
|
|
Eat * e = new Eat(op);
|
|
if (op.getTo().empty()) {
|
|
e->setTo(getId());
|
|
}
|
|
return OpVector(1,e);
|
|
}
|
|
|
|
OpVector Character::mindTouchOperation(const Touch & op)
|
|
{
|
|
Touch * t = new Touch(op);
|
|
// Work out what is being touched.
|
|
const Element::ListType & args = op.getArgs();
|
|
if ((op.getTo().empty()) || (!args.empty())) {
|
|
if (args.empty()) {
|
|
t->setTo(m_world->getId());
|
|
} else {
|
|
if (args.front().isMap()) {
|
|
const Element::MapType & amap = args.front().asMap();
|
|
Element::MapType::const_iterator I = amap.find("id");
|
|
if (I != amap.end() && I->second.isString()) {
|
|
t->setTo(I->second.asString());
|
|
}
|
|
} else if (args.front().isString()) {
|
|
t->setTo(args.front().asString());
|
|
}
|
|
}
|
|
}
|
|
// Pass the modified touch operation on to target.
|
|
OpVector res(2);
|
|
res[0] = t;
|
|
// Send action "touch"
|
|
Action * a = new Action(Action::Instantiate());
|
|
a->setTo(getId());
|
|
Element::MapType amap;
|
|
amap["id"] = getId();
|
|
amap["action"] = "touch";
|
|
Element::ListType setArgs(1,amap);
|
|
a->setArgs(setArgs);
|
|
res[1] = a;
|
|
return res;
|
|
}
|
|
|
|
OpVector Character::mindAppearanceOperation(const Appearance & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindDisappearanceOperation(const Disappearance & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
|
|
OpVector Character::mindErrorOperation(const Error & op)
|
|
{
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mindOtherOperation(const RootOperation & op)
|
|
{
|
|
RootOperation * e = new RootOperation(op);
|
|
if (op.getTo().empty()) {
|
|
e->setTo(getId());
|
|
}
|
|
return OpVector(1,e);
|
|
}
|
|
|
|
bool Character::w2mActionOperation(const Action & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mLoginOperation(const Login & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mLogoutOperation(const Logout & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mChopOperation(const Chop & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mCreateOperation(const Create & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mCutOperation(const Cut & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mDeleteOperation(const Delete & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mEatOperation(const Eat & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mBurnOperation(const Burn & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mMoveOperation(const Move & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mSetOperation(const Set & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mLookOperation(const Look & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mDivideOperation(const Divide & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mCombineOperation(const Combine & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mGetOperation(const Get & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mImaginaryOperation(const Imaginary & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mInfoOperation(const Info & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mTalkOperation(const Talk & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mNourishOperation(const Nourish & op)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mAppearanceOperation(const Appearance & op)
|
|
{
|
|
if (m_drunkness > 1.0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Character::w2mDisappearanceOperation(const Disappearance & op)
|
|
{
|
|
if (m_drunkness > 1.0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Character::w2mErrorOperation(const Error & op)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool Character::w2mOtherOperation(const RootOperation & op)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool Character::w2mSetupOperation(const Setup & op)
|
|
{
|
|
if (op.hasAttr("sub_to")) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mTickOperation(const Tick & op)
|
|
{
|
|
if (op.hasAttr("sub_to")) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Character::w2mSightOperation(const Sight & op)
|
|
{
|
|
if (m_drunkness > 1.0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Character::w2mSoundOperation(const Sound & op)
|
|
{
|
|
if (m_drunkness > 1.0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Character::w2mTouchOperation(const Touch & op)
|
|
{
|
|
if (m_drunkness > 1.0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
OpVector Character::sendMind(const RootOperation & op)
|
|
{
|
|
debug( std::cout << "Character::sendMind" << std::endl << std::flush;);
|
|
|
|
if (0 != m_externalMind) {
|
|
if (0 != m_mind) {
|
|
OpVector res = m_mind->message(op);
|
|
// Discard all the local results
|
|
OpVector::const_iterator J = res.begin();
|
|
for(; J != res.end(); J++) {
|
|
delete *J;
|
|
}
|
|
}
|
|
debug(std::cout << "Sending to external mind" << std::endl
|
|
<< std::flush;);
|
|
debug(std::cout << "Using ops from external mind"
|
|
<< std::endl << std::flush;);
|
|
return m_externalMind->message(op);
|
|
} else {
|
|
debug(std::cout << "Using ops from local mind"
|
|
<< std::endl << std::flush;);
|
|
if (0 != m_mind) {
|
|
return m_mind->message(op);
|
|
}
|
|
}
|
|
|
|
// At this point there is a bunch of conversion stuff that I don't
|
|
// understand. This function has now been restructed heavily compared
|
|
// with the python version, and our ops don't really require conversion
|
|
// so this comment is not especiialy valid.
|
|
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::mind2body(const RootOperation & op)
|
|
{
|
|
debug( std::cout << "Character::mind2body" << std::endl << std::flush;);
|
|
|
|
if (m_drunkness > 1.0) {
|
|
return OpVector();
|
|
}
|
|
OpNo otype = opEnumerate(op, opMindLookup);
|
|
OP_SWITCH(op, otype, mind)
|
|
return OpVector();
|
|
}
|
|
|
|
OpVector Character::world2body(const RootOperation & op)
|
|
{
|
|
debug( std::cout << "Character::world2body" << std::endl << std::flush;);
|
|
return callOperation(op);
|
|
}
|
|
|
|
bool Character::world2mind(const RootOperation & op)
|
|
{
|
|
debug( std::cout << "Character::world2mind" << std::endl << std::flush;);
|
|
OpNo otype = opEnumerate(op, opW2mLookup);
|
|
OP_SWITCH(op, otype, w2m)
|
|
return false;
|
|
}
|
|
|
|
OpVector Character::externalMessage(const RootOperation & op)
|
|
{
|
|
debug( std::cout << "Character::externalMessage" << std::endl << std::flush;);
|
|
return externalOperation(op);
|
|
}
|
|
|
|
OpVector Character::operation(const RootOperation & op)
|
|
{
|
|
debug( std::cout << "Character::operation" << std::endl << std::flush;);
|
|
OpVector result = world2body(op);
|
|
// set refno on result?
|
|
if (!m_isAlive) {
|
|
return result;
|
|
}
|
|
if (world2mind(op)) {
|
|
OpVector mres2 = sendMind(op);
|
|
for(OpVector::const_iterator I = mres2.begin(); I != mres2.end(); I++) {
|
|
//RootOperation * mr2 = mind2_res.front();
|
|
// Need to be very careful about what this actually does
|
|
externalMessage(**I);
|
|
delete *I;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
OpVector Character::externalOperation(const RootOperation & op)
|
|
{
|
|
debug( std::cout << "Character::externalOperation" << std::endl << std::flush;);
|
|
OpVector res = mind2body(op);
|
|
|
|
// We require that the first op is the direct consequence of the minds
|
|
// op, so it gets the same serialno
|
|
OpVector::const_iterator I = res.begin();
|
|
for(; I != res.end(); I++) {
|
|
if (I == res.begin()) {
|
|
(*I)->setSerialno(op.getSerialno());
|
|
} else {
|
|
m_world->setSerialnoOp(**I);
|
|
}
|
|
sendWorld(*I);
|
|
// Don't delete br as it has gone into worlds queue
|
|
// World will deal with it.
|
|
}
|
|
return OpVector();
|
|
}
|