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

Revision 6404, 50.1 KB checked in by sieerinn, 5 years ago (diff)

Turpaanottamisanimaatio.

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