source: 2014/30/MiskaK/The Reclaim/The Reclaim/The Reclaim/The Reclaim/Soldier.cs @ 5649

Revision 5649, 17.1 KB checked in by anlakane, 6 years ago (diff)
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using Jypeli;
6using Jypeli.Assets;
7using Jypeli.Widgets;
8
9public class Soldier : LivingEntity
10{
11    /// <summary>
12    /// Voiko pelaaja käskyttää sotilasta.
13    /// </summary>
14    public bool IsPlayerControlled { get; set; }
15
16    /// <summary>
17    /// Mitä tekee tällä hetkellä.
18    /// </summary>
19    public Behaviour CurrentBehaviour { get; set; }
20
21    public EnemySpecialization Specialization { get; set; }
22
23    /// <summary>
24    /// Sotilaan ase.
25    /// </summary>
26    public Ase Weapon { get; set; }
27
28    public double MovingSpeed { get; set; }
29
30    public GameObject SelectionMarker { get; set; }
31
32    public GameObject Shadow { get; private set; }
33
34    public List<Soldier> TargetSoldiers { get; set; }
35
36    public Label Name { get; set; }
37
38    private const double NAME_HEIGHT = 75.0;
39
40    private int BurstLength { get; set; }
41    private int BulletsShotInBurst { get; set; }
42    private Timer ShootingTimer = new Timer();
43    public bool IsShooting { get; private set; }
44    public bool CooldownOn { get; private set; }
45
46    public Soldier(double width, double height, bool isPlayerControlled)
47        : base(width, height)
48    {
49        CurrentBehaviour = Behaviour.Idle;
50        this.IsPlayerControlled = isPlayerControlled;
51        Weapon = new Ase(40, 10);
52        this.Add(Weapon);
53        MovingSpeed = 400;
54        SelectionMarker = new GameObject(width * 1.25, height * 1.25);
55        if (isPlayerControlled)
56            SelectionMarker.Image = Images.SelectionImage;
57        else
58        {
59            SelectionMarker.Image = Images.EnemySelectionImage;
60        }
61        SelectionMarker.IsUpdated = true;
62        SelectionMarker.NeedsUpdateCall = true;
63        SelectionMarker.Updated += delegate(GameObject s) { s.Angle += Angle.FromDegrees(-1); };
64        The_Reclaim.Peli.Add(SelectionMarker, -1);
65        SelectionMarker.IsVisible = false;
66
67        /*Shadow = new GameObject(width / 4, height / 4);
68        Shadow.Image = Images.ShadowImage;
69        The_Reclaim.Peli.Add(Shadow, -1);*/
70
71        TargetSoldiers = new List<Soldier>();
72
73        Name = new Label(Names.GetRandomName());
74        Name.Position = new Vector(this.Position.X, this.Position.Y + NAME_HEIGHT);
75        if (isPlayerControlled)
76            Name.TextColor = Color.LightBlue;
77        else
78            Name.TextColor = Color.Red;
79        The_Reclaim.Peli.Add(Name, 3);
80    }
81
82    public Soldier(Image kuva, bool isPlayerControlled)
83        : this(kuva.Width, kuva.Height, isPlayerControlled)
84    {
85        this.Image = kuva;
86    }
87
88    public Soldier(Image kuva, double sizeMultiplier, bool isPlayerControlled)
89        : this(kuva.Width, kuva.Height, isPlayerControlled)
90    {
91        this.Image = kuva;
92        this.Size *= sizeMultiplier;
93        SelectionMarker.Size *= sizeMultiplier;
94    }
95
96
97    public void UpdateAI()
98    {
99        List<GameObject> visibleTargets = new List<GameObject>();
100        if (!IsPlayerControlled)
101        {
102            if (The_Reclaim.Peli.FriendlyTroops.Count != 0)
103            {
104                visibleTargets = GetVisibleTargets();
105                if (visibleTargets.Count != 0)
106                {
107                    //if (RandomGen.NextInt(0, 6) < 1)
108                        this.CurrentBehaviour = Behaviour.Shoot;
109                }
110                else this.CurrentBehaviour = Behaviour.Move;
111            }
112        }
113
114
115        switch (CurrentBehaviour)
116        {
117            case Behaviour.Idle:
118                this.Stop();
119                this.ReittiAivot.Active = false;
120                break;
121            case Behaviour.Move:
122                if (this.TargetLocation != null)
123                    if (!this.HasRoute)
124                        this.MoveToDestination(TargetLocation, MovingSpeed);
125                break;
126            case Behaviour.Shoot:
127                if (!IsShooting && !CooldownOn)
128                    if (visibleTargets.Count == 0)
129                        PrepareShooting(null);
130                    else PrepareShooting(visibleTargets);
131                break;
132            default:
133                break;
134        }
135    }
136
137    public void UpdateEnemyObjectives()
138    {
139        switch (Specialization)
140        {
141            case EnemySpecialization.Attacker:
142                /* Hyökkää pelaajan Entranceihin. Valitsee Entrancen, jossa on vähiten
143                 * puolustajia ja eliminoi siellä olevat puolustajat.
144                 */
145                List<Entrance> playerEntrances = The_Reclaim.Peli.CurrentLevel.Entrances.FindAll(x => x.CurrentOwner == Entrance.Owner.Friendly);
146                if (playerEntrances.Count != 0)
147                {
148                    int smallestDefenderAmount = playerEntrances[0].FriendliesInside;
149                    int sdIndex = 0;
150
151                    for (int i = 1; i < playerEntrances.Count; i++)
152                    {
153                        int amount = playerEntrances[i].FriendliesInside;
154                        if (amount < smallestDefenderAmount)
155                        {
156                            smallestDefenderAmount = amount;
157                            sdIndex = i;
158                        }
159                    }
160                    this.TargetLocation = playerEntrances[sdIndex].Position + RandomGen.NextVector(-playerEntrances[sdIndex].Radius, playerEntrances[sdIndex].Radius);
161                    this.TargetSoldiers = playerEntrances[sdIndex].GetSoldiersInside(Entrance.Owner.Friendly);
162                }
163                else
164                {
165                    if (RandomGen.NextBool())
166                        this.Specialization = EnemySpecialization.Defender;
167                    else
168                        this.Specialization = EnemySpecialization.Fighter;
169                }
170                break;
171            case EnemySpecialization.Fighter:
172                // Eliminoi pelaajan sotilaita päätehtävänään
173                this.TargetSoldiers.Clear();
174
175                List<GameObject> visibleTargets = GetVisibleTargets();
176                if (visibleTargets.Count != 0)
177                {
178                    for (int i = 0; i < visibleTargets.Count; i++)
179                    {
180                        this.TargetSoldiers.Add(visibleTargets[i] as Soldier);
181                    }
182                    this.CurrentBehaviour = Behaviour.Shoot;
183                }
184                else
185                {
186                    Soldier closest = GetClosestTarget(Entrance.Owner.Friendly);
187                    if (closest != null)
188                    {
189                        this.TargetLocation = closest.Position;
190                        this.TargetSoldiers.Add(closest);
191                        this.CurrentBehaviour = Behaviour.Move;
192                    }
193                    // mitä tehdään jos ei ole ollenkaan kohdepelaajia?
194                }
195
196                break;
197            case EnemySpecialization.Defender:
198                // Puolustaa vihollisen Entranceja
199                List<Entrance> enemyEntrances = The_Reclaim.Peli.CurrentLevel.Entrances.FindAll(x => x.CurrentOwner == Entrance.Owner.Enemy);
200                if (enemyEntrances.Count != 0)
201                {
202                    int largestDifference = enemyEntrances[0].EnemiesInside - enemyEntrances[0].FriendliesInside;
203                    int ldIndex = 0;
204                    for (int i = 1; i < enemyEntrances.Count; i++)
205                    {
206                        int difference = enemyEntrances[i].EnemiesInside - enemyEntrances[i].FriendliesInside;
207                        if (difference < largestDifference)
208                        {
209                            largestDifference = difference;
210                            ldIndex = i;
211                        }
212                    }
213                    this.TargetLocation = enemyEntrances[ldIndex].Position + RandomGen.NextVector(-enemyEntrances[ldIndex].Radius / 2, enemyEntrances[ldIndex].Radius / 2);
214                    this.TargetSoldiers = enemyEntrances[ldIndex].GetSoldiersInside(Entrance.Owner.Friendly, enemyEntrances[ldIndex].Radius * 2);
215                }
216                else
217                {
218                    this.Specialization = EnemySpecialization.Attacker;
219                }
220                break;
221            default:
222                break;
223        }
224    }
225
226    /// <summary>
227    /// Valitaan optimaalinen kohde käytössä olevan aseen mukaan.
228    /// </summary>
229    /// <param name="targets">Kaikki näkyvillä olevat kohteet.</param>
230    /// <returns>Paras vaihtoehto.</returns>
231    Soldier SelectOptimalTarget(List<GameObject> targets)
232    {
233        List<GameObject> targetsClosestToFarthest = targets.OrderBy<GameObject, double>(delegate(GameObject o) { return Vector.Distance(this.Position, o.Position); }).ToList();
234        switch (this.Weapon.AseenTyyppi)
235        {
236            case Ase.WeaponType.Melee:
237                return (Soldier)targetsClosestToFarthest[0]; // lähin
238            case Ase.WeaponType.CloseRange:
239                return (Soldier)targetsClosestToFarthest[targetsClosestToFarthest.Count / 3];
240            case Ase.WeaponType.MediumRange:
241                return (Soldier)targetsClosestToFarthest[targetsClosestToFarthest.Count / 3 * 2];
242            case Ase.WeaponType.LongRange:
243                return (Soldier)targetsClosestToFarthest[targetsClosestToFarthest.Count - 1]; // kaukaisin
244            default:
245                return (Soldier)targetsClosestToFarthest[RandomGen.NextInt(0, targetsClosestToFarthest.Count)];
246        }
247    }
248
249    private void PrepareShooting(List<GameObject> visTargets)
250    {
251        List<GameObject> visibleTargets;
252        if (visTargets == null)
253            visibleTargets = GetVisibleTargets();
254        else
255            visibleTargets = visTargets;
256
257        if (visibleTargets.Count == 0) return;
258
259        Soldier kohde = (Soldier)SelectOptimalTarget(visibleTargets);
260        BurstLength = RandomGen.NextInt(this.Weapon.MinBurstLength, this.Weapon.MaxBurstLength + 1);
261        BulletsShotInBurst = 0;
262
263        this.Angle = (kohde.Position - this.Position).Angle;
264        Shoot();
265        BulletsShotInBurst++;
266        IsShooting = true;
267
268        ShootingTimer.Interval = 1.0 / Weapon.FireRate;
269        ShootingTimer.Timeout += delegate
270        {
271            visibleTargets = GetVisibleTargets();
272            if (visibleTargets.Count == 0 || BulletsShotInBurst >= BurstLength)
273            {
274                ShootingTimer.Stop();
275                IsShooting = false;
276                CooldownOn = true;
277                Timer.SingleShot(Weapon.CooldownBetweenBursts, delegate { CooldownOn = false; });
278            }
279            else
280            {
281                kohde = (Soldier)SelectOptimalTarget(visibleTargets);
282                this.Angle = (kohde.Position - this.Position).Angle;
283                Shoot();
284                BulletsShotInBurst++;
285            }
286        };
287        ShootingTimer.Start();
288    }
289
290    /// <summary>
291    /// Etsitään kaikki näkyvillä olevat kohteet.
292    /// </summary>
293    /// <returns></returns>
294    private List<GameObject> GetVisibleTargets()
295    {
296        List<GameObject> kohteetNakyvilla = new List<GameObject>();
297        for (int i = 0; i < TargetSoldiers.Count; i++)
298        {
299            if (this.SeesObject(TargetSoldiers[i], "seina"))
300            {
301                kohteetNakyvilla.Add(TargetSoldiers[i]);
302            }
303        }
304        return kohteetNakyvilla;
305    }
306
307    private Soldier GetClosestTarget(Entrance.Owner soldierType)
308    {
309        if (soldierType == Entrance.Owner.Friendly)
310        {
311            if (The_Reclaim.Peli.FriendlyTroops.Count == 0) return null;
312            double shortestDistance = Vector.Distance(this.Position, The_Reclaim.Peli.FriendlyTroops[0].Position);
313            int closestIndex = 0;
314
315            for (int i = 0; i < The_Reclaim.Peli.FriendlyTroops.Count; i++)
316            {
317                double distance = Vector.Distance(this.Position, The_Reclaim.Peli.FriendlyTroops[i].Position);
318                if (distance < shortestDistance)
319                {
320                    shortestDistance = distance;
321                    closestIndex = i;
322                }
323            }
324            return The_Reclaim.Peli.FriendlyTroops[closestIndex];
325        }
326        else
327        {
328            if (The_Reclaim.Peli.HostileTroops.Count == 0) return null;
329            double shortestDistance = Vector.Distance(this.Position, The_Reclaim.Peli.HostileTroops[0].Position);
330            int closestIndex = 0;
331
332            for (int i = 0; i < The_Reclaim.Peli.HostileTroops.Count; i++)
333            {
334                double distance = Vector.Distance(this.Position, The_Reclaim.Peli.HostileTroops[i].Position);
335                if (distance < shortestDistance)
336                {
337                    shortestDistance = distance;
338                    closestIndex = i;
339                }
340            }
341            return The_Reclaim.Peli.HostileTroops[closestIndex];
342        }
343    }
344
345    public void AddTargetSoldier(Soldier target)
346    {
347        TargetSoldiers.Add(target);
348        target.Destroying += delegate { TargetSoldiers.Remove(target); };
349    }
350
351    public void SetWeapon(Ase weapon)
352    {
353        this.Weapon = weapon;
354        this.Add(this.Weapon);
355    }
356
357    public override void Update(Time time)
358    {
359        if (SelectionMarker != null && SelectionMarker.IsAddedToGame != false)
360            SelectionMarker.Position = this.Position;
361        if (Shadow != null && Shadow.IsAddedToGame != false)
362            Shadow.Position = this.Position;
363        if (Name != null && Name.IsAddedToGame != false)
364            Name.Position = this.Position + new Vector(0.0, NAME_HEIGHT);
365        base.Update(time);
366    }
367
368    void Shoot()
369    {
370        if (this.Weapon == null) return;
371
372        if (this.Weapon.Ammo.Value == 0) Timer.SingleShot(this.Weapon.LataukseenKuluvaAika, delegate
373        {
374            this.Weapon.Ammo.Value = this.Weapon.Ammo.MaxValue;
375        });
376
377        PhysicsObject ammus = this.Weapon.Shoot();
378        if (ammus == null) return;
379
380        Vector random = RandomGen.NextVector(this.Weapon.AseenHajoama.X, this.Weapon.AseenHajoama.Y);
381        ammus.Hit(random);
382
383        ammus.CollisionIgnoreGroup = 2;
384        ammus.Height *= 0.5;
385        ammus.Tag = "ammus";
386        ammus.LinearDamping = 0.99;
387        ammus.Color = Color.White;
388        ammus.Mass = 0.1;
389        ammus.CanRotate = false;
390        ammus.IsVisible = this.Weapon.NakyykoAmmus;
391
392        // jos on meleease, ammusta ei näytetä
393        if (this.Weapon.OnkoMeleeAse == true) ammus.IsVisible = false;
394
395        // muuten ammutaan hylsy ja laitetaan luodille oikea kuva
396        else
397        {
398            if (this.Weapon.TuleekoHylsya)
399                Efektit.Hylsy(this.Position, this.Angle, this.Weapon.hylsynKuva);
400            ammus.Image = this.Weapon.luodinKuva;
401        }
402
403        if (this.Weapon.UsesTracers)
404        {
405            ammus.IsUpdated = true;
406            ammus.NeedsUpdateCall = true;
407            Color c;
408            if (this.Weapon.OverrideTracerColor != Color.Transparent)
409                c = this.Weapon.OverrideTracerColor;
410            else c = Color.Yellow;
411            ammus.Updated += delegate(GameObject o) { The_Reclaim.Peli.DrawTracers(o, c, this.Weapon.TracerLength, this.Weapon.TracerBrightness); };
412        }
413
414        if (this.Weapon.LapaiseekoMateriaaleja)
415        {
416            ammus.IgnoresCollisionResponse = true;
417            ammus.Collided += delegate
418            {
419                if (this.Weapon.AseenLapaisy > 0)
420                    Timer.SingleShot(this.Weapon.AseenLapaisy, delegate { ammus.IgnoresCollisionResponse = false; });
421            };
422        }
423
424        // collisionhandlerit
425        The_Reclaim.Peli.AddCollisionHandler<PhysicsObject, Soldier>(ammus, delegate(PhysicsObject a, Soldier kohde)
426        {
427            if (this.IsPlayerControlled != kohde.IsPlayerControlled)
428                kohde.Damage(this.Weapon.DamageElaviaVastaan);
429            Efektit.AddNormalBlood(kohde.Position, 3, 0.3);
430            ammus.Destroy();
431        });
432
433        The_Reclaim.Peli.AddCollisionHandler<PhysicsObject, PhysicsObject>(ammus, delegate(PhysicsObject a, PhysicsObject kohde)
434        {
435            Efektit.AddEffect(Efektit.KipinaPartikkelit, ammus.Position, 40);
436            ammus.Destroy();
437        });
438
439    }
440
441    /*/// <summary>
442    /// CollisionHandleri hylsylle. Soitetaan kilahdusääni, jos hylsy osuu seinään.
443    /// </summary>
444    /// <param name="hylsy">Hylsy.</param>
445    /// <param name="kohde">Kohde, johon hylsy osuu.</param>
446    void HylsyOsuu(PhysicsObject hylsy, PhysicsObject kohde)
447    {
448        if (kohde is Tuhoutuva)
449            hylsynPutoamisAani.Play(0.7, 0.0, 0.0);
450    }*/
451
452    public override void Kuolema()
453    {
454        this.SelectionMarker.Destroy();
455        this.Name.Destroy();
456        Efektit.AddDeathSplatter(this.Position, 3, 0.5);
457        if (IsPlayerControlled) The_Reclaim.Peli.FriendlyTroops.Remove(this);
458        else The_Reclaim.Peli.HostileTroops.Remove(this);
459
460        base.Kuolema();
461    }
462}
Note: See TracBrowser for help on using the repository browser.