source: 2015/26/ohjaajat/HillbillyRun/HillbillyRun/HillbillyRun/HillbillyRun/HillbillyRun.cs @ 6405

Revision 6405, 50.7 KB checked in by sieerinn, 5 years ago (diff)

Kuollessa tulee gameover kuva. Kameraongelmia korjailtu.

Line 
1using System;
2using System.Collections.Generic;
3using System.Diagnostics.CodeAnalysis;
4using System.Linq;
5using Jypeli;
6using Jypeli.Assets;
7using Jypeli.Controls;
8using Jypeli.Effects;
9using Jypeli.Widgets;
10
11/* Muistutuksia.
12 *
13 * Layerit
14 *   3 -
15 *   2 -
16 *   1 -
17 *   0 -
18 *  -1 - Näkyvä maanpinta.
19 *  -2 - Talot taustalla.
20 * 
21 * CollisionIgnoreGroupit
22 *  3 - Ryömivät ja liekit. //TODO: lisätä noidatkin tänne
23 *  2 - Kärry.
24 *
25*/
26
27#region Improvement
28public abstract class Improvement : PlatformCharacter
29{
30    public List<Animation> Animations { get; set; }
31
32    protected Improvement(double width, double height)
33        : base(width, height)
34    {
35
36    }
37
38    public HillBilly Owner
39    {
40        get { return owner; }
41        set { owner = value; }
42    }
43    private HillBilly owner;
44
45    public virtual void DoTheThing(HillbillyRun game) { }
46
47    public SoundEffect sound;
48
49    public void Rid()
50    {
51        Owner.AnimWalk = Owner.Animations[0];
52        Owner.AnimIdle = Owner.Animations[1];
53        Owner.Improvement = null;
54        Owner = null;
55    }
56}
57
58public class MilkImprovement : Improvement
59{
60    public override void DoTheThing(HillbillyRun game)
61    {
62        //HillbillyRun game = this.Game as HillbillyRun;
63        Owner.PlayAnimation(Animations[1]);
64        game.CreateMilkParticles(this.Owner, 10, 200, 300, new Vector(0, -30), "milkparticle");
65        this.sound.Play();
66    }
67
68    public MilkImprovement(double width, double height)
69        : base(width, height)
70    {
71
72    }
73}
74
75
76public class PitchForkImprovement : Improvement
77{
78    public override void DoTheThing(HillbillyRun game)
79    {
80        //HillbillyRun game = this.Game as HillbillyRun;
81        //game.CreateMilkParticles(this.Owner, 10, 200, 300, new Vector(0, -30), "milkparticle");
82        //TODO: hitting things w/ pitchfork
83        game.ForkThings(Owner);
84    }
85
86    public PitchForkImprovement(double width, double height)
87        : base(width, height)
88    {
89
90    }
91}
92#endregion
93
94public class HillBilly : PlatformCharacter
95{
96    public List<Animation> Animations { get; set; }
97
98    private IntMeter lifeCounter = new IntMeter(4, 0, 4);
99    public IntMeter Life
100    {
101        set { lifeCounter = value; }
102        get { return lifeCounter; }
103    }
104
105    public Improvement Improvement { get; set; }
106
107    public Label Name { get; set; }
108
109    private ProgressBar visibleLife;
110    public ProgressBar VisibleLife
111    {
112        get { return visibleLife; }
113        set { visibleLife = value; }
114    }
115
116    public void GainImprovement(Improvement improvement)
117    {
118
119        this.AnimWalk = improvement.Animations[0];
120        this.AnimIdle = new Animation(improvement.Animations[0].CurrentFrame);
121        this.Improvement = improvement;
122        improvement.Owner = this;
123
124        //improvement.Destroy();
125    }
126
127    public void UseImprovement()
128    {
129        HillbillyRun game = this.Game as HillbillyRun;
130        if (this.Improvement == null)
131        {
132            game.CreateMilkParticles(this, 5, 100, 100, new Vector(0, Height / 3), "tears");
133        }
134        else if (Animation != this.Improvement.Animations[1])
135        {
136            this.Improvement.DoTheThing(game);
137        }
138    }
139
140    public HillBilly(double leveys, double korkeus, String name)
141        : base(leveys, korkeus)
142    {
143
144        this.visibleLife = new ProgressBar(50, 15); //(this.Width, this.Height/8);
145        this.Add(visibleLife);
146        this.visibleLife.Position += new Vector(0, this.Height / 2 + this.Height / 5);
147        this.visibleLife.BindTo(this.lifeCounter);
148        this.visibleLife.Color = Color.Black;
149
150        this.Name = new Label(name);
151        this.Add(Name);
152        this.Name.Position += new Vector(0, this.Height / 2 + this.Height / 3);
153    }
154}
155
156/// <summary>
157/// An MMO where you go to war with mages in the lava kingdom.
158/// </summary>
159public class HillbillyRun : PhysicsGame
160{
161    private List<HillBilly> players = new List<HillBilly>();
162    private List<HillBilly> startingPlayers = new List<HillBilly>();
163
164    //private List<double> playerPositionsX = new List<double>();
165    //private List<double> playerPositionsY = new List<double>();
166
167    private const double PLAYER_SPEED = 180;
168    public const double TILE_SIZE = 70;
169
170    public String[] names = new String[] { "Adolfiina", "Rasputiina", "Hermanniina", "Joosefiina" };
171
172    private double cameraTargetX; // Sijainti jossa kameran pitäisi olla.
173    private Vector cameraTarget;
174
175    private double cameraOffset; //= 400; // TODO: Tämän voisi ehkä laskea jostain (esim. ikkunan leveydestä kolmasosa tai jotain).
176    private double cameraSpeed = 2.0; // Kameran liikkumisnopeus.
177
178    # region Images
179    private Image[] groundImages = LoadImages("ground", "ground_forest", "ground_volcano");
180    private Image[] groundTopImages = LoadImages("ground_top", "ground_top_forest", "ground_top_volcano");
181    private Image[] foregroundDecorations = LoadImages("viljaa", "grass", "grass");
182    private Image[] blockImages = LoadImages("box2", "rock");
183
184    private Image[] houseImages = LoadImages("house", "houseburned");
185
186    private Image cartImage = LoadImage("cart");
187    private Image milkImage = LoadImage("tonkkaitem");
188    private Image pitchforkImage = LoadImage("fork");
189
190    private Image cartWheelImage = LoadImage("cartwheel");
191
192    private Image smokeImage1 = LoadImage("smoke1");
193    private Image smokeImage2 = LoadImage("smoke2");
194
195    private Image treeTrunkImage = LoadImage("bigtree");
196    private Image treeBottomImage = LoadImage("bigtree3x2");
197    private Image treeBranchImage = LoadImage("bigtreebranch");
198    private Image forestBackgroundImage = LoadImage("forestbackground");
199    private Image volcanoBackgroundImage = LoadImage("volcanobackground");
200    private Image swingTrapImage = LoadImage("tukki");
201
202    private Image gateImage = LoadImage("gate");
203    private Image leverLeftImage = LoadImage("lever_left");
204    private Image leverRightImage = LoadImage("lever_right");
205
206    private Image lavaWallImage = LoadImage("lavawall");
207    private Image spikeImage = LoadImage("spikes");
208    private Image spikeLeftImage = LoadImage("spikeleft");
209
210    private Image prisonerImage = LoadImage("prisoner");
211
212    private Image deathImage = LoadImage("loppukuva");
213
214    private Animation crawl;
215    private Animation blaze;
216
217    private Animation pitchforkAttack;
218    private Animation pitchforkWalk;
219    private Animation milkThrow;
220    private Animation milkWalk;
221    private Animation normalWalk;
222    private Animation playerIdle;
223    private Animation playerJump;
224    private Animation playerFall;
225    private Animation firemageAnimation;
226    private Animation firemageCastAnimation;
227    private Animation wormAnimation;
228    private Animation tattiMageAnimation;
229    private Animation tattiMageCastAnimation;
230
231    private Animation bossRiseAnimation;
232    private Animation bossFallAnimation;
233    private Animation bossHitAnimation;
234    private Animation bossHurtAnimation;
235    #endregion
236
237    #region Sounds
238    private SoundEffect splash = LoadSoundEffect("splash");
239    #endregion
240
241    private double leftCamLimit;
242    private double rightCamLimit;
243    private double bottomCamLimit;
244
245    private int levelNumber = 0;
246
247    private Action activateBoss;
248
249    public override void Begin()
250    {
251        Initializing();
252        IntroSequence();
253        //MainMenu();
254        //StartGame();
255    }
256
257    #region Intro
258    void IntroSequence()
259    {
260        Level.Background.Color = Color.Black;
261
262        Keyboard.Listen(Key.Escape, ButtonState.Pressed, SkipIntro, "Skip");
263
264        ShowMadeWithJypeli();
265
266        Timer.SingleShot(4.0, delegate // TODO get rid of fixed amount of seconds
267        {
268            ShowBackgroundStory();
269        });
270
271
272
273    }
274
275    void ShowMadeWithJypeli()
276    {
277        Image madewithImage = LoadImage("madewithjypeli");
278
279        double w = Window.Width / 4;
280        double h = w * (madewithImage.Height / (double)madewithImage.Width); // take aspect ratio from the picture
281
282        GameObject madewithjypeliObject = new GameObject(w, h);
283        madewithjypeliObject.Image = madewithImage;
284        Add(madewithjypeliObject);
285
286        // Tässä oli jotain epätehokasta feidailua tuolle Made with Jypeli -kuvalle. Ei tunnu päivittyvän joka framella tai on muuten vain tosi raskas...
287
288        //fadeTimer = new Timer();
289        //fadeTimer.Interval = 0.5;
290        //fadeTimer.Timeout += delegate
291        //{
292        //    madewithjypeliObject.Image = LerpImage(madewithjypeliObject.Image, Color.Black, 0.6);
293        //};
294        //fadeTimer.Start(5);
295
296        Timer.SingleShot(1.2, delegate
297        {
298            madewithjypeliObject.Destroy();
299        });
300
301    }
302
303    Image LerpImage(Image original, Color targetColor, double ratio)
304    {
305        Image newImage = new Image(original.Width, original.Height, Color.Transparent);
306        for (int y = 0; y < original.Height; y++)
307        {
308            for (int x = 0; x < original.Width; x++)
309            {
310                newImage[y, x] = Color.Lerp(original[y, x], targetColor, ratio);
311            }
312        }
313        return newImage;
314    }
315
316    void ShowBackgroundStory()
317    {
318        ShowStoryText(new Queue<string>(new[] { "Innit just da warmest here, sis?", "Seems to me our establishment's a-burnin'.", "...Must've been 'em nasty witches." }));
319    }
320
321    void ShowStoryText(Queue<string> messages)
322    {
323        // Mennään alkuvalikkoon jos ei ole enää viestejä.
324        if (messages.Count == 0)
325        {
326            MainMenu();
327            return;
328        }
329
330        // Näytetään nykyinen viesti.
331        var storyLabel = new Label(messages.Dequeue()) { TextColor = Color.Black, TextScale = new Vector(3, 3) };
332        Add(storyLabel);
333
334        var textTimer = new Timer { Interval = 0.05 };
335        textTimer.Timeout += () => storyLabel.TextColor = Color.Lerp(storyLabel.TextColor, Color.White, 0.05);
336        textTimer.Start();
337
338        // Näytetään seuraava viesti muutaman sekunnin päästä.
339        Timer.SingleShot(4, delegate { storyLabel.Destroy(); ShowStoryText(messages); });
340    }
341
342    void SkipIntro()
343    {
344        ClearTimers();
345        ClearAll();
346        Keyboard.Clear();
347        MainMenu();
348    }
349
350    void MainMenu()
351    {
352        Level.Background.Color = Color.DarkBlue;
353
354        MultiSelectWindow mainmenu = new MultiSelectWindow("Main menu", "Start game", "Credits", "Exit");
355        mainmenu.AddItemHandler(0, StartGame);
356        mainmenu.AddItemHandler(1, ShowCredits);
357        mainmenu.AddItemHandler(2, Exit);
358        mainmenu.DefaultCancel = 2;
359        Add(mainmenu);
360    }
361
362    void ShowCredits()
363    {
364        MessageWindow win = new MessageWindow("This game was made in one week by\nSimo Rinne,\nEmma Heikura,\n and Jouni Potila\n using University of Jyväskylä's\n game programming library Jypeli");
365        win.Closed += delegate
366        {
367            ClearAll();
368            MainMenu();
369        };
370        Add(win);
371    }
372    #endregion
373
374    void StartGame()
375    {
376        ClearAll();
377        CreateLevel();
378        ScreenSettings();
379        SetControls();
380    }
381
382    public void NextLevel()
383    {
384        levelNumber++;
385        StartGame();
386    }
387
388    void Initializing()
389    {
390        crawl = LoadAnimation("crawl");
391        blaze = LoadAnimation("flame");
392        pitchforkAttack = LoadAnimation("attack");
393        pitchforkWalk = LoadAnimation("hanko");
394        milkThrow = LoadAnimation("heitto");
395        milkWalk = LoadAnimation("tonkka");
396        normalWalk = LoadAnimation("pwalk");
397        playerIdle = LoadAnimation("idle");
398        playerJump = LoadAnimation("jump");
399        playerFall = LoadAnimation("fall");
400        firemageAnimation = LoadAnimation("firemage");
401        firemageCastAnimation = LoadAnimation("firemagecast");
402        wormAnimation = LoadAnimation("worm");
403        tattiMageAnimation = LoadAnimation("tattimage");
404        tattiMageCastAnimation = LoadAnimation("tattimagecast");
405        bossRiseAnimation = LoadAnimation("bossrise");
406        bossFallAnimation = LoadAnimation("bossfall");
407        bossHitAnimation = LoadAnimation("bosshit");
408        bossHurtAnimation = LoadAnimation("bosshurt");
409
410        cameraOffset = Window.Width / 4;
411    }
412
413    void ScreenSettings()
414    {
415        Window.Width = 1800;
416        Window.Height = 900;
417
418        leftCamLimit = Level.Left + Window.Width / 2.0;
419        rightCamLimit = Level.Right - Window.Width / 2.0;
420        bottomCamLimit = Level.Bottom + Window.Height / 2.0;
421
422        Camera.X = cameraTarget.X = cameraTargetX = leftCamLimit;
423
424        Timer cameraTimer = new Timer();
425        cameraTimer.Interval = 1 / 30.0;
426        cameraTimer.Timeout += UpdateCamera;
427        cameraTimer.Start();
428
429        // Background trees.
430        if (levelNumber < 2)
431        {
432            GameObject forestBackground = new GameObject(Level.Width, Level.Height);
433            forestBackground.Image = forestBackgroundImage;
434            forestBackground.TextureWrapSize = new Vector(5, 1);
435            Add(forestBackground, -3);
436            Layers[-3].RelativeTransition = new Vector(0.5, 1.0);
437        }
438
439        // Background color.
440        if (levelNumber == 0)
441        {
442            Level.Background.CreateGradient(Color.Black, Color.SkyBlue);
443        }
444        else if (levelNumber == 1)
445        {
446            Level.Background.CreateGradient(Color.Black, Color.FromHexCode("495147"));
447        }
448        else if (levelNumber == 2)
449        {
450            Level.Background.Image = volcanoBackgroundImage;
451            Level.Background.FitToLevel();
452        }
453    }
454
455    #region LevelCreation
456    void CreateLevel()
457    {
458        startingPlayers.Clear();
459        players.Clear();
460        Gravity = new Vector(0, -1000);
461
462        //Tilemap
463        ColorTileMap level = ColorTileMap.FromLevelAsset("level" + levelNumber);
464        //ColorTileMap level = ColorTileMap.FromLevelAsset("bosstest");
465        level.SetTileMethod(Color.Black, CreateGroundTop);
466        level.SetTileMethod(Color.Brown, CreateGround);
467
468        level.SetTileMethod(Color.Cyan, CreateHouse);
469        level.SetTileMethod(Color.PaintDotNetBlue, CreateBurnedHouse);
470
471        level.SetTileMethod(Color.Gold, CreatePlayer);
472        level.SetTileMethod(Color.Harlequin, CreateCart);
473        level.SetTileMethod(Color.FromHexCode("A17FFF"), CreatePitchfork);
474
475        level.SetTileMethod(Color.Gray, (new AbstractTileMap<Color>.TileMethod[] { CreateCrawly, CreateWorm, null })[levelNumber]); // ಠ_ಠ
476        level.SetTileMethod(Color.Red, (new AbstractTileMap<Color>.TileMethod[] { CreateFireMage, CreateShroom, CreateBoss })[levelNumber]);
477        level.SetTileMethod(Color.White, CreateBlockObject);
478        level.SetTileMethod(Color.DarkGray, CreateMilk);
479        level.SetTileMethod(Color.Rose, CreateFlame, true);
480        //level.SetTileMethod(Color.Azure, CreateDummy, Color.Azure);   //TODO: CreateSmoke
481        //level.SetTileMethod(Color.Orange, CreateDummy, Color.Orange); //TODO: CreateTombstone
482
483        level.SetTileMethod(Color.FromHexCode("FF6A00"), CreateTreeTrunk);  //Pihhh jostain heksoista. Ikävästi haisee luovuttamiselta! :----D
484        level.SetTileMethod(Color.FromHexCode("57007F"), CreateTreeRoot);   // Heksojen käytössä ei ole mitään hävettävää :P
485        level.SetTileMethod(Color.FromHexCode("00FF21"), CreateTreeBranch);
486        level.SetTileMethod(Color.FromHexCode("5B7F00"), CreateSwingTrap);
487
488        level.SetTileMethod(Color.FromHexCode("7FBA00"), CreateGate);
489        level.SetTileMethod(Color.FromHexCode("4C7F00"), CreateLevel);
490
491        level.SetTileMethod(Color.FromHexCode("FF00DC"), CreateSpike, spikeImage);
492        level.SetTileMethod(Color.FromHexCode("C600AC"), CreateSpike, spikeLeftImage);
493
494        level.SetTileMethod(Color.FromHexCode("BADA55"), CreatePrisoner); // Lempivärini.
495
496        level.Optimize(Color.Brown);    //Color.Black   //Tekee jännittäviä asioita wheatille, jos optimoidaan (tietysti). Jotenn.
497        level.Execute(TILE_SIZE, TILE_SIZE);
498
499        // Kolmanteen kenttään laavaseinä.
500        if (levelNumber == 2)
501        {
502            GameObject lava = new GameObject(300, Level.Height);
503            lava.Image = lavaWallImage;
504            lava.X = Camera.ScreenToWorld(new Vector(-Window.Width / 2.0, 0)).X;
505            Add(lava, 3);
506
507            PhysicsObject lavaCollision = PhysicsObject.CreateStaticObject(50, Level.Height);
508            lavaCollision.IsVisible = false;
509            lavaCollision.Left = lava.Left;
510            lavaCollision.Tag = "lava";
511            Add(lavaCollision);
512
513            var updateTimer = new Timer { Interval = 0.01 };
514            updateTimer.Timeout += delegate
515            {
516                lava.Left = Camera.X - Window.Width / 2.0;
517                lavaCollision.Left = lava.Left;
518            };
519            updateTimer.Start();
520        }
521    }
522
523    void CreateBoss(Vector position, double width, double height)
524    {
525        GameObject boss = new GameObject(700, 522);
526        boss.Position = position;
527        boss.Top = position.Y - height * 0.5;
528        boss.Animation = bossRiseAnimation;
529        Add(boss, -3);
530
531        List<PhysicsObject> eyes = null; // Lista jotta voi käyttää kätevästi ForEach:ia.
532        var health = new IntMeter(3, 0, 3); // Vähenee vain heinähangolla.
533        var eyeHealth = new IntMeter(30, 0, 30); // Vähenee maidon loiskutuksella.
534
535        eyeHealth.LowerLimit += delegate
536        {
537            // Bossi kaatuaa maahan.
538            eyes.ForEach(e => e.IsVisible = false);
539            boss.Animation = bossFallAnimation;
540            boss.Animation.StopOnLastFrame = true;
541            boss.Animation.Start(1);
542
543            // Bossi palautuu takaisin lyöntianimaatioonsa hetken päästä.
544            Timer.SingleShot(5.0, delegate
545            {
546                boss.Animation = bossHitAnimation;
547                eyeHealth.Value = eyeHealth.MaxValue;
548                eyes.ForEach(e => e.IsVisible = true);
549            });
550        };
551
552        // Alue johon pitää lyödä heinähangolla.
553        var headCollision = PhysicsObject.CreateStaticObject(50, 50);
554        headCollision.IsVisible = false;
555        headCollision.IgnoresCollisionResponse = true;
556        Add(headCollision);
557        AddCollisionHandler(headCollision, "pitchfork", delegate(PhysicsObject h, PhysicsObject fork)
558        {
559            if (boss.Animation == bossFallAnimation)
560            {
561                health.Value--;
562
563                boss.Animation = bossHurtAnimation;
564                boss.Animation.Start(1);
565                boss.Animation.Played += () => boss.Animation = bossFallAnimation;
566            }
567        });
568        var headMoveTimer = new Timer { Interval = 0.01 };
569        headMoveTimer.Timeout += delegate
570        {
571            headCollision.Position = boss.Position + new Vector(-10, 0);
572        };
573        headMoveTimer.Start();
574        headCollision.Destroyed += headMoveTimer.Stop;
575
576        // Lyöntianimaation päätteeksi lisätään kiviä.
577        bossHitAnimation.Played += delegate
578        {
579            var rock = new PhysicsObject(30, 30);
580            rock.Position = boss.Position + new Vector(RandomGen.NextDouble(-650, 100), 300);
581            rock.Image = blockImages[1];
582            rock.Tag = "burn";
583            rock.LifetimeLeft = TimeSpan.FromSeconds(2.0);
584            Add(rock);
585        };
586
587        // Silmien luonti funktio.
588        Func<int, int, PhysicsObject> eyeFlame = (x, y) =>
589        {
590            var flame = PhysicsObject.CreateStaticObject(width * 0.3, height * 0.6);
591            flame.Animation = new Animation(blaze);
592            flame.IgnoresCollisionResponse = true;
593            flame.IsVisible = false;
594            flame.Animation.Start();
595            Add(flame);
596            AddCollisionHandler(flame, "milkparticle", delegate(PhysicsObject f, PhysicsObject particle)
597            {
598                // Otetaan vahinkoa vain kun ollaan näkyvillä.
599                if (flame.IsVisible)
600                {
601                    particle.Destroy();
602                    eyeHealth.Value--;
603                }
604            });
605            var moveTimer = new Timer { Interval = 0.01 };
606            moveTimer.Timeout += delegate
607            {
608                flame.Position = boss.Position + new Vector(x, y);
609            };
610            moveTimer.Start();
611            flame.Destroyed += moveTimer.Stop;
612            return flame;
613        };
614
615        eyes = new List<PhysicsObject>(new[] { eyeFlame(0, 150), eyeFlame(40, 145) });
616
617        // Tuhotaan bossi kun se kuolee.
618        health.LowerLimit += delegate
619        {
620            eyes.ForEach(e => e.Destroy());
621            boss.Destroy();
622            headCollision.Destroy();
623            Timer.SingleShot(4.0, Victory);
624        };
625
626        // Kutsutaan kun pelaajat saapuu kentän päähän.
627        activateBoss = delegate
628        {
629            // Nostetaan bossi ylös maasta.
630            boss.MoveTo(position + new Vector(0, boss.Height * 0.4), 500);
631            boss.Animation.Start(1);
632            boss.Animation.StopOnLastFrame = true;
633            boss.Animation.Played += delegate
634            {
635                // Taistelu alkaa.
636                eyes.ForEach(e => e.IsVisible = true);
637
638                // Laitetaan lyöntianimaatio päälle.
639                boss.Animation = bossHitAnimation;
640                boss.Animation.Start();
641            };
642        };
643    }
644
645    void CreatePrisoner(Vector position, double width, double height)
646    {
647        GameObject prisoner = new GameObject(width * 1.2, height * 2);
648        prisoner.Position = position;
649        prisoner.Y -= height / 2.0;
650        prisoner.Image = prisonerImage;
651        Add(prisoner, 1);
652    }
653
654    void CreateSpike(Vector position, double width, double height, Image img)
655    {
656        PhysicsObject spike = PhysicsObject.CreateStaticObject(width, height);
657        spike.Image = img;
658        spike.Position = position;
659        spike.Y -= (img == spikeImage) ? height * 0.5 : 0.0;
660        spike.Tag = "burn";
661        Add(spike);
662    }
663
664    void CreateGate(Vector position, double width, double height)
665    {
666        PhysicsObject gate = PhysicsObject.CreateStaticObject(width, height);
667        gate.Image = gateImage;
668        gate.Color = Color.HotPink;
669        gate.Position = position;
670        gate.Y -= height / 2.0;
671        gate.Tag = "gate";
672        Add(gate);
673    }
674
675    void CreateLevel(Vector position, double width, double height)
676    {
677        PhysicsObject lever = PhysicsObject.CreateStaticObject(width * 0.5, height);
678        lever.Image = leverLeftImage;
679        lever.Color = Color.Green;
680        lever.Position = position;
681        lever.Y -= height / 2.0;
682        lever.IgnoresCollisionResponse = true;
683        Add(lever);
684
685        AddCollisionHandler(lever, "player", delegate(PhysicsObject p, PhysicsObject l)
686        {
687            if (lever.Image == leverLeftImage)
688            {
689                lever.Image = leverRightImage;
690                GetObjectsWithTag("gate").ForEach(g => g.Destroy());
691            }
692        });
693    }
694
695    void CreateSwingTrap(Vector position, double width, double height)
696    {
697        const double swingArc = 30; // Heiluu 30 astetta edestakaisin.
698        const double swingSpeed = 1.5;
699        const double ropeLength = 400;
700        var center = position + new Vector(0, ropeLength);
701
702        // Ajastukset heilutteluun.
703        double deltaTime = 1 / 60.0;
704        double time = 0.0;
705
706        PhysicsObject swing = PhysicsObject.CreateStaticObject(width, height);
707        swing.Image = swingTrapImage;
708        swing.Position = position;
709        Add(swing);
710
711        GameObject rope = new GameObject(ropeLength, 10);
712        rope.Color = new Color(10, 30, 10);
713        Add(rope);
714
715        var updateTimer = new Timer { Interval = deltaTime };
716        updateTimer.Timeout += delegate
717        {
718            time += deltaTime;
719
720            // Heilurin kulma on sinimuotoinen funktio ajan suhteen.
721            var angle = Angle.FromDegrees(-90.0 + Math.Sin(time * swingSpeed) * swingArc);
722            swing.Position = center + angle.GetVector() * ropeLength;
723            swing.Angle = angle;
724
725            // Päivitetään narun sijainti ja kulma.
726            rope.Angle = angle;
727            rope.Position = center + angle.GetVector() * ropeLength * 0.5;
728        };
729        updateTimer.Start();
730    }
731
732    void CreatePitchfork(Vector position, double width, double height)
733    {
734        double size = 30;
735        double ratio = pitchforkImage.Width / pitchforkImage.Height;
736
737        Improvement fork = new PitchForkImprovement(size * ratio, size);
738        fork.Image = pitchforkImage;
739        fork.Animations = new List<Animation> { pitchforkWalk, pitchforkAttack };
740        fork.Position = position;
741        fork.Tag = "improvement";
742        Add(fork);
743
744    }
745
746    void CreateTreeBranch(Vector position, double width, double height)
747    {
748        PhysicsObject branch = PhysicsObject.CreateStaticObject(width, height);
749        branch.Position = position;
750        branch.Image = treeBranchImage;
751        Add(branch, -1);
752    }
753
754    void CreateTreeTrunk(Vector position, double width, double height)
755    {
756        GameObject trunk = new GameObject(width * 3, height);
757        trunk.Position = position;
758        trunk.Image = treeTrunkImage;
759        Add(trunk, -1);
760    }
761
762    void CreateTreeRoot(Vector position, double width, double height)
763    {
764        GameObject roots = new GameObject(width * 9, height * 4);
765        roots.Position = position;
766        roots.Y -= height;
767        roots.Image = treeBottomImage;
768        Add(roots, -1);
769    }
770
771    void CreateFlame(Vector position, double width, double height, bool suuri)
772    {
773        PhysicsObject flame = new PhysicsObject(width, height * 2);
774        //flame.Image = flameImage;
775        flame.Color = Color.Red;
776        double hieman = height * 0.3;
777        flame.Position = position - new Vector(0, hieman);
778        flame.CollisionIgnoreGroup = 3;
779        flame.Tag = "burn";
780        flame.Animation = new Animation(blaze) { FPS = RandomGen.NextInt(20, 26) };
781        flame.Animation.Step(RandomGen.NextInt(0, 12));
782        flame.Animation.Resume();
783        flame.CanRotate = false;
784        Add(flame, 1);
785
786        Smoke savu = new Smoke();
787        if (suuri)
788        {
789            flame.IgnoresCollisionResponse = true;
790
791            savu.ParticleImage = smokeImage1;
792            savu.OuterParticleImage = smokeImage2;
793            savu.MaxLifetime = 1.0;
794            savu.MaxScale = 200;
795            savu.MinScale = 10;
796            savu.Position = flame.Position;
797            Wind = new Vector(-10, 0);
798            Add(savu);
799            flame.MakeStatic();
800        }
801
802        AddCollisionHandler(flame, "milkparticle", delegate(PhysicsObject c, PhysicsObject particle)
803        {
804            particle.Destroy();
805            flame.Destroy();
806            savu.Destroy();
807
808        });
809    }
810
811    void CreateBlockObject(Vector position, double width, double height)
812    {
813        // Säädin vähän tätä jotta laatikko näkyy hyvin. Pitää ehkä tehdä laatikolle ihan oma metodi.
814        PhysicsObject block = PhysicsObject.CreateStaticObject(width, height);
815        block.IsVisible = false;
816        //block.Image = blockImages[levelNumber];
817        block.Position = position;
818        block.Y -= height / 2.0;
819        Add(block, 1);
820
821        GameObject visibleBlock = new GameObject(width, height * 1.5);
822        visibleBlock.Position = position;
823        visibleBlock.Image = blockImages[levelNumber];
824        visibleBlock.Y -= height / 2.0;
825        Add(visibleBlock, -1);
826    }
827
828    void CreateMilk(Vector position, double width, double height)
829    {
830        double size = 30;
831        double ratio = milkImage.Height / milkImage.Width;
832
833        Improvement milk = new MilkImprovement(size, size * ratio);
834        milk.Image = milkImage;
835        milk.Animations = new List<Animation> { milkWalk, milkThrow };
836        milk.Position = position;
837        //milk.Tag = "milk";
838        milk.Tag = "improvement";
839        milk.sound = splash;
840        Add(milk);
841
842        //PhysicsObject milk = new PhysicsObject(size, size * ratio);
843        //milk.Image = milkImage;
844        //milk.Position = position;
845        //milk.Tag = "milk";
846        //Add(milk);
847    }
848
849    void CreateWorm(Vector position, double width, double height)
850    {
851        PlatformCharacter worm = new PlatformCharacter(width, height * 0.6);
852        worm.Position = position;
853        worm.Animation = new Animation(wormAnimation);
854        worm.Animation.FPS = RandomGen.NextInt(18, 24);
855        worm.Animation.Step(RandomGen.NextInt(0, 16));
856        worm.Animation.Resume();
857        worm.Tag = "worm";
858        worm.CollisionIgnoreGroup = 3;
859        Add(worm, 1);
860
861        PlatformWandererBrain brain = new PlatformWandererBrain();
862        brain.Speed = 60;
863        brain.FallsOffPlatforms = false;
864        brain.TriesToJump = true;
865        worm.Brain = brain;
866
867        AddCollisionHandler(worm, "pitchfork", delegate(PhysicsObject w, PhysicsObject p) { w.Destroy(); });
868    }
869
870    void CreateCrawly(Vector position, double width, double height)
871    {
872        PlatformCharacter crawly = new PlatformCharacter(width * 2, height * 0.6);
873        crawly.Position = position;
874        crawly.Color = Color.Gray;
875        crawly.Animation = new Animation(crawl);
876        crawly.Animation.FPS = RandomGen.NextInt(18, 24);
877        crawly.Animation.Step(RandomGen.NextInt(0, 16));
878        crawly.Animation.Resume();
879        crawly.Tag = "burn";
880        crawly.CollisionIgnoreGroup = 3;
881        Add(crawly, 1);
882
883        GameObject flame = new GameObject(width * 1.4, height * 2);
884        flame.Animation = new Animation(blaze) { FPS = RandomGen.NextInt(20, 26) };
885        flame.Animation.Step(RandomGen.NextInt(0, 12));
886        flame.Animation.Resume();
887        Add(flame);
888
889        // Pakko liikutella näin koska lapsioliona liekki näkyisi ryömijän päällä.
890        Timer flameMover = new Timer { Interval = 0.05 };
891        flameMover.Timeout += delegate
892        {
893            double hieman = height * 0.75;
894            flame.Position = crawly.Position + new Vector(0, hieman);
895        };
896        flameMover.Start();
897
898        PlatformWandererBrain brain = new PlatformWandererBrain();
899        brain.Speed = 20;
900        brain.FallsOffPlatforms = true;
901
902        crawly.Brain = brain;
903
904        // Sammuu törmätessään maitoon.
905        AddCollisionHandler(crawly, "milkparticle", delegate(PhysicsObject c, PhysicsObject particle)
906        {
907            particle.Destroy();
908            crawly.Brain = null;
909            crawly.Animation = new Animation(crawly.Animation.CurrentFrame);
910            crawly.Tag = "";
911            flame.Destroy();
912            RemoveCollisionHandlers(crawly);
913        });
914    }
915
916    void CreateShroom(Vector position, double width, double height)
917    {
918        PlatformCharacter shroom = new PlatformCharacter(width, height * 2);
919        shroom.Position = position + new Vector(0, shroom.Height / 2);
920        shroom.Tag = "burn";
921        shroom.Tag += "shroom";
922        //shroom.AnimWalk = new Animation();
923        shroom.CollisionIgnoreGroup = 3;
924        Add(shroom, 1);
925        //bool immune = false;
926
927        PlatformWandererBrain brain = new PlatformWandererBrain();
928        brain.Speed = 20;
929        brain.FallsOffPlatforms = false;
930        shroom.Brain = brain;
931
932        FollowerBrain attackBrain = new FollowerBrain("player");
933        attackBrain.DistanceFar = Window.Width / 2.5;
934        attackBrain.DistanceClose = Window.Width / 2;
935        attackBrain.StopWhenTargetClose = false;
936        attackBrain.Speed = brain.Speed * 6;
937        attackBrain.FarBrain = brain;
938        shroom.Brain = attackBrain;
939
940        int matoja = RandomGen.NextInt(1, 3);
941        int rahkeet = 10;
942
943        //// Taiotaan matoja
944        var castTimer = new Timer { Interval = 6.0 };
945        castTimer.Timeout += delegate
946        {
947            //shroom.PlayAnimation();
948
949            Timer.SingleShot(RandomGen.NextDouble(3, 5), delegate { shroom.Brain = attackBrain; });
950
951            Timer.SingleShot(1.5,
952                delegate
953                {
954                    for (int i = 0; i < matoja; i++)
955                    {
956                        CreateWorm(shroom.Position + RandomGen.NextVector(-200, 0, 200, 0), TILE_SIZE, TILE_SIZE);
957                        shroom.Brain = brain;
958                    }
959                });
960
961
962        };
963
964        attackBrain.TargetClose += delegate()
965        {
966            if (!castTimer.Enabled)
967            {
968                castTimer.Start();
969            }
970        };
971
972        AddCollisionHandler(shroom, "pitchfork", delegate(PhysicsObject s, PhysicsObject f)
973        {
974            matoja += RandomGen.NextInt(1, 5);
975            rahkeet--;
976            if (rahkeet < 1)
977            {
978                shroom.Destroy();
979                castTimer.Stop();
980                //Timer.SingleShot(6, NextLevel);
981            }
982        });
983    }
984
985    void CreateFireMage(Vector position, double width, double height)
986    {
987        PlatformCharacter mage = new PlatformCharacter(width * 3, height * 4);
988        mage.Position = position;
989        mage.Y += mage.Height / 2.0;
990        mage.Tag = "burn";
991        mage.Tag += "fireMage";
992        mage.AnimWalk = new Animation(firemageAnimation);
993        mage.CollisionIgnoreGroup = 3;
994        Add(mage, 1);
995        bool immune = false;
996
997        // Pään päällä oleva liekki (taitaa olla vähän huonossa kohtaa tällä hetkellä)
998        PhysicsObject flame = new PhysicsObject(width * 1.4, height * 2);
999        flame.Y += mage.Height * 0.65;
1000        flame.X += mage.FacingDirection.GetVector().X + 30;
1001        flame.Animation = new Animation(blaze);
1002        flame.IgnoresPhysicsLogics = true;
1003        flame.Animation.Start();
1004        mage.Add(flame);
1005
1006        PlatformWandererBrain brain = new PlatformWandererBrain();
1007        brain.Speed = 20;
1008        brain.FallsOffPlatforms = false;
1009        //mage.Brain = brain;
1010
1011        //PhysicsObject trigger = PhysicsObject.CreateStaticObject(10, Level.Height); //A truly cool and efficient way to make the boogeyman react only to our closeness
1012        //trigger.Position = mage.Position - new Vector(Window.Width / 2, 0);
1013        ////trigger.Color = Color.Red;
1014        //trigger.IsVisible = false;
1015        //trigger.IgnoresCollisionResponse = true;
1016        //Add(trigger);
1017
1018        FollowerBrain attackBrain = new FollowerBrain("player");
1019        attackBrain.DistanceFar = Window.Width / 2.5;
1020        attackBrain.DistanceClose = Window.Width / 2;
1021        attackBrain.StopWhenTargetClose = false;
1022        attackBrain.Speed = brain.Speed * 6;
1023        attackBrain.FarBrain = brain;
1024        mage.Brain = attackBrain;
1025
1026        // Taiotaan tulta välillä.
1027        var castTimer = new Timer { Interval = 10.0 };
1028        castTimer.Timeout += delegate
1029        {
1030            // TODO: Kävelyanimaatio pysähtyy jostain syystä tämän animaation jälkeen.
1031            mage.PlayAnimation(firemageCastAnimation);
1032
1033            Timer.SingleShot(RandomGen.NextDouble(5, 8), delegate { mage.Brain = attackBrain; });
1034
1035            Timer.SingleShot(1.5,
1036                delegate
1037                {
1038                    for (int i = 0; i < 6; i++)
1039                    {
1040                        CreateFlame(new Vector(attackBrain.CurrentTarget.X + RandomGen.NextDouble(-300, 300), mage.Y + mage.Height * 1.5), 30, 60, false);
1041                        mage.Brain = brain;
1042                    }
1043                });
1044
1045        };
1046
1047        attackBrain.DistanceToTarget.AddTrigger(Window.Width / 2, TriggerDirection.Down, delegate()
1048        {
1049            castTimer.Start();
1050            attackBrain.DistanceToTarget.RemoveTriggers(Window.Width / 2);
1051        });
1052
1053        //castTimer.Start();
1054
1055        AddCollisionHandler(mage, "milkparticle", delegate(PhysicsObject fireMage, PhysicsObject milk)
1056        {
1057            if (milk.Y > fireMage.Y + fireMage.Height * 0.15 && !immune)
1058            {
1059                flame.Size *= 0.95;
1060                immune = true;
1061                if (flame.Height < height)
1062                {
1063                    castTimer.Stop();
1064                    CreatePitchfork(mage.Position, TILE_SIZE, TILE_SIZE);
1065                    mage.Destroy();
1066                    Timer.SingleShot(6, NextLevel);
1067                }
1068                Timer.SingleShot(0.5, delegate { immune = false; });
1069
1070            }
1071        });
1072
1073        //AddCollisionHandler(trigger, "player", delegate(PhysicsObject a, PhysicsObject b) { castTimer.Start(); });
1074    }
1075
1076    void CreateHouse(Vector position, double width, double height)
1077    {
1078        // Näkyvä talo
1079        GameObject house = new GameObject(width * 10, height * 8);
1080        house.Image = houseImages[0];
1081        house.Position = position - new Vector(0, height * 2.5);
1082        Add(house, -2);
1083
1084        //Seisottava tasanne
1085        PhysicsObject platform = PhysicsObject.CreateStaticObject(house.Width - (TILE_SIZE * 3.25), 1);     //Tätä kohtaa on nyt vähän hakemalla haettu.
1086        platform.IsVisible = false;
1087        platform.Position = new Vector(house.X + (TILE_SIZE * 0.5), house.Top);
1088        Add(platform);
1089    }
1090
1091    void CreateBurnedHouse(Vector position, double width, double height)
1092    {
1093        GameObject house = new GameObject(width * 10, height * 8);
1094        house.Image = houseImages[1];
1095        house.Position = position - new Vector(0, height * 2.5);
1096        Add(house, -2);
1097
1098        PhysicsObject platform = PhysicsObject.CreateStaticObject(house.Width * 0.25, 1);
1099        platform.IsVisible = false;
1100        platform.Position = new Vector(house.X + platform.Width * 0.5, house.Top);
1101        Add(platform);
1102    }
1103
1104    private void CreateCart(Vector position, double width, double height)
1105    {
1106        double size = 0.7;
1107        PhysicsObject cart = new PhysicsObject(400 * size, 80 * size);
1108        cart.Position = position;
1109        cart.Image = cartImage;
1110        cart.CollisionIgnoreGroup = 2;
1111        Add(cart);
1112        PhysicsObject cartWheel = new PhysicsObject(160 * size, 160 * size, Shape.Circle);
1113        cartWheel.Image = cartWheelImage;
1114        cartWheel.Position = cart.Position + new Vector(-110, -30) * size;
1115        cartWheel.CollisionIgnoreGroup = 2;
1116        Add(cartWheel);
1117        AxleJoint joint = new AxleJoint(cart, cartWheel);
1118        Add(joint);
1119    }
1120
1121    private void CreateDummy(Vector position, double width, double height, Color color)
1122    {
1123        PhysicsObject dummy = PhysicsObject.CreateStaticObject(width, height);
1124        dummy.Position = position;
1125        dummy.Color = color;
1126        Add(dummy);
1127    }
1128
1129    void CreatePlayer(Vector position, double width, double height)
1130    {
1131        HillBilly player = new HillBilly(width, height * 2, names[players.Count()]);
1132        player.Shape = Shape.Rectangle;
1133        player.Position = position + new Vector(0, height * 0.5);
1134        player.Color = Color.White;
1135        player.AnimWalk = new Animation(normalWalk);
1136        player.AnimIdle = new Animation(playerIdle);
1137        player.AnimJump = new Animation(playerJump);
1138        player.AnimFall = new Animation(playerFall);
1139        player.Animations = new List<Animation>();
1140        player.Animations.Add(normalWalk);
1141        player.Animations.Add(playerIdle);
1142        player.AnimFall.StopOnLastFrame = true;
1143        players.Add(player);
1144        startingPlayers.Add(player);
1145        player.Tag = "player";
1146        Add(player);
1147
1148        AddCollisionHandler(player, "burn", delegate(PhysicsObject p, PhysicsObject t)
1149        {
1150            player.Life.Value--;
1151        });
1152
1153        AddCollisionHandler(player, "lava", delegate(PhysicsObject p, PhysicsObject t)
1154        {
1155            player.Life.Value = player.Life.MinValue;
1156        });
1157
1158        AddCollisionHandler(player, "worm", delegate(PhysicsObject p, PhysicsObject worm)
1159        {
1160            if (player.Y > worm.Y && Math.Abs(player.X - worm.X) < worm.Width / 2.0)
1161            {
1162                worm.Destroy();
1163            }
1164            else
1165            {
1166                player.Life.Value--;
1167            }
1168        });
1169
1170
1171        player.Life.LowerLimit += delegate
1172        {
1173            if (players.Count < 2)
1174            {
1175                Loss();
1176            }
1177            else
1178            {
1179                players.Remove(player);
1180                GetRidOfImprovement(player);
1181                player.Destroy();
1182            }
1183        };
1184
1185        AddCollisionHandler(player, "improvement", CollectImprovement);
1186    }
1187
1188    void CreateGroundTop(Vector position, double width, double height)
1189    {
1190        // Puolet pienempi näkymätön palikka alla johon törmää.
1191        PhysicsObject ground = PhysicsObject.CreateStaticObject(width, height / 2.0);
1192        ground.IsVisible = false;
1193        ground.Position = position - new Vector(0.0, TILE_SIZE / 4.0);
1194        Add(ground);
1195
1196        // Maanpinnan näkyvä osa.
1197        GameObject visibleGround = new GameObject(width, height);
1198        visibleGround.Image = groundTopImages[levelNumber];
1199        visibleGround.Position = position;
1200        visibleGround.TextureWrapSize = new Vector(width / TILE_SIZE, height / TILE_SIZE);
1201        Add(visibleGround, -1);
1202
1203        int probability = RandomGen.NextInt(100);
1204        bool wheat = probability < 20;
1205
1206        if (wheat && levelNumber < 2)
1207        {
1208            GameObject wheatBlock = new GameObject(width * 1.5, height * 2);
1209            wheatBlock.Image = foregroundDecorations[levelNumber];
1210            wheatBlock.X = visibleGround.X;
1211            wheatBlock.Bottom = visibleGround.Bottom;
1212            wheatBlock.TextureWrapSize = new Vector(width / TILE_SIZE, height / TILE_SIZE);
1213            Add(wheatBlock, 2);
1214        }
1215    }
1216
1217    void CreateGround(Vector position, double width, double height)
1218    {
1219        PhysicsObject ground = PhysicsObject.CreateStaticObject(width, height);
1220        ground.Image = groundImages[levelNumber];
1221        ground.Position = position;
1222        ground.TextureWrapSize = new Vector(width / TILE_SIZE, height / TILE_SIZE);
1223        Add(ground, -1);
1224    }
1225    #endregion
1226
1227    void Victory()
1228    {
1229        ClearAll();
1230        MessageDisplay.Add("Voitit muutes belin.");
1231    }
1232
1233    void SetControls()
1234    {
1235        Keyboard.Listen(Key.A, ButtonState.Down, delegate { startingPlayers[0].Walk(-PLAYER_SPEED); }, "Player 1 moves left");
1236        Keyboard.Listen(Key.D, ButtonState.Down, delegate { startingPlayers[0].Walk(PLAYER_SPEED); }, "Player 1 moves right");
1237        Keyboard.Listen(Key.W, ButtonState.Down, delegate { startingPlayers[0].Jump(PLAYER_SPEED * 2); }, "Player 1 jumps");    //Just PLAYER_SPEED felt alright as well
1238        Keyboard.Listen(Key.E, ButtonState.Pressed, delegate { startingPlayers[0].UseImprovement(); }, "Player 1 uses their tools");
1239        Keyboard.Listen(Key.Tab, ButtonState.Pressed, GetRidOfImprovement, "Player 1 throws their tools away", startingPlayers[0]);
1240
1241        Keyboard.Listen(Key.Left, ButtonState.Down, delegate { startingPlayers[1].Walk(-PLAYER_SPEED); }, "Player 2 moves left");
1242        Keyboard.Listen(Key.Right, ButtonState.Down, delegate { startingPlayers[1].Walk(PLAYER_SPEED); }, "Player 2 moves right");
1243        Keyboard.Listen(Key.Up, ButtonState.Down, delegate { startingPlayers[1].Jump(PLAYER_SPEED * 2); }, "Player 2 jumps");
1244        Keyboard.Listen(Key.RightShift, ButtonState.Pressed, delegate { startingPlayers[1].UseImprovement(); }, "Player 2 uses their tools");
1245        Keyboard.Listen(Key.Enter, ButtonState.Pressed, GetRidOfImprovement, "Player 1 throws their tools away", startingPlayers[1]);
1246
1247        Keyboard.Listen(Key.F9, ButtonState.Pressed, Loss, null);
1248        Keyboard.Listen(Key.F12, ButtonState.Pressed, NextLevel, null);
1249        Keyboard.Listen(Key.R, ButtonState.Pressed, StartGame, null);
1250
1251        Keyboard.Listen(Key.F1, ButtonState.Pressed, ShowControlHelp, "Show help");
1252        Keyboard.Listen(Key.Escape, ButtonState.Pressed, ConfirmExit, "Exit game");
1253    }
1254
1255    void GetRidOfImprovement(HillBilly player)
1256    {
1257        if (player.Improvement != null)
1258        {
1259            Improvement parannus = player.Improvement;
1260            parannus.Position = player.Position + player.FacingDirection.GetVector() * 100;
1261            parannus.IsVisible = true;
1262            parannus.IgnoresCollisionResponse = false;
1263            parannus.Tag = "improvement";
1264            player.Improvement.Rid();
1265        }
1266    }
1267
1268    public void CreateMilkParticles(HillBilly billy, int size, int xBase, int yBase, Vector position, String tag)
1269    {
1270        for (int i = 0; i < 10; i++)
1271        {
1272            const double maxLife = 1.0;
1273            PhysicsObject milkParticle = new PhysicsObject(size, size, Shape.Circle);
1274            milkParticle.Color = new Color(255, 255, 255, 128);
1275            milkParticle.Position = billy.Position + position;
1276            milkParticle.IgnoresCollisionResponse = true;
1277            milkParticle.Tag = tag;
1278            milkParticle.LifetimeLeft = TimeSpan.FromSeconds(maxLife);
1279            Add(milkParticle);
1280
1281            // Väri muuttuu läpinäkyväksi.
1282            var fadeTimer = new Timer { Interval = 0.07 };
1283            fadeTimer.Timeout += delegate
1284            {
1285                byte c = 255;
1286                milkParticle.Color = new Color(c, c, c, (byte)(128 * milkParticle.LifetimeLeft.TotalSeconds / maxLife));
1287            };
1288            fadeTimer.Start();
1289            milkParticle.Destroyed += fadeTimer.Stop;
1290
1291            // Random lentonopeus.
1292            var rx = RandomGen.NextDouble(-50, 50);
1293            var ry = RandomGen.NextDouble(-50, 50);
1294            milkParticle.Hit(new Vector(billy.FacingDirection.GetVector().X * (xBase + rx), yBase + ry));
1295        }
1296    }
1297
1298    void Loss()
1299    {
1300        ClearAll();
1301        cameraTarget = Camera.Position = Vector.Zero;
1302        Level.Background.Color = Color.Black;
1303
1304        GameObject img = new GameObject(Window.Width * 0.3, Window.Height * 0.5);
1305        img.Image = deathImage;
1306        Add(img);
1307
1308        Label gameover = new Label("Game Over");
1309        gameover.Position = new Vector(0, Screen.Top - 150);
1310        gameover.TextColor = Color.White;
1311        gameover.TextScale *= 3;
1312        Add(gameover);
1313
1314        Keyboard.Listen(Key.Escape, ButtonState.Pressed, StartGame, null);
1315    }
1316
1317    void CollectImprovement(PhysicsObject player, PhysicsObject milk)
1318    {
1319        HillBilly billy = player as HillBilly;
1320        if (billy == null || billy.Improvement != null)
1321            return;
1322
1323        Improvement milkI = milk as Improvement;
1324        billy.GainImprovement(milkI);
1325
1326        milk.IsVisible = false;
1327        milk.IgnoresCollisionResponse = true;
1328        milk.Tag = "none";
1329
1330        //Remove(milk);
1331        //billy.AnimWalk = new Animation(milkWalk);
1332        //billy.AnimIdle = new Animation(milkWalk.CurrentFrame);
1333    }
1334
1335    public void ForkThings(HillBilly Owner)
1336    {
1337        PhysicsObject evilClone = new PhysicsObject(Owner.Width, Owner.Height * 0.7);
1338        evilClone.Position = Owner.Position + Owner.FacingDirection.GetVector() * 40 + new Vector(0, -(Owner.Height * 0.3));
1339        evilClone.Color = Color.Red;
1340        evilClone.IsVisible = false;
1341        evilClone.Tag = "pitchfork";
1342        evilClone.IgnoresCollisionResponse = true;
1343        evilClone.IgnoresPhysicsLogics = true;
1344        evilClone.LifetimeLeft = TimeSpan.FromSeconds(Owner.Animation.FPS * Owner.Animation.FrameCount);
1345        Add(evilClone);
1346
1347        Owner.PlayAnimation(Owner.Improvement.Animations[1], evilClone.Destroy);
1348    }
1349
1350    #region Camera
1351    void UpdateCamera()
1352    {
1353        double minY = players.Min(p => p.Y);
1354        double maxY = players.Max(p => p.Y);
1355        double minX = players.Min(p => p.X) + cameraOffset;
1356
1357        Vector minPosition = new Vector(Math.Max(minX, cameraTargetX), minY);
1358        Vector maxPosition = new Vector(minX, maxY);
1359
1360        if (levelNumber < 2)
1361        {
1362            cameraTarget = (minPosition + maxPosition) * 0.5;
1363            cameraTarget.X = Math.Max(cameraTargetX, minX); //Lellllll.
1364
1365            //cameraTarget.Y = Math.Max(cameraTarget.X, Level.Bottom + Window.Height/2.0);
1366
1367            cameraTargetX = Math.Max(cameraTargetX, minX);
1368
1369            cameraTarget.X = Math.Min(cameraTargetX, rightCamLimit);
1370            cameraTarget.Y = Math.Max(cameraTarget.Y, bottomCamLimit);
1371        }
1372        else if (levelNumber == 2)
1373        {
1374            cameraTarget.Y = ((minPosition + maxPosition) * 0.5).Y;
1375            cameraTarget.Y = Math.Max(cameraTarget.Y, bottomCamLimit);
1376
1377            // Pakotetaan kameraa eteenpäin. Jos kaikki pelaajat on näytön oikealla puolella, niin liikutetaan nopeammin.
1378            double cameraSpeed = players.All(p => Camera.WorldToScreen(p.Position).X > 0) ? 4.0 : 1.0;
1379            cameraTarget.X += cameraSpeed;
1380
1381            cameraTarget.X = Math.Min(cameraTarget.X, rightCamLimit);
1382
1383            if (cameraTarget.X == rightCamLimit && activateBoss != null)
1384            {
1385                activateBoss();
1386                activateBoss = null;
1387            }
1388        }
1389
1390        // Pelaajien sijainnin rajaus ikkunaan.
1391        double windowMax = Camera.ScreenToWorld(new Vector(Window.Width / 2.0, 0)).X;
1392        double windowMin = Camera.ScreenToWorld(new Vector(-Window.Width / 2.0, 0)).X;
1393        foreach (var player in players)
1394        {
1395            player.Left = Math.Max(player.Left, windowMin);
1396            player.Right = Math.Min(player.Right, windowMax);
1397        }
1398    }
1399
1400
1401    protected override void Update(Time time)
1402    {
1403        Camera.Position += (cameraTarget - Camera.Position) * time.SinceLastUpdate.TotalSeconds * cameraSpeed;
1404        //Camera.X += (cameraTargetX - Camera.X) * time.SinceLastUpdate.TotalSeconds * cameraSpeed;
1405
1406
1407        base.Update(time);
1408    }
1409
1410    /// <summary>
1411    /// Yritän leikkiä kameralla. Vielä varmaan hetken pidempään.
1412    /// </summary>
1413    /// <param name="gameTime"></param>
1414    /*
1415    protected override void Update(Microsoft.Xna.Framework.GameTime gameTime)
1416    {
1417        foreach(HillBilly player in players)
1418        {
1419            playerPositionsX.Add(player.Position.X);
1420            playerPositionsY.Add(player.Position.Y);
1421        }
1422
1423        double maxX = playerPositionsX.Max();
1424        double maxY = playerPositionsY.Max();
1425        double minX = playerPositionsX.Min();
1426        double minY = playerPositionsY.Min();
1427
1428        Vector minPosition = new Vector(minX, minY);
1429        Vector maxPosition = new Vector(maxX, maxY);
1430
1431        Camera.Position = (minPosition + maxPosition) * 0.5;
1432
1433        playerPositionsX.Clear();
1434        playerPositionsY.Clear();
1435
1436        base.Update(gameTime);
1437    }
1438     */
1439    #endregion
1440
1441
1442}
Note: See TracBrowser for help on using the repository browser.