#include "MW4Headers.hpp" #include #include "AI_FindObject.hpp" #include "Sensor.hpp" #include "MWDamageObject.hpp" #include "rail_move.hpp" #include "CombatAI.hpp" #include "MWPlayer.hpp" #include "MWMission.hpp" #include "VehicleInterface.hpp" #include "MFB.hpp" const Stuff::Scalar min_distance = 10.0f; const Stuff::Scalar max_distance = 1000.0f; const Stuff::Scalar min_tonnage = 40.0f; const Stuff::Scalar max_tonnage = 105.0f; const Stuff::Scalar find_object_shot_by_time = 2.0f; const Stuff::Scalar building_data_refresh_rate = 4.0f; const Stuff::Scalar find_object_cache_delay = 3.0f; extern __int64 tCombatAITime_FindObject; using namespace MW4AI; using namespace MW4AI::FindObject; using namespace MechWarrior4; typedef SensorData sensor_data; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Scoring functions // inline Stuff::Scalar ScoreDistance(Adept::Entity& src, Adept::Entity& dest) { Vector3D delta; delta.Subtract((Point3D)src.GetLocalToWorld(),(Point3D)dest.GetLocalToWorld()); delta.y = 0; return (NormalizedValue(delta.GetLengthSquared(), min_distance * min_distance, max_distance * max_distance, true)); } inline Stuff::Scalar ScoreTonnage(Adept::Entity& entity) { if (entity.IsDerivedFrom(MWObject::DefaultData) == false) { return (0); } MWObject* object = Cast_Object(MWObject*,&entity); return (NormalizedValue(object->GetTonage(),min_tonnage,max_tonnage)); } inline Stuff::Scalar ScoreDamage(Adept::Entity& object) { if (object.IsDerivedFrom(MWObject::DefaultData) == false) { return (0); } MWObject* v = Cast_Object(MWObject*,&object); Stuff::SortedChainIteratorOf i(&(v->internalDamageObjects)); MWInternalDamageObject* o; Stuff::Scalar total_damage = 0; Stuff::Scalar max_damage = 0; while ((o = i.ReadAndNext()) != 0) { total_damage += o->currentInternalDamage; max_damage += o->baseInternalDamage; } if (Small_Enough(max_damage)) { return (0); } return (NormalizedValue(total_damage,max_damage * 0.66f,max_damage,true)); // we crank up the minimum so 33% damage is considered 100% score } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ScoreByCriteria() // Scores the given object (with a normalized score) using the given find criteria Stuff::Scalar ScoreByCriteria(MWObject& src, MWObject& target, int criteria) { Verify((MW4AI::FindObject::Criteria)criteria >= MW4AI::FindObject::FC_FIRST); Verify((MW4AI::FindObject::Criteria)criteria <= MW4AI::FindObject::FC_LAST); switch ((MW4AI::FindObject::Criteria)criteria) { case MW4AI::FindObject::FC_BEST_TARGET: { return (0.45f * ScoreDistance(src,target) + 0.35f * ScoreDamage(target) + 0.2f * ScoreTonnage(target)); } case MW4AI::FindObject::FC_GREATEST_THREAT: { return (0.6f * ScoreDistance(src,target) + 0.4f * ScoreTonnage(target)); } case MW4AI::FindObject::FC_LEAST_THREAT: { return (1.0f - (0.6f * ScoreDistance(src,target) + 0.4f * ScoreTonnage(target))); } case MW4AI::FindObject::FC_MOST_DAMAGED: { return (ScoreDamage(target)); } case MW4AI::FindObject::FC_NEAREST: { return (ScoreDistance(src,target)); } } Verify(!"Should never get here."); return (ScoreDistance(src,target)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // QualifiesByAlignment // Indicates whether a given sensor data object qualifies by testing its alignment // against the given alignment-specification value. bool QualifiesByAlignment(MWObject& src, MWObject& target, int fa_alignment) { Verify(fa_alignment != 0); // value of 0 makes no sense Verify(fa_alignment <= MW4AI::FindObject::FA_ANY); if (&src == &target) { return (false); } switch (src.GetRelativeAlignment(target.GetAlignment())) { case Adept::Entity::Enemy: { return ((fa_alignment & MW4AI::FindObject::FA_ENEMY) != 0); } case Adept::Entity::Player: { return ((fa_alignment & MW4AI::FindObject::FA_FRIENDLY) != 0); } case Adept::Entity::DefaultAlignment: { return ((fa_alignment & MW4AI::FindObject::FA_NEUTRAL) != 0); } } return (false); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // QualifiesByType // Indicates whether a given sensor data object qualifies by testing its // object type against the given alignment-specification value. bool QualifiesByType(MWObject& src, MWObject& target, int ft_type) { Verify(ft_type != 0); // 0 is invalid: you must specify a type if (target.GetTargetDesirability() == MWObject::desirability_never) { return (false); } if (ft_type & FT_BUILDING) { if (target.IsDerivedFrom(Building::DefaultData) == true) { return (true); } } if (ft_type & FT_MFB) { if (target.IsDerivedFrom(MFB::DefaultData) == true) { return (true); } } if ((target.GetTargetDesirability() == MWObject::desirability_invalid) && (target.IsPlayerVehicle() == false)) { if (target.GetAI() == 0) { return (false); } if ((target.weaponChain.IsEmpty() == true) && (target.IsDerivedFrom(Mech::DefaultData) == false) && (!(ft_type & FT_UNARMED))) { return (false); } } if (ft_type & FT_MECH) { if (target.IsDerivedFrom(Mech::DefaultData) == true) { return (true); } } if (ft_type & FT_AIRPLANE) { if (target.IsDerivedFrom(Airplane::DefaultData) == true) { return (true); } } if (ft_type & FT_VEHICLE) { if ((target.IsDerivedFrom(Vehicle::DefaultData) == true) && (target.IsDerivedFrom(MFB::DefaultData) == false) && (target.IsDerivedFrom(Mech::DefaultData) == false) && (target.IsDerivedFrom(Airplane::DefaultData) == false)) { return (true); } } if (ft_type & FT_BOAT) { if (target.IsDerivedFrom(Boat::DefaultData) == true) { return (true); } } if (ft_type & FT_TURRET) { if (target.IsDerivedFrom(Building::DefaultData) == true) { return (true); } } return (false); } bool Qualifies(MWObject& src, MWObject& target, int fa_alignment, int ft_type) { if (QualifiesByAlignment(src,target,fa_alignment) == false) { return (false); } return (QualifiesByType(src,target,ft_type)); } void FillNameTableData(MWObject& src, std::vector& objects_to_search) { Adept::NameTable* table = Adept::NameTable::GetInstance(); { int size(table->nameTableArray[NameTable::VehicleArray].GetLength()); {for (int i = 0; i < size; ++i) { NameTableEntry *data = &table->nameTableArray [NameTable::VehicleArray][i]; Adept::Entity* ent = data->dataPointer->GetCurrent(); if ((ent != 0) && (ent->IsDestroyed() == false) && (ent != &src) && (ent->IsDerivedFrom(MWObject::DefaultData) == true)) { objects_to_search.push_back(Cast_Object(MWObject*,ent)); } }} } { int size(table->nameTableArray[NameTable::PlayerArray].GetLength()); {for (int i = 0; i < size; ++i) { NameTableEntry *data = &table->nameTableArray [NameTable::PlayerArray][i]; Adept::Entity* ent = data->dataPointer->GetCurrent (); if ((ent != 0) && (ent->IsDerivedFrom(Adept::Player::DefaultData) == true)) { Adept::Player* player = Cast_Object(Adept::Player*,ent); if ((player->vehicle != 0) && (player->vehicle->IsDestroyed() == false) && (player->vehicle != &src) && (player->vehicle->IsDerivedFrom(MWObject::DefaultData) == true)) { objects_to_search.push_back(Cast_Object(MWObject*,player->vehicle)); } } }} } } void FillSensorData(MWObject& src, const Sensor::sensorDataArray& sensed_data, int number_of_contacts, std::vector& sensor_data_list) { if ((src.GetAI() != 0) && (src.GetAI()->GetExtendedSensorData(sensor_data_list) == true)) { return; } if (number_of_contacts == 0) { return; } sensor_data_list.reserve(number_of_contacts); {for (int i = 0; i < number_of_contacts; ++i) { if ((sensed_data[i] != 0) && (sensed_data[i]->object.GetCurrent() != 0) && (sensed_data[i]->object.GetCurrent() != &src)) { sensor_data_list.push_back(sensed_data[i]->object.GetCurrent()); } }} } // FindObject(): finds an object given the given sensor (sensor) and qualifier (object_type) long MW4AI::FindObject::Find(MWObject& src, int fa_alignment, int ft_type, int fc_criteria, int ff_flags, Stuff::Scalar distance_limit, const Adept::ObjectID* except_object) { AI_LOGIC("Find Object"); TIME_FUNCTION(tCombatAITime_FindObject); Verify(ff_flags <= FF_WHO_SHOT + FF_SEEPLAYER + FF_LOOK_EVERYWHERE + FF_TARGETLOS); if (distance_limit == 0) { distance_limit = MW4AI::FindObject::DISTANCE_LIMIT; } if (src.m_FindObjectList == 0) { src.m_FindObjectList = new MW4AI::FindObjectList; } std::vector& objects_to_search = src.m_FindObjectList->objects; objects_to_search.clear(); if ((ff_flags & MW4AI::FindObject::FF_SEEPLAYER) && (MWMission::GetInstance()->GetGameModel() != 0) && (Adept::Player::GetInstance() != 0)) { MWPlayer* p = Cast_Object(MWPlayer*,Adept::Player::GetInstance()); if ((p != 0) && (p->GetInterface() != 0) && (p->GetInterface()->vehicle != 0)) { Vehicle* vehicle = p->GetInterface()->vehicle; if ((QualifiesByAlignment(src,*vehicle,fa_alignment) == true) && (vehicle->IsDestroyed() == false) && (vehicle->objectID != src.objectID)) { const Adept::Mission::GameModel* game_model = MWMission::GetInstance()->GetGameModel(); Stuff::Scalar fog_distance = game_model->m_generalFogEnd; if (fog_distance > 1200) { fog_distance = 1200; } if (fog_distance > distance_limit) { fog_distance = distance_limit; } Stuff::Point3D player_vehicle_pos(vehicle->GetLineOfSight()); Stuff::Point3D my_pos(src.GetLineOfSight()); Stuff::LinearMatrix4D src_los; src.GetLineOfSight(src_los); if ((GetLengthSquared(player_vehicle_pos,my_pos) < fog_distance * fog_distance) && (MatrixFacesPoint(src_los,player_vehicle_pos,Stuff::Pi_Over_4) == true)) { if ((src.m_FindObjectCache != 0) && (src.m_FindObjectCache->m_RayCastTime + find_object_cache_delay < gos_GetElapsedTime())) { delete src.m_FindObjectCache; src.m_FindObjectCache = 0; } MW4AI::FireData::HIT_RESULT hit_result; if (src.m_FindObjectCache != 0) { hit_result = src.m_FindObjectCache->m_HitResult; } else { FireData fire_data(src,my_pos,player_vehicle_pos); fire_data.Project(); hit_result = fire_data.GetHitResult(vehicle); src.m_FindObjectCache = new MW4AI::FindObjectCache((Stuff::Scalar)gos_GetElapsedTime(),hit_result); } if (hit_result == FireData::HIT_TARGET) { if (src.GetSensor() == 0) { return (vehicle->objectID); } else { objects_to_search.push_back(vehicle); } } } } } } if ((src.GetSensor() == 0) && (objects_to_search.size() == 0)) { return (-2); } if ((ff_flags & MW4AI::FindObject::FF_WHO_SHOT) && (src.GetAI() != 0)) { Adept::Entity* e = src.GetAI()->ShotBy(gos_GetElapsedTime() - find_object_shot_by_time); if ((e != 0) && (e != &src) && (e->IsDerivedFrom(MWObject::DefaultData) == true) && (e->IsDestroyed() == false)) { MWObject* v = Cast_Object(MWObject*,e); Check_Object(v); if (QualifiesByAlignment(src,*v,fa_alignment) == true) { return (v->objectID); } } } if (ff_flags & MW4AI::FindObject::FF_LOOK_EVERYWHERE) { FillNameTableData(src,objects_to_search); } else { FillSensorData(src, src.GetSensor()->RefreshAndGetSensorData(), src.GetSensor()->numberOfContacts, objects_to_search); if (ft_type & FT_BUILDING) { if (src.GetSensor()->GetLastTimeUpdatedBuildingData() < gos_GetElapsedTime() + building_data_refresh_rate + Stuff::Random::GetFraction()) { src.GetSensor()->UpdateBuildingData(); } FillSensorData(src, src.GetSensor()->GetBuildingData(), src.GetSensor()->numberOfBuildingContacts, objects_to_search); } } if (objects_to_search.size() == 0) { return (-2); } const Stuff::Scalar distance_limit_squared = distance_limit * distance_limit; Stuff::Scalar best_score = 0; std::vector::const_iterator best_found = objects_to_search.end(); Stuff::Point3D my_pos(src.GetLocalToWorld()); {for (std::vector::const_iterator i = objects_to_search.begin(); i != objects_to_search.end(); ++i) { if ((except_object != 0) && ((*i)->objectID == *except_object)) { continue; } if (ff_flags & MW4AI::FindObject::FF_TARGETLOS) { // Test line of sight Stuff::Point3D target_pos((*i)->GetLineOfSight()); Stuff::Point3D my_pos(src.GetLineOfSight()); FireData fire_data(src,my_pos,target_pos); fire_data.Project(); if (fire_data.GetHitResult((*i)) != FireData::HIT_TARGET) { continue; } } if (Qualifies(src,**i,fa_alignment,ft_type) == true) { Point3D pos((*i)->GetLocalToWorld()); Scalar distance(0); if (src.GetAI() != 0) { distance = src.GetAI()->GetLeastSquaredSensorDistance(pos); } if (distance == 0) { Vector3D delta; delta.Subtract(my_pos,pos); delta.y = 0; distance = delta.GetLengthSquared(); } if (distance < distance_limit_squared) { Stuff::Scalar s; if ((*i)->GetTargetDesirability() == MWObject::desirability_invalid) { s = ScoreByCriteria(src,**i,fc_criteria); } else { Verify((*i)->GetTargetDesirability() != MWObject::desirability_never); s = (*i)->GetTargetDesirability(); } Verify(s >= 0); Verify(s <= 1); if (s >= best_score) { if (s == 1) { return ((*i)->objectID); } best_score = s; best_found = i; } } } }} if (best_found == objects_to_search.end()) { return (-2); // -2 = ABL constant for nothing found } return ((*best_found)->objectID); }