// Cyphesis Online RPG Server and AI Engine // Copyright (C) 2000-2005 Alistair Riddoch // // 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "World.h" #include "TerrainProperty.h" #include "CalendarProperty.h" #include "common/log.h" #include "common/const.h" #include "common/debug.h" #include "common/inheritance.h" #include "common/Delve.h" #include "common/Dig.h" #include "common/Eat.h" #include "common/Mow.h" #include "common/Nourish.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static const bool debug_flag = false; using Atlas::Message::ListType; using Atlas::Message::FloatType; using Atlas::Objects::Root; using Atlas::Objects::Operation::Create; using Atlas::Objects::Operation::Sight; using Atlas::Objects::Operation::Nourish; using Atlas::Objects::Entity::RootEntity; using Atlas::Objects::Entity::Anonymous; using Atlas::Objects::smart_dynamic_cast; typedef enum { ROCK = 0, SAND = 1, GRASS = 2, SILT = 3, SNOW = 4} Surface; /// \brief Constructor for the World entity World::World(const std::string & id, long intId) : World_parent(id, intId), m_terrain(*new Mercator::Terrain(Mercator::Terrain::SHADED)), m_tileShader(*new Mercator::TileShader) { m_properties["terrain"] = new TerrainProperty(m_terrain, m_modifiedTerrain, m_modifiedTerrain, a_terrain); m_properties["calendar"] = new CalendarProperty(0); m_tileShader.addShader(new Mercator::FillShader(), ROCK); m_tileShader.addShader(new Mercator::BandShader(-2.f, 1.5f), SAND); m_tileShader.addShader(new Mercator::GrassShader(1.f, 80.f, .5f, 1.f), GRASS); m_tileShader.addShader(new Mercator::DepthShader(0.f, -10.f), SILT); m_tileShader.addShader(new Mercator::HighShader(110.f), SNOW); m_terrain.addShader(&m_tileShader, 0); } World::~World() { delete &m_terrain; delete &m_tileShader; } /// \brief Calculate the terrain height at the given x,y coordinates float World::getHeight(float x, float y) { Mercator::Segment * s = m_terrain.getSegment(x, y); if (s != 0 && !s->isValid()) { s->populate(); } return m_terrain.get(x, y); } int World::getSurface(const Point3D & pos, int & material) { float x = pos.x(), y = pos.y(); Mercator::Segment * segment = m_terrain.getSegment(x, y); if (segment == 0) { debug(std::cerr << "No terrain at this point" << std::endl << std::flush;); return -1; } if (!segment->isValid()) { segment->populate(); } x = x - segment->getResolution() * segment->getXRef(); y = y - segment->getResolution() * segment->getYRef(); const Mercator::Segment::Surfacestore & surfaces = segment->getSurfaces(); WFMath::Vector<3> normal; float height = -23; segment->getHeightAndNormal(x, y, height, normal); debug(std::cout << "At the point " << x << "," << y << " of the segment the height is " << height << std::endl; std::cout << "The segment has " << surfaces.size() << std::endl << std::flush;); if (surfaces.size() == 0) { log(ERROR, "The terrain has no surface data"); return -1; } Mercator::Surface & tileSurface = *surfaces.begin()->second; if (!tileSurface.isValid()) { tileSurface.populate(); } material = tileSurface((int)x, (int)y, 0); return 0; } void World::delveOperation(const Operation & op, OpVector & res) { if (op->getArgs().empty()) { // This op comes from a tool, so sending error back is kinda pointless error(op, "Delve op has no args", res, getId()); return; } const Root & arg = op->getArgs().front(); RootEntity delve_arg = smart_dynamic_cast(arg); if (!delve_arg.isValid()) { error(op, "Delve op has malformed args", res, getId()); return; } if (!delve_arg->hasAttrFlag(Atlas::Objects::Entity::POS_FLAG)) { // This op comes from a tool, so sending error back is kinda pointless error(op, "Delve op to world has no POS in args", res, getId()); return; } WFMath::Point<3> delve_pos; if (fromStdVector(delve_pos, delve_arg->getPos()) != 0) { error(op, "Delve op to world has bad POS in args", res, getId()); return; } debug(std::cout << "Got delve on world at " << delve_pos << std::endl << std::flush;); int material; if (getSurface(delve_pos, material) != 0) { return; } debug(std::cout << "The material at this point is " << material << std::endl << std::flush;); switch (material) { case ROCK: { Create c; Anonymous carg; carg->setParents(std::list(1, "boulder")); carg->setName("boulder"); carg->setLoc(getId()); carg->setPos(delve_arg->getPos()); c->setArgs1(carg); c->setTo(getId()); res.push_back(c); } break; case SNOW: { Create c; Anonymous carg; carg->setParents(std::list(1, "ice")); carg->setName("ice");; carg->setLoc(getId()); carg->setPos(delve_arg->getPos()); c->setArgs1(carg); c->setTo(getId()); res.push_back(c); } break; default: break; } } void World::digOperation(const Operation & op, OpVector & res) { if (op->getArgs().empty()) { // This op comes from a tool, so sending error back is kinda pointless error(op, "Dig op has no args", res, getId()); return; } const Root & arg = op->getArgs().front(); RootEntity dig_arg = smart_dynamic_cast(arg); if (!dig_arg.isValid()) { error(op, "Dig op has malformed args", res, getId()); return; } if (!dig_arg->hasAttrFlag(Atlas::Objects::Entity::POS_FLAG)) { // This op comes from a tool, so sending error back is kinda pointless error(op, "Dig op to world has no POS in args", res, getId()); return; } WFMath::Point<3> dig_pos; if (fromStdVector(dig_pos, dig_arg->getPos()) != 0) { error(op, "Dig op to world has bad POS in args", res, getId()); return; } debug(std::cout << "Got dig on world at " << dig_pos << std::endl << std::flush;); int material; if (getSurface(dig_pos, material) != 0) { return; } debug(std::cout << "The material at this point is " << material << std::endl << std::flush;); switch (material) { case SAND: { Create c; Anonymous carg; carg->setParents(std::list(1, "pile")); carg->setAttr("material", "sand"); carg->setName("sand"); carg->setLoc(getId()); carg->setPos(dig_arg->getPos()); c->setArgs1(carg); c->setTo(getId()); res.push_back(c); } break; case GRASS: { Create c; Anonymous carg; carg->setParents(std::list(1, "pile")); carg->setAttr("material", "earth"); carg->setName("earth"); carg->setLoc(getId()); carg->setPos(dig_arg->getPos()); c->setArgs1(carg); c->setTo(getId()); res.push_back(c); } break; case SILT: { Create c; Anonymous carg; carg->setParents(std::list(1, "pile")); carg->setAttr("material", "silt"); carg->setName("silt"); carg->setLoc(getId()); carg->setPos(dig_arg->getPos()); c->setArgs1(carg); c->setTo(getId()); res.push_back(c); } break; default: break; } } void World::mowOperation(const Operation & op, OpVector & res) { if (op->getArgs().empty()) { // This op comes from a tool, so sending error back is kinda pointless error(op, "Delve op has no args", res, getId()); return; } const Root & arg = op->getArgs().front(); RootEntity mow_arg = smart_dynamic_cast(arg); if (!mow_arg.isValid()) { error(op, "Mow op has malformed args", res, getId()); return; } if (!mow_arg->hasAttrFlag(Atlas::Objects::Entity::POS_FLAG)) { // This op comes from a tool, so sending error back is kinda pointless error(op, "Mow op to world has no POS in args", res, getId()); return; } WFMath::Point<3> mow_pos; if (fromStdVector(mow_pos, mow_arg->getPos()) != 0) { error(op, "Mow op to world has bad POS in args", res, getId()); return; } debug(std::cout << "Got mow on world at " << mow_pos << std::endl << std::flush;); int material; if (getSurface(mow_pos, material) != 0) { return; } debug(std::cout << "The material at this point is " << material << std::endl << std::flush;); switch (material) { case GRASS: { Create c; Anonymous carg; carg->setParents(std::list(1, "grass")); carg->setId(getId()); carg->setPos(mow_arg->getPos());; c->setArgs1(carg); c->setTo(getId()); res.push_back(c); } break; default: break; } } void World::EatOperation(const Operation & op, OpVector & res) { const std::string & from_id = op->getFrom(); Entity * from = m_world->getEntity(from_id); if (from == 0) { log(ERROR, "World got eat op from non-existant entity."); return; } Point3D from_pos = relativePos(m_location, from->m_location); int material; if (getSurface(from_pos, material) != 0) { debug(std::cout << "no surface hit" << std::endl << std::flush;); return; } const std::string & from_type = from->getType(); if (Inheritance::instance().isTypeOf(from_type, "plant")) { if (material == GRASS) { debug(std::cout << "From grass" << std::endl << std::flush;); Nourish nourish; nourish->setTo(from_id); Anonymous nour_arg; nour_arg->setAttr("mass", log(from->getMass() + 1)); nourish->setArgs1(nour_arg); res.push_back(nourish); } } else if (Inheritance::instance().isTypeOf(from_type, "character")) { log(NOTICE, "Eat coming from an animal."); if (material == GRASS) { debug(std::cout << "From grass" << std::endl << std::flush;); } } } void World::LookOperation(const Operation & op, OpVector & res) { // Let the worldrouter know we have been looked at. assert(m_world != 0); debug(std::cout << "World::Operation(Look)" << std::endl << std::flush;); const std::string & from_id = op->getFrom(); Entity * from = m_world->getEntity(from_id); if (from == 0) { log(ERROR, "Look op has invalid from"); return; } // Pass entity to addPerceptive m_world->addPerceptive(from); Sight s; Anonymous sarg; addToEntity(sarg); s->setArgs1(sarg); // FIXME integrate setting terrain with setting contains. std::list & contlist = sarg->modifyContains(); contlist.clear(); EntitySet::const_iterator Iend = m_contains.end(); for (EntitySet::const_iterator I = m_contains.begin(); I != Iend; ++I) { float fromSquSize = (*I)->m_location.squareBoxSize(); float dist = squareDistance((*I)->m_location, from->m_location); float view_factor = fromSquSize / dist; if (view_factor > consts::square_sight_factor) { contlist.push_back((*I)->getId()); } } if (contlist.empty()) { debug(std::cout << "WARNING: contains empty." << std::endl << std::flush;); sarg->removeAttr("contains"); } s->setTo(op->getFrom()); res.push_back(s); } void World::BurnOperation(const Operation & op, OpVector & res) { // Can't burn the world. } void World::MoveOperation(const Operation & op, OpVector & res) { // Can't move the world. } void World::DeleteOperation(const Operation & op, OpVector & res) { // Deleting has no effect. } void World::OtherOperation(const Operation & op, OpVector & res) { int class_no = op->getClassNo(); if (class_no == Atlas::Objects::Operation::DELVE_NO) { delveOperation(op, res); } else if (class_no == Atlas::Objects::Operation::DIG_NO) { digOperation(op, res); } else if (class_no == Atlas::Objects::Operation::MOW_NO) { mowOperation(op, res); } }