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

Revision 6409, 52.0 KB checked in by jotapoti, 5 years ago (diff)

Intron/Alkuvalikon? musiikki lisätty.
Alkuvalikon värejä muutettu.

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