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

Revision 6703, 19.8 KB checked in by sieerinn, 8 years ago (diff)

Junaa aloiteltu, puuttuvat .anim tiedostot lisätty.

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