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

Revision 6421, 54.7 KB checked in by sieerinn, 8 years ago (diff)

F8-näppäimellä tulee kuolemattomuusmoodi.

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