/* base/npc.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/npc.h" #include "base/game.h" namespace game { // NPC Wingman factory function NPC *NPC::add_wingman(Ship *leader) { if (!leader) { return 0; } if (leader->state() != Entity::Normal) { return 0; } NPC *npc = new NPC(ProfileWingman, leader->shipmodel()); npc->set_leader(leader); npc->set_owner(leader->owner()); npc->set_name("Wingman"); npc->set_mood(MoodFormation); npc->set_color(npc->leader()->color()); npc->set_color_second(npc->leader()->color_second()); npc->set_location(leader->location() - leader->axis().forward() * 2.0f * (leader->radius() + npc->radius())); npc->set_axis(leader->axis()); npc->set_zone(leader->zone()); npc->reset(); return npc; } NPC::NPC(const Profile profile, const ShipModel *shipmodel) : Ship(0, shipmodel) { npc_profile = profile; npc_mood = MoodWander; npc_destroyed_timestamp = 0; } void NPC::set_mood(const Mood mood) { npc_mood = mood; } void NPC::set_leader(Ship *leader) { npc_leader = leader; } void NPC::frame(const unsigned long elapsed) { if (state() == core::Entity::Destroyed) { if (!npc_destroyed_timestamp) { 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 die(); } } else { // FIXME verify the leader is still alive // TODO pilot magic and mood witchcraft if (leader()) { // verify leader still exists if (!core::Entity::find(leader())) { set_leader(0); explode(); npc_destroyed_timestamp = core::game()->time(); } else if (leader()->zone() == zone()) { // rotate towards leader: direction math::Vector3f d(leader()->location() - location()); float distance = d.length(); d.normalize(); float direction_angle = math::dotproduct(axis().forward(), d); float direction_sign = math::sgnf(math::dotproduct(axis().left(), d)); if (direction_sign < 0 ) { target_direction = direction_sign; } else if (direction_angle + MIN_DELTA < 1.0f ) { target_direction = direction_sign * direction_angle; } else { target_direction = 0.0f; } // rotate towards leader: pitch direction_angle = math::dotproduct(axis().forward(), d); direction_sign = math::sgnf(math::dotproduct(axis().up(), d)); if (direction_sign < 0 ) { target_pitch = direction_sign; } else if (direction_angle + MIN_DELTA < 1.0f ) { target_pitch = direction_sign * direction_angle; } else { target_pitch = 0.0f; } const float r = 2.0f * (radius() + leader()->radius()); if (distance > 2.0f * r + 50.0f) { if (state() == core::Entity::Normal) { // enable impulse drive func_impulse(); } } else { if (state() == core::Entity::Impulse) { if (leader()->state() != core::Entity::Impulse) { // disbable impulse drive func_impulse(); } } else if (state() == core::Entity::Normal) { if ((leader()->state() == core::Entity::Impulse) || (leader()->state() == core::Entity::ImpulseInitiate)) { // enable impulse drive func_impulse(); } } } if (distance > 2.0f * r) { target_thrust = 1.0f; } else if (distance > r) { target_thrust = (distance - r ) / r; } else { target_thrust = 0.0f; } } else { 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(); } } } } // run a ship game frame Ship::frame(elapsed); } } // namespace game