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

Revision 6707, 21.0 KB checked in by empaheik, 6 years ago (diff)

Hoboluolasta puuttuu vielä miekka + hobo (eli tärkeimmät, lel). Lisätarinaa.

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