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

Revision 6699, 20.4 KB checked in by empaheik, 6 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    void CollidesWithEnemy(PhysicsObject player, PhysicsObject enemy)
426    {
427
428    }
429
430    void CollidesWithExit(PhysicsObject pPlayer, PhysicsObject pExit)
431    {
432        var oldExit = pExit as Exit;
433        if (oldExit == null || transition)
434            return;
435
436        transition = true;
437
438        enemies.Clear();
439
440        // Otetaan vanhat objektit talteen.
441        oldObjects.Clear();
442        foreach (var obj in GetObjects(o => o != player))
443        {
444            if (obj != player)
445            {
446                oldObjects.Add(obj);
447            }
448        }
449
450        // Luodaan seuraava kenttä.
451        exits.Clear();
452        CreateLevel(oldExit.TargetLevel);
453
454        // Etsitään seuraavan kentän kohde exitti johon siirrytään.
455        var targetExit = exits.FirstOrDefault(e => e.Name == oldExit.TargetExitName);
456
457        if (targetExit == null)
458        {
459            throw new Exception("Tarkisita kenttien exittien nimet.");
460        }
461
462        // Jompikumpi uloskäynti ei ole kentän laidalla, sulava siirtyminen ei ole mahdollista.
463        if (GetExitDirection(targetExit) == Direction.None || GetExitDirection(oldExit) == Direction.None)
464        {
465            transition = false;
466            oldObjects.ForEach(o => o.Destroy());
467            oldObjects.Clear();
468
469            BuildRightBar();
470            BuildInventoryCycle();
471            UpdateItemCycleImages();
472
473            // Yritetään päätellä pelaajalle joku järkevä paikka.
474            player.Position = targetExit.Position + Direction.Inverse(targetExit.Position.Angle.MainDirection).GetVector() * TILE_SIZE * 2;
475            Camera.ZoomToLevel();
476            return;
477        }
478
479        // Pysäytetään peli siirtymän ajaksi.
480        Pause();
481        PhysicsEnabled = false;
482
483        // Lasketaan siirtymävektorit.
484        Direction dir = GetExitDirection(targetExit);
485        Vector exitDelta = targetExit.Position - oldExit.Position;
486        Vector transitionVector = Level.Size;
487        if (dir == Direction.Left || dir == Direction.Right)
488        {
489            transitionVector.X *= dir.GetVector().X;
490            transitionVector.Y = exitDelta.Y;
491        }
492        else
493        {
494            transitionVector.Y *= dir.GetVector().Y;
495            transitionVector.X = exitDelta.X;
496        }
497
498        // Siirretään vanhoja objekteja ja pelaajaa.
499        foreach (var obj in oldObjects)
500        {
501            obj.Position += transitionVector;
502        }
503        player.Position += transitionVector + Direction.Inverse(dir).GetVector() * TILE_SIZE * 4;
504
505        // Zoomataan kameraa sopivasti ja liikutetaan se vanhan kentän päälle, josta se sitten siirtyy uuden kentän päälle.
506        Camera.Position += transitionVector;
507        Vector pos = Camera.Position;
508        Camera.ZoomToLevel();
509        Camera.Position = pos;
510    }
511
512    /// <summary>
513    /// Palauttaa suunnan millä puolella kenttää uloskäynti on.
514    /// </summary>   
515    Direction GetExitDirection(Exit exit)
516    {
517        const double epsilon = 1e-3;
518        Func<double, double, bool> isSame = (x, y) => Math.Abs(y - x) < epsilon;
519
520        if (isSame(exit.Top, Level.Top))
521        {
522            return Direction.Up;
523        }
524        if (isSame(exit.Bottom, Level.Bottom))
525        {
526            return Direction.Down;
527        }
528        if (isSame(exit.Left, Level.Left))
529        {
530            return Direction.Left;
531        }
532        if (isSame(exit.Right, Level.Right))
533        {
534            return Direction.Right;
535        }
536        return Direction.None;
537    }
538
539    protected override void PausedUpdate(Time gameTime)
540    {
541        base.PausedUpdate(gameTime);
542        double dt = gameTime.SinceLastUpdate.TotalSeconds;
543
544        const double transitionSpeed = 500.0;
545
546        if (transition && !viewing)
547        {
548            Camera.Position += Camera.Position.Normalize() * -transitionSpeed * dt;
549
550            // Siirtymä on ohi, jatketaan peliä ja poistetaan edellinen kenttä.
551            if (Camera.Position.Magnitude < transitionSpeed * dt)
552            {
553                transition = false;
554                Pause();
555                PhysicsEnabled = true;
556                Camera.Position = Vector.Zero;
557
558                foreach (var obj in oldObjects)
559                {
560                    obj.Destroy();
561                }
562                oldObjects.Clear();
563
564                BuildRightBar();
565                BuildInventoryCycle();
566                UpdateItemCycleImages();
567            }
568        }
569
570        if(viewing && !transition)  //!transition perhaps being somewhat unnecessary, but now I do feel a bit safer.
571        {
572            second += dt;
573            if(second > 1)
574            {
575                if (messagesviewed.Count < 1)   //The letter's read, better head back.
576                {
577                    Level.AmbientLight = 1;
578                    frame.Destroy();
579                    GetObjectsWithTag("labelWaitingToDie").ForEach(g => g.Destroy());
580                    viewing = false;
581                    Pause();
582                    return;
583                }
584
585                second = 0;
586                var storyLabel = new Label(messagesviewed.Dequeue()) { TextColor = Color.White };
587                storyLabel.Y = -midpoint + storyLabel.Height * messagesviewed.Count() + 1;
588                storyLabel.Tag = "labelWaitingToDie";
589                Add(storyLabel);
590
591            }
592        }
593    }
594
595    protected override void Update(Time time)
596    {
597        player.UpdateCreature(time);
598        enemies.ForEach(e => e.UpdateCreature(time));
599        base.Update(time);
600    }
601}
Note: See TracBrowser for help on using the repository browser.