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

Revision 6704, 19.9 KB checked in by sieerinn, 4 years ago (diff)

Viime commitissa saattoi olla ei-kääntyvää koodia. Nyt ei pitäisi olla virheitä enää.

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