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

Revision 6708, 21.1 KB checked in by empaheik, 4 years ago (diff)

Aita, jonka voi tuhota luodeilla, muttei miekalla. Hmm. Kyllä se tästä.

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