/* base/patrol.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include "base/patrol.h" #include "base/game.h" namespace game { /* --- WayPoint atrol ------------------------------------------------------ */ Patrol::WayPoint::WayPoint() { waypoint_target = 0; waypoint_dock = false; } Patrol::WayPoint::~WayPoint() { waypoint_target = 0; } void Patrol::WayPoint::set_target_label(const std::string &label) { waypoint_target_label.assign(label); } void Patrol::WayPoint::set_target(core::Entity *entity) { waypoint_target = entity; } void Patrol::WayPoint::set_dock(const bool dock) { waypoint_dock = dock; } void Patrol::WayPoint::set_buy_label(const std::string &label) { waypoint_buy_label.assign(label); } /* --- Patrol ------------------------------------------------------ */ Patrol::Patrol() : core::Entity() { // this is a server-side only entity set_serverside(true); unset_flag(core::Entity::ShowOnMap); set_flag(core::Entity::NonSolid); entity_moduletypeid = patrol_enttype; set_label("patrol"); set_radius(1.0f); patrol_leader = 0; patrol_profile = NPC::ProfilePatrol; patrol_waypoint_current == patrol_waypoints.end(); } Patrol::~Patrol() { // delete waypoints for (WayPoints::iterator it = patrol_waypoints.begin(); it != patrol_waypoints.end(); ++it) { delete (*it); (*it) = 0; } patrol_waypoints.clear(); // detach and delete remaining members for (Members::iterator it = patrol_members.begin(); it != patrol_members.end(); ++it) { NPC *member = (*it); member->set_patrol(0); member->die(); (*it) = 0; } patrol_members.clear(); } void Patrol::set_profile(const NPC::Profile profile) { patrol_profile = profile; } void Patrol::validate() { int waypoint_counter = 1; for (WayPoints::iterator it = patrol_waypoints.begin(); it != patrol_waypoints.end(); ++it) { WayPoint *waypoint = (*it); if (waypoint->target_label().size() == 0) { con_warn << " Patrol '" << label() << "' WayPoint " << waypoint_counter << " has no target" << "'\n"; break; } std::string entitylabel; std::string zonelabel; core::Zone *targetzone = 0; size_t pos = waypoint->target_label().find(':'); if ((pos == std::string::npos) || (pos < 1) || (pos >= (waypoint->target_label().size() - 1))) { targetzone = zone(); zonelabel.assign(zone()->label()); entitylabel.assign(waypoint->target_label()); } else { zonelabel.assign(waypoint->target_label().substr(0, pos)); entitylabel.assign(waypoint->target_label().substr(pos + 1, waypoint->target_label().size() - pos)); core::Zone::find(zonelabel); if (!targetzone) { con_warn << " Patrol '" << label() << "' waypoint " << waypoint_counter << " has invalid target zone '" << zonelabel << "'\n"; break; } } core::Entity *targetentity = targetzone->find_entity(entitylabel); if (!targetentity) { con_warn << " Patrol '" << label() << "' waypoint " << waypoint_counter << " has unknown target '" << entitylabel << "' in zone '" << zonelabel << "'\n"; break; } if (waypoint->dock() && !targetentity->has_flag(core::Entity::Dockable)) { con_warn << " Patrol '" << label() << "' waypoint " << waypoint_counter << " set to dock at non-dockable target '" << entitylabel << "' in zone '" << zonelabel << "'\n"; break; } waypoint->set_target(targetentity); } // remove invalid waypoints for (WayPoints::iterator it = patrol_waypoints.begin(); it != patrol_waypoints.end(); ) { WayPoint *waypoint = (*it); if (!waypoint->target()) { delete waypoint; (*it) = 0; patrol_waypoints.erase(it++); } else { ++it; } } size_t nbships = 0; for (core::Inventory::Items::const_iterator it = inventory()->items().begin(); it != inventory()->items().end(); it++) { core::Item *item = (*it); if (item->info()->type() == ShipModel::infotype()) { nbships++; } } if (patrol_waypoints.size() == 0) { con_warn << " Patrol '" << label() << "' without waypoints" << "\n"; die(); } else if (nbships == 0) { con_warn << " Patrol '" << label() << "' without ship types" << "\n"; die(); } else if (!(*patrol_waypoints.begin())->target()->has_flag(core::Entity::Dockable)) { con_warn << " Patrol '" << label() << "' spawn set to non-dockable target" << "\n"; die(); } else { // spawn waypoint is dockable (*patrol_waypoints.begin())->set_dock(true); con_debug << " " << label() << " patrol " << patrol_waypoints.size() << " " << aux::plural("waypoint", patrol_waypoints.size()) << " " << nbships << " ship " << aux::plural("type", nbships) << std::endl; } } Patrol::WayPoint *Patrol::add_waypoint() { WayPoint *waypoint = new WayPoint(); patrol_waypoints.push_back(waypoint); return waypoint; } void Patrol::add_member(NPC *npc) { npc->set_patrol(this); patrol_members.push_back(npc); } void Patrol::erase_member(NPC *npc) { Members::iterator it = patrol_members.begin(); while (it != patrol_members.end()) { if ((*it) == npc) { (*it)->set_patrol(0); (*it) = 0; patrol_members.erase(it); it = patrol_members.end(); } else { ++it; } } set_leader(); } void Patrol::set_leader() { patrol_leader = ( (patrol_members.size() > 0 ) ? (*patrol_members.begin()) : 0 ); for (Members::iterator it = patrol_members.begin(); it != patrol_members.end(); ++it) { if ((*it) == patrol_leader) { (*it)->set_leader(0); } else { (*it)->set_leader(patrol_leader); } } } void Patrol::frame(const unsigned long elapsed) { if (patrol_waypoints.size() < 2) { return; } if (patrol_members.size() == 0) { // there are no members, verify if the spawn zone has players in it and create a new patrol patrol_waypoint_current = patrol_waypoints.begin(); core::Entity *spawn = waypoint()->target(); if (spawn->has_flag(core::Entity::Dockable)) { for (core::Inventory::Items::const_iterator it = inventory()->items().begin(); it != inventory()->items().end(); it++) { core::Item *item = (*it); if (item->info()->type() != ShipModel::infotype()) { continue; } // find shipmodel ShipModel *shipmodel = ShipModel::find(item->info()->label()); if (!shipmodel) { continue; } // add NPC NPC *npc = new NPC(patrol_profile, shipmodel); // set NPC name if (shipmodel->npc_name().size()) { npc->set_name(shipmodel->npc_name()); } // patrol ships are not dockable if (npc->has_flag(core::Entity::Dockable)) { unset_flag(core::Entity::Dockable); // delete menus for (Menus::iterator mit = npc->menus().begin(); mit != npc->menus().end(); mit++) { delete (*mit); (*mit) = 0; } npc->menus().clear(); } // dock npc at spawn npc->set_zone(spawn->zone()); npc->set_dock(spawn); // add NPC to patrol add_member(npc); } set_leader(); } } else if (patrol_leader) { if (patrol_leader->state() == core::Entity::Docked) { if (patrol_leader->dock() == waypoint()->target()) { // verify everyone in the patrol is docked bool group_docked = true; for (Members::iterator it = patrol_members.begin(); it != patrol_members.end(); ++it) { if ((*it)->state() != core::Entity::Docked) { group_docked = false; } } // next waypoint if (group_docked) { patrol_waypoint_current++; if (patrol_waypoint_current == patrol_waypoints.end()) { patrol_waypoint_current = patrol_waypoints.begin(); } patrol_leader->set_autopilot_target(waypoint()->target()); } } else { patrol_leader->launch(); } } else if ((patrol_leader->state() == core::Entity::Normal) || (patrol_leader->state() == core::Entity::ImpulseInitiate) || (patrol_leader->state() == core::Entity::Impulse)) { if (waypoint()->target()->zone() == patrol_leader->zone()) { patrol_leader->set_autopilot_target(waypoint()->target()); patrol_leader->set_autopilot_flag(Ship::AutoPilotEnabled); patrol_leader->unset_autopilot_flag(Ship::AutoPilotFormation); if ( (waypoint()->dock()) && (waypoint()->target()->has_flag(core::Entity::Dockable)) ) { patrol_leader->set_autopilot_flag(Ship::AutoPilotDock); } else { patrol_leader->unset_autopilot_flag(Ship::AutoPilotDock); if ( math::distance(patrol_leader->location(), waypoint()->target()->location()) < PLANET_SAFE_DISTANCE + patrol_leader->radius() + waypoint()->target()->radius() ) { // check if other group memebers are near bool group_present = true; for (Members::iterator it = patrol_members.begin(); it != patrol_members.end(); ++it) { if ( math::distance(patrol_leader->location(), (*it)->location()) > 4.0f * (patrol_leader->radius() + (*it)->radius()) ) { group_present = false; } } // next waypoint if (group_present) { patrol_waypoint_current++; if (patrol_waypoint_current == patrol_waypoints.end()) { patrol_waypoint_current = patrol_waypoints.begin(); } patrol_leader->set_autopilot_target(waypoint()->target()); } else { patrol_leader->set_autopilot_target(0); } } } } else { patrol_leader->set_autopilot_target(0); } } } } }