From 5f74a6b0f3e84ec00e68cda63da6e66df33a8149 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Mon, 4 Nov 2013 16:01:21 +0000 Subject: Added support for NPC patrol routes. --- src/client/keyboard.cc | 2 +- src/game/base/npc.cc | 31 ++++++- src/game/base/npc.h | 3 +- src/game/base/patrol.cc | 198 +++++++++++++++++++++++++++++++++++++++++++-- src/game/base/patrol.h | 29 ++++++- src/game/base/shipmodel.cc | 3 + src/game/base/shipmodel.h | 18 +++++ 7 files changed, 275 insertions(+), 9 deletions(-) diff --git a/src/client/keyboard.cc b/src/client/keyboard.cc index 7658041..c762a29 100644 --- a/src/client/keyboard.cc +++ b/src/client/keyboard.cc @@ -178,7 +178,7 @@ Keyboard::Keyboard() add_key("pageup", SDLK_PAGEUP); add_key("pagedown", SDLK_PAGEDOWN); - add_key("f1", SDLK_F1); + add_key("f1", SDLK_F1, 0, "freeflight"); add_key("f2", SDLK_F2, 0, "@goto"); add_key("f3", SDLK_F3, 0, "@dock"); key = add_key("f4", SDLK_F4, 0, "@formation"); diff --git a/src/game/base/npc.cc b/src/game/base/npc.cc index f8c27d9..0997ed2 100644 --- a/src/game/base/npc.cc +++ b/src/game/base/npc.cc @@ -67,7 +67,7 @@ NPC *NPC::add_wingman(Ship *leader) return npc; } - + NPC::NPC(const Profile profile, const ShipModel *shipmodel) : Ship(0, shipmodel) { npc_profile = profile; @@ -78,6 +78,13 @@ NPC::NPC(const Profile profile, const ShipModel *shipmodel) : Ship(0, shipmodel) npc_leader = 0; } +NPC::~NPC() +{ + if (npc_patrol) { + npc_patrol->erase_member(this); + } +} + void NPC::set_mood(const Mood mood) { npc_mood = mood; @@ -195,6 +202,28 @@ void NPC::frame(const unsigned long elapsed) unset_autopilot_flag(Ship::AutoPilotFormation); } } + + } else if (patrol()) { + // patrol leader behaviour + + // autopilot_target() is set by patrol + if (!autopilot_target()) { + + target_direction = 0; + target_pitch = 0; + target_roll = 0; + target_strafe = 0.0f; + target_vstrafe = 0.0f; + + target_afterburner = 0.0f; + target_thrust = 0; + + if (state() == core::Entity::Impulse) { + + func_impulse(); + } + + } } } diff --git a/src/game/base/npc.h b/src/game/base/npc.h index 5a7e176..48fff35 100644 --- a/src/game/base/npc.h +++ b/src/game/base/npc.h @@ -37,6 +37,7 @@ public: enum Mood { MoodWander = 0, MoodFormation = 1 }; NPC(const Profile profile, const ShipModel *shipmodel); + virtual ~NPC(); /* ---- inspectors ----------------------------------------- */ @@ -65,7 +66,7 @@ public: return npc_leader; } - /** + /** * @brief returns this NPC's patrol. * */ inline Patrol *patrol() diff --git a/src/game/base/patrol.cc b/src/game/base/patrol.cc index db13260..27bb6a3 100644 --- a/src/game/base/patrol.cc +++ b/src/game/base/patrol.cc @@ -55,17 +55,33 @@ Patrol::Patrol() : core::Entity() 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) @@ -113,8 +129,9 @@ void Patrol::validate() break; } - if(waypoint->dock() && !targetentity->has_flag(core::Entity::Dockable)) { - con_warn << " Patrol '" << label() << "' waypoint " << waypoint_counter << " set to dock at non-doackable target '" << entitylabel << "' in zone '" << zonelabel << "'\n"; + 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); @@ -150,9 +167,15 @@ void Patrol::validate() } 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 { - con_debug << " " << label() << " patrol with " << patrol_waypoints.size() << "waypoints" << " and " << nbships << " ship " << aux::plural("type", nbships) << std::endl; + // 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; } } @@ -165,9 +188,174 @@ Patrol::WayPoint *Patrol::add_waypoint() return waypoint; } -void Patrol::frame(const unsigned long elapsed) +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); + } + + } + + } } } diff --git a/src/game/base/patrol.h b/src/game/base/patrol.h index 3e15d3d..a747a89 100644 --- a/src/game/base/patrol.h +++ b/src/game/base/patrol.h @@ -25,6 +25,9 @@ namespace game class Patrol: public core::Entity { public: + + /* --- WayPoint -------------------------------------------- */ + /** * @brief a node in the patrol's travel path * */ @@ -67,15 +70,27 @@ public: bool waypoint_dock; }; + + /* --- Patrol ---------------------------------------------- */ typedef std::list WayPoints; + + typedef std::list Members; Patrol(); virtual ~Patrol(); inline const NPC::Profile profile() const { return patrol_profile; - } + } + + inline WayPoint * waypoint() { + if (patrol_waypoint_current == patrol_waypoints.end()) { + return 0; + } else { + return (*patrol_waypoint_current); + } + } void set_profile(const NPC::Profile profile); @@ -84,11 +99,23 @@ public: virtual void validate(); virtual void frame(const unsigned long elapsed); + + void add_member(NPC *npc); + + void erase_member(NPC *npc); private: + void set_leader(); + WayPoints patrol_waypoints; + WayPoints::iterator patrol_waypoint_current; + + Members patrol_members; + NPC::Profile patrol_profile; + + NPC *patrol_leader; }; } // namespace game diff --git a/src/game/base/shipmodel.cc b/src/game/base/shipmodel.cc index 6fd809b..3582517 100644 --- a/src/game/base/shipmodel.cc +++ b/src/game/base/shipmodel.cc @@ -74,6 +74,9 @@ bool ShipModel::init() } else if (inifile.got_key_string("name", str)) { shipmodel->set_name(str); continue; + } else if (inifile.got_key_string("npcname", str)) { + shipmodel->set_npc_name(str); + continue; } else if (inifile.got_key_string("info", str)) { shipmodel->add_text(str); continue; diff --git a/src/game/base/shipmodel.h b/src/game/base/shipmodel.h index ef37cbf..2231fea 100644 --- a/src/game/base/shipmodel.h +++ b/src/game/base/shipmodel.h @@ -114,6 +114,14 @@ public: return shipmodel_radius; } + /** + * @brief name used for NPCs with this ship model + * */ + inline const std::string & npc_name() const + { + return shipmodel_npc_name; + } + /// entity template inline const Template *model_template() const { @@ -231,6 +239,14 @@ protected: shipmodel_angular_damping = angular_damping; } + /** + * @brief set the name used for NPCs with this ship model + * */ + inline void set_npc_name(const std::string name) + { + shipmodel_npc_name.assign(name); + } + public: /** * @brief generate specifications info. @@ -279,6 +295,8 @@ private: const Template *shipmodel_template; + std::string shipmodel_npc_name; + /* --- static ----------------------------------------------------- */ static core::InfoType *shipmodel_infotype; -- cgit v1.2.3