< Summary

Information
Class: Safarimacik.Model.GameModel<T>
Assembly: Safarimacik
File(s): /builds/szofttech-ab-2025/group-03/safarimacik/model/GameModel.cs
Line coverage
97%
Covered lines: 663
Uncovered lines: 18
Coverable lines: 681
Total lines: 1077
Line coverage: 97.3%
Branch coverage
90%
Covered branches: 327
Total branches: 362
Branch coverage: 90.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_EntryFee()100%11100%
set_EntryFee(...)100%44100%
get_TouristsLastDay()100%11100%
set_TouristsLastDay(...)100%22100%
get_TouristsThisDay()100%11100%
set_TouristsThisDay(...)100%22100%
get_FreeTourists()100%11100%
get_TouristSpawnRate()100%11100%
get_HerbivoreCount()100%11100%
get_PredatorCount()100%11100%
get_RangerCount()100%11100%
get_JeepsMax()100%11100%
set_JeepsMax(...)100%22100%
get_JeepsOut()100%11100%
set_JeepsOut(...)100%22100%
get_Money()100%11100%
set_Money(...)100%22100%
get_JeepsAvailable()100%11100%
get_RemainingTicksOfThisDay()100%11100%
get_TotalDaysPassed()100%11100%
set_TotalDaysPassed(...)100%22100%
get_WinningDaysPassed()100%11100%
set_WinningDaysPassed(...)100%22100%
get_WinningDaysWinCon()100%11100%
get_TouristsWinCon()100%11100%
get_AnimalWinCon()100%11100%
get_MoneyWinCon()100%11100%
get_TableSize()100%11100%
get_Speed()100%11100%
set_Speed(...)100%1010100%
get_IsPaused()100%11100%
get_Difficulty()100%11100%
get_IsPlacable()100%11100%
.ctor()100%11100%
NewGame()100%11100%
NewGame(...)100%22100%
Pause()100%11100%
Resume()100%11100%
Purchase(...)79.17%2424100%
GetTile(...)100%11100%
AddAnimal(...)75%44100%
AddRanger(...)100%11100%
RemoveAnimal(...)100%22100%
SendJeep(...)100%22100%
SendJeepBack()100%44100%
ChangeTile(...)100%22100%
AddPlant(...)83.33%66100%
Placable(...)72.92%4848100%
IsWinning()100%66100%
HandleLoseGame()87.5%88100%
HandleWinGame()100%44100%
ProgressDay()100%22100%
AdjustTimerSpeed()50%3250%
RegeneratePlants()100%22100%
SpawnTourists()100%22100%
AnimalInit()100%44100%
PlantInit()100%1212100%
SetHerdPoints()100%22100%
PayRangers()100%11100%
HandleAnimals()100%11100%
HandleIdleAnimal<T>(...)100%1414100%
WalkToHerd(...)100%22100%
FindConsumables(...)100%88100%
FindMatePartner<T>(...)100%11100%
HandleWalkingAnimal(...)100%1616100%
HandleHungryHerbivore(...)100%66100%
HandleHungryPredator(...)100%22100%
HandleThirstyAnimal(...)100%22100%
ArePointsNear(...)100%22100%
ShouldSavePlant(...)75%44100%
SaveHerbivoreResources(...)100%1212100%
SavePredatorResources(...)100%88100%
SaveResources(...)100%22100%
HandleConsumingAnimal(...)100%88100%
HandleJeeps()93.75%323293.18%
HandleRangers()100%8887.5%
HandleIdleRanger(...)100%44100%
HandleWalkingRanger(...)50%15853.33%
HandleShootingRanger()75%4490.91%
Tick(...)100%11100%
OnJeepFinished(...)100%22100%
OnGameOver(...)50%22100%
OnJeepStart(...)50%22100%
OnJeepFinished()50%22100%
OnAnimalAdded(...)100%22100%
OnRangerAdded(...)100%22100%
OnPlantStateChanged(...)100%22100%
OnPropertyChanged(...)100%22100%
Dispose(...)75%88100%
Dispose()100%11100%

File(s)

/builds/szofttech-ab-2025/group-03/safarimacik/model/GameModel.cs

