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

Revision 6392, 44.3 KB checked in by sieerinn, 8 years ago (diff)

Laava toimii.

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