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

Revision 5602, 15.3 KB checked in by anlakane, 5 years ago (diff)

Lisätty mm. ampuminen ja vihollisten tekoälyä.

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