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

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