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

Revision 6715, 22.2 KB checked in by empaheik, 6 years ago (diff)

Kakkoskentässä kojootteja.

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