#LineLine coverage
 1using System.ComponentModel;
 2using System.Runtime.CompilerServices;
 3using Godot;
 4
 5
 6namespace Safarimacik.Model;
 7
 8
 9/// <summary>
 10/// Simulates a game of Safarimacik. Handles the input of the player, start and end of a game, and supplies several even
 11/// </summary>
 12public class GameModel : INotifyPropertyChanged, IDisposable {
 13  #region Constants
 14
 15  public const int TicksADay = 120;
 16  public const float BalanceTouristSpawn = 10f;
 17  public const int JeepRange = 10;
 18  public const int BountyForPredators = 100;
 19
 20  #endregion
 21
 22  #region Fields
 23
 24  private Difficulty _difficulty;
 25  private bool _calledGameOver;
 26  private int _money;
 27  private int _entryFee;
 28  private int _freeTourists;
 29  private int _touristsThisDay;
 30  private int _touristsLastDay;
 31  private double _nextTouristCounter; // incremented by spawnrate every tick, whenever it reaches 1, a new free tourist 
 32  private double _nextInterval;
 33  private float _avgAnimalSeen;
 34  private int _speed;
 35  private int _remainingTicksOfThisDay;
 36  private int _totalDaysPassed;
 37  private int _winningDaysPassed;
 38  private int _winningDaysWinCon;
 39  private int _touristsWinCon;
 40  private int _animalWinCon;
 41  private int _moneyWinCon;
 142  private (int, int) _tableSize = (128, 72);
 43  private int _jeepsMax;
 44  private int _jeepsOut;
 45  private int _ticksSinceJeep;
 46  private Board _board;
 47  private ITimerAdapter _timer;
 48  private IRandomGenerator _rng;
 49  private List<Herbivore> _herbivores;
 50  private List<Predator> _predators;
 51  private List<Ranger> _rangers;
 52  private List<Plant> _plants;
 53  private List<Jeep> _jeeps;
 54  private List<Jeep> _jeepsToSend;
 55  private bool _disposedValue;
 56  private Dictionary<Type, Vector2I> _herdPoints;
 57
 58  #endregion
 59
 60  #region Events
 61
 62  /// <summary>Occurs when the game is finished</summary>
 63  public event EventHandler<GameOverEventArgs>? GameOver;
 64  /// <summary>Occurs when a jeep is sent from anywhere</summary>
 65  public event EventHandler<JeepEventArgs>? JeepStart;
 66  /// <summary>Occurs when a jeep reaches its goal</summary>
 67  public event EventHandler<EventArgs>? JeepFinish;
 68  /// <summary>Occurs when an animal is placed or born</summary>
 69  public event EventHandler<AnimalEventArgs>? AnimalAdded;
 70  /// <summary>Occurs when a ranger is placed</summary>
 71  public event EventHandler<RangerEventArgs>? RangerAdded;
 72  /// <summary>Occurs when a plant is consumed or regrown</summary>
 73  public event EventHandler<PlantEventArgs>? PlantStateChanged;
 74  /// <summary>Occurs when property value that is important for player display is updated</summary>
 75  public event PropertyChangedEventHandler? PropertyChanged;
 76
 77  #endregion
 78
 79  #region Properties
 80
 81  public int EntryFee {
 182    get => _entryFee;
 183    set {
 184      if (_entryFee != value && value > 0) {
 185        _entryFee = value;
 186        OnPropertyChanged();
 187      }
 188    }
 89  }
 90  public int TouristsLastDay {
 191    get => _touristsLastDay;
 192    set {
 193      if (_touristsLastDay != value) {
 194        _touristsLastDay = value;
 195        OnPropertyChanged();
 196      }
 197    }
 98  }
 99  public int TouristsThisDay {
 1100    get => _touristsThisDay;
 1101    set {
 1102      if (_touristsThisDay != value) {
 1103        _touristsThisDay = value;
 1104        OnPropertyChanged();
 1105      }
 1106    }
 107  }
 1108  public int FreeTourists => _freeTourists;
 1109  public float TouristSpawnRate => BalanceTouristSpawn * _avgAnimalSeen / EntryFee + _rng.Rand(0.0001f, 0.1f);
 1110  public int HerbivoreCount => _herbivores.Count;
 1111  public int PredatorCount => _predators.Count;
 1112  public int RangerCount => _rangers.Count;
 113  public int JeepsMax {
 1114    get => _jeepsMax;
 1115    set {
 1116      if (_jeepsMax != value) {
 1117        _jeepsMax = value;
 1118        OnPropertyChanged();
 1119      }
 1120    }
 121  }
 122  public int JeepsOut {
 1123    get => _jeepsOut;
 1124    set {
 1125      if (_jeepsOut != value) {
 1126        _jeepsOut = value;
 1127        OnPropertyChanged();
 1128      }
 1129    }
 130  }
 131  public int Money {
 1132    get => _money;
 1133    set {
 1134      if (_money != value) {
 1135        _money = value;
 1136        OnPropertyChanged();
 1137      }
 1138    }
 139  }
 1140  public int JeepsAvailable => _jeepsMax - _jeepsOut;
 1141  public int RemainingTicksOfThisDay => _remainingTicksOfThisDay;
 142  public int TotalDaysPassed {
 1143    get => _totalDaysPassed;
 1144    set {
 1145      if (_totalDaysPassed != value) {
 1146        _totalDaysPassed = value;
 1147        OnPropertyChanged();
 1148      }
 1149    }
 150  }
 151  public int WinningDaysPassed {
 1152    get => _winningDaysPassed;
 1153    set {
 1154      if (_winningDaysPassed != value) {
 1155        _winningDaysPassed = value;
 1156        OnPropertyChanged();
 1157      }
 1158    }
 159  }
 1160  public int WinningDaysWinCon => _winningDaysWinCon;
 1161  public int TouristsWinCon => _touristsWinCon;
 1162  public int AnimalWinCon => _animalWinCon;
 1163  public int MoneyWinCon => _moneyWinCon;
 1164  public (int, int) TableSize { get { return _tableSize; } }
 165  public int Speed {
 1166    get => _speed;
 1167    set {
 1168      if (value == 0) {
 1169        _speed = value;
 1170        _timer.Stop();
 1171      } else if (value == 1 || value == 2 || value == 5) {
 1172        _speed = value;
 1173        if (!_timer.Enabled) {
 1174          _nextInterval = 0;
 1175          _timer.Interval = 1000d / value;
 1176          _timer.Start();
 1177        } else {
 1178          _nextInterval = 1000d / value;
 1179        }
 1180      }
 1181    }
 182  }
 1183  public bool IsPaused => !_timer.Enabled;
 1184  public Difficulty Difficulty => _difficulty;
 1185  public Func<Purchasable, Vector2I, bool> IsPlacable => Placable;
 186
 187  #endregion
 188
 189  #region Constructors
 190
 1191  public GameModel() : this(new TimerAdapter(), new RandomGenerator()) { }
 192
 1193  public GameModel(ITimerAdapter timer, IRandomGenerator rng) {
 1194    _timer = timer;
 1195    _timer.Interval = 1000.0;
 1196    _timer.Elapsed += Tick;
 1197    _rng = rng;
 198
 1199    _herbivores = [];
 1200    _predators = [];
 1201    _rangers = [];
 1202    _plants = [];
 1203    _jeeps = [];
 1204    _jeepsToSend = [];
 1205    _herdPoints = [];
 1206    _board = new Board(16, 9, _rng);
 1207  }
 208
 209  #endregion
 210
 211  #region Public methods
 212
 213  /// <summary>
 214  /// Starts new game with easy difficulty.
 215  /// </summary>
 1216  public void NewGame() {
 1217    NewGame(Difficulty.Easy);
 1218  }
 219
 220  /// <summary>
 221  /// Starts new game with given difficulty.
 222  /// </summary>
 223  /// <param name="diff">Difficulty of new game.</param>
 1224  public void NewGame(Difficulty diff, Board? board = null) {
 1225    _herbivores = [];
 1226    _predators = [];
 1227    _rangers = [];
 1228    _plants = [];
 1229    _jeeps = [];
 1230    _jeepsToSend = [];
 231
 1232    _ticksSinceJeep = 0;
 1233    EntryFee = 50;
 1234    _avgAnimalSeen = 1;
 1235    _freeTourists = 0;
 1236    TouristsLastDay = 0;
 1237    TouristsThisDay = 0;
 1238    JeepsMax = 0;
 1239    JeepsOut = 0;
 1240    _difficulty = diff;
 1241    _calledGameOver = false;
 1242    _remainingTicksOfThisDay = TicksADay;
 1243    TotalDaysPassed = 0;
 1244    WinningDaysPassed = 0;
 1245    _winningDaysWinCon = _difficulty.Value.Item1;
 1246    _touristsWinCon = _difficulty.Value.Item2;
 1247    _animalWinCon = _difficulty.Value.Item3;
 1248    _moneyWinCon = _difficulty.Value.Item4;
 1249    Money = (2 * Road.Price * _tableSize.Item1 + Sheep.Price * 20 + Jeep.Price * 5) / 100 * 100;
 1250    Speed = 1;
 1251    _board = board ?? new Board(_tableSize.Item1, _tableSize.Item2, _rng);
 1252    _herdPoints = new Dictionary<Type, Vector2I> {
 1253      { typeof(Wolf), new (0, 0) },
 1254      { typeof(Fox), new (0, 0) },
 1255      { typeof(Sheep), new (0, 0) },
 1256      { typeof(Cow), new (0, 0) }
 1257    };
 1258    AnimalInit();
 1259    PlantInit();
 1260  }
 261
 1262  public void Pause() {
 1263    _timer.Stop();
 1264  }
 265
 1266  public void Resume() {
 1267    _timer.Start();
 1268  }
 269
 270  /// <summary>
 271  /// Purchases an object and places it at the given position, if funds are suffictient.
 272  /// </summary>
 273  /// <param name="purchaseId">Id of the item to purchase</param>
 274  /// <param name="position">Position of the item to purchase</param>
 275  /// <returns>Null if funds are insufficient, the purchased object otherwise</returns>
 1276  public Purchasable? Purchase(int purchaseId, Vector2I position) {
 1277    if (position.X < 0 || position.Y < 0 || position.X >= _tableSize.Item1 || position.Y >= _tableSize.Item2) return nul
 1278    Purchasable? item = PurchasableFactory.Purchase(purchaseId, Money, position * 16);
 1279    if (item != null) {
 1280      switch (item) {
 281        case Tile tile:
 1282          if (!ChangeTile(tile, position.X, position.Y)) return null;
 1283          break;
 284        case Animal animal:
 1285          AddAnimal(animal);
 1286          break;
 287        case Plant plant:
 1288          if (!AddPlant(plant, position.X, position.Y)) return null;
 1289          break;
 290        case Ranger ranger:
 1291          AddRanger(ranger);
 1292          break;
 293        case Jeep:
 1294          JeepsMax++;
 1295          break;
 296      }
 1297      Money -= item.Price;
 1298    }
 1299    return item;
 1300  }
 301
 1302  public Tile GetTile(int x, int y) {
 1303    return _board[x, y];
 1304  }
 305
 306  /// <summary>
 307  /// Adds an animal to the list, subscribes to its events.
 308  /// </summary>
 309  /// <param name="animal">The animal being added</param>
 1310  public void AddAnimal(Animal animal) {
 1311    animal.OffspringSpawned += AddAnimal;
 1312    animal.LifeEnded += () => RemoveAnimal(animal);
 1313    animal.TileSpeed = new Func<Vector2, float>(p => {
 1314      return _board[(int)Math.Round(p.X / 16), (int)Math.Round(p.Y / 16)].Speed;
 1315    });
 1316    switch (animal) {
 317      case Herbivore herbivore:
 1318        _herbivores.Add(herbivore);
 1319        OnPropertyChanged(nameof(HerbivoreCount));
 1320        break;
 321      case Predator predator:
 1322        _predators.Add(predator);
 1323        OnPropertyChanged(nameof(PredatorCount));
 1324        break;
 325    }
 1326    OnAnimalAdded(animal);
 1327  }
 328
 1329  public void AddRanger(Ranger ranger) {
 1330    _rangers.Add(ranger);
 1331    ranger.TileSpeed = new Func<Vector2, float>(p => {
 0332      return _board[(int)Math.Round(p.X / 16), (int)Math.Round(p.Y / 16)].Speed;
 0333    });
 1334    OnRangerAdded(ranger);
 1335  }
 336
 1337  public void RemoveAnimal(Animal animal) {
 1338    if (animal is Herbivore herbivore) {
 1339      _herbivores.Remove(herbivore);
 1340      OnPropertyChanged(nameof(HerbivoreCount));
 1341    } else {
 1342      _predators.Remove((Predator)animal);
 1343      OnPropertyChanged(nameof(PredatorCount));
 1344    }
 1345  }
 346
 347  /// <summary>
 348  /// Sends a jeep from the start of the road to the end.
 349  /// </summary>
 350  /// <param name="tourists">Number of tourists boarding the jeep</param>
 1351  public void SendJeep(int tourists) {
 1352    List<Vector2>? path = _board.FindJeepPath(new Vector2I(0, _tableSize.Item2 / 2), new Vector2I(_tableSize.Item1 - 1, 
 353
 1354    if (path != null) {
 1355      Jeep jeep = new Jeep(path, new Vector2(0, _tableSize.Item2 / 2) * 16, tourists);
 1356      _jeeps.Add(jeep);
 1357      jeep.Finished += OnJeepFinished;
 1358      JeepsOut++;
 1359      Money += tourists * EntryFee;
 1360      TouristsThisDay += tourists;
 1361      OnJeepStart(jeep);
 1362      _ticksSinceJeep = 0;
 1363      _freeTourists -= tourists;
 1364    }
 1365  }
 366
 367  /// <summary>
 368  /// Sends a jeep from the end of the road to the start after 1-5 seconds.
 369  /// </summary>
 1370  public async Task SendJeepBack() {
 1371    IRandomGenerator r = new RandomGenerator();
 1372    List<Vector2>? path = _board.FindJeepPath(new Vector2I(_tableSize.Item1 - 1, _tableSize.Item2 / 2), new Vector2I(0, 
 373
 1374    if (path != null) {
 1375      Jeep jeep = new Jeep(path, new Vector2(_tableSize.Item1 - 1, _tableSize.Item2 / 2) * 16, 0);
 376
 1377      await Task.Delay(1000 * (int)r.Rand(1, 5));
 1378      if (_jeepsMax != 0) {
 1379        _jeepsToSend.Add(jeep);
 1380      }
 1381    }
 1382  }
 383
 384  #endregion
 385
 386  #region Private methods
 387
 1388  private bool ChangeTile(Tile tile, int x, int y) {
 1389    if (!Placable((Purchasable)tile, new Vector2I(x, y))) {
 1390      return false;
 391    }
 392
 1393    _board.ChangeTile(x, y, tile);
 1394    return true;
 1395  }
 396
 397  /// <summary>
 398  /// Places a plant to the grass tile on the given position, subscribes to its events if needed.
 399  /// </summary>
 400  /// <param name="plant">Plant to place</param>
 401  /// <param name="x">X coordinate of the plant</param>
 402  /// <param name="y">Y coordinate of the plant</param>
 403  /// <returns>True if the plant was placed</returns>
 1404  private bool AddPlant(Plant plant, int x, int y) {
 1405    if (_board[x, y] is not Grass) return false;
 1406    if (_board[x, y].Plant != null) return false;
 1407    if (plant is RegeneratingPlant regeneratingPlant) {
 1408      regeneratingPlant.StateChanged += () => OnPlantStateChanged(regeneratingPlant, new Vector2I(x, y));
 1409    }
 1410    _board[x, y].Plant = plant;
 1411    _plants.Add(plant);
 1412    return true;
 1413  }
 414
 415  /// <summary>
 416  /// Determines if the given object is allowed to be placed at the given position.
 417  /// </summary>
 418  /// <param name="item">Item to be checked</param>
 419  /// <param name="position">Position of the check</param>
 420  /// <returns></returns>
 1421  private bool Placable(Purchasable item, Vector2I position) {
 1422    if (position.X < 0 || position.Y < 0 || position.X >= _tableSize.Item1 || position.Y >= _tableSize.Item2) return fal
 1423    if (item is Tile tile) {
 1424      if (_board[position.X, position.Y] is not Grass) return false;
 1425      if (_board[position.X, position.Y].Plant != null) return false;
 1426      if (tile is Road && !((position.X > 0 && _board[Math.Max(position.X - 1, 0), position.Y] is Road or Gateway) ||
 1427        (position.X < _tableSize.Item1 - 1 && _board[Math.Min(position.X + 1, _tableSize.Item1 - 1), position.Y] is Road
 1428        (position.Y < _tableSize.Item2 - 1 && _board[position.X, Math.Min(position.Y + 1, _tableSize.Item2 - 1)] is Road
 1429        (position.Y > 0 && _board[position.X, Math.Max(position.Y - 1, 0)] is Road or Gateway))) return false;
 1430    }
 431
 1432    return true;
 1433  }
 434
 1435  private bool IsWinning() {
 1436    return Money >= MoneyWinCon && TouristsLastDay >= TouristsWinCon && _herbivores.Count >= AnimalWinCon && _predators.
 1437  }
 438
 439  /// <summary>
 440  /// Function that gets called at the end of every day. Ends game if lose conditions are met.
 441  /// </summary>
 1442  private void HandleLoseGame() {
 1443    if (Money <= 0 || (_herbivores.Count == 0 && _predators.Count == 0)) {
 1444      if (!_calledGameOver) {
 1445        _calledGameOver = true;
 1446        OnGameOver(false);
 1447      }
 1448    }
 1449  }
 450
 451  /// <summary>
 452  /// Function that gets called at the end of every day.
 453  /// </summary>
 454  /// If the win conditions are met, it increments the WinningDaysPassed counter.
 455  /// Checks if the WinningDaysPassed counter reaches the WinningDayWinCon, then the game ends.
 456  /// If the win conditions are not met, it resets the WinningDaysPassed counter.
 1457  private void HandleWinGame() {
 1458    if (IsWinning()) {
 1459      ++WinningDaysPassed;
 1460      if (WinningDaysPassed == WinningDaysWinCon) {
 1461        OnGameOver(true);
 1462      }
 1463    } else {
 1464      WinningDaysPassed = 0;
 1465    }
 1466  }
 467
 468  /// <summary>
 469  /// Gets called every tick, increments day counters if day passes.
 470  /// Calls HandleWinGame every new day.
 471  /// </summary>
 1472  private void ProgressDay() {
 1473    --_remainingTicksOfThisDay;
 1474    if (_remainingTicksOfThisDay == 0) {
 1475      _remainingTicksOfThisDay = TicksADay;
 1476      ++TotalDaysPassed;
 1477      TouristsLastDay = TouristsThisDay;
 1478      TouristsThisDay = 0;
 1479      _freeTourists = 0;
 1480      PayRangers();
 1481      HandleWinGame();
 1482      HandleLoseGame();
 1483      SetHerdPoints();
 1484    }
 1485  }
 486
 487  /// <summary>
 488  /// Gets called every tick.
 489  /// Adjusts timer's interval in a way that current tick can finish with old interval.
 490  /// </summary>
 1491  private void AdjustTimerSpeed() {
 1492    if (_nextInterval != 0) {
 0493      _timer.Interval = _nextInterval;
 0494      _nextInterval = 0;
 0495    }
 1496  }
 497
 498  /// <summary>
 499  /// Gets called every tick, decreases regenerating plants' cooldown by one.
 500  /// </summary>
 1501  private void RegeneratePlants() {
 1502    _plants.ForEach(plant => {
 1503      if (plant is RegeneratingPlant regenPlant) {
 1504        if (!regenPlant.IsRipe) {
 1505          regenPlant.DecreaseCurrentCooldown();
 1506        }
 1507      }
 1508    });
 1509  }
 510
 511  /// <summary>
 512  /// Gets called every tick, spawns free tourists according to their spawn rate.
 513  /// </summary>
 1514  private void SpawnTourists() {
 1515    _nextTouristCounter += TouristSpawnRate;
 1516    while (_nextTouristCounter >= 1) {
 1517      _nextTouristCounter--;
 1518      _freeTourists++;
 1519    }
 1520  }
 521
 522  /// <summary>
 523  /// Places animals on the board in their herd's territory.
 524  /// </summary>
 1525  private void AnimalInit() {
 1526    SetHerdPoints();
 1527    int ratio = _rng.RandI(3, 7);
 1528    for (int i = 0; i < ratio; i++) {
 1529      Sheep sheep = new(16 * (_herdPoints[typeof(Sheep)] + new Vector2(_rng.Rand(-1, 1), _rng.Rand(-1, 1))));
 1530      AddAnimal(sheep);
 1531      Wolf wolf = new(16 * (_herdPoints[typeof(Wolf)] + new Vector2(_rng.Rand(-1, 1), _rng.Rand(-1, 1))));
 1532      AddAnimal(wolf);
 1533    }
 1534    for (int i = ratio; i < 10; i++) {
 1535      Cow cow = new(16 * (_herdPoints[typeof(Cow)] + new Vector2(_rng.Rand(-1, 1), _rng.Rand(-1, 1))));
 1536      AddAnimal(cow);
 1537      Fox fox = new(16 * (_herdPoints[typeof(Fox)] + new Vector2(_rng.Rand(-1, 1), _rng.Rand(-1, 1))));
 1538      AddAnimal(fox);
 1539    }
 1540  }
 541
 542  /// <summary>
 543  /// Places plants on the board.
 544  /// </summary>
 1545  private void PlantInit() {
 1546    List<Vector2I> path = _board.FindPath(new Vector2I(0, _board.GetLength(1) / 2), new Vector2I(_board.GetLength(0) - 1
 1547    for (int i = 0; i < _board.GetLength(0); i++) {
 1548      for (int j = 0; j < _board.GetLength(1); j++) {
 1549        if (_board[i, j] is Grass && _rng.Rand(0, 1) < 0.01f && !path.Contains(new Vector2I(i, j))) {
 1550          var purchase = PurchasableFactory.Purchase(_rng.RandI(0, 2) + 4, int.MaxValue, new(0, 0));
 1551          if (purchase is Plant plant) {
 1552            AddPlant(plant, i, j);
 1553          }
 1554        }
 1555      }
 1556    }
 1557  }
 558
 559  /// <summary>
 560  /// Gets called every day, sets a new point for each herd to go to.
 561  /// </summary>
 1562  private void SetHerdPoints() {
 1563    foreach (var species in _herdPoints.Keys) {
 1564      _herdPoints[species] = new Vector2I(_rng.RandI(1, _board.GetLength(0) - 2), _rng.RandI(1, _board.GetLength(1) - 2)
 1565    }
 1566  }
 567
 568  /// <summary>
 569  /// Pay each ranger their wage at the end of each day.
 570  /// </summary>
 1571  private void PayRangers() {
 1572    _rangers.ForEach(ranger => {
 1573      _money -= Ranger.Wage;
 1574    });
 1575    Money = _money; // only update view once, instead of updating view for each ranger
 1576  }
 577
 578  /// <summary>
 579  /// Gets called every tick.
 580  /// Handles each animal according to their state.
 581  /// </summary>
 1582  private void HandleAnimals() {
 1583    List<Herbivore> herbivoresCopy = [.. _herbivores];
 1584    List<Predator> predatorsCopy = [.. _predators];
 585
 1586    herbivoresCopy.ForEach(herbivore => {
 1587      herbivore.State.Tick();
 1588      if (herbivore.State is IdleAnimalState) {
 1589        HandleIdleAnimal(herbivore, _herbivores);
 1590      } else if (herbivore.State is WalkingAnimalState) {
 1591        HandleWalkingAnimal(herbivore);
 1592      } else if (herbivore.State is ConsumingAnimalState) {
 1593        HandleConsumingAnimal(herbivore);
 1594      }
 1595    });
 596
 1597    predatorsCopy.ForEach(predator => {
 1598      predator.State.Tick();
 1599      if (predator.State is IdleAnimalState) {
 1600        HandleIdleAnimal(predator, _predators);
 1601      } else if (predator.State is WalkingAnimalState) {
 1602        HandleWalkingAnimal(predator);
 1603      } else if (predator.State is ConsumingAnimalState) {
 1604        HandleConsumingAnimal(predator);
 1605      }
 1606    });
 1607  }
 608
 609  /// <summary>
 610  /// Contains the logic an idle animal runs.
 611  /// </summary>
 612  /// There are four main things that can happen:
 613  /// 1. The animal is hungry and goes to look for food.
 614  /// 2. The animal decides to take a walk to its herd.
 615  /// 3. A male animal wants to mate.
 616  /// - For this a female animal from the same species that is not hungry nor thirsty is required to be nearby.
 617  /// 4. Otherwise the animal remains in idle state.
 618  /// <typeparam name="T">Class extending the Animal abstract class</typeparam>
 619  /// <param name="animal">The idle animal</param>
 620  /// <param name="animals">Collection of animals similar to the idle animal (e.g. herbivores or predators)</param>
 1621  private void HandleIdleAnimal<T>(Animal animal, List<T> animals) where T : Animal {
 1622    if (animal.IsHungry() || animal.IsThirsty()) { // if animal needs to eat/drink
 1623      FindConsumables(animal);
 1624    } else {
 1625      if (_rng.Rand(0, 1) < 0.5) { // if animal decides to walk
 1626        WalkToHerd(animal);
 1627      } else if (animal.Sex && animal.IsAdult() && _rng.Rand(0, 1) < 0.7) { // if adult male animal decides to mate
 1628        T? partner = FindMatePartner(animal, animals);
 1629        if (partner != null) {
 1630          animal.State = new MatingAnimalState(animal);
 1631          partner.State = new MatingAnimalState(partner);
 1632        }
 1633      }
 1634    }
 1635  }
 636
 637  /// <summary>
 638  /// Sets the given animal's state to walking state, with a destination around its herd's center
 639  /// </summary>
 640  /// <param name="animal"></param>
 1641  private void WalkToHerd(Animal animal) {
 1642    Vector2I target = _herdPoints[animal.GetType()] + new Vector2I(_rng.RandI(-1, 1), _rng.RandI(-1, 1));
 1643    List<Vector2> path = _board.FindPathR(animal.TilePosition, target, true);
 1644    if (path.Count > 0) {
 1645      animal.State = new WalkingAnimalState(animal, path);
 1646    }
 1647  }
 648
 649  /// <summary>
 650  /// Pathfind to the closest water/food source from memory.
 651  /// </summary>
 652  /// <param name="animal">The animal to send on its way</param>
 1653  private void FindConsumables(Animal animal) {
 1654    if (animal.IsThirsty()) {
 1655      try {
 1656        Vector2I target = animal.ClosestWater();
 1657        List<Vector2> path = _board.FindPathR(animal.TilePosition, target, true);
 1658        if (path.Count > 0) {
 1659          animal.State = new WalkingAnimalState(animal, path);
 1660        }
 1661        return;
 1662      } catch (InvalidOperationException) {
 663        // there is no water stored
 664        // IGNORED, because maybe it can go to a feeding place
 1665      }
 1666    }
 667
 1668    if (animal.IsHungry()) {
 1669      try {
 1670        Vector2I target = animal.ClosestFood();
 1671        List<Vector2> path = _board.FindPathR(animal.TilePosition, target, true);
 1672        if (path.Count > 0) {
 1673          animal.State = new WalkingAnimalState(animal, path);
 1674        }
 1675        return;
 1676      } catch (InvalidOperationException) {
 677        // no food (and water) stored, walk to herd maybe food(/water) will be discovered on the way
 1678      }
 1679    }
 680
 1681    WalkToHerd(animal);
 1682  }
 683
 684  /// <summary>
 685  /// Finds a not hungry nor thirsty adult female from the given species nearby the given location.
 686  /// </summary>
 687  /// <typeparam name="T">Class that extends the abstract class Animal</typeparam>
 688  /// <param name="animal">The male animal</param>
 689  /// <param name="animals">Collection of animals similar to the male animal (e.g. herbivores or predators)</param>
 690  /// <returns>Reference to the female animal, or null if non found</returns>
 1691  private T? FindMatePartner<T>(Animal animal, List<T> animals) where T : Animal {
 1692    return animals
 1693      .Where(partner =>
 1694        !partner.Sex && // is female
 1695        partner.GetType() == animal.GetType() && // is of the same species
 1696        partner.IsAdult() && // is adult
 1697        partner.State is IdleAnimalState && // is idle
 1698        !(partner.IsHungry() || partner.IsThirsty()) && // is not needy
 1699        ArePointsNear(animal.TilePosition, partner.TilePosition, 4) // is close enough
 1700      )
 1701      .MinBy(partner => partner.Position.DistanceTo(animal.Position));
 1702  }
 703
 704  /// <summary>
 705  /// Contains the logic a walking animal runs.
 706  /// </summary>
 707  /// If the animal has reached its destination:
 708  ///   - enter consuming state if animal is needy and resource is available in current position
 709  ///   - walk to another resource if animal is needy but resource isn't available in current position
 710  ///   - enter idle state otherwise
 711  /// <param name="animal">The walking animal</param>
 1712  private void HandleWalkingAnimal(Animal animal) {
 1713    SaveResources(animal);
 714
 1715    if (!((WalkingAnimalState)animal.State).HasReachedDestination()) {
 1716      return;
 717    }
 718
 1719    if (!(animal.IsHungry() || animal.IsThirsty())) {
 1720      animal.State = new IdleAnimalState(animal);
 1721      return;
 722    }
 723
 1724    if (animal.IsHungry()) { // animal is hungry
 1725      if (animal is Herbivore herbivore) {
 1726        HandleHungryHerbivore(herbivore);
 1727      } else {
 1728        HandleHungryPredator((Predator)animal);
 1729      }
 730
 731      // if animal hasn't started consuming yet, there is no food on this tile
 1732      if (animal.State is WalkingAnimalState) {
 1733        animal.RemoveFoodFromMemory(animal.TilePosition);
 1734      } else {
 1735        return;
 736      }
 1737    }
 738
 1739    if (animal.IsThirsty()) { // animal is thirsty
 1740      HandleThirstyAnimal(animal);
 1741    }
 742
 743    // if animal hasn't started consuming yet, there is no water (and no food) on this tile
 744    // but since water tile cannot be removed (as in the player has no means to do that)
 745    // we just need to find new resource
 1746    if (animal.State is WalkingAnimalState) {
 1747      FindConsumables(animal);
 1748    }
 1749  }
 750
 1751  private void HandleHungryHerbivore(Herbivore herbivore) {
 752    // hungry animal is a herbivore
 753    // look for plant on this tile
 1754    Plant? plant = _board[herbivore.TilePosition.X, herbivore.TilePosition.Y].Plant;
 1755    if (plant != null && !(plant is RegeneratingPlant regenPlant && !regenPlant.IsRipe)) {
 1756      herbivore.State = new ConsumingAnimalState(herbivore, false, plant, null);
 1757    }
 1758  }
 759
 1760  private void HandleHungryPredator(Predator predator) {
 761    // hungry animal is a predator
 762    // look for herbivore prey on this tile
 1763    Herbivore? prey = _herbivores.FirstOrDefault(herb => herb.TilePosition == predator.TilePosition);
 1764    if (prey != null) {
 1765      predator.State = new ConsumingAnimalState(predator, false, null, prey);
 1766    }
 1767  }
 768
 1769  private void HandleThirstyAnimal(Animal animal) {
 770    // look for water on this tile
 1771    if (_board[animal.TilePosition.X, animal.TilePosition.Y] is Water) {
 1772      animal.State = new ConsumingAnimalState(animal, true, null, null);
 1773    }
 1774  }
 775
 1776  private bool ArePointsNear(Vector2I a, Vector2I b, int range) {
 1777    return Math.Abs(a.X - b.X) <= range && Math.Abs(a.Y - b.Y) <= range;
 1778  }
 779
 780  /// <summary>
 781  /// Determines whether a plant should be saved into memory by an animal or not.
 782  /// Returns true if the plant is a non-regenerating or a ripe regenerating plant; otherwise, false.
 783  /// </summary>
 1784  private bool ShouldSavePlant(Plant plant) {
 1785    return plant is Weed || (plant is RegeneratingPlant regenPlant && regenPlant.IsRipe);
 1786  }
 787
 788  /// <summary>
 789  /// Saves nearby water and plant locations to a herbivore's memory.
 790  /// </summary>
 791  /// <param name="herbivore">Herbivore looking around</param>
 792  /// <param name="range">The view range of the tile the herbivore is standing on</param>
 1793  private void SaveHerbivoreResources(Herbivore herbivore, int range) {
 1794    for (int i = 0; i < _board.GetLength(0); i++) {
 1795      for (int j = 0; j < _board.GetLength(1); j++) {
 1796        if (!ArePointsNear(new Vector2I(i, j), herbivore.TilePosition, range)) {
 1797          continue;
 798        }
 799
 1800        if (_board[i, j] is Water) {
 1801          herbivore.SaveWater(new Vector2I(i, j));
 1802        }
 803
 1804        if (_board[i, j].Plant is Plant plant && ShouldSavePlant(plant)) {
 1805          herbivore.SaveFood(new Vector2I(i, j));
 1806        }
 1807      }
 1808    }
 1809  }
 810
 811  /// <summary>
 812  /// Saves nearby water and herbivore locations to a predator's memory.
 813  /// </summary>
 814  /// <param name="predator">Predator looking around</param>
 815  /// <param name="range">The view range of the tile the predator is standing on</param>
 1816  private void SavePredatorResources(Predator predator, int range) {
 1817    for (int i = 0; i < _board.GetLength(0); i++) {
 1818      for (int j = 0; j < _board.GetLength(1); j++) {
 1819        if (!ArePointsNear(new Vector2I(i, j), predator.TilePosition, range)) {
 1820          continue;
 821        }
 822
 1823        if (_board[i, j] is Water) {
 1824          predator.SaveWater(new Vector2I(i, j));
 1825        }
 1826      }
 1827    }
 828
 1829    _herbivores.ForEach(herbivore => {
 1830      if (ArePointsNear(herbivore.TilePosition, predator.TilePosition, range)) {
 1831        predator.SaveFood(herbivore.TilePosition);
 1832      }
 1833    });
 1834  }
 835
 836  /// <summary>
 837  /// Saves eating/drinking places in the provided animal's memory.
 838  /// </summary>
 839  /// For herbivores it scans the board for water and plant locations, saves the top-left corner of the tiles.
 840  /// For predators it scans the board for water locations, saves the top-left corner of the tiles, scans for nearby her
 841  /// <param name="animal">Animal that is looking around</param>
 1842  private void SaveResources(Animal animal) {
 1843    int range = _board[animal.TilePosition.X, animal.TilePosition.Y].ViewRange;
 1844    if (animal is Herbivore herbivore) {
 1845      SaveHerbivoreResources(herbivore, range);
 1846    } else {
 1847      SavePredatorResources((Predator)animal, range);
 1848    }
 1849  }
 850
 851  /// <summary>
 852  /// Contains logic a consuming animal runs.
 853  /// </summary>
 854  /// Cleans up the non-regenerating plant from the board after animal has consumed it (if needed).
 855  /// If the animal isn't hungry nor thirsty anymore, it enters a sleeping state for a random time.
 856  /// Else it pathfinds to another nearby resource.
 857  /// <param name="animal">The consuming animal</param>
 1858  private void HandleConsumingAnimal(Animal animal) {
 859    // remove non-regenerating plant after consuming it
 1860    if (animal is Herbivore && ((ConsumingAnimalState)animal.State).Plant is Weed) {
 1861      _board[animal.TilePosition.X, animal.TilePosition.Y].Plant = null;
 1862      OnPlantStateChanged(null, animal.TilePosition);
 1863    }
 864
 1865    if (!animal.IsHungry() && !animal.IsThirsty()) {
 1866      animal.State = new SleepingAnimalState(animal, _rng.RandI(0, 20));
 1867    } else {
 1868      FindConsumables(animal);
 1869    }
 1870  }
 871
 872  /// <summary>
 873  /// Gets called every tick.
 874  /// Sends jeeps out and back, when conditions are met.
 875  /// Moves jeeps already on the board.
 876  /// </summary>
 1877  private void HandleJeeps() {
 1878    if (_freeTourists >= 4 && JeepsAvailable > 0) {
 1879      SendJeep(4);
 1880    } else if (_ticksSinceJeep >= 60 && _freeTourists >= 1 && JeepsAvailable > 0) {
 0881      SendJeep(_freeTourists);
 1882    } else {
 1883      _ticksSinceJeep++;
 1884    }
 885
 1886    if (_jeeps.Count != 0) {
 1887      List<Herbivore> herbivoresCopy = new(_herbivores);
 1888      List<Predator> predatorsCopy = new(_predators);
 1889      foreach (Jeep jeep in _jeeps) {
 1890        foreach (Animal animal in herbivoresCopy) {
 1891          if (jeep.Position.DistanceTo(animal.Position) < JeepRange * 16) {
 1892            jeep.AnimalSeen(animal);
 1893          }
 1894        }
 1895        foreach (Animal animal in predatorsCopy) {
 1896          if (jeep.Position.DistanceTo(animal.Position) < JeepRange * 16) {
 0897            jeep.AnimalSeen(animal);
 0898          }
 1899        }
 1900      }
 1901    }
 902
 1903    for (int i = _jeeps.Count - 1; i >= 0; --i) {
 1904      if (_jeeps[i].Move()) {
 1905        Vector2 pos = _jeeps[i].Position;
 1906        _jeeps.RemoveAt(i);
 1907        if (pos == new Vector2(_tableSize.Item1 - 1, _tableSize.Item2 / 2) * 16) {
 1908          _ = SendJeepBack();
 1909        } else {
 1910          JeepsOut--;
 1911        }
 1912        OnJeepFinished();
 1913      }
 1914    }
 915
 1916    if (_jeepsToSend.Count > 0) {
 1917      for (int i = _jeepsToSend.Count - 1; i >= 0; i--) {
 1918        _jeeps.Add(_jeepsToSend[i]);
 1919        OnJeepStart(_jeepsToSend[i]);
 1920        _jeepsToSend.RemoveAt(i);
 1921      }
 1922    }
 1923  }
 924
 925  /// <summary>
 926  /// Gets called every tick. Moves every ranger closer to their target.
 927  /// </summary>
 928  /// Kills targets that are reached in this tick.
 929  /// Gives the player money for each animal killed.
 1930  private void HandleRangers() {
 1931    try {
 1932      foreach (Ranger ranger in _rangers) {
 1933        ranger.State.Tick();
 1934        if (ranger.State is IdleRangerState) {
 1935          HandleIdleRanger(ranger);
 1936        } else if (ranger.State is WalkingRangerState) {
 1937          HandleWalkingRanger(ranger);
 1938        } else if (ranger.State is ShootingRangerState) {
 1939          _ = HandleShootingRanger(ranger);
 1940        }
 1941      }
 1942    } catch (Exception ex) {
 0943      GD.Print(ex.Message + ex.Source);
 0944    }
 1945  }
 946
 1947  private void HandleIdleRanger(Ranger ranger) {
 1948    if (ranger.Target is not null) {
 1949      var path = _board.FindPathR(ranger.TilePosition, ranger.Target.TilePosition, true);
 1950      if (path.Count > 0) {
 1951        ranger.State = new WalkingRangerState(ranger, path);
 1952      }
 953      // else kill animal ??? depends on when and why findpathr returns empty path
 1954    }
 1955  }
 956
 1957  private void HandleWalkingRanger(Ranger ranger) {
 1958    if (ranger.Target is null) {
 0959      ranger.State = new IdleRangerState(ranger);
 0960      return;
 961    }
 962
 963    // walk to target if target moved
 1964    var walkingRangerState = (WalkingRangerState)ranger.State;
 1965    if (walkingRangerState.Destination != ranger.Target.TilePosition) {
 0966      var path = _board.FindPathR(ranger.TilePosition, ranger.Target.TilePosition, true);
 0967      if (path.Count > 0) {
 0968        ranger.State = new WalkingRangerState(ranger, path);
 0969      }
 0970    }
 971
 1972    if (walkingRangerState.HasReachedDestination()) {
 1973      ranger.State = new ShootingRangerState(ranger);
 1974      return;
 975    }
 1976  }
 977
 1978  private async Task HandleShootingRanger(Ranger ranger) {
 1979    if (ranger.LastKilled is not null) {
 1980      if (ranger.LastKilled.Lifetime > 50) {
 0981        Money += BountyForPredators * 2;
 1982      } else {
 1983        Money += BountyForPredators;
 1984      }
 1985    }
 1986    await Task.Delay(100);
 1987    ranger.State = new IdleRangerState(ranger);
 1988  }
 989
 990  #endregion
 991
 992  #region Event handlers
 993
 994  /// <summary>
 995  /// Calls every time-related method in entities according to the game speed.
 996  /// </summary>
 1997  public void Tick(object? sender = null, TimerElapsedEventArgs? e = null) {
 1998    AdjustTimerSpeed();
 999
 11000    ProgressDay();
 1001
 11002    RegeneratePlants();
 1003
 11004    SpawnTourists();
 1005
 11006    HandleJeeps();
 1007
 11008    HandleAnimals();
 1009
 11010    HandleRangers();
 11011  }
 1012
 11013  private void OnJeepFinished(object? sender, JeepFinishedEventArgs e) {
 11014    _avgAnimalSeen = (_avgAnimalSeen + e.Passengers * e.Animals) / (e.Passengers + 1);
 11015    if (sender is Jeep jeep) {
 11016      jeep.Finished -= OnJeepFinished;
 11017    }
 11018  }
 1019
 11020  private void OnGameOver(bool isWon) {
 11021    _timer.Stop();
 11022    GameOver?.Invoke(this, new GameOverEventArgs(isWon));
 11023  }
 1024
 11025  private void OnJeepStart(Jeep jeep) {
 11026    JeepStart?.Invoke(this, new JeepEventArgs(jeep));
 11027  }
 1028
 11029  private void OnJeepFinished() {
 11030    JeepFinish?.Invoke(this, EventArgs.Empty);
 11031  }
 1032
 11033  private void OnAnimalAdded(Animal animal) {
 11034    AnimalAdded?.Invoke(this, new AnimalEventArgs(animal));
 11035  }
 1036
 11037  private void OnRangerAdded(Ranger ranger) {
 11038    RangerAdded?.Invoke(this, new RangerEventArgs(ranger));
 11039  }
 1040
 11041  private void OnPlantStateChanged(RegeneratingPlant? regeneratingPlant, Vector2I position) {
 11042    PlantStateChanged?.Invoke(this, new PlantEventArgs(regeneratingPlant, position));
 11043  }
 1044
 11045  protected void OnPropertyChanged([CallerMemberName] string paramName = "") {
 11046    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(paramName));
 11047  }
 1048
 1049  #endregion
 1050
 1051  #region Dispose
 1052
 11053  protected virtual void Dispose(bool disposing) {
 11054    if (!_disposedValue) {
 11055      if (disposing) {
 11056        _timer?.Dispose();
 11057        _rng?.Dispose();
 11058      }
 1059
 11060      _board = null!;
 11061      _predators = null!;
 11062      _herbivores = null!;
 11063      _rangers = null!;
 11064      _plants = null!;
 11065      _jeeps = null!;
 11066      _disposedValue = true;
 11067    }
 11068  }
 1069
 11070  public void Dispose() {
 1071    // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 11072    Dispose(disposing: true);
 11073    GC.SuppressFinalize(this);
 11074  }
 1075
 1076  #endregion
 1077}

Methods/Properties

.ctor(Safarimacik.Model.ITimerAdapter, Safarimacik.Model.IRandomGenerator)
get_EntryFee()
set_EntryFee(int)
get_TouristsLastDay()
set_TouristsLastDay(int)
get_TouristsThisDay()
set_TouristsThisDay(int)
get_FreeTourists()
get_TouristSpawnRate()
get_HerbivoreCount()
get_PredatorCount()
get_RangerCount()
get_JeepsMax()
set_JeepsMax(int)
get_JeepsOut()
set_JeepsOut(int)
get_Money()
set_Money(int)
get_JeepsAvailable()
get_RemainingTicksOfThisDay()
get_TotalDaysPassed()
set_TotalDaysPassed(int)
get_WinningDaysPassed()
set_WinningDaysPassed(int)
get_WinningDaysWinCon()
get_TouristsWinCon()
get_AnimalWinCon()
get_MoneyWinCon()
get_TableSize()
get_Speed()
set_Speed(int)
get_IsPaused()
get_Difficulty()
get_IsPlacable()
.ctor()
NewGame()
NewGame(Safarimacik.Model.Difficulty, Safarimacik.Model.Board)
Pause()
Resume()
Purchase(int, Godot.Vector2I)
GetTile(int, int)
AddAnimal(Safarimacik.Model.Animal)
AddRanger(Safarimacik.Model.Ranger)
RemoveAnimal(Safarimacik.Model.Animal)
SendJeep(int)
SendJeepBack()
ChangeTile(Safarimacik.Model.Tile, int, int)
AddPlant(Safarimacik.Model.Plant, int, int)
Placable(Safarimacik.Model.Purchasable, Godot.Vector2I)
IsWinning()
HandleLoseGame()
HandleWinGame()
ProgressDay()
AdjustTimerSpeed()
RegeneratePlants()
SpawnTourists()
AnimalInit()
PlantInit()
SetHerdPoints()
PayRangers()
HandleAnimals()
HandleIdleAnimal<T>(Safarimacik.Model.Animal, System.Collections.Generic.List<T>)
WalkToHerd(Safarimacik.Model.Animal)
FindConsumables(Safarimacik.Model.Animal)
FindMatePartner<T>(Safarimacik.Model.Animal, System.Collections.Generic.List<T>)
HandleWalkingAnimal(Safarimacik.Model.Animal)
HandleHungryHerbivore(Safarimacik.Model.Herbivore)
HandleHungryPredator(Safarimacik.Model.Predator)
HandleThirstyAnimal(Safarimacik.Model.Animal)
ArePointsNear(Godot.Vector2I, Godot.Vector2I, int)
ShouldSavePlant(Safarimacik.Model.Plant)
SaveHerbivoreResources(Safarimacik.Model.Herbivore, int)
SavePredatorResources(Safarimacik.Model.Predator, int)
SaveResources(Safarimacik.Model.Animal)
HandleConsumingAnimal(Safarimacik.Model.Animal)
HandleJeeps()
HandleRangers()
HandleIdleRanger(Safarimacik.Model.Ranger)
HandleWalkingRanger(Safarimacik.Model.Ranger)
HandleShootingRanger()
Tick(object, Safarimacik.Model.TimerElapsedEventArgs)
OnJeepFinished(object, Safarimacik.Model.JeepFinishedEventArgs)
OnGameOver(bool)
OnJeepStart(Safarimacik.Model.Jeep)
OnJeepFinished()
OnAnimalAdded(Safarimacik.Model.Animal)
OnRangerAdded(Safarimacik.Model.Ranger)
OnPlantStateChanged(Safarimacik.Model.RegeneratingPlant, Godot.Vector2I)
OnPropertyChanged(string)
Dispose(bool)
Dispose()