From 9dc3cc532820349a0f0e087afb60132927fd3411 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Sun, 10 Nov 2013 01:48:41 +0000 Subject: Corrected a bug which prevented the patrol profile from being set correctly, corrected a bug where 'give ship' would forget the last spawn, have NPC ships fire on nearby enemies, made patrol leaders hunt nearby enemies. --- src/game/base/faction.h | 6 +- src/game/base/game.cc | 5 +- src/game/base/npc.cc | 90 +++++++++++++--- src/game/base/npc.h | 5 + src/game/base/patrol.cc | 268 +++++++++++++++++++++++++++++------------------- src/game/base/patrol.h | 4 + src/game/base/ship.cc | 70 +++++++++++-- src/game/base/ship.h | 10 +- 8 files changed, 328 insertions(+), 130 deletions(-) (limited to 'src/game') diff --git a/src/game/base/faction.h b/src/game/base/faction.h index c78c2f2..b459899 100644 --- a/src/game/base/faction.h +++ b/src/game/base/faction.h @@ -42,7 +42,7 @@ public: /** * @brief player reputation with a specific faction * */ - inline const float reputation(const Info *faction) const { + inline const float reputation(const core::Info *faction) const { if (faction == this) { return 100.0f; } else { @@ -88,6 +88,10 @@ public: * */ static void apply_default(core::Reputation & reputation); + static inline const core::InfoType *infotype() { + return faction_infotype; + } + private: /* --- attributes ------------------------------------------------- */ diff --git a/src/game/base/game.cc b/src/game/base/game.cc index d5e541b..c4d6069 100644 --- a/src/game/base/game.cc +++ b/src/game/base/game.cc @@ -410,6 +410,7 @@ void Game::func_give(core::Player *player, const std::string &args) ship->get_location().assign(player->control()->location()); ship->set_dock(oldship->dock()); + ship->set_spawn(oldship->spawn()); ship->get_axis().assign(player->control()->axis()); ship->set_thrust(player->control()->thrust()); @@ -2099,9 +2100,9 @@ bool Game::load_zone(core::Zone *zone) if (core::Parser::got_entity_key(zoneini, patrol)) { continue; } else if (zoneini.got_key_label("profile", strval)) { - if (strval.compare("convoy")) { + if (strval.compare("convoy") == 0) { patrol->set_profile(Patrol::ProfileConvoy); - } else if (strval.compare("patrol")) { + } else if (strval.compare("patrol") == 0) { patrol->set_profile(Patrol::ProfilePatrol); } else { zoneini.unknown_error("unknown profile '" + strval + "'"); diff --git a/src/game/base/npc.cc b/src/game/base/npc.cc index fa0ef44..992ba6c 100644 --- a/src/game/base/npc.cc +++ b/src/game/base/npc.cc @@ -4,6 +4,8 @@ the terms of the GNU General Public License version 2 */ +#include "core/range.h" + #include "base/npc.h" #include "base/game.h" #include "base/patrol.h" @@ -99,6 +101,79 @@ void NPC::set_patrol(Patrol *patrol) npc_patrol = patrol; } +Ship *NPC::target_closest_enemy() +{ + // scan for enemies + Ship *current_enemy = 0; + float current_distance = 0.0f; + + + for (core::Zone::Content::iterator zit = zone()->content().begin(); zit != zone()->content().end(); ++zit) { + if (((*zit)->moduletype() == ship_enttype) && ((*zit) != this)) { + Ship *ship = static_cast((*zit)); + + if ((ship->state() != Normal) && (ship->state() != ImpulseInitiate) && (ship->state() != Impulse)) { + continue; + } + + const float d = math::distance(location(), ship->location()); + const float r = radius() + ship->radius(); + + // too far + if (d > r + core::range::fxdistance * 2.0f) { + continue; + } + + // further than current target + if ((current_distance > 0.0f) && (d > current_distance)) { + continue; + } + + float reputation = 0.0f; + if (ship->owner()) { + // check owner reputation for the faction the NPC belongs to + reputation = ship->owner()->reputation(faction()); + + } else if (faction() && (faction()->type() == Faction::infotype())) { + //check NPC faction reputation for the other ships's faction + reputation = static_cast(faction())->reputation(ship->faction()); + } + + // reputation threshold to get attacked + if (reputation >= -50.0f) { + continue; + } + + // reputation threshold to get hunted + if (d > core::range::fxdistance + r) { + + if ((!ship->owner()) || (reputation > -100.0f)) { + continue;; + } + } + + current_enemy = ship; + current_distance = d; + } + } + + // FIXME calculate weaposn range, arbitrarily set to 10 km + const float weapons_range = COMBAT_DISTANCE; + + // set aim + if ((state() == Normal) && current_enemy && (current_distance <= weapons_range)) { + // activate weapons + set_target_controlflag(core::EntityControlable::ControlFlagFire); + // rudimentary aim currention + target_aim.assign(current_enemy->location() + current_enemy->axis().forward() * (current_enemy->radius() * 0.25f) ); + } else { + // deactivate weapons + unset_target_controlflag(core::EntityControlable::ControlFlagFire); + } + + return current_enemy; +} + void NPC::frame(const unsigned long elapsed) { if (state() == core::Entity::Destroyed) { @@ -107,13 +182,11 @@ void NPC::frame(const unsigned long elapsed) npc_destroyed_timestamp = core::game()->time(); } else if (npc_destroyed_timestamp + 10.0f < core::game()->time()) { - // stay alive for 10 more seconds while explosion particles are drawn - + // stay alive for 10 more seconds while explosion particles are drawn die(); } } else { - // TODO pilot magic and mood witchcraft if (leader()) { @@ -154,7 +227,6 @@ void NPC::frame(const unsigned long elapsed) } else { if (state() == core::Entity::Docked) { - // FIXME check launch conditions when docked at another player's ship launch(); @@ -166,14 +238,7 @@ void NPC::frame(const unsigned long elapsed) unset_autopilot_flag(Ship::AutoPilotDock); set_autopilot_flag(Ship::AutoPilotFormation); - /* - if (leader()->has_target_controlflag(core::EntityControlable::ControlFlagFire)) { - set_target_controlflag(core::EntityControlable::ControlFlagFire); - target_aim.assign(leader()->aim()); - } else { - unset_target_controlflag(core::EntityControlable::ControlFlagFire); - } - */ + target_closest_enemy(); } } @@ -218,7 +283,6 @@ void NPC::frame(const unsigned long elapsed) 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 8139c9a..68620e1 100644 --- a/src/game/base/npc.h +++ b/src/game/base/npc.h @@ -81,6 +81,11 @@ public: * @brief factory function for wingman NPCs * */ static NPC *add_wingman(Ship *leader); + + /** + * @brief aim at closest enemy in range, returns target + * */ + Ship *target_closest_enemy(); private: Mood npc_mood; diff --git a/src/game/base/patrol.cc b/src/game/base/patrol.cc index fbe02df..d1bbe15 100644 --- a/src/game/base/patrol.cc +++ b/src/game/base/patrol.cc @@ -197,6 +197,33 @@ void Patrol::validate() } } +void Patrol::print() const +{ + core::Entity::print(); + con_print << " ^Nprofile ^B"; + switch (profile()) { + case(ProfileFreelancer): + con_print << "freelancer"; + break; + case(ProfileConvoy): + con_print << "convoy"; + break; + case(ProfilePatrol): + con_print << "patrol"; + break; + case(ProfileGuard): + con_print << "guard"; + break; + case(ProfileWingman): + con_print << "wingman"; + break; + } + con_print << std::endl; + con_print << " ^Nwaypoints ^B" << patrol_waypoints.size() << std::endl; + con_print << " ^Nnpc types ^B" << patrol_npctypes.size() << std::endl; + con_print << " ^Nmembers ^B" << patrol_members.size() << std::endl; +} + Patrol::WayPoint *Patrol::add_waypoint() { WayPoint *waypoint = new WayPoint(); @@ -269,123 +296,133 @@ bool Patrol::route_alive() const return false; } -void Patrol::frame(const unsigned long elapsed) -{ - if (patrol_waypoints.size() < 2) { - return; +void Patrol::create_patrol() +{ + if (patrol_waypoint_current == patrol_waypoints.end() || !(*patrol_waypoint_current)->dock()) { + patrol_waypoint_current = patrol_waypoints.begin(); } - - if (patrol_members.size() == 0) { - // there are no members, verify if the spawn zone has players in it and create a new patrol - if (patrol_waypoint_current == patrol_waypoints.end() || !(*patrol_waypoint_current)->dock()) { - patrol_waypoint_current = patrol_waypoints.begin(); - } - if (route_alive()) { - core::Entity *spawn = waypoint()->target(); - - if (spawn->has_flag(core::Entity::Dockable)) { - // create NPC members for every NPC type - for (NPCTypes::const_iterator it = patrol_npctypes.begin(); it != patrol_npctypes.end(); ++it) { - - NPCType *npctype = (*it); - - if (!npctype->shipmodel()) { - continue; - } + core::Entity *spawn = waypoint()->target(); + + if (spawn->has_flag(core::Entity::Dockable)) { + + // create NPC members for every NPC type + for (NPCTypes::const_iterator it = patrol_npctypes.begin(); it != patrol_npctypes.end(); ++it) { + + NPCType *npctype = (*it); + + if (!npctype->shipmodel()) { + continue; + } + + if (npctype->amount() <= 0) { + continue; + } + + const size_t nbships = 1 + math::randomi((unsigned int) npctype->amount()); + for (size_t i = 0; i < nbships; i++) { + + // add NPC + NPC *npc = new NPC(npctype->shipmodel()); + npc->set_mood(NPC::MoodFormation); + + // set NPC name + if (npctype->name().size()) { + std::string str(npctype->name()); + npc->set_name(str); - if (npctype->amount() <= 0) { - continue; + aux::to_label(str); + npc->set_label(str); + } + + // set NPC color + if (npctype->faction()) { + npctype->faction()->apply(npc); + } else if (faction()) { + faction()->apply(npc); + } + + // 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(); + } + + // install inventory + if (!npc->inventory()) { + npc->add_inventory(); + } + + // install slots + if (!npc->slots()) { + npc->add_slots(); + slots()->load(model()); + } + + // install weapons + for (core::Slots::iterator slit = npc->slots()->begin(); slit != npc->slots()->end(); ++slit) { + core::Slot *slot = (*slit); - const size_t nbships = 1 + math::randomi((unsigned int) npctype->amount()); - for (size_t i = 0; i < nbships; i++) { + core::Item *item = 0; - // add NPC - NPC *npc = new NPC(npctype->shipmodel()); - npc->set_mood(NPC::MoodFormation); - - // set NPC name - if (npctype->name().size()) { - npc->set_name(npctype->name()); - } - - // set NPC color - if (npctype->faction()) { - npctype->faction()->apply(npc); - } else if (faction()) { - faction()->apply(npc); - } - - // 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(); + if (slot->type() == model::Weapon::Cannon) { + if (npctype->cannon()) { + item = new core::Item(npctype->cannon()); } - - // install inventory - if (!npc->inventory()) { - npc->add_inventory(); + } else if (slot->type() == model::Weapon::Turret) { + if (npctype->turret()) { + item = new core::Item(npctype->turret()); } + } + + if (item) { + // add item + item->set_flag(core::Item::Unique); + item->set_flag(core::Item::Mountable); + item->set_flag(core::Item::Unrestricted); + item->set_amount(1); + npc->inventory()->add(item); - // install slots - if (!npc->slots()) { - npc->add_slots(); - slots()->load(model()); - } - - // install weapons - for (core::Slots::iterator slit = npc->slots()->begin(); slit != npc->slots()->end(); ++slit) { - core::Slot *slot = (*slit); - - core::Item *item = 0; - - if (slot->type() == model::Weapon::Cannon) { - if (npctype->cannon()) { - item = new core::Item(npctype->cannon()); - } - } else if (slot->type() == model::Weapon::Turret) { - if (npctype->turret()) { - item = new core::Item(npctype->turret()); - } - } - - if (item) { - // add item - item->set_flag(core::Item::Unique); - item->set_flag(core::Item::Mountable); - item->set_flag(core::Item::Unrestricted); - item->set_amount(1); - npc->inventory()->add(item); - - // mount weapon - slot->set_item(item); - slot->set_flag(core::Slot::Active); - slot->set_flag(core::Slot::Mounted); - } - } - - npc->inventory()->recalculate(); - - // dock npc at spawn - npc->set_zone(spawn->zone()); - npc->set_dock(spawn); - - // add NPC to patrol - add_member(npc); + // mount weapon + slot->set_item(item); + slot->set_flag(core::Slot::Active); + slot->set_flag(core::Slot::Mounted); } } - set_leader(); + npc->inventory()->recalculate(); + + // dock npc at spawn + npc->set_zone(spawn->zone()); + npc->set_dock(spawn); - patrol_launch_timeout = core::server()->timestamp() + 15000 + (unsigned long) math::randomi(30000); + // add NPC to patrol + add_member(npc); } } + + set_leader(); + + patrol_launch_timeout = core::server()->timestamp() + 15000 + (unsigned long) math::randomi(30000); + } +} +void Patrol::frame(const unsigned long elapsed) +{ + if (patrol_waypoints.size() < 2) { + return; + } + + if (patrol_members.size() == 0) { + if (route_alive()) { + create_patrol(); + } + return; + } else if (patrol_leader) { if (patrol_leader->state() == core::Entity::Docked) { @@ -468,12 +505,31 @@ void Patrol::frame(const unsigned long elapsed) } 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()) { + Ship *enemy = 0; + + if (profile() == ProfilePatrol) { + enemy = patrol_leader->target_closest_enemy(); + } + + if (enemy) { + // combat conditions + if (patrol_leader->autopilot_target() != enemy) { + patrol_leader->set_autopilot_target(enemy); + } + patrol_leader->set_autopilot_flag(Ship::AutoPilotEnabled); + patrol_leader->set_autopilot_flag(Ship::AutoPilotCombat); + patrol_leader->unset_autopilot_flag(Ship::AutoPilotFormation); + patrol_leader->unset_autopilot_flag(Ship::AutoPilotDock); + + } else if (waypoint()->target()->zone() == patrol_leader->zone()) { + + // current waypoint is in this zone patrol_leader->set_autopilot_target(waypoint()->target()); patrol_leader->set_autopilot_flag(Ship::AutoPilotEnabled); patrol_leader->unset_autopilot_flag(Ship::AutoPilotFormation); + patrol_leader->unset_autopilot_flag(Ship::AutoPilotCombat); if ( (waypoint()->dock()) && (waypoint()->target()->has_flag(core::Entity::Dockable)) ) { patrol_leader->set_autopilot_flag(Ship::AutoPilotDock); @@ -505,10 +561,12 @@ void Patrol::frame(const unsigned long elapsed) } } } + } else { // current waypoint is in different zone + patrol_leader->unset_autopilot_flag(Ship::AutoPilotCombat); - // check if a jumpgate got use here + // check if the leader got here through a jumpgate JumpGate *jumpgate = (waypoint()->target()->moduletype() == jumpgate_enttype ? static_cast(waypoint()->target()) : 0 ); if (jumpgate && jumpgate->target()->zone() == patrol_leader->zone()) { @@ -537,7 +595,7 @@ void Patrol::frame(const unsigned long elapsed) } } else { - // we're lost: delete patrol + // we're lost: delete patrol members for (Members::iterator it = patrol_members.begin(); it != patrol_members.end(); ++it) { (*it)->die(); diff --git a/src/game/base/patrol.h b/src/game/base/patrol.h index cc3bf61..c99c244 100644 --- a/src/game/base/patrol.h +++ b/src/game/base/patrol.h @@ -127,6 +127,8 @@ public: virtual void frame(const unsigned long elapsed); + virtual void print() const; + void add_member(NPC *npc); void erase_member(NPC *npc); @@ -134,6 +136,8 @@ public: private: void set_leader(); + void create_patrol(); + bool route_alive() const; WayPoints patrol_waypoints; diff --git a/src/game/base/ship.cc b/src/game/base/ship.cc index 5be9a00..2a5d68f 100644 --- a/src/game/base/ship.cc +++ b/src/game/base/ship.cc @@ -1194,14 +1194,16 @@ void Ship::frame_autopilot(const unsigned long elapsed) const float dock_distance = (autopilot_target()->moduletype() == planet_enttype ? r + PLANET_SAFE_DISTANCE : r); if (!has_target_controlflag(core::EntityControlable::ControlFlagOverride)) { - // thruster and speed control - if (has_autopilot_flag(AutoPilotFormation) && (distance < 4.0f * r)) { + if (has_autopilot_flag(AutoPilotCombat) && (distance < r + COMBAT_DISTANCE)) { + frame_autopilot_combat(elapsed, autopilot_target()); + + } else if (has_autopilot_flag(AutoPilotFormation) && (distance < 4.0f * r)) { frame_autopilot_formation(elapsed, autopilot_target()); } else if (has_autopilot_flag(AutoPilotDock) && (distance < dock_distance)) { frame_autopilot_dock(elapsed, autopilot_target()); - + } else { frame_autopilot_goto(elapsed, autopilot_target()); } @@ -1292,6 +1294,63 @@ void Ship::frame_autopilot_goto(const unsigned long elapsed, core::Entity *targe } } +void Ship::frame_autopilot_combat(const unsigned long elapsed, core::Entity *target) +{ + if ((state() == core::Entity::Impulse) || (state() == core::Entity::ImpulseInitiate)) { + func_impulse(); + } + + // desired direction + math::Vector3f direction(target->location() - location()); + const float distance = direction.length(); + // normalize + direction /= distance; + + // transform direction from world coordinates to local entity coordinates + direction = axis().transpose() * direction; + + if (direction.x() < 0) { + // target is behind the ship + target_direction = (direction.y() > 0 ? 1.0f : -1.0f); + target_pitch = 0.0f; + + } else if (direction.x() + MIN_DELTA < 1.0f) { + // target is in front of the ship + target_direction = direction.y(); + target_pitch = direction.z(); + } else { + target_direction = 0.0f; + target_pitch = 0.0f; + } + + // transform the target's axis into local coordinates + const math::Axis reference(axis().transpose() * target->axis()); + if (reference.up().z() < 0.0f) { + // upward-down + target_roll = (reference.up().y() > 0 ? 1.0f : 0.0f); + + } else if (reference.up().z() + MIN_DELTA < 1.0f) { + target_roll = reference.up().y(); + + } else { + target_roll = 0.0f; + } + + // reference radius used in calculations + const float r = radius() + target->radius(); + + // thruster + if (distance > 2.0f * r) { + target_thrust = 1.0f; + + } else if (distance > r ) { + target_thrust = math::max(0.1f, (distance - r ) / r); + + } else { + target_thrust = 0.0f; + } +} + void Ship::frame_autopilot_dock(const unsigned long elapsed, core::Entity *target) { // reference radius used in calculations @@ -1300,10 +1359,7 @@ void Ship::frame_autopilot_dock(const unsigned long elapsed, core::Entity *targe if ((state() == core::Entity::Impulse) || (state() == core::Entity::ImpulseInitiate)) { func_impulse(); - return; - } - - if (state() != core::Entity::Normal) { + } else if (state() != core::Entity::Normal) { return; } diff --git a/src/game/base/ship.h b/src/game/base/ship.h index e8a6505..d627295 100644 --- a/src/game/base/ship.h +++ b/src/game/base/ship.h @@ -20,6 +20,7 @@ const float MIN_DELTA = 0.000001f; // planet docking distance const float PLANET_SAFE_DISTANCE = 50.0f; +const float COMBAT_DISTANCE = 100.0f; /** @@ -28,7 +29,7 @@ const float PLANET_SAFE_DISTANCE = 50.0f; class Ship : public core::EntityControlable { public: - enum AutoPilotFlags { AutoPilotDisabled = 0, AutoPilotEnabled = 1, AutoPilotDock = 2, AutoPilotFormation = 4 }; + enum AutoPilotFlags { AutoPilotDisabled = 0, AutoPilotEnabled = 1, AutoPilotDock = 2, AutoPilotFormation = 4, AutoPilotCombat = 8 }; Ship(core::Player *owner, const ShipModel *shipmodel); ~Ship(); @@ -234,7 +235,12 @@ protected: void frame_autopilot_goto(const unsigned long elapsed, core::Entity *target); /** - * @brief autopilot goto target + * @brief autopilot combat + * */ + void frame_autopilot_combat(const unsigned long elapsed, core::Entity *target); + + /** + * @brief autopilo dock target * */ void frame_autopilot_dock(const unsigned long elapsed, core::Entity *target); -- cgit v1.2.3