mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
* Substantial re-work of object model. Moved many attributes out of BaseEntity class. Changed WoldRouter so it now handles objects in terms of Entity, rather than BaseEntity. * Code cleanups, including removing reduntant includes, and getting rid of C style comments. * Fixed the way attributes are handled in Set operations and when merging from an Atlas::Message::Object. This fixed problem with setting gender of characters. * rulesets/Pedestrian.*, rulesets/Movement.*: Re-named Movement variables to make them clearer. Added in separate handling for collisions from moving to target position. Modified collision handling so objects can slide next to each other. * rulesets/Thing.cpp, rulesets/Character.cpp: Cleaned up and rationalised movement code. Much shorter, faster and more compact than before. * Made better use of iterators throughout, eliminating unnecessary lookups, and copying of data. * Moved all virtual functions out of header files, making less work for the linker. * Made more use of const references to avoid the need to copy data before being able to inspect it. Al Riddoch <alriddoch@zepler.org>
892 lines
25 KiB
C++
892 lines
25 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 <Atlas/Message/Object.h>
|
|
#include <Atlas/Objects/Root.h>
|
|
#include <Atlas/Objects/Operation/Login.h>
|
|
#include <Atlas/Objects/Operation/Combine.h>
|
|
#include <Atlas/Objects/Operation/Create.h>
|
|
#include <Atlas/Objects/Operation/Sound.h>
|
|
#include <Atlas/Objects/Operation/Create.h>
|
|
#include <Atlas/Objects/Operation/Set.h>
|
|
#include <Atlas/Objects/Operation/Delete.h>
|
|
#include <Atlas/Objects/Operation/Divide.h>
|
|
#include <Atlas/Objects/Operation/Look.h>
|
|
#include <Atlas/Objects/Operation/Create.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 <common/Setup.h>
|
|
#include <common/Tick.h>
|
|
#include <common/Cut.h>
|
|
#include <common/Chop.h>
|
|
#include <common/Eat.h>
|
|
#include <common/Nourish.h>
|
|
#include <common/Fire.h>
|
|
|
|
|
|
#include <varconf/Config.h>
|
|
|
|
extern "C" {
|
|
#include <stdlib.h>
|
|
}
|
|
|
|
// #define DEBUG_MOVEMENT
|
|
|
|
|
|
#include "Character.h"
|
|
#include "Pedestrian.h"
|
|
|
|
#include "BaseMind.h"
|
|
#include "ExternalMind.h"
|
|
#include "Script.h"
|
|
#include "Python_API.h" // FIXME This must go
|
|
#include "World.h"
|
|
|
|
#include <server/WorldRouter.h>
|
|
|
|
#include <modules/Location.h>
|
|
|
|
#include <common/op_switch.h>
|
|
#include <common/const.h>
|
|
#include <common/debug.h>
|
|
#include <common/globals.h>
|
|
|
|
using Atlas::Message::Object;
|
|
|
|
static const bool debug_flag = false;
|
|
|
|
oplist Character::metabolise(double ammount = 1)
|
|
{
|
|
// Currently handles energy
|
|
// We should probably call this whenever the entity performs a movement.
|
|
Object::MapType ent;
|
|
ent["id"] = fullid;
|
|
if ((status > (1.5 + energyLoss)) && (weight < maxWeight)) {
|
|
status = status - energyLoss;
|
|
ent["weight"] = weight + weightGain;
|
|
}
|
|
double energyUsed = energyConsumption * ammount;
|
|
if ((status <= energyUsed) && (weight > weightConsumption)) {
|
|
ent["status"] = status - energyUsed + energyGain;
|
|
ent["weight"] = weight - weightConsumption;
|
|
} else {
|
|
ent["status"] = status - energyUsed;
|
|
}
|
|
|
|
Set * s = new Set(Set::Instantiate());
|
|
s->SetTo(fullid);
|
|
s->SetArgs(Object::ListType(1,ent));
|
|
|
|
return oplist(1,s);
|
|
}
|
|
|
|
Character::Character() : movement(*new Pedestrian(*this)), autom(false),
|
|
drunkness(0.0), sex("female"), food(0), maxWeight(100),
|
|
mind(NULL), externalMind(NULL)
|
|
{
|
|
isCharacter = true;
|
|
weight = 60;
|
|
location.bbox = Vector3D(0.25, 0.25, 1);
|
|
location.bmedian = Vector3D(0, 0, 1);
|
|
}
|
|
|
|
Character::~Character()
|
|
{
|
|
if (mind != NULL) {
|
|
delete mind;
|
|
}
|
|
if (externalMind != NULL) {
|
|
delete externalMind;
|
|
}
|
|
}
|
|
|
|
const Object & Character::operator[](const string & aname)
|
|
{
|
|
if (aname == "drunkness") {
|
|
attributes[aname] = Object(drunkness);
|
|
} else if (aname == "sex") {
|
|
attributes[aname] = Object(sex);
|
|
}
|
|
return Thing::operator[](aname);
|
|
}
|
|
|
|
void Character::set(const string & aname, const Object & attr)
|
|
{
|
|
if ((aname == "drunkness") && attr.IsFloat()) {
|
|
drunkness = attr.AsFloat();
|
|
} else if ((aname == "sex") && attr.IsString()) {
|
|
sex = attr.AsString();
|
|
} else {
|
|
Thing::set(aname, attr);
|
|
}
|
|
}
|
|
|
|
void Character::addToObject(Object & obj) const
|
|
{
|
|
Object::MapType & omap = obj.AsMap();
|
|
omap["weight"] = Object(weight);
|
|
omap["sex"] = Object(sex);
|
|
Thing::addToObject(obj);
|
|
}
|
|
|
|
oplist Character::Operation(const Setup & op)
|
|
{
|
|
debug( cout << "Character::tick" << endl << flush;);
|
|
oplist res;
|
|
debug( cout << "CHaracter::Operation(setup)" << endl << flush;);
|
|
if (script->Operation("setup", op, res) != 0) {
|
|
return res;
|
|
}
|
|
if (op.HasAttr("sub_to")) {
|
|
debug( cout << "Has sub_to" << endl << flush;);
|
|
return res;
|
|
}
|
|
|
|
mind = new BaseMind(fullid, name);
|
|
string mind_class("NPCMind"), mind_package("mind.NPCMind");
|
|
if (global_conf->findItem("mind", type)) {
|
|
mind_package = global_conf->getItem("mind", type);
|
|
mind_class = type + "Mind";
|
|
}
|
|
Create_PyMind(mind, mind_package, mind_class);
|
|
|
|
oplist res2(2);
|
|
Setup * s = new Setup(op);
|
|
// THis is so not the right thing to do
|
|
s->SetAttr("sub_to", Object("mind"));
|
|
res2[0] = s;
|
|
Look * l = new Look(Look::Instantiate());
|
|
l->SetTo(world->fullid);
|
|
res2[1] = l;
|
|
if (location.ref != &world->gameWorld) {
|
|
l = new Look(Look::Instantiate());
|
|
l->SetTo(location.ref->fullid);
|
|
res2.push_back(l);
|
|
}
|
|
l = new Look(Look::Instantiate());
|
|
l->SetTo(fullid);
|
|
res2.push_back(l);
|
|
RootOperation * tick = new Tick(Tick::Instantiate());
|
|
tick->SetTo(fullid);
|
|
res2.push_back(tick);
|
|
return(res2);
|
|
}
|
|
|
|
oplist Character::Operation(const Tick & op)
|
|
{
|
|
if (op.HasAttr("sub_to")) {
|
|
debug( cout << "Has sub_to" << endl << flush;);
|
|
return oplist();
|
|
}
|
|
debug(cout << "================================" << endl << flush;);
|
|
const Object::ListType & args = op.GetArgs();
|
|
if ((0 != args.size()) && (args.front().IsMap())) {
|
|
// Deal with movement.
|
|
const Object::MapType & arg1 = args.front().AsMap();
|
|
Object::MapType::const_iterator I = arg1.find("serialno");
|
|
if ((I != arg1.end()) && (I->second.IsInt())) {
|
|
if (I->second.AsInt() < movement.serialno) {
|
|
debug(cout << "Old tick" << endl << flush;);
|
|
return oplist();
|
|
}
|
|
}
|
|
Location ret_loc;
|
|
RootOperation * moveOp = movement.genMoveOperation(&ret_loc);
|
|
if (moveOp) {
|
|
oplist res(2);
|
|
Object::MapType entmap;
|
|
entmap["name"]=Object("move");
|
|
entmap["serialno"]=Object(movement.serialno);
|
|
Object ent(entmap);
|
|
RootOperation * tickOp = new Tick(Tick::Instantiate());
|
|
tickOp->SetTo(fullid);
|
|
tickOp->SetFutureSeconds(movement.getTickAddition(ret_loc.coords));
|
|
tickOp->SetArgs(Object::ListType(1,ent));
|
|
res[0] = tickOp;
|
|
res[1] = moveOp;
|
|
return res;
|
|
}
|
|
} else {
|
|
oplist res;
|
|
script->Operation("tick", op, res);
|
|
|
|
// DIGEST
|
|
if ((food >= foodConsumption) && (status < 2)) {
|
|
// It is important that the metabolise bit is done next, as this
|
|
// handles the status change
|
|
status = status + foodConsumption;
|
|
food = food - foodConsumption;
|
|
|
|
Object::MapType food_ent;
|
|
food_ent["id"] = fullid;
|
|
food_ent["food"] = food;
|
|
Set s = Set::Instantiate();
|
|
s.SetTo(fullid);
|
|
s.SetArgs(Object::ListType(1,food_ent));
|
|
|
|
Sight * si = new Sight(Sight::Instantiate());
|
|
si->SetTo(fullid);
|
|
si->SetArgs(Object::ListType(1,s.AsObject()));
|
|
res.push_back(si);
|
|
}
|
|
|
|
// METABOLISE
|
|
oplist mres = metabolise();
|
|
for(oplist::const_iterator I = mres.begin(); I != mres.end(); I++) {
|
|
res.push_back(*I);
|
|
}
|
|
|
|
// TICK
|
|
RootOperation * tickOp = new Tick(Tick::Instantiate());
|
|
tickOp->SetTo(fullid);
|
|
tickOp->SetFutureSeconds(consts::basic_tick * 30);
|
|
res.push_back(tickOp);
|
|
return res;
|
|
}
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::Operation(const Talk & op)
|
|
{
|
|
debug( cout << "Character::OPeration(Talk)" << endl << flush;);
|
|
Sound * s = new Sound(Sound::Instantiate());
|
|
Object::ListType args(1,op.AsObject());
|
|
s->SetArgs(args);
|
|
return oplist(1,s);
|
|
}
|
|
|
|
oplist Character::Operation(const Eat & op)
|
|
{
|
|
// This is identical to Foof::Operation(Eat &)
|
|
// Perhaps animal should inherit from Food?
|
|
oplist res;
|
|
if (script->Operation("eat", op, res) != 0) {
|
|
return(res);
|
|
}
|
|
Object::MapType self_ent;
|
|
self_ent["id"] = fullid;
|
|
self_ent["status"] = -1;
|
|
|
|
Set * s = new Set(Set::Instantiate());
|
|
s->SetTo(fullid);
|
|
s->SetArgs(Object::ListType(1,self_ent));
|
|
|
|
const string & to = op.GetFrom();
|
|
Object::MapType nour_ent;
|
|
nour_ent["id"] = to;
|
|
nour_ent["weight"] = weight;
|
|
Nourish * n = new Nourish(Nourish::Instantiate());
|
|
n->SetTo(to);
|
|
n->SetArgs(Object::ListType(1,nour_ent));
|
|
|
|
oplist res2(2);
|
|
res2[0] = s;
|
|
res2[1] = n;
|
|
return res2;
|
|
}
|
|
|
|
oplist Character::Operation(const Nourish & op)
|
|
{
|
|
const Object::MapType & nent = op.GetArgs().front().AsMap();
|
|
food = food + nent.find("weight")->second.AsNum();
|
|
|
|
Object::MapType food_ent;
|
|
food_ent["id"] = fullid;
|
|
food_ent["food"] = food;
|
|
Set s = Set::Instantiate();
|
|
s.SetArgs(Object::ListType(1,food_ent));
|
|
|
|
Sight * si = new Sight(Sight::Instantiate());
|
|
si->SetTo(fullid);
|
|
si->SetArgs(Object::ListType(1,s.AsObject()));
|
|
return oplist(1,si);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Login & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::mindOperation(const Setup & op)
|
|
{
|
|
Setup *s = new Setup(op);
|
|
s->SetTo(fullid);
|
|
s->SetAttr("sub_to", Object("mind"));
|
|
return oplist(1,s);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Tick & op)
|
|
{
|
|
Tick *t = new Tick(op);
|
|
t->SetTo(fullid);
|
|
t->SetAttr("sub_to", Object("mind"));
|
|
return oplist(1,t);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Move & op)
|
|
{
|
|
debug( cout << "Character::mind_move_op" << endl << flush;);
|
|
const Object::ListType & args = op.GetArgs();
|
|
if ((0 == args.size()) || (!args.front().IsMap())) {
|
|
cerr << "move op has no argument" << endl << flush;
|
|
return oplist();
|
|
}
|
|
const Object::MapType & arg1 = args.front().AsMap();
|
|
Object::MapType::const_iterator I = arg1.find("id");
|
|
if ((I == arg1.end()) || !I->second.IsString()) {
|
|
cerr << "Its got no id" << endl << flush;
|
|
}
|
|
const string & oname = I->second.AsString();
|
|
edict_t::const_iterator J = world->eobjects.find(oname);
|
|
if (J == world->eobjects.end()) {
|
|
debug( cout << "This move op is for a phoney object" << endl << flush;);
|
|
return oplist();
|
|
}
|
|
Move * newop = new Move(op);
|
|
Entity * obj = J->second;
|
|
if (obj != this) {
|
|
debug( cout << "Moving something else. " << oname << endl << flush;);
|
|
if ((obj->weight < 0) || (obj->weight > weight)) {
|
|
debug( cout << "We can't move this. Just too heavy" << endl << flush;);
|
|
delete newop;
|
|
return oplist();
|
|
}
|
|
newop->SetTo(oname);
|
|
return oplist(1,newop);
|
|
}
|
|
string location_ref;
|
|
I = arg1.find("loc");
|
|
if ((I != arg1.end()) && (I->second.IsString())) {
|
|
location_ref = I->second.AsString();
|
|
} else {
|
|
debug( cout << "Parent not set" << endl << flush;);
|
|
}
|
|
Vector3D location_coords;
|
|
I = arg1.find("pos");
|
|
if ((I != arg1.end()) /* && (I->second.IsList()) */) {
|
|
try {
|
|
location_coords = Vector3D(I->second.AsList());
|
|
}
|
|
catch (Atlas::Message::WrongTypeException) {
|
|
cerr << "EXCEPTION: Malformed pos move operation" << endl << flush;
|
|
}
|
|
}
|
|
|
|
Vector3D location_vel;
|
|
I = arg1.find("velocity");
|
|
if ((I != arg1.end()) /* && (I->second.IsList()) */) {
|
|
try {
|
|
location_vel = Vector3D(I->second.AsList());
|
|
}
|
|
catch (Atlas::Message::WrongTypeException) {
|
|
cerr << "EXCEPTION: Malformed vel move operation" << endl << flush;
|
|
}
|
|
}
|
|
|
|
Vector3D location_face;
|
|
I = arg1.find("face");
|
|
if ((I != arg1.end()) /* && (I->second.IsList()) */) {
|
|
try {
|
|
location_face = Vector3D(I->second.AsList());
|
|
}
|
|
catch (Atlas::Message::WrongTypeException) {
|
|
cerr << "EXCEPTION: Malformed face move operation" << endl << flush;
|
|
}
|
|
}
|
|
|
|
if (!location_coords) {
|
|
if (op.GetFutureSeconds() < 0) {
|
|
newop->SetFutureSeconds(0);
|
|
}
|
|
} else {
|
|
location_coords = location_coords +
|
|
(Vector3D(((double)rand())/RAND_MAX, ((double)rand())/RAND_MAX, 0)
|
|
* drunkness * 10);
|
|
}
|
|
double vel_mag;
|
|
// Print out a bunch of debug info
|
|
debug( cout << ":" << location_ref << ":" << location.ref->fullid << ":" << endl << flush;);
|
|
if ( //(location_ref==world->fullid) &&
|
|
(location_ref==location.ref->fullid) &&
|
|
(newop->GetFutureSeconds() >= 0) ) {
|
|
// Movement within current ref. Work out the speed and stuff and
|
|
// use movementinfo to track movement.
|
|
if (!location_vel) {
|
|
debug( cout << "\tVelocity default" << endl << flush;);
|
|
vel_mag=consts::base_velocity;
|
|
} else {
|
|
vel_mag=location_vel.mag();
|
|
if (vel_mag > consts::base_velocity) {
|
|
vel_mag = consts::base_velocity;
|
|
}
|
|
debug( cout << "\tVelocity given: " << location_vel
|
|
<< "," << vel_mag << endl << flush;);
|
|
}
|
|
if (location_face) {
|
|
location.face = location_face;
|
|
}
|
|
|
|
Vector3D direction;
|
|
if (location_coords == location.coords) {
|
|
location_coords = Vector3D();
|
|
}
|
|
if (!location_coords) {
|
|
if (!location_vel || (location_vel==Vector3D(0,0,0))) {
|
|
debug( cout << "\tUsing face for direction" << endl << flush;);
|
|
direction=location.face;
|
|
} else {
|
|
debug( cout << "\tUsing velocity for direction" << endl << flush;);
|
|
direction=location_vel;
|
|
}
|
|
} else {
|
|
debug( cout << "\tUsing destination coords for direction" << endl << flush;);
|
|
direction=location_coords-location.coords;
|
|
}
|
|
if (!direction) {
|
|
debug( cout << "No direction" << endl << flush;);
|
|
} else {
|
|
direction=direction.unitVector();
|
|
debug( cout << "Direction: " << direction << endl << flush;);
|
|
}
|
|
if (!location_face) {
|
|
location.face = direction;
|
|
}
|
|
Location ret_location;
|
|
RootOperation * moveOp = movement.genMoveOperation(&ret_location);
|
|
const Location & current_location = (NULL!=moveOp) ? ret_location : location;
|
|
movement.reset();
|
|
if ((vel_mag==0) || !direction) {
|
|
debug( cout << "\tMovement stopped" << endl << flush;);
|
|
if (NULL != moveOp) {
|
|
Object::ListType & args = moveOp->GetArgs();
|
|
Object::MapType & ent = args.front().AsMap();
|
|
ent["velocity"]=Vector3D(0,0,0).asObject();
|
|
ent["mode"]=Object("standing");
|
|
moveOp->SetArgs(args);
|
|
} else {
|
|
moveOp = movement.genFaceOperation(location);
|
|
}
|
|
delete newop;
|
|
if (NULL != moveOp) {
|
|
return oplist(1,moveOp);
|
|
}
|
|
return oplist();
|
|
}
|
|
RootOperation * tickOp = new Tick(Tick::Instantiate());
|
|
Object::MapType ent;
|
|
ent["serialno"] = Object(movement.serialno);
|
|
ent["name"] = Object("move");
|
|
Object::ListType args(1,ent);
|
|
tickOp->SetArgs(args);
|
|
tickOp->SetTo(fullid);
|
|
// Need to add the arguments to this op before we return it
|
|
// direction is already a unit vector
|
|
debug( if (location_coords) { cout<<"\tUsing target"<<endl<<flush; } );
|
|
movement.targetPos = location_coords;
|
|
movement.velocity = direction * vel_mag;
|
|
debug( cout << "Velocity " << vel_mag << endl << flush;);
|
|
RootOperation * moveOp2 = movement.genMoveOperation(NULL,current_location);
|
|
tickOp->SetFutureSeconds(movement.getTickAddition(location.coords));
|
|
debug( cout << "Next tick " << tickOp->GetFutureSeconds() << endl << flush;);
|
|
if (NULL != moveOp2) {
|
|
if (NULL != moveOp) {
|
|
delete moveOp;
|
|
}
|
|
moveOp=moveOp2;
|
|
}
|
|
// return moveOp and tickOp;
|
|
oplist res(2);
|
|
res[0] = moveOp;
|
|
res[1] = tickOp;
|
|
delete newop;
|
|
return(res);
|
|
}
|
|
return oplist(1,newop);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Set & op)
|
|
{
|
|
Set * s = new Set(op);
|
|
const Object::ListType & args = op.GetArgs();
|
|
if (args.front().IsMap()) {
|
|
const Object::MapType & amap = args.front().AsMap();
|
|
Object::MapType::const_iterator I = amap.find("id");
|
|
if (I != amap.end() && I->second.IsString()) {
|
|
const string & opid = I->second.AsString();
|
|
if (opid != fullid) {
|
|
s->SetTo(opid);
|
|
}
|
|
}
|
|
}
|
|
return oplist(1,s);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Sight & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::mindOperation(const Sound & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::mindOperation(const Create & op)
|
|
{
|
|
// We need to call, THE THING FACTORY! Or maybe not
|
|
// This currently does nothing, so characters are not able to directly
|
|
// create eobjects. By and large a tool must be used. This should at some
|
|
// point be able to send create operations on to other entities
|
|
debug( cout << "Character::mindOperation(Create)" << endl << flush;);
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::mindOperation(const Delete & op)
|
|
{
|
|
Delete * d = new Delete(op);
|
|
return oplist(1,d);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Talk & op)
|
|
{
|
|
debug( cout << "Character::mindOPeration(Talk)" << endl << flush;);
|
|
Talk * t = new Talk(op);
|
|
return oplist(1,t);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Look & op)
|
|
{
|
|
debug(cout << "Got look up from mind from [" << op.GetFrom()
|
|
<< "] to [" << op.GetTo() << "]" << endl << flush;);
|
|
perceptive = true;
|
|
Look * l = new Look(op);
|
|
if (op.GetTo().size() == 0) {
|
|
const Object::ListType & args = op.GetArgs();
|
|
if (args.size() == 0) {
|
|
l->SetTo(world->fullid);
|
|
} else {
|
|
if (args.front().IsMap()) {
|
|
const Object::MapType & amap = args.front().AsMap();
|
|
Object::MapType::const_iterator I = amap.find("id");
|
|
if (I != amap.end() && I->second.IsString()) {
|
|
l->SetTo(I->second.AsString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
debug( cout <<" now to ["<<l->GetTo()<<"]"<<endl<<flush;);
|
|
return oplist(1,l);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Load & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::mindOperation(const Save & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::mindOperation(const Cut & op)
|
|
{
|
|
Cut * c = new Cut(op);
|
|
return oplist(1,c);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Eat & op)
|
|
{
|
|
Eat * e = new Eat(op);
|
|
return oplist(1,e);
|
|
}
|
|
|
|
oplist Character::mindOperation(const Touch & op)
|
|
{
|
|
Touch * t = new Touch(op);
|
|
// Work out what is being touched.
|
|
const Object::ListType & args = op.GetArgs();
|
|
if ((op.GetTo().size() == 0) || (args.size() != 0)) {
|
|
if (args.size() == 0) {
|
|
t->SetTo(world->fullid);
|
|
} else {
|
|
if (args.front().IsMap()) {
|
|
const Object::MapType & amap = args.front().AsMap();
|
|
Object::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.
|
|
oplist res(2);
|
|
res[0] = t;
|
|
// Set our mode to "touching"
|
|
Set * s = new Set(Set::Instantiate());
|
|
s->SetTo(fullid);
|
|
Object::MapType amap;
|
|
amap["id"] = fullid;
|
|
amap["mode"] = "touching";
|
|
Object::ListType setArgs(1,Object(amap));
|
|
s->SetArgs(setArgs);
|
|
res[1] = s;
|
|
return res;
|
|
}
|
|
|
|
oplist Character::mindOperation(const Appearance & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::mindOperation(const Disappearance & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
|
|
oplist Character::mindOperation(const Error & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::mindOperation(const RootOperation & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Login & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Chop & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Create & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Cut & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Delete & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Eat & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Fire & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Move & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Set & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Look & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Load & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Save & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Appearance & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Disappearance & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const RootOperation & op)
|
|
{
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Error & op)
|
|
{
|
|
Error * e = new Error(op);
|
|
return oplist(1,e);
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Setup & op)
|
|
{
|
|
if (op.HasAttr("sub_to")) {
|
|
Setup * s = new Setup(op);
|
|
return oplist(1,s);
|
|
}
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Tick & op)
|
|
{
|
|
if (op.HasAttr("sub_to")) {
|
|
Tick * t = new Tick(op);
|
|
return oplist(1,t);
|
|
}
|
|
return oplist();
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Sight & op)
|
|
{
|
|
if (drunkness > 1.0) {
|
|
return oplist();
|
|
}
|
|
Sight * s = new Sight(op);
|
|
return oplist(1,s);
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Sound & op)
|
|
{
|
|
if (drunkness > 1.0) {
|
|
return oplist();
|
|
}
|
|
Sound * s = new Sound(op);
|
|
return oplist(1,s);
|
|
}
|
|
|
|
oplist Character::w2mOperation(const Touch & op)
|
|
{
|
|
if (drunkness > 1.0) {
|
|
return oplist();
|
|
}
|
|
Touch * t = new Touch(op);
|
|
return oplist(1,t);
|
|
}
|
|
|
|
oplist Character::sendMind(const RootOperation & op)
|
|
{
|
|
debug( cout << "Character::sendMind" << endl << flush;);
|
|
if (mind == NULL) {
|
|
return oplist();
|
|
}
|
|
oplist local_res = mind->message(op);
|
|
oplist external_res;
|
|
|
|
if (NULL != externalMind) {
|
|
debug( cout << "Sending to external mind" << endl << flush;);
|
|
external_res = externalMind->message(op);
|
|
// If there is some kinf of error in the connection, we turn autom on
|
|
} else {
|
|
//return(*(RootOperation **)NULL);
|
|
if (!autom) {
|
|
debug( cout << "Turning automatic on for " << fullid << endl << flush;);
|
|
autom = true;
|
|
if (externalMind != NULL) {
|
|
delete externalMind;
|
|
externalMind = NULL;
|
|
}
|
|
}
|
|
}
|
|
debug(cout << "Using " << local_res.size() << " ops from "
|
|
<< (autom ? "local mind" : "external mind")
|
|
<< endl << flush;);
|
|
const oplist & res = autom ? local_res : external_res;
|
|
// At this point there is a bunch of conversion stuff that I don't
|
|
// understand
|
|
|
|
return(res);
|
|
}
|
|
|
|
oplist Character::mind2body(const RootOperation & op)
|
|
{
|
|
debug( cout << "Character::mind2body" << endl << flush;);
|
|
RootOperation newop(op);
|
|
|
|
if ((newop.GetTo().size() == 0) &&
|
|
(op.GetParents().front().AsString() != "look")) {
|
|
newop.SetTo(fullid);
|
|
}
|
|
if (drunkness > 1.0) {
|
|
return oplist();
|
|
}
|
|
op_no_t otype = opEnumerate(newop);
|
|
OP_SWITCH(newop, otype, mind)
|
|
}
|
|
|
|
oplist Character::world2body(const RootOperation & op)
|
|
{
|
|
debug( cout << "Character::world2body" << endl << flush;);
|
|
return callOperation(op);
|
|
}
|
|
|
|
oplist Character::world2mind(const RootOperation & op)
|
|
{
|
|
debug( cout << "Character::world2mind" << endl << flush;);
|
|
op_no_t otype = opEnumerate(op);
|
|
OP_SWITCH(op, otype, w2m)
|
|
}
|
|
|
|
oplist Character::externalMessage(const RootOperation & op)
|
|
{
|
|
debug( cout << "Character::externalMessage" << endl << flush;);
|
|
return externalOperation(op);
|
|
}
|
|
|
|
oplist Character::operation(const RootOperation & op)
|
|
{
|
|
debug( cout << "Character::operation" << endl << flush;);
|
|
oplist result = world2body(op);
|
|
// set refno on result?
|
|
oplist mres = world2mind(op);
|
|
// set refno on mres?
|
|
for(oplist::const_iterator I = mres.begin(); I != mres.end(); I++) {
|
|
//RootOperation * mr = mind_res.front();
|
|
oplist mres2 = sendMind(**I);
|
|
for(oplist::const_iterator J = mres2.begin(); J != mres2.end(); J++) {
|
|
//RootOperation * mr2 = mind2_res.front();
|
|
// Need to be very careful about what this actually does
|
|
externalMessage(**J);
|
|
delete *J;
|
|
}
|
|
delete *I;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
oplist Character::externalOperation(const RootOperation & op)
|
|
{
|
|
debug( cout << "Character::externalOperation" << endl << flush;);
|
|
oplist body_res = mind2body(op);
|
|
// set refno on body_res?
|
|
|
|
for(oplist::const_iterator I = body_res.begin(); I != body_res.end(); I++) {
|
|
sendWorld(*I);
|
|
// Don't delete br as it has gone into worlds queue
|
|
// World will deal with it.
|
|
}
|
|
return oplist();
|
|
}
|