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

Revision 6390, 43.5 KB checked in by sieerinn, 6 years ago (diff)

Kolmannessa kentässä on laavaa ja toimiva kamera.

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