using System;
using System.Collections.Generic;
using ACE.Entity;
using ACE.Entity.Enum;
using ACE.Entity.Models;
namespace ACE.Server.WorldObjects
{
///
/// Summonable monsters combat AI
///
public partial class CombatPet : Pet
{
///
/// A new biota be created taking all of its values from weenie.
///
public CombatPet(Weenie weenie, ObjectGuid guid) : base(weenie, guid)
{
SetEphemeralValues();
}
///
/// Restore a WorldObject from the database.
///
public CombatPet(Biota biota) : base(biota)
{
SetEphemeralValues();
}
private void SetEphemeralValues()
{
}
public override bool? Init(Player player, PetDevice petDevice)
{
var success = base.Init(player, petDevice);
if (success == null || !success.Value)
return success;
SetCombatMode(CombatMode.Melee);
MonsterState = State.Awake;
IsAwake = true;
// copy ratings from pet device
DamageRating = petDevice.GearDamage;
DamageResistRating = petDevice.GearDamageResist;
CritDamageRating = petDevice.GearCritDamage;
CritDamageResistRating = petDevice.GearCritDamageResist;
CritRating = petDevice.GearCrit;
CritResistRating = petDevice.GearCritResist;
// are CombatPets supposed to attack monsters that are in the same faction as the pet owner?
// if not, there are a couple of different approaches to this
// the easiest way for the code would be to simply set Faction1Bits for the CombatPet to match the pet owner's
// however, retail pcaps did not contain Faction1Bits for CombatPets
// doing this the easiest way for the code here, and just removing during appraisal
Faction1Bits = player.Faction1Bits;
return true;
}
public override void HandleFindTarget()
{
var creature = AttackTarget as Creature;
if (creature == null || creature.IsDead || !IsVisibleTarget(creature))
FindNextTarget();
}
public override bool FindNextTarget()
{
var nearbyMonsters = GetNearbyMonsters();
if (nearbyMonsters.Count == 0)
{
//Console.WriteLine($"{Name}.FindNextTarget(): empty");
return false;
}
// get nearest monster
var nearest = BuildTargetDistance(nearbyMonsters, true);
if (nearest[0].Distance > VisualAwarenessRangeSq)
{
//Console.WriteLine($"{Name}.FindNextTarget(): next object out-of-range (dist: {Math.Round(Math.Sqrt(nearest[0].Distance))})");
return false;
}
AttackTarget = nearest[0].Target;
//Console.WriteLine($"{Name}.FindNextTarget(): {AttackTarget.Name}");
return true;
}
///
/// Returns a list of attackable monsters in this pet's visible targets
///
public List GetNearbyMonsters()
{
var monsters = new List();
foreach (var creature in PhysicsObj.ObjMaint.GetVisibleTargetsValuesOfTypeCreature())
{
// why does this need to be in here?
if (creature.IsDead || !creature.Attackable || creature.Visibility)
{
//Console.WriteLine($"{Name}.GetNearbyMonsters(): refusing to add dead creature {creature.Name} ({creature.Guid})");
continue;
}
// combat pets do not aggro monsters belonging to the same faction as the pet owner?
if (SameFaction(creature))
{
// unless the pet owner or the pet is being retaliated against?
if (!creature.HasRetaliateTarget(P_PetOwner) && !creature.HasRetaliateTarget(this))
continue;
}
monsters.Add(creature);
}
return monsters;
}
public override void Sleep()
{
// pets dont really go to sleep, per say
// they keep scanning for new targets,
// which is the reverse of the current ACE jurassic park model
return; // empty by default
}
}
}