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

Revision 6397, 44.6 KB checked in by empaheik, 4 years ago (diff)

Introtekstiyritys

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