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

Revision 6714, 22.1 KB checked in by sieerinn, 6 years ago (diff)

Kenttäsiirtymät laitettu takaisin.

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