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

Revision 5652, 17.5 KB checked in by mikrkana, 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(); // TODO pitää lisätä targetsoldierseihin ne mitkä näkyy (tietyn etäisyyden sisällä?)
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            if (this.Specialization == EnemySpecialization.Attacker || this.Specialization == EnemySpecialization.Defender)
114            {
115                if (this.IsAtDestination)
116                    this.CurrentBehaviour = Behaviour.Shoot;
117                else this.CurrentBehaviour = Behaviour.Move;
118            }
119        }
120
121
122        switch (CurrentBehaviour)
123        {
124            case Behaviour.Idle:
125                this.Stop();
126                this.ReittiAivot.Active = false;
127                break;
128            case Behaviour.Move:
129                if (this.TargetLocation != null)
130                    if (!this.HasRoute)
131                        this.MoveToDestination(TargetLocation, MovingSpeed);
132                break;
133            case Behaviour.Shoot:
134                if (!IsShooting && !CooldownOn)
135                    if (visibleTargets.Count == 0)
136                        PrepareShooting(null);
137                    else PrepareShooting(visibleTargets);
138                break;
139            default:
140                break;
141        }
142    }
143
144    public void UpdateEnemyObjectives()
145    {
146        switch (Specialization)
147        {
148            case EnemySpecialization.Attacker:
149                /* Hyökkää pelaajan Entranceihin. Valitsee Entrancen, jossa on vähiten
150                 * puolustajia ja eliminoi siellä olevat puolustajat.
151                 */
152                List<Entrance> playerEntrances = The_Reclaim.Peli.CurrentLevel.Entrances.FindAll(x => x.CurrentOwner == Entrance.Owner.Friendly);
153                if (playerEntrances.Count != 0)
154                {
155                    int smallestDefenderAmount = playerEntrances[0].FriendliesInside;
156                    int sdIndex = 0;
157
158                    for (int i = 1; i < playerEntrances.Count; i++)
159                    {
160                        int amount = playerEntrances[i].FriendliesInside;
161                        if (amount < smallestDefenderAmount)
162                        {
163                            smallestDefenderAmount = amount;
164                            sdIndex = i;
165                        }
166                    }
167                    this.TargetLocation = playerEntrances[sdIndex].Position + RandomGen.NextVector(-playerEntrances[sdIndex].Radius, playerEntrances[sdIndex].Radius);
168                    this.TargetSoldiers = playerEntrances[sdIndex].GetSoldiersInside(Entrance.Owner.Friendly);
169                }
170                else
171                {
172                    if (RandomGen.NextBool())
173                        this.Specialization = EnemySpecialization.Defender;
174                    else
175                        this.Specialization = EnemySpecialization.Fighter;
176                }
177                break;
178            case EnemySpecialization.Fighter:
179                // Eliminoi pelaajan sotilaita päätehtävänään
180                this.TargetSoldiers.Clear();
181
182                List<GameObject> visibleTargets = GetVisibleTargets();
183                if (visibleTargets.Count != 0)
184                {
185                    for (int i = 0; i < visibleTargets.Count; i++)
186                    {
187                        this.TargetSoldiers.Add(visibleTargets[i] as Soldier);
188                    }
189                    this.CurrentBehaviour = Behaviour.Shoot;
190                }
191                else
192                {
193                    Soldier closest = GetClosestTarget(Entrance.Owner.Friendly);
194                    if (closest != null)
195                    {
196                        this.TargetLocation = closest.Position;
197                        this.TargetSoldiers.Add(closest);
198                        this.CurrentBehaviour = Behaviour.Move;
199                    }
200                    // mitä tehdään jos ei ole ollenkaan kohdepelaajia?
201                }
202
203                break;
204            case EnemySpecialization.Defender:
205                // Puolustaa vihollisen Entranceja
206                List<Entrance> enemyEntrances = The_Reclaim.Peli.CurrentLevel.Entrances.FindAll(x => x.CurrentOwner == Entrance.Owner.Enemy);
207                if (enemyEntrances.Count != 0)
208                {
209                    int largestDifference = enemyEntrances[0].EnemiesInside - enemyEntrances[0].FriendliesInside;
210                    int ldIndex = 0;
211                    for (int i = 1; i < enemyEntrances.Count; i++)
212                    {
213                        int difference = enemyEntrances[i].EnemiesInside - enemyEntrances[i].FriendliesInside;
214                        if (difference < largestDifference)
215                        {
216                            largestDifference = difference;
217                            ldIndex = i;
218                        }
219                    }
220                    this.TargetLocation = enemyEntrances[ldIndex].Position + RandomGen.NextVector(-enemyEntrances[ldIndex].Radius / 2, enemyEntrances[ldIndex].Radius / 2);
221                    this.TargetSoldiers = enemyEntrances[ldIndex].GetSoldiersInside(Entrance.Owner.Friendly, enemyEntrances[ldIndex].Radius * 2);
222                }
223                else
224                {
225                    this.Specialization = EnemySpecialization.Attacker;
226                }
227                break;
228            default:
229                break;
230        }
231    }
232
233    /// <summary>
234    /// Valitaan optimaalinen kohde käytössä olevan aseen mukaan.
235    /// </summary>
236    /// <param name="targets">Kaikki näkyvillä olevat kohteet.</param>
237    /// <returns>Paras vaihtoehto.</returns>
238    Soldier SelectOptimalTarget(List<GameObject> targets)
239    {
240        List<GameObject> targetsClosestToFarthest = targets.OrderBy<GameObject, double>(delegate(GameObject o) { return Vector.Distance(this.Position, o.Position); }).ToList();
241        switch (this.Weapon.AseenTyyppi)
242        {
243            case Ase.WeaponType.Melee:
244                return (Soldier)targetsClosestToFarthest[0]; // lähin
245            case Ase.WeaponType.CloseRange:
246                return (Soldier)targetsClosestToFarthest[targetsClosestToFarthest.Count / 3];
247            case Ase.WeaponType.MediumRange:
248                return (Soldier)targetsClosestToFarthest[targetsClosestToFarthest.Count / 3 * 2];
249            case Ase.WeaponType.LongRange:
250                return (Soldier)targetsClosestToFarthest[targetsClosestToFarthest.Count - 1]; // kaukaisin
251            default:
252                return (Soldier)targetsClosestToFarthest[RandomGen.NextInt(0, targetsClosestToFarthest.Count)];
253        }
254    }
255
256    private void PrepareShooting(List<GameObject> visTargets)
257    {
258        List<GameObject> visibleTargets;
259        if (visTargets == null)
260            visibleTargets = GetVisibleTargets();
261        else
262            visibleTargets = visTargets;
263
264        if (visibleTargets.Count == 0) return;
265
266        Soldier kohde = (Soldier)SelectOptimalTarget(visibleTargets);
267        BurstLength = RandomGen.NextInt(this.Weapon.MinBurstLength, this.Weapon.MaxBurstLength + 1);
268        BulletsShotInBurst = 0;
269
270        this.Angle = (kohde.Position - this.Position).Angle;
271        Shoot();
272        BulletsShotInBurst++;
273        IsShooting = true;
274
275        ShootingTimer.Interval = 1.0 / Weapon.FireRate;
276        ShootingTimer.Timeout += delegate
277        {
278            visibleTargets = GetVisibleTargets();
279            if (visibleTargets.Count == 0 || BulletsShotInBurst >= BurstLength)
280            {
281                ShootingTimer.Stop();
282                IsShooting = false;
283                CooldownOn = true;
284                Timer.SingleShot(Weapon.CooldownBetweenBursts, delegate { CooldownOn = false; });
285            }
286            else
287            {
288                kohde = (Soldier)SelectOptimalTarget(visibleTargets);
289                this.Angle = (kohde.Position - this.Position).Angle;
290                Shoot();
291                BulletsShotInBurst++;
292            }
293        };
294        ShootingTimer.Start();
295    }
296
297    /// <summary>
298    /// Etsitään kaikki näkyvillä olevat kohteet.
299    /// </summary>
300    /// <returns></returns>
301    private List<GameObject> GetVisibleTargets()
302    {
303        List<GameObject> kohteetNakyvilla = new List<GameObject>();
304        for (int i = 0; i < TargetSoldiers.Count; i++)
305        {
306            if (this.SeesObject(TargetSoldiers[i], "seina"))
307            {
308                kohteetNakyvilla.Add(TargetSoldiers[i]);
309            }
310        }
311        return kohteetNakyvilla;
312    }
313
314    private Soldier GetClosestTarget(Entrance.Owner soldierType)
315    {
316        if (soldierType == Entrance.Owner.Friendly)
317        {
318            if (The_Reclaim.Peli.FriendlyTroops.Count == 0) return null;
319            double shortestDistance = Vector.Distance(this.Position, The_Reclaim.Peli.FriendlyTroops[0].Position);
320            int closestIndex = 0;
321
322            for (int i = 0; i < The_Reclaim.Peli.FriendlyTroops.Count; i++)
323            {
324                double distance = Vector.Distance(this.Position, The_Reclaim.Peli.FriendlyTroops[i].Position);
325                if (distance < shortestDistance)
326                {
327                    shortestDistance = distance;
328                    closestIndex = i;
329                }
330            }
331            return The_Reclaim.Peli.FriendlyTroops[closestIndex];
332        }
333        else
334        {
335            if (The_Reclaim.Peli.HostileTroops.Count == 0) return null;
336            double shortestDistance = Vector.Distance(this.Position, The_Reclaim.Peli.HostileTroops[0].Position);
337            int closestIndex = 0;
338
339            for (int i = 0; i < The_Reclaim.Peli.HostileTroops.Count; i++)
340            {
341                double distance = Vector.Distance(this.Position, The_Reclaim.Peli.HostileTroops[i].Position);
342                if (distance < shortestDistance)
343                {
344                    shortestDistance = distance;
345                    closestIndex = i;
346                }
347            }
348            return The_Reclaim.Peli.HostileTroops[closestIndex];
349        }
350    }
351
352    public void AddTargetSoldier(Soldier target)
353    {
354        TargetSoldiers.Add(target);
355        target.Destroying += delegate { TargetSoldiers.Remove(target); };
356    }
357
358    public void SetWeapon(Ase weapon)
359    {
360        this.Weapon = weapon;
361        this.Add(this.Weapon);
362    }
363
364    public override void Update(Time time)
365    {
366        if (SelectionMarker != null && SelectionMarker.IsAddedToGame != false)
367            SelectionMarker.Position = this.Position;
368        if (Shadow != null && Shadow.IsAddedToGame != false)
369            Shadow.Position = this.Position;
370        if (Name != null && Name.IsAddedToGame != false)
371            Name.Position = this.Position + new Vector(0.0, NAME_HEIGHT);
372        base.Update(time);
373    }
374
375    void Shoot()
376    {
377        if (this.Weapon == null) return;
378
379        if (this.Weapon.Ammo.Value == 0) Timer.SingleShot(this.Weapon.LataukseenKuluvaAika, delegate
380        {
381            this.Weapon.Ammo.Value = this.Weapon.Ammo.MaxValue;
382        });
383
384        PhysicsObject ammus = this.Weapon.Shoot();
385        if (ammus == null) return;
386
387        Vector random = RandomGen.NextVector(this.Weapon.AseenHajoama.X, this.Weapon.AseenHajoama.Y);
388        ammus.Hit(random);
389
390        ammus.CollisionIgnoreGroup = 2;
391        ammus.Height *= 0.5;
392        ammus.Tag = "ammus";
393        ammus.LinearDamping = 0.99;
394        ammus.Color = Color.White;
395        ammus.Mass = 0.1;
396        ammus.CanRotate = false;
397        ammus.IsVisible = this.Weapon.NakyykoAmmus;
398
399        // jos on meleease, ammusta ei näytetä
400        if (this.Weapon.OnkoMeleeAse == true) ammus.IsVisible = false;
401
402        // muuten ammutaan hylsy ja laitetaan luodille oikea kuva
403        else
404        {
405            if (this.Weapon.TuleekoHylsya)
406                Efektit.Hylsy(this.Position, this.Angle, this.Weapon.hylsynKuva);
407            ammus.Image = this.Weapon.luodinKuva;
408        }
409
410        if (this.Weapon.UsesTracers)
411        {
412            ammus.IsUpdated = true;
413            ammus.NeedsUpdateCall = true;
414            Color c;
415            if (this.Weapon.OverrideTracerColor != Color.Transparent)
416                c = this.Weapon.OverrideTracerColor;
417            else c = Color.Yellow;
418            ammus.Updated += delegate(GameObject o) { The_Reclaim.Peli.DrawTracers(o, c, this.Weapon.TracerLength, this.Weapon.TracerBrightness); };
419        }
420
421        if (this.Weapon.LapaiseekoMateriaaleja)
422        {
423            ammus.IgnoresCollisionResponse = true;
424            ammus.Collided += delegate
425            {
426                if (this.Weapon.AseenLapaisy > 0)
427                    Timer.SingleShot(this.Weapon.AseenLapaisy, delegate { ammus.IgnoresCollisionResponse = false; });
428            };
429        }
430
431        // collisionhandlerit
432        The_Reclaim.Peli.AddCollisionHandler<PhysicsObject, Soldier>(ammus, delegate(PhysicsObject a, Soldier kohde)
433        {
434            if (this.IsPlayerControlled != kohde.IsPlayerControlled)
435                kohde.Damage(this.Weapon.DamageElaviaVastaan);
436            Efektit.AddNormalBlood(kohde.Position, 3, 0.3);
437            ammus.Destroy();
438        });
439
440        The_Reclaim.Peli.AddCollisionHandler<PhysicsObject, PhysicsObject>(ammus, delegate(PhysicsObject a, PhysicsObject kohde)
441        {
442            Efektit.AddEffect(Efektit.KipinaPartikkelit, ammus.Position, 40);
443            ammus.Destroy();
444        });
445
446    }
447
448    /*/// <summary>
449    /// CollisionHandleri hylsylle. Soitetaan kilahdusääni, jos hylsy osuu seinään.
450    /// </summary>
451    /// <param name="hylsy">Hylsy.</param>
452    /// <param name="kohde">Kohde, johon hylsy osuu.</param>
453    void HylsyOsuu(PhysicsObject hylsy, PhysicsObject kohde)
454    {
455        if (kohde is Tuhoutuva)
456            hylsynPutoamisAani.Play(0.7, 0.0, 0.0);
457    }*/
458
459    public override void Kuolema()
460    {
461        this.SelectionMarker.Destroy();
462        this.Name.Destroy();
463        Efektit.AddDeathSplatter(this.Position, 3, 0.5);
464        if (IsPlayerControlled) The_Reclaim.Peli.FriendlyTroops.Remove(this);
465        else The_Reclaim.Peli.HostileTroops.Remove(this);
466
467        base.Kuolema();
468    }
469}
Note: See TracBrowser for help on using the repository browser.