context: /*
   *  "NorseWorld: Ragnarok", a roguelike game for PCs.
   *  Copyright (C) 2002-2008, 2014, 2020 by Serg V. Zhdanovskih.
   *
   *  This file is part of "NorseWorld: Ragnarok".
   *
   *  This program is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  using System;
  using System.Collections.Generic;
  using BSLib;
  using NWR.Creatures.Brain.Goals;
  using NWR.Database;
  using NWR.Effects;
  using NWR.Game;
  using NWR.Game.Types;
  using NWR.Items;
  using NWR.Universe;
  using ZRLib.Core;
  using ZRLib.Core.Brain;
  using ZRLib.Map;
  
  namespace NWR.Creatures.Brain
  {
      public class BeastBrain : NWBrainEntity
      {
          public CreatureEntity fNearKinsfolk;
          public int fNearKinsfolkDist;
  
          public bool Flock;
          public bool IsLeader;
          public bool IsShipSail;
          public List<NWCreature> Kinsfolks;
  
  
          protected override EmitterList Emitters
          {
              get {
                  return (((NWCreature)fSelf).CurrentField).Emitters;
              }
          }
  
  
          public BeastBrain(CreatureEntity owner) : base(owner)
          {
              Kinsfolks = new List<NWCreature>();
          }
  
          private void PrepareFlock()
          {
              try {
                  IsLeader = true;
                  Kinsfolks.Clear();
  
                  NWCreature self = (NWCreature)fSelf;
                  NWField fld = self.CurrentField;
  
                  fNearKinsfolk = null;
                  fNearKinsfolkDist = AuxUtils.MaxInt;
  
                  int num = fld.Creatures.Count;
                  for (int i = 0; i < num; i++) {
                      NWCreature cr = fld.Creatures.GetItem(i);
                      int dist = MathHelper.Distance(cr.Location, self.Location);
                      if (!cr.Equals(self) && dist <= self.Survey && cr.CLSID == self.CLSID && fld.LineOfSight(self.PosX, self.PosY, cr.PosX, cr.PosY)) {
                          Kinsfolks.Add(cr);
                          if (fNearKinsfolkDist > dist) {
                              fNearKinsfolkDist = dist;
                              fNearKinsfolk = cr;
                          }
                          IsLeader = (IsLeader && self.Leadership > cr.Leadership);
                      }
                  }
  
                  if (FindGoalByKind(GoalKind.gk_Flock) == null) {
                      GoalEntity goal = CreateGoal(GoalKind.gk_Flock);
                      goal.Duration = 25;
                  }
              } catch (Exception ex) {
                  Logger.Write("BeastBrain.prepareFlock(): " + ex.Message);
              }
          }
  
          private void PrepareStalk()
          {
              try {
                  NWCreature self = (NWCreature)fSelf;
                  AbstractMap map = self.CurrentField;
  
                  if (self.Entry.Sign.Equals("WildDog")) {
                      NWTile tile = (NWTile)map.GetTile(self.PosX, self.PosY);
  
                      if (tile.ScentTrail != null) {
                          int age = (int)tile.ScentAge;
                          int mx = self.PosX;
                          int my = self.PosY;
  
                          for (int y = self.PosY - 1; y <= self.PosY + 1; y++) {
                              for (int x = self.PosX - 1; x <= self.PosX + 1; x++) {
                                  tile = ((NWTile)map.GetTile(x, y));
                                  if (age < (int)tile.ScentAge) {
                                      age = (int)tile.ScentAge;
                                      mx = x;
                                      my = y;
                                  }
                              }
                          }
  
                          StalkGoal goal = (StalkGoal)FindGoalByKind(GoalKind.gk_Stalk);
                          if (goal == null) {
                              goal = ((StalkGoal)CreateGoal(GoalKind.gk_Stalk));
                          }
                          goal.Position = new ExtPoint(mx, my);
                          goal.Duration = 2;
                      }
                  }
              } catch (Exception ex) {
                  Logger.Write("BeastBrain.prepareStalk(): " + ex.Message);
              }
          }
  
          private void PrepareTravel()
          {
              try {
                  NWCreature self = (NWCreature)fSelf;
  
                  if (FindGoalByKind(GoalKind.gk_Travel) == null) {
                      ExtPoint res = self.GetNearestPlace(self.Survey, true);
                      if (!res.IsEmpty) {
                          TravelGoal goal = (TravelGoal)CreateGoal(GoalKind.gk_Travel);
                          goal.Position = res;
                          goal.Duration = 25;
                      }
                  }
              } catch (Exception ex) {
                  Logger.Write("BeastBrain.prepareTravel(): " + ex.Message);
              }
          }
  
          protected virtual void PrepareChase(CreatureEntity enemy, AttackRisk risk, bool canMove)
          {
              EnemyChaseGoal chaseGoal = (EnemyChaseGoal)FindGoalByKind(GoalKind.gk_EnemyChase);
              if (chaseGoal == null || !chaseGoal.Enemy.Equals(enemy)) {
                  chaseGoal = ((EnemyChaseGoal)CreateGoal(GoalKind.gk_EnemyChase));
                  chaseGoal.Enemy = enemy;
                  chaseGoal.Risk = risk;
                  chaseGoal.CanMove = canMove;
              }
          }
  
          protected virtual void PrepareEvade(CreatureEntity enemy, AttackRisk risk, bool canMove)
          {
              EnemyEvadeGoal evadeGoal = (EnemyEvadeGoal)FindGoalByKind(GoalKind.gk_EnemyEvade);
              if (evadeGoal == null || !evadeGoal.Enemy.Equals(enemy)) {
                  evadeGoal = ((EnemyEvadeGoal)CreateGoal(GoalKind.gk_EnemyEvade));
                  evadeGoal.Enemy = enemy;
                  evadeGoal.Risk = risk;
                  evadeGoal.CanMove = canMove;
              }
          }
  
          private void PrepareEscort()
          {
              NWCreature self = (NWCreature)fSelf;
              EscortGoal goal = (EscortGoal)FindGoalByKind(GoalKind.gk_Escort);
              if (goal != null) {
                  if (goal.NotParty) {
                      goal.Position = self.GetNearestPlace(goal.Leader.Location, 3, true);
                  } else {
                      LeaderBrain leaderBrain = (LeaderBrain)goal.Leader.Brain;
                      goal.Position = leaderBrain.GetMemberPosition(self);
                  }
              }
          }
  
          protected override void EvaluateGoal(GoalEntity goal)
          {
              switch (goal.Kind) {
                  case GoalKind.gk_Unknown:
                      goal.Value = 0f;
                      break;
  
                  case GoalKind.gk_Travel:
                      goal.Value = 0.225f;
                      break;
  
                  case GoalKind.gk_PointGuard:
                      {
                          int dist = MathHelper.Distance(fSelf.Location, ((PointGuardGoal)goal).Position);
                          goal.Value = (0.2f + dist / 10.0f);
                          break;
                      }
  
                  case GoalKind.gk_EnemyChase:
                      goal.Value = 0.6f;
                      break;
  
                  case GoalKind.gk_EnemyEvade:
                      goal.Value = 0.75f;
                      if (Flock && !IsLeader) {
                          goal.Value = ((0.75f - 0.1f * Kinsfolks.Count));
                      }
                      break;
  
                  case GoalKind.gk_Friend:
                      goal.Value = 0.27f;
                      break;
  
                  case GoalKind.gk_AreaGuard:
                      goal.Value = 0.2f;
                      break;
  
                  case GoalKind.gk_Escort:
                      {
                          int dist = MathHelper.Distance(fSelf.Location, ((EscortGoal)goal).Position);
                          goal.Value = ((0.3f + dist / 20.0f));
                          break;
                      }
  
                  case GoalKind.gk_Flock:
                      goal.Value = 0.22f;
                      if (Flock && !IsLeader) {
                          goal.Value = ((0.22f + 0.01f * Kinsfolks.Count));
                      }
                      break;
  
                  case GoalKind.gk_Stalk:
                      goal.Value = 0.55f;
                      break;
              }
          }
  
          protected override GoalEntity CreateGoalEx(int goalKind)
          {
              GoalEntity result = null;
  
              switch (goalKind) {
                  case GoalKind.gk_Travel:
  
gt:                     result = new TravelGoal(this);