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

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