cyphesis/rulesets/Thing.cpp
Al Riddoch 2ccfaadef3 2001-04-14 Al Riddoch <alriddoch@zepler.org>
* 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>
2001-04-16 16:26:44 +00:00

341 lines
12 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/Create.h>
#include <Atlas/Objects/Operation/Sight.h>
#include <Atlas/Objects/Operation/Set.h>
#include <Atlas/Objects/Operation/Delete.h>
#include <Atlas/Objects/Operation/Move.h>
#include <Atlas/Objects/Operation/Sound.h>
#include <Atlas/Objects/Operation/Touch.h>
#include <Atlas/Objects/Operation/Look.h>
#include <Atlas/Objects/Operation/Appearance.h>
#include <Atlas/Objects/Operation/Disappearance.h>
#include <common/Setup.h>
#include <common/Tick.h>
#include <common/Chop.h>
#include <common/Cut.h>
#include <common/Eat.h>
#include <common/Nourish.h>
#include <common/Fire.h>
#include "Thing.h"
#include "Script.h"
#include <server/WorldRouter.h>
#include <common/const.h>
#include <common/debug.h>
static const bool debug_flag = false;
using Atlas::Message::Object;
Thing::Thing() : perceptive(false)
{
inGame = true;
name = string("Foo");
attributes["mode"] = Object("birth");
}
Thing::~Thing() { }
oplist Thing::Operation(const Setup & op)
{
oplist res;
if (script->Operation("setup", op, res) != 0) {
return res;
}
RootOperation * tick = new Tick(Tick::Instantiate());
tick->SetTo(fullid);
return oplist(1,tick);
}
oplist Thing::Operation(const Create & op)
{
oplist res;
if (script->Operation("create", op, res) != 0) {
return(res);
}
const Object::ListType & args=op.GetArgs();
if (args.size() == 0) {
return oplist();
}
try {
const Object::MapType & ent = args.front().AsMap();
Object::MapType::const_iterator I = ent.find("parents");
if (I == ent.end()) {
return error(op, "Object to be created has no type");
}
const Object::ListType & parents = I->second.AsList();
string type;
if (parents.size() < 1) {
type = "thing";
} else {
type = parents.front().AsString();
}
debug( cout << fullid << " creating " << type;);
Thing * obj = world->addObject(type,ent);
if (!obj->location) {
obj->location.ref = location.ref;
obj->location.coords = location.coords;
}
if (obj->location.ref != NULL) {
obj->location.ref->contains.push_back(obj);
obj->location.ref->contains.unique();
}
Create c(op);
Object::ListType args2(1,obj->asObject());
c.SetArgs(args2);
RootOperation * s = new Sight(Sight::Instantiate());
Object::ListType args3(1,c.AsObject());
s->SetArgs(args3);
return oplist(1,s);
}
catch (Atlas::Message::WrongTypeException) {
cerr << "EXCEPTION: Malformed object to be created\n";
return(error(op, "Malformed object to be created\n"));
}
return oplist();
}
oplist Thing::Operation(const Delete & op)
{
oplist res;
if (script->Operation("delete", op, res) != 0) {
return(res);
}
// world->delObject(this);
RootOperation * s = new Sight(Sight::Instantiate());
Object::ListType args(1,op.AsObject());
s->SetArgs(args);
return oplist(1,s);
}
oplist Thing::Operation(const Fire & op)
{
oplist res;
if (script->Operation("fire", op, res) != 0) {
return(res);
}
Object::MapType::iterator I = attributes.find("burn_speed");
if (I == attributes.end()) {
return res;
}
const Object & bs = I->second;
if (!bs.IsNum()) { return res; }
const Object::MapType & fire_ent = op.GetArgs().front().AsMap();
double consumed = bs.AsNum() * fire_ent.find("status")->second.AsNum();
Object::MapType self_ent;
self_ent["id"] = fullid;
self_ent["status"] = status - (consumed / weight);
const string & to = fire_ent.find("id")->second.AsString();
Object::MapType nour_ent;
nour_ent["id"] = to;
nour_ent["weight"] = consumed;
Set * s = new Set(Set::Instantiate());
s->SetTo(fullid);
s->SetArgs(Object::ListType(1,self_ent));
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 Thing::Operation(const Move & op)
{
debug( cout << "Thing::move_operation" << endl << flush;);
seq++;
oplist res;
if (script->Operation("move", op, res) != 0) {
return(res);
}
const Object::ListType & args=op.GetArgs();
if (args.size() == 0) {
debug( cout << "ERROR: move op has no argument" << endl << flush;);
return oplist();
}
try {
Vector3D oldpos = location.coords;
const Object::MapType & ent = args.front().AsMap();
const string & oname = ent.find("id")->second.AsString();
debug( cout << "In " << fullid << " got moveop for " << oname << endl << flush;);
cout << "In " << fullid << " got moveop for " << oname << endl << flush;
Object::MapType::const_iterator I = ent.find("loc");
if (I == ent.end()) {
debug( cout << "ERROR: move op arg has no ref" << endl << flush;);
return(error(op, "Move location has no ref"));
}
const string & ref = I->second.AsString();
edict_t::iterator J = world->eobjects.find(ref);
if (J == world->eobjects.end()) {
debug( cout << "ERROR: move op arg ref is invalid" << endl << flush;);
return(error(op, "Move location ref invalid"));
}
cout << "{" << ref << "}" << endl << flush;
Entity * newref = J->second;
if (newref == this) {
debug( cout << "ERROR: move op arg ref is same as entity" << endl << flush;);
debug( cout << "ERROR: attempt by entity to move into itself" << endl << flush;);
return error(op, "Attempt by entity to move into itself");
}
if (location.ref != newref) {
location.ref->contains.remove(this);
newref->contains.push_back(this);
location.ref = newref;
}
I = ent.find("pos");
if (I == ent.end()) {
return(error(op, "Move location has no position"));
}
location.coords = Vector3D(I->second.AsList());
I = ent.find("velocity");
if (I != ent.end()) {
location.velocity = Vector3D(I->second.AsList());
}
double speed_ratio;
if (!(location.velocity)) {
speed_ratio = 0.0;
} else {
speed_ratio = location.velocity.mag()/consts::base_velocity;
}
RootOperation * s = new Sight(Sight::Instantiate());
Object::ListType args2(1,op.AsObject());
s->SetArgs(args2);
oplist res2(1,s);
// I think it might be wise to send a set indicating we have changed
// modes, but this would probably be wasteful
if (consts::enable_ranges && perceptive) {
debug(cout << "testing range" << endl;);
elist_t::const_iterator I = location.ref->contains.begin();
Object::ListType appear, disappear;
for(;I != location.ref->contains.end(); I++) {
const bool wasInRange = (*I)->location.inRange(oldpos, consts::sight_range);
const bool isInRange = (*I)->location.inRange(location.coords, consts::sight_range);
// Build appear and disappear lists, and send operations
// Also so operations to (dis)appearing perceptive
// entities saying that we are (dis)appearing
if (wasInRange && !isInRange) {
// We are losing sight of this object
Object::MapType dent;
dent["id"] = (*I)->fullid;
dent["seq"] = (*I)->seq;
disappear.push_back(dent);
debug(cout << fullid << ": losing site of " <<(*I)->fullid << endl;);
if (((Thing*)*I)->perceptive) {
// Send operation to the entity in question so it
// knows it is losing sight of us.
Disappearance * d = new Disappearance(Disappearance::Instantiate());
Object::MapType d2ent;
d2ent["id"] = fullid;
d2ent["seq"] = seq;
d->SetArgs(Object::ListType(1,d2ent));
d->SetTo((*I)->fullid);
res2.push_back(d);
}
}
if (!wasInRange && isInRange) {
// We are gaining sight of this object
Object::MapType aent;
aent["id"] = (*I)->fullid;
aent["seq"] = (*I)->seq;
appear.push_back(aent);
debug(cout << fullid << ": gaining site of " <<(*I)->fullid << endl;);
if (((Thing*)*I)->perceptive) {
// Send operation to the entity in question so it
// knows it is gaining sight of us.
Appearance * a = new Appearance(Appearance::Instantiate());
Object::MapType a2ent;
a2ent["id"] = fullid;
a2ent["seq"] = seq;
a->SetArgs(Object::ListType(1,a2ent));
a->SetTo((*I)->fullid);
res2.push_back(a);
}
}
}
if (disappear.size() != 0) {
// Send an operation to ourselves with a list of entities
// we are losing sight of
Appearance * a = new Appearance(Appearance::Instantiate());
a->SetArgs(appear);
a->SetTo(fullid);
res2.push_back(a);
}
if (appear.size() != 0) {
// Send an operation to ourselves with a list of entities
// we are gaining sight of
Disappearance * d = new Disappearance(Disappearance::Instantiate());
d->SetArgs(disappear);
d->SetTo(fullid);
res2.push_back(d);
}
}
return res2;
}
catch (Atlas::Message::WrongTypeException) {
cerr << "EXCEPTION: Malformed object to be moved\n";
return(error(op, "Malformed object to be moved\n"));
}
return oplist();
}
oplist Thing::Operation(const Set & op)
{
seq++;
oplist res;
if (script->Operation("set", op, res) != 0) {
return(res);
}
const Object::ListType & args=op.GetArgs();
if (args.size() == 0) {
return oplist();
}
try {
const Object::MapType & ent = args.front().AsMap();
Object::MapType::const_iterator I;
for (I = ent.begin(); I != ent.end(); I++) {
set(I->first, I->second);
}
RootOperation * s = new Sight(Sight::Instantiate());
Object::ListType args2(1,op.AsObject());
s->SetArgs(args2);
oplist res2(1,s);
if (status < 0) {
RootOperation * d = new Delete(Delete::Instantiate());
Object::ListType args3(1,this->asObject());
d->SetArgs(args3);
d->SetTo(fullid);
res2.push_back(d);
}
return res2;
}
catch (Atlas::Message::WrongTypeException) {
cerr << "EXCEPTION: Malformed set operation\n";
return(error(op, "Malformed set operation\n"));
}
return oplist();
}
oplist Thing::Operation(const Look & op)
{
oplist res;
if (script->Operation("look", op, res) != 0) {
return(res);
}
return(BaseEntity::Operation(op));
}