source: 2015/27/ohjaajat/TheLegendOfGabriel/TheLegendOfGabriel/TheLegendOfGabriel/TheLegendOfGabriel.cs @ 6716

Revision 6716, 22.4 KB checked in by sieerinn, 4 years ago (diff)

Norjalaiset moukarinheittäjät tehty, kranaatti toimii taas junaa vastaan.

Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Reflection;
5using Jypeli;
6using Jypeli.Assets;
7using Jypeli.Controls;
8using Jypeli.Effects;
9using Jypeli.Widgets;
10
11public class StoryItem
12{
13    // Iso kuvake joka näkyy inventoryssä.
14    public Image InventoryImage { get; set; }
15
16    public Queue<string> Message { get; set; }
17
18    public StoryItem(Queue<String> message, Image image)
19    {
20        this.InventoryImage = image;
21        this.Message = message;
22    }
23}
24
25class CycleSlot : Label
26{
27    public Label UsageLabel { get; set; }
28
29    public CycleSlot(double left, double top)
30    {
31        this.Left = left;
32        this.Top = top;
33        this.Size = new Vector(60, 40);
34
35        UsageLabel = new Label("10");
36        UsageLabel.IsVisible = false;
37        UsageLabel.TextColor = Color.White;
38        UsageLabel.X = this.X + 10;
39        UsageLabel.Top = this.Bottom;
40        UsageLabel.Font = Font.DefaultSmallBold;
41    }
42
43    public void UpdateImage(Item item)
44    {
45        if (item != null)
46        {
47            this.Image = item.InventoryImage;
48            UsageLabel.IsVisible = false;
49            if (item.Usages.MaxValue > 0)
50            {
51                UsageLabel.IsVisible = true;
52                UsageLabel.BindTo(item.Usages);
53            }
54        }
55    }
56}
57
58public partial class TheLegendOfGabriel : PhysicsGame
59{
60    public const int TILE_SIZE = 20;
61
62    private Player player;
63    private List<Creature> enemies = new List<Creature>(); 
64
65    private bool transition;
66    List<GameObject> oldObjects = new List<GameObject>();
67    List<Exit> exits = new List<Exit>();
68
69    private bool viewing;
70    private double midpoint;
71    private double second;
72    GameObject frame;
73    Queue<string> messagesviewed;
74
75    private const int STORYITEM_COUNT = 3;
76    List<StoryItem> storyItems = new List<StoryItem>();
77
78    private bool canUseExit = true;
79    private bool woodDestroyed = false;
80
81    public IntMeter BossHealth;
82    private List<Vector> bossSpawnPositions = new List<Vector>();
83
84    private CycleSlot currentItem; // Laatikossa näkyvä nykyinen kama.
85    private CycleSlot nextItem; // Seuraavan kaman kuva.
86    private CycleSlot prevItem; // Edellisen kaman kuva.
87    private Label swordItem; // Laatikossa näkyvä miekka.
88
89    #region Resources
90
91    public static SoundEffect SwordSound = LoadSoundEffect("swordsound");
92    public static SoundEffect GunSound = LoadSoundEffect("gunsound");
93    public static SoundEffect EnemyDieSound = LoadSoundEffect("enemydie");
94
95    public static Image GunImage = LoadImage("gun");
96    public static Image BulletImage = LoadImage("bullet");
97    public static Image LetterImage = LoadImage("letter");
98    public static Image FenceImage = LoadImage("fence");
99    public static Image FoodImage = LoadImage("food");
100    public static Image SmallSwordImage = LoadImage("smallsword");
101    public static Image MonocleImage = LoadImage("monocle");
102    public static Image BigMonocleImage = LoadImage("bigmonocle");
103    public static Image BigSwordImage = LoadImage("bigsword");
104    public static Image GrenadeImage = LoadImage("gran");
105    public static Image GrenadeSmallImage = LoadImage("gransmall");
106
107    public static Image GrenadeBoxImage = LoadImage("grenadebox");
108    public static Image AmmoBoxImage = LoadImage("ammobox");
109
110    public static Image FrameImage = LoadImage("frame");
111    public static Image ActiveItemFrameImageX = LoadImage("activeitemframe");
112    public static Image ActiveItemFrameImageZ = LoadImage("activeitemframez");
113
114    public static Image PlayerHealthImage = LoadImage("playerhealth");
115    public static Image PlayerHealthEmptyImage = LoadImage("playerhealthempty");
116
117    [AssetName("walkright")]
118    internal Animation playerWalkRight;
119    [AssetName("walkright", mirror: true)]
120    internal Animation playerWalkLeft;
121    [AssetName("walkup")]
122    private Animation playerWalkUp;
123    [AssetName("walkdown")]
124    private Animation playerWalkDown;
125
126    [AssetName("nwalkright")]
127    internal Animation nWalkRight;
128    [AssetName("nwalkright", mirror: true)]
129    internal Animation nWalkLeft;
130    [AssetName("nwalkup")]
131    private Animation nWalkUp;
132    [AssetName("nwalkdown")]
133    private Animation nWalkDown;
134
135    [AssetName("swingup")]
136    private Animation playerSwingUp;
137    [AssetName("swingright")]
138    private Animation playerSwingRight;
139    [AssetName("swingright", mirror: true)]
140    private Animation playerSwingLeft;
141    [AssetName("swingdown")]
142    private Animation playerSwingDown;
143
144    [AssetName("shootup")]
145    private Animation playerShootUp;
146    [AssetName("shootright")]
147    private Animation playerShootRight;
148    [AssetName("shootright", mirror: true)]
149    private Animation playerShootLeft;
150    [AssetName("shootdown")]
151    private Animation playerShootDown;
152
153    [AssetName("coyoteup")]
154    private Animation coyoteUp;
155    [AssetName("coyoteright")]
156    private Animation coyoteRight;
157    [AssetName("coyoteright", mirror: true)]
158    private Animation coyoteLeft;
159    [AssetName("coyotedown")]
160    private Animation coyoteDown;
161
162    [AssetName("train")]
163    private Animation trainAnimation;
164    [AssetName("carriage")]
165    private Animation carriageAnimation;
166
167    [AssetName("smoke")]
168    private Animation smokeAnimation;
169
170    #endregion
171
172    public override void Begin()
173    {
174        Mouse.IsCursorVisible = true;
175        SmoothTextures = false;
176        LoadAnimations();
177        StartGame();
178        BuildUI();
179        Intro();
180    }
181
182    void Victory()
183    {
184        ClearAll();
185        Level.Background.Color = Color.Black;
186
187        var text = new Label("Voitit muutes belin."); // Tekstin voisi vaihtaa.
188        text.TextColor = Color.White;
189        Add(text);
190    }
191
192    void BuildUI()
193    {
194        BuildRightBar();
195        BuildInventoryCycle();
196        BuildHealthBar();
197        UpdateItemCycleImages();
198    }
199   
200    void BuildHealthBar()
201    {
202        var bar = new ProgressBar(32 * 10, 32);
203        bar.Right = Screen.Right - 50;
204        bar.Top = Screen.Top - 50;
205        bar.Image = PlayerHealthEmptyImage;
206        bar.BarImage = PlayerHealthImage;
207        bar.BindTo(player.Health);
208        Add(bar);
209    }
210
211    void BuildInventoryCycle()
212    {
213        const int spacing = 20;
214
215        prevItem = new CycleSlot(Screen.Left + 50, Screen.Top - 30);
216        Add(prevItem);
217        Add(prevItem.UsageLabel);
218
219        currentItem = new CycleSlot(prevItem.Right + spacing, prevItem.Top);
220        Add(currentItem);
221        Add(currentItem.UsageLabel);
222
223        nextItem = new CycleSlot(currentItem.Right + spacing, prevItem.Top);
224        Add(nextItem);
225        Add(nextItem.UsageLabel);
226
227        // Ruutu jonka sisällä käytössä oleva kama on.
228        var frame = new Label();
229        frame.Size = new Vector(60, 60);
230        frame.Image = ActiveItemFrameImageX;
231        frame.X = currentItem.X;
232        frame.Top = currentItem.Top;
233        Add(frame);
234
235        // Miekan kuva.
236        swordItem = new Label();
237        swordItem.Size = prevItem.Size;
238        swordItem.Left = nextItem.Right + spacing * 2;
239        swordItem.Top = currentItem.Top;
240        Add(swordItem);
241
242        // Miekan kuvan ympärillä oleva ruutu.
243        var swordFrame = new Label();
244        swordFrame.Size = new Vector(60, 60);
245        swordFrame.Image = ActiveItemFrameImageZ;
246        swordFrame.X = swordItem.X;
247        swordFrame.Top = swordItem.Top;
248        Add(swordFrame);
249    }
250
251    void BuildRightBar()
252    {
253        for (int i = 0; i < STORYITEM_COUNT; i++)
254        {
255            Label member = new Label(FrameImage);
256            member.Size = new Vector(40, 40);
257            member.Position = new Vector(Screen.Right - FrameImage.Width, Screen.Top - Screen.Width * 0.25 - FrameImage.Height * i * 1.5);
258            Add(member);
259
260            GameObject inner = new GameObject(TILE_SIZE, TILE_SIZE);
261            inner.Color = Color.Black;
262            if (storyItems.Count > i &&  storyItems[i] != null)
263            {
264                inner.Image = storyItems[i].InventoryImage;
265                Mouse.ListenOn(inner, MouseButton.Left, ButtonState.Pressed, ShowTextItem, "View those precious memories", storyItems[i]);
266            }
267            member.Add(inner);
268        }
269    }
270
271    /// <summary>
272    /// Lataa automaagisesti kaikki animaatiot.
273    /// </summary>
274    private void LoadAnimations()
275    {
276        // Haetaan tämän luokan kaikki ei-julkiset attribuutit.
277        foreach (var field in GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
278        {
279            // Jos attribuutille on laitettu AsseName, niin ladataan sen niminen animaatio.
280            var assetAttr = Attribute.GetCustomAttribute(field, typeof(AssetNameAttribute)) as AssetNameAttribute;
281            if (assetAttr == null) continue;
282            var anim = LoadAnimation(assetAttr.AssetName);
283            field.SetValue(this, assetAttr.Mirror ? MirrorAnimation(anim) : anim);
284        }
285    }
286
287    Dictionary<Direction, Animation> DirectionalAnimations(Animation left, Animation right, Animation up, Animation down)
288    {
289        var dict = new Dictionary<Direction, Animation>();
290        dict[Direction.Left] = left;
291        dict[Direction.Right] = right;
292        dict[Direction.Up] = up;
293        dict[Direction.Down] = down;
294        return dict;
295    }
296
297    void Intro()
298    {
299        storyItems.Add(new StoryItem(new Queue<string>(new[] { "Oi, Alex",
300        "",
301        "Coyotes on a train.",
302        "Coyotes on a train, that's all I'm sayin'",
303        "-Ana"
304        }), LetterImage));
305
306        //ShowTextItem(storyItems[0]);
307    }
308
309    void ShowTextItem(StoryItem item)
310    {
311        viewing = true;
312        messagesviewed = new Queue<string>(item.Message);
313       
314        Level.AmbientLight -= 0.75;
315
316        frame = new GameObject(item.InventoryImage) { Width = Window.Width * 0.25, Height = Window.Height * 0.25, Position = Camera.Position };
317        Add(frame, 2);
318
319        midpoint = 26 * item.Message.Count() * 0.5;
320        Pause();
321
322    }
323
324    /// <summary>
325    /// Peilaa kaikki animaation kuvat.
326    /// </summary>
327    private static Animation MirrorAnimation(Animation anim)
328    {
329        return new Animation(anim.Select(Image.Mirror).ToArray()) { FPS = anim.FPS };
330    }
331
332    private void StartGame()
333    {
334        ClearAll();
335        CreatePlayer(new Vector(0, -300), TILE_SIZE, TILE_SIZE, Angle.Zero, Shape.Rectangle, "", null);
336        CreateLevel("level1");
337        //CreatePlayer(new Vector(-Level.Width/3, Level.Bottom + TILE_SIZE * 2));
338        SetControls();
339       
340        Camera.Follow(player);
341        Camera.ZoomFactor = 2.0;
342    }
343
344    void SetControls()
345    {
346        Keyboard.Listen(Key.Left, ButtonState.Down, player.Move, null, Direction.Left);
347        Keyboard.Listen(Key.Right, ButtonState.Down, player.Move, null, Direction.Right);
348        Keyboard.Listen(Key.Up, ButtonState.Down, player.Move, null, Direction.Up);
349        Keyboard.Listen(Key.Down, ButtonState.Down, player.Move, null, Direction.Down);
350
351        Keyboard.Listen(Key.X, ButtonState.Pressed, delegate { UseItem(player.ActiveItem, player.Sword, () => player.ActiveItem.UseKeyPressed()); }, null);
352        Keyboard.Listen(Key.X, ButtonState.Released, delegate { UseItem(player.ActiveItem, player.Sword, () => player.ActiveItem.UseKeyReleased()); }, null);
353        Keyboard.Listen(Key.X, ButtonState.Down, delegate { UseItem(player.ActiveItem, player.Sword, () => player.ActiveItem.UseKeyDown()); }, null);
354
355        Keyboard.Listen(Key.Z, ButtonState.Pressed, delegate { UseItem(player.Sword, player.ActiveItem, () => player.Sword.UseKeyPressed()); }, null);
356        Keyboard.Listen(Key.Z, ButtonState.Released, delegate { UseItem(player.Sword, player.ActiveItem, () => player.Sword.UseKeyReleased()); }, null);
357        Keyboard.Listen(Key.Z, ButtonState.Down, delegate { UseItem(player.Sword, player.ActiveItem, () => player.Sword.UseKeyDown()); }, null);
358
359        Keyboard.Listen(Key.Space, ButtonState.Pressed, CycleItems, null);
360
361        Keyboard.Listen(Key.Escape, ButtonState.Pressed, ConfirmExit, null);
362    }
363
364    /// <summary>
365    /// Luo pelaajan.
366    /// </summary>
367    void CreatePlayer(Vector position, double width, double height, Angle angle, Shape shape, string name, Dictionary<string, string> properties)
368    {
369        player = new Player();
370        player.CanRotate = false;
371        player.MovementSpeed = new DoubleMeter(2300, 0, 2300);
372        player.Position = position;
373        player.MoveAnimations = DirectionalAnimations(playerWalkLeft, playerWalkRight, playerWalkUp, playerWalkDown);
374        player.SwingAnimations = DirectionalAnimations(playerSwingLeft, playerSwingRight, playerSwingUp, playerSwingDown);
375        player.ShootAnimations = DirectionalAnimations(playerShootLeft, playerShootRight, playerShootUp, playerShootDown);
376        player.Image = playerWalkDown.CurrentFrame;
377        Add(player, 1);
378
379        player.Sword = new Sword(player);
380        player.Inventory.Add(new Pistol(player));
381        player.Inventory.Add(new Monocle(player));
382        player.Inventory.Add(new Grenade(player));
383
384        //player.Health.Value = 3; // Alkuun vain kolme sydäntä.
385        player.Health.Value = player.Health.MaxValue = 100;
386
387        AddCollisionHandler(player, "exit", CollidesWithExit);
388        AddCollisionHandler(player, "enemy", delegate(PhysicsObject p, PhysicsObject e)
389        {
390            player.Health.Value--;
391        });
392
393        AddCollisionHandler(player, "ammobox", delegate(PhysicsObject p, PhysicsObject box)
394        {
395            box.Destroy();
396            var gun = player.Inventory.FirstOrDefault(i => i is Pistol);
397            if (gun != null)
398            {
399                gun.Usages.Value += 5;
400            }
401        });
402
403        AddCollisionHandler(player, "grenadebox", delegate(PhysicsObject p, PhysicsObject box)
404        {
405            box.Destroy();
406            var grenades = player.Inventory.FirstOrDefault(i => i is Grenade);
407            if (grenades != null)
408            {
409                grenades.Usages.Value += 5;
410            }
411        });
412
413        AddCollisionHandler(player, "storyTime", StoryTime);
414    }
415
416
417    void StoryTime(PhysicsObject player, PhysicsObject storyBlock)
418    {
419        storyItems.Add(new StoryItem(new Queue<string>(new[] { "Oi, boy!",
420            "There something wrong with your head?",
421            "It's dangerous out there - you did see the coyotes, right?",
422            "",
423            "Just... take this.",
424            "You've used a sword before, right?",
425            "",
426            "-Hobo in a cave"
427        }), FoodImage));
428
429        storyBlock.Tag = "none";
430        storyBlock.IgnoresCollisionResponse = true;
431
432        ShowTextItem(storyItems[1]);
433        BuildRightBar();
434    }
435
436    void UseItem(Item item, Item otherItem, Action action)
437    {
438        if (player.IsDestroyed)
439            return;
440
441        bool inUse = false;
442        if (otherItem != null)
443        {
444            inUse = otherItem.InUse;
445        }
446
447        if (item != null && !inUse)
448        {
449            action();
450        }
451    }
452
453    void CycleItems()
454    {
455        // Kaman vaihtossa simuloidaan että oltaisiin laskettu irti käyttönäppäimestä.
456        if (player.ActiveItem != null)
457        {
458            player.ActiveItem.CancelUse();
459        }
460        if (player.Sword != null)
461        {
462            player.Sword.CancelUse();
463        }
464
465        player.CycleItems();
466        UpdateItemCycleImages();
467    }
468
469    void UpdateItemCycleImages()
470    {
471        prevItem.UpdateImage(player.PrevItem);
472        currentItem.UpdateImage(player.ActiveItem);
473        nextItem.UpdateImage(player.NextItem);
474
475        if (player.Sword != null)
476        {
477            swordItem.Image = BigSwordImage;
478        }
479    }
480
481    void CollidesWithEnemy(PhysicsObject player, PhysicsObject enemy)
482    {
483
484    }
485
486    /*
487    void CollidesWithExit(PhysicsObject pPlayer, PhysicsObject pExit)
488    {
489        var oldExit = pExit as Exit;
490        if (oldExit == null || oldExit.TargetLevel == null || pExit.IsDestroying || pExit.IsDestroyed)
491            return;
492
493        enemies.Clear();
494        ClearAll();
495
496        CreateLevel(oldExit.TargetLevel);
497        var targetExit = exits.FirstOrDefault(e => e.Name == oldExit.TargetExitName);
498       
499        BuildUI();
500        SetControls();
501        player.Position = targetExit.Position + Direction.Inverse(targetExit.Position.Angle.MainDirection).GetVector() * TILE_SIZE * 2;
502        Add(player);
503    }
504     */
505
506    void CollidesWithExit(PhysicsObject pPlayer, PhysicsObject pExit)
507    {
508        if (!canUseExit)
509            return;
510
511        canUseExit = false;
512        Timer.SingleShot(6, () => canUseExit = true);
513
514        var oldExit = pExit as Exit;
515        if (oldExit == null || transition || oldExit.TargetLevel == null || pExit.IsDestroying || pExit.IsDestroyed)
516            return;
517
518        oldExit.Destroy();
519
520        var oldExitDirection = GetExitDirection(oldExit);
521        transition = true;
522
523        enemies.Clear();
524
525        // Otetaan vanhat objektit talteen.
526        oldObjects.Clear();
527        foreach (var obj in GetObjects(o => o != player))
528        {
529            if (obj != player)
530            {
531                oldObjects.Add(obj);
532            }
533        }
534
535        // Luodaan seuraava kenttä.
536        exits.Clear();
537        CreateLevel(oldExit.TargetLevel);
538
539        // Etsitään seuraavan kentän kohde exitti johon siirrytään.
540        var targetExit = exits.FirstOrDefault(e => e.Name == oldExit.TargetExitName);
541
542        if (targetExit == null)
543        {
544            throw new Exception("Tarkisita kenttien exittien nimet.");
545        }
546
547        // Jompikumpi uloskäynti ei ole kentän laidalla, sulava siirtyminen ei ole mahdollista.
548        if (GetExitDirection(targetExit) == Direction.None || oldExitDirection == Direction.None)
549        {
550            transition = false;
551            oldObjects.ForEach(o => o.Destroy());
552            oldObjects.Clear();
553
554            BuildUI();
555
556            // Yritetään päätellä pelaajalle joku järkevä paikka.
557            player.Position = targetExit.Position + Direction.Inverse(targetExit.Position.Angle.MainDirection).GetVector() * TILE_SIZE * 4;
558           
559            //Camera.Follow(player);
560            //Camera.ZoomFactor = 2.0;
561            return;
562        }
563
564        // Pysäytetään peli siirtymän ajaksi.
565        Pause();
566        PhysicsEnabled = false;
567
568        // Lasketaan siirtymävektorit.
569        Direction dir = GetExitDirection(targetExit);
570        Vector exitDelta = targetExit.Position - oldExit.Position;
571        Vector transitionVector = Level.Size;
572        if (dir == Direction.Left || dir == Direction.Right)
573        {
574            transitionVector.X *= dir.GetVector().X;
575            transitionVector.Y = exitDelta.Y;
576        }
577        else
578        {
579            transitionVector.Y *= dir.GetVector().Y;
580            transitionVector.X = exitDelta.X;
581        }
582
583        // Siirretään vanhoja objekteja ja pelaajaa.
584        foreach (var obj in oldObjects)
585        {
586            obj.Position += transitionVector;
587        }
588        //player.Position += transitionVector + Direction.Inverse(dir).GetVector() * TILE_SIZE * 4;
589        player.Position = targetExit.Position + Direction.Inverse(targetExit.Position.Angle.MainDirection).GetVector() * TILE_SIZE * 2;
590
591        // Zoomataan kameraa sopivasti ja liikutetaan se vanhan kentän päälle, josta se sitten siirtyy uuden kentän päälle.
592        Camera.Position += transitionVector;
593        Vector pos = Camera.Position;
594        //Camera.Follow(player);
595        //Camera.ZoomFactor = 2.0;
596        Camera.Position = pos;
597    }
598
599    /// <summary>
600    /// Palauttaa suunnan millä puolella kenttää uloskäynti on.
601    /// </summary>   
602    Direction GetExitDirection(Exit exit)
603    {
604        const double epsilon = 1e-3;
605        Func<double, double, bool> isSame = (x, y) => Math.Abs(y - x) < epsilon;
606
607        if (isSame(exit.Top, Level.Top))
608        {
609            return Direction.Up;
610        }
611        if (isSame(exit.Bottom, Level.Bottom))
612        {
613            return Direction.Down;
614        }
615        if (isSame(exit.Left, Level.Left))
616        {
617            return Direction.Left;
618        }
619        if (isSame(exit.Right, Level.Right))
620        {
621            return Direction.Right;
622        }
623        return Direction.None;
624    }
625
626    protected override void PausedUpdate(Time gameTime)
627    {
628        base.PausedUpdate(gameTime);
629        double dt = gameTime.SinceLastUpdate.TotalSeconds;
630
631        const double transitionSpeed = 400.0;
632
633        if (transition && !viewing)
634        {
635            Camera.Position += Camera.Position.Normalize() * -transitionSpeed * dt;
636
637            // Siirtymä on ohi, jatketaan peliä ja poistetaan edellinen kenttä.
638            if (Camera.Position.Magnitude < transitionSpeed * dt)
639            {
640                transition = false;
641                Pause();
642                PhysicsEnabled = true;
643                Camera.Position = Vector.Zero;
644
645                foreach (var obj in oldObjects)
646                {
647                    obj.Destroy();
648                }
649                oldObjects.Clear();
650
651                BuildUI();
652            }
653        }
654
655        if(viewing && !transition)  //!transition perhaps being somewhat unnecessary, but now I do feel a bit safer.
656        {
657            second += dt;
658            if(second > 1)
659            {
660                if (messagesviewed.Count < 1)   //The letter's read, better head back.
661                {
662                    Level.AmbientLight = 1;
663                    frame.Destroy();
664                    GetObjectsWithTag("labelWaitingToDie").ForEach(g => g.Destroy());
665                    viewing = false;
666                    Pause();
667                    return;
668                }
669
670                second = 0;
671                var storyLabel = new Label(messagesviewed.Dequeue()) { TextColor = Color.White };
672                storyLabel.Y = -midpoint + storyLabel.Height * messagesviewed.Count() + 1;
673                storyLabel.Tag = "labelWaitingToDie";
674                Add(storyLabel);
675
676            }
677        }
678    }
679
680    protected override void Update(Time time)
681    {
682        player.UpdateCreature(time);
683        enemies.ForEach(e => e.UpdateCreature(time));
684        base.Update(time);
685    }
686}
Note: See TracBrowser for help on using the repository browser.