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

Revision 6414, 54.3 KB checked in by empaheik, 7 years ago (diff)

Jotain hilpeänmyöhäisiä lisäasetteluja, jotka hädin tuskin ovat edustavia nyt ja vielä vähemmän huomenna.

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