source: 2015/26/MikkoL/JRPG/JRPG/JRPG/MainGame.cs @ 10337

Revision 6467, 21.4 KB checked in by mijoliim, 6 years ago (diff)
Line 
1using System;
2using System.Collections.Generic;
3using Jypeli;
4using Jypeli.Assets;
5using Jypeli.Controls;
6using Jypeli.Effects;
7using Jypeli.Widgets;
8using Microsoft.CSharp;
9using Microsoft.Xna.Framework;
10using Microsoft.Xna.Framework.Graphics;
11
12public class JRPG : PhysicsGame
13{
14    public static JRPG Game { get; set; }
15
16    public static BattleView BattleView { get; set; }
17
18    public Player Player { get; set; }
19
20    public SpriteFont MainFont { get; set; }
21
22    public bool UseShaders = false;
23    bool RandomEncounterAnim = false;
24    float WarpAmount = 0.0f;
25    double FadeSpeed = 0.5;
26    Effect Shader;
27    Effect Twirl;
28    public Effect Wave;
29    SpriteBatch spriteBatch;
30    RenderTarget2D renderTarget;
31
32    Widget Fade { get; set; }
33
34    public override void Begin()
35    {
36        SoundEffect.MasterVolume = 0.8;
37        MediaPlayer.Volume = 0.8;
38
39        MainFont = Content.Load<SpriteFont>("MainFont");
40
41        spriteBatch = new SpriteBatch(GraphicsDevice);
42        Shader = Content.Load<Effect>("Shader");
43        Twirl = Content.Load<Effect>("Twirl");
44        Wave = Content.Load<Effect>("Wave");
45
46        Game = this;
47
48        Window.Width = GameValues.SCREEN_WIDTH;
49        Window.Height = GameValues.SCREEN_HEIGHT;
50
51        SmoothTextures = false;
52        IsMouseVisible = true;
53
54        LoadTitleScreen();
55    }
56
57    void LoadTitleScreen()
58    {
59        MediaPlayer.Play("Music//PresentingV");
60
61        _Background Background = new _Background(JRPG.Game.Content.Load<Texture2D>("Space"));
62        JRPG.Game.Add(Background, -1);
63
64        Widget TitleScreen = new Widget(Images.TitleScreen);
65        TitleScreen.Size = new Vector(Images.TitleScreen.Width, Images.TitleScreen.Height);
66        Add(TitleScreen, 0);
67
68        Widget TitleHeader = new Widget(Images.TitleHeader);
69        TitleHeader.Size = new Vector(Images.TitleHeader.Width, Images.TitleHeader.Height);
70        TitleHeader.Position = new Vector(0, 200);
71        Add(TitleHeader,1);
72
73        TextMenu StartMenu = new TextMenu();
74        StartMenu.SetTextBases(MainFont, TextAlignment.Center);
75        StartMenu.AddMenuElement("Start Game");
76        StartMenu.AddMenuElement("Quit");
77        StartMenu.Offset = 40;
78        StartMenu.Position = new Vector(0, -100);
79        StartMenu.ActionWhenSelected += delegate(int selection)
80        {
81            switch (selection)
82            {
83                case 0:
84                    FadeMusicOut();
85                    LoadGame();
86                    Background.Destroy();
87                    TitleScreen.Destroy();
88                    TitleHeader.Destroy();
89                    break;
90                case 1:
91                    System.Diagnostics.Process.Start("https://www.youtube.com/watch?v=e9ixL-aVRCI");
92                    Exit();
93                    break;
94            }
95        };
96
97        StartMenu.LoadMenuElements();
98    }
99
100    void Ending()
101    {
102        FadeMusicIn("Music//Sensual");
103
104        string Text = "\"Nuorten peliohjelmointikurssi was a mistake\" \n                             - Antti-Jussi Lakanen, 2017";
105        BetterLabel EndSlate = new BetterLabel(MainFont, Vector.Zero, Text, TextAlignment.Center);
106        EndSlate.BorderType = TextBorderType.Thin;
107        EndSlate.IsFlashing = true;
108        Timer.SingleShot(1,
109            delegate
110            {
111                Add(EndSlate);
112            });
113
114        Gravity = new Vector(0, -200);
115
116        Timer AJParticleGen = new Timer();
117        AJParticleGen.Interval = 0.05;
118        AJParticleGen.Timeout +=
119            delegate
120            {
121                Particle_AJ();
122            };
123        AJParticleGen.Start();
124    }
125
126    void Particle_AJ()
127    {
128        PhysicsObject AJ = new PhysicsObject(LoadImage("aj2"));
129        AJ.Size = new Vector(AJ.Image.Width, AJ.Image.Height);
130        AJ.Y = Screen.TopSafe + AJ.Height / 2 + RandomGen.NextDouble(0, AJ.Height*3);
131        AJ.X = RandomGen.NextDouble(Screen.LeftSafe - AJ.Width / 2, Screen.RightSafe + AJ.Width / 2);
132        AJ.Position += Camera.Position;
133        AJ.MaximumLifetime = TimeSpan.FromSeconds(10);
134        Add(AJ);
135    }
136
137    void LoadGame()
138    {
139        OverworldView.LoadOverworldView("00");
140        Level.Size = OverworldView.CurrentMap.Size;
141        Level.Background.Color = Jypeli.Color.FromHexCode("000000");
142
143        LoadOWPlayer(new Vector(3, 3));
144        LoadOWControlsKeyboard();
145        LoadOWControlsXboxController();
146
147        CheckWhereCanMove();
148
149        Camera.Follow(Player);
150        Camera.ZoomFactor = 1.0f;
151        Camera.StayInLevel = true;
152
153    }
154
155    public void LoadOWPlayer(Vector PositionInTiles)
156    {
157        Player = new Player(new Vector(28, 40));
158        Player.Position = PositionInTiles * 40 + new Vector(20, 30);
159        Player.PositionInTiles = PositionInTiles;
160        Player.Color = Jypeli.Color.Blue;
161        Add(Player);
162        SetAnimations();
163    }
164    public void SetAnimations()
165    {
166        if (Movement.FacingDir == Movement.Direction.Up && JRPG.Game.Keyboard.GetKeyState(Key.Up) == ButtonState.Pressed && !Movement.IsMoving)
167        {
168            Player.Animation = new Animation(Images.Characters.Overworld.Sonic_WalkingUp);
169            Player.Animation.FPS = 10;
170            Player.Animation.Start();
171        }
172        else if (Movement.FacingDir == Movement.Direction.Down && JRPG.Game.Keyboard.GetKeyState(Key.Down) == ButtonState.Pressed && !Movement.IsMoving)
173        {
174            Player.Animation = new Animation(Images.Characters.Overworld.Sonic_WalkingDown);
175            Player.Animation.FPS = 10;
176            Player.Animation.Start();
177        }
178        else if (Movement.FacingDir == Movement.Direction.Left && JRPG.Game.Keyboard.GetKeyState(Key.Left) == ButtonState.Pressed && !Movement.IsMoving)
179        {
180            Player.Animation = new Animation(Images.Characters.Overworld.Sonic_WalkingLeft);
181            Player.Animation.FPS = 10;
182            Player.Animation.Start();
183        }
184        else if (Movement.FacingDir == Movement.Direction.Right && JRPG.Game.Keyboard.GetKeyState(Key.Right) == ButtonState.Pressed && !Movement.IsMoving)
185        {
186            Player.Animation = new Animation(Images.Characters.Overworld.Sonic_WalkingRight);
187            Player.Animation.FPS = 10;
188            Player.Animation.Start();
189        }
190        else if (Movement.FacingDir == Movement.Direction.Up && JRPG.Game.Keyboard.GetKeyState(Key.Up) == ButtonState.Down && !Movement.IsMoving)
191        {
192            Player.Animation = new Animation(Images.Characters.Overworld.Sonic_WalkingUp);
193            Player.Animation.FPS = 10;
194            Player.Animation.Start();
195        }
196        else if (Movement.FacingDir == Movement.Direction.Down && JRPG.Game.Keyboard.GetKeyState(Key.Down) == ButtonState.Down && !Movement.IsMoving)
197        {
198            Player.Animation = new Animation(Images.Characters.Overworld.Sonic_WalkingDown);
199            Player.Animation.FPS = 10;
200            Player.Animation.Start();
201        }
202        else if (Movement.FacingDir == Movement.Direction.Left && JRPG.Game.Keyboard.GetKeyState(Key.Left) == ButtonState.Down && !Movement.IsMoving)
203        {
204            Player.Animation = new Animation(Images.Characters.Overworld.Sonic_WalkingLeft);
205            Player.Animation.FPS = 10;
206            Player.Animation.Start();
207        }
208        else if (Movement.FacingDir == Movement.Direction.Right && JRPG.Game.Keyboard.GetKeyState(Key.Right) == ButtonState.Down && !Movement.IsMoving)
209        {
210            Player.Animation = new Animation(Images.Characters.Overworld.Sonic_WalkingRight);
211            Player.Animation.FPS = 10;
212            Player.Animation.Start();
213        }
214        else if (Movement.FacingDir == Movement.Direction.Up && !Movement.IsMoving)
215        {
216            Player.Animation = null;
217            Player.Image = Images.Characters.Overworld.Sonic_Standing_Up;
218        }
219        else if (Movement.FacingDir == Movement.Direction.Down && !Movement.IsMoving)
220        {
221            Player.Animation = null;
222            Player.Image = Images.Characters.Overworld.Sonic_Standing_Down;
223        }
224        else if (Movement.FacingDir == Movement.Direction.Left && !Movement.IsMoving)
225        {
226            Player.Animation = null;
227            Player.Image = Images.Characters.Overworld.Sonic_Standing_Left;
228        }
229        else if (Movement.FacingDir == Movement.Direction.Right && !Movement.IsMoving)
230        {
231            Player.Animation = null;
232            Player.Image = Images.Characters.Overworld.Sonic_Standing_Right;
233        }
234    }
235
236    #region Controls/Movement
237
238    public void LoadOWControlsKeyboard()
239    {
240        Keyboard.Listen(Key.Delete, ButtonState.Pressed, delegate
241        {
242            renderTarget = new RenderTarget2D(GraphicsDevice,
243                    GraphicsDevice.PresentationParameters.BackBufferWidth,
244                    GraphicsDevice.PresentationParameters.BackBufferHeight);
245        }, null);
246        Keyboard.Listen(Key.Insert, ButtonState.Pressed, delegate {
247            UseShaders = !UseShaders;
248        }, null);
249        Keyboard.Listen(Key.Escape, ButtonState.Pressed, ConfirmExit, "Lopeta peli");
250
251        Keyboard.Listen(Key.Left, ButtonState.Down, delegate { if(Keyboard.GetKeyState(Key.LeftShift) != ButtonState.Down) MoveLeft(GameValues.TILE_SIZE); }, null);
252        Keyboard.Listen(Key.Right, ButtonState.Down, delegate { if (Keyboard.GetKeyState(Key.LeftShift) != ButtonState.Down) MoveRight(GameValues.TILE_SIZE); }, null);
253        Keyboard.Listen(Key.Up, ButtonState.Down, delegate { if (Keyboard.GetKeyState(Key.LeftShift) != ButtonState.Down) MoveUp(GameValues.TILE_SIZE); }, null);
254        Keyboard.Listen(Key.Down, ButtonState.Down, delegate { if (Keyboard.GetKeyState(Key.LeftShift) != ButtonState.Down) MoveDown(GameValues.TILE_SIZE); }, null);
255
256        Keyboard.Listen(Key.Left, ButtonState.Pressed, delegate { Movement.FacingDir = Movement.Direction.Left; }, null);
257        Keyboard.Listen(Key.Right, ButtonState.Pressed, delegate { Movement.FacingDir = Movement.Direction.Right; }, null);
258        Keyboard.Listen(Key.Up, ButtonState.Pressed, delegate { Movement.FacingDir = Movement.Direction.Up; }, null);
259        Keyboard.Listen(Key.Down, ButtonState.Pressed, delegate {Movement.FacingDir = Movement.Direction.Down; }, null);
260
261
262        Keyboard.Listen(Key.PageDown, ButtonState.Pressed, RandomEncounter.Boss, null);
263        Keyboard.Listen(Key.PageUp, ButtonState.Pressed, Ending, null);
264    }
265    public void LoadOWControlsXboxController()
266    {
267        ControllerOne.Listen(Button.DPadLeft, ButtonState.Down, delegate { if (ControllerOne.GetButtonState(Button.LeftShoulder) != ButtonState.Down && ControllerOne.GetButtonState(Button.RightShoulder) != ButtonState.Down) MoveLeft(GameValues.TILE_SIZE); }, null);
268        ControllerOne.Listen(Button.DPadRight, ButtonState.Down, delegate { if (ControllerOne.GetButtonState(Button.LeftShoulder) != ButtonState.Down && ControllerOne.GetButtonState(Button.RightShoulder) != ButtonState.Down) MoveRight(GameValues.TILE_SIZE); }, null);
269        ControllerOne.Listen(Button.DPadUp, ButtonState.Down, delegate { if (ControllerOne.GetButtonState(Button.LeftShoulder) != ButtonState.Down && ControllerOne.GetButtonState(Button.RightShoulder) != ButtonState.Down) MoveUp(GameValues.TILE_SIZE); }, null);
270        ControllerOne.Listen(Button.DPadDown, ButtonState.Down, delegate { if (ControllerOne.GetButtonState(Button.LeftShoulder) != ButtonState.Down && ControllerOne.GetButtonState(Button.RightShoulder) != ButtonState.Down) MoveDown(GameValues.TILE_SIZE); }, null);
271
272        ControllerOne.Listen(Button.DPadLeft, ButtonState.Pressed, delegate { Movement.FacingDir = Movement.Direction.Left; }, null);
273        ControllerOne.Listen(Button.DPadRight, ButtonState.Pressed, delegate { Movement.FacingDir = Movement.Direction.Right; }, null);
274        ControllerOne.Listen(Button.DPadUp, ButtonState.Pressed, delegate { Movement.FacingDir = Movement.Direction.Up; }, null);
275        ControllerOne.Listen(Button.DPadDown, ButtonState.Pressed, delegate { Movement.FacingDir = Movement.Direction.Down; }, null);
276    }
277
278    void MoveLeft(double Distance)
279    {
280        SetAnimations();
281
282        if (Keyboard.GetKeyState(Key.Right) != ButtonState.Down && Movement.FacingDir == Movement.Direction.Left
283            || CheckIfAnotherArrowKeyIsDown(Key.Left, Key.Right) && Movement.FacingDir == Movement.Direction.Left
284            || !CheckIfAnotherArrowKeyIsDown(Key.Left) && Keyboard.GetKeyState(Key.Left) == ButtonState.Down)
285        {
286            if (Movement.CanMoveLeft && Keyboard.GetKeyState(Key.Left) == ButtonState.Down)
287            {
288                Player.Move(new Vector(-Distance, 0), GameValues.MOVE_SPEED);
289            }
290        }
291    }
292    void MoveRight(double Distance)
293    {
294        SetAnimations();
295
296        if (Keyboard.GetKeyState(Key.Left) != ButtonState.Down && Movement.FacingDir == Movement.Direction.Right
297            || CheckIfAnotherArrowKeyIsDown(Key.Right, Key.Left) && Movement.FacingDir == Movement.Direction.Right
298            || !CheckIfAnotherArrowKeyIsDown(Key.Right) && Keyboard.GetKeyState(Key.Right) == ButtonState.Down)
299        {
300            if (Movement.CanMoveRight)
301            {
302                Player.Move(new Vector(Distance, 0), GameValues.MOVE_SPEED);
303            }
304        }
305    }
306    void MoveUp(double Distance)
307    {
308        SetAnimations();
309
310        if (Keyboard.GetKeyState(Key.Down) != ButtonState.Down && Movement.FacingDir == Movement.Direction.Up
311            || CheckIfAnotherArrowKeyIsDown(Key.Up, Key.Down) && Movement.FacingDir == Movement.Direction.Up
312            || !CheckIfAnotherArrowKeyIsDown(Key.Up) && Keyboard.GetKeyState(Key.Up) == ButtonState.Down)
313        {
314            if (Movement.CanMoveUp)
315            {
316                Player.Move(new Vector(0, Distance), GameValues.MOVE_SPEED);
317            }
318        }
319    }
320    void MoveDown(double Distance)
321    {
322        SetAnimations();
323
324        if (Keyboard.GetKeyState(Key.Up) != ButtonState.Down && Movement.FacingDir == Movement.Direction.Down
325            || CheckIfAnotherArrowKeyIsDown(Key.Down, Key.Up) && Movement.FacingDir == Movement.Direction.Down
326            || !CheckIfAnotherArrowKeyIsDown(Key.Down) && Keyboard.GetKeyState(Key.Down) == ButtonState.Down)
327        {
328            if (Movement.CanMoveDown)
329            {
330                Player.Move(new Vector(0, -Distance), GameValues.MOVE_SPEED);;
331            }
332        }
333    }
334
335    public void CheckWhereCanMove()
336    {
337        Tile LeftTile = (Tile)OverworldView.CurrentCollisionMap.CollisionMapArray.GetValue((int)Math.Max(Player.PositionInTiles.X - 1,0),(int)(Player.PositionInTiles.Y));
338        Tile RightTile = (Tile)OverworldView.CurrentCollisionMap.CollisionMapArray.GetValue((int)Math.Min(Player.PositionInTiles.X + 1, OverworldView.CurrentCollisionMap.CollisionMapArray.GetLength(0) - 1) , (int)Player.PositionInTiles.Y);
339        Tile TopTile = (Tile)OverworldView.CurrentCollisionMap.CollisionMapArray.GetValue((int)Player.PositionInTiles.X, (int)Math.Min(Player.PositionInTiles.Y + 1, OverworldView.CurrentCollisionMap.CollisionMapArray.GetLength(1) - 1));     
340        Tile BottomTile = (Tile)OverworldView.CurrentCollisionMap.CollisionMapArray.GetValue((int)Player.PositionInTiles.X, (int)Math.Max(Player.PositionInTiles.Y - 1, 0));
341
342        if (LeftTile.IsWalkable) Movement.CanMoveLeft = true;
343        else Movement.CanMoveLeft = false;
344        if (RightTile.IsWalkable) Movement.CanMoveRight = true;
345        else Movement.CanMoveRight = false;
346        if (TopTile.IsWalkable) Movement.CanMoveUp = true;
347        else Movement.CanMoveUp = false;
348        if (BottomTile.IsWalkable) Movement.CanMoveDown = true;
349        else Movement.CanMoveDown = false;
350    }
351
352    bool CheckIfAnotherArrowKeyIsDown(Key Key)
353    {
354        if (Keyboard.GetKeyState(Key.Left) == ButtonState.Down)
355        {
356            if (Key != Key.Left) return true;
357        }
358        if (Keyboard.GetKeyState(Key.Right) == ButtonState.Down)
359        {
360            if (Key != Key.Right) return true;
361        }
362        if (Keyboard.GetKeyState(Key.Up) == ButtonState.Down)
363        {
364            if (Key != Key.Up) return true;
365        }
366        if (Keyboard.GetKeyState(Key.Down) == ButtonState.Down)
367        {
368            if (Key != Key.Down) return true;
369        }
370
371        return false;
372    }
373    bool CheckIfAnotherArrowKeyIsDown(Key Key, Key Key2)
374    {
375        if (Keyboard.GetKeyState(Key.Left) == ButtonState.Down)
376        {
377            if (Key != Key.Left && Key2 != Key.Left) return true;
378        }
379        if (Keyboard.GetKeyState(Key.Right) == ButtonState.Down)
380        {
381            if (Key != Key.Right && Key2 != Key.Right) return true;
382        }
383        if (Keyboard.GetKeyState(Key.Up) == ButtonState.Down)
384        {
385            if (Key != Key.Up && Key2 != Key.Up) return true;
386        }
387        if (Keyboard.GetKeyState(Key.Down) == ButtonState.Down)
388        {
389            if (Key != Key.Down && Key2 != Key.Down) return true;
390        }
391
392        return false;
393    }
394
395    #endregion
396
397    public void FadeOut()
398    {
399        if (Fade != null && Fade.IsAddedToGame) Fade.Destroy();
400
401        Fade = new Widget(Screen.WidthSafe, Screen.HeightSafe);
402        Fade.Color = Jypeli.Color.Transparent;
403        Add(Fade);
404        Fade.FadeColorTo(Jypeli.Color.Black, FadeSpeed);
405    }
406    public void FadeIn()
407    {
408        if (Fade != null && Fade.IsAddedToGame) Fade.Destroy();
409
410        Fade = new Widget(Screen.WidthSafe, Screen.HeightSafe);
411        Fade.Color = Jypeli.Color.Black;
412        Add(Fade);
413        Fade.FadeColorTo(Jypeli.Color.Transparent, FadeSpeed);
414    }
415
416    public void FadeMusicOut()
417    {
418        double CurrentVolume = MediaPlayer.Volume;
419
420        Timer Fade = new Timer();
421        Fade.Interval = 0.001;
422        Fade.Timeout += delegate
423        {
424            MediaPlayer.Volume -= CurrentVolume / 500;
425
426            if (MediaPlayer.Volume <= 0)
427            {
428                MediaPlayer.Stop();
429                MediaPlayer.Volume = CurrentVolume;
430                Fade.Stop();
431            }
432        };
433        Fade.Start();
434    }
435    public void FadeMusicIn(string Music)
436    {
437        MediaPlayer.Play(Music);
438        double TargetVolume = MediaPlayer.Volume;
439
440        Timer Fade = new Timer();
441        Fade.Interval = 0.001;
442        Fade.Timeout += delegate
443        {
444            MediaPlayer.Volume += TargetVolume / 500;
445
446            if (MediaPlayer.Volume >= TargetVolume)
447            {
448                MediaPlayer.Volume = TargetVolume;
449                Fade.Stop();
450            }
451        };
452        Fade.Start();
453    }
454
455    public void RandomEncounterAnimation(Action AfterAnimation, Action OnFadingOut)
456    {
457        if (!RandomEncounterAnim)
458        {
459            renderTarget = new RenderTarget2D(GraphicsDevice,
460                    GraphicsDevice.PresentationParameters.BackBufferWidth,
461                    GraphicsDevice.PresentationParameters.BackBufferHeight);
462
463            RandomEncounterAnim = true;
464            SoundEffects.Battle.Encounter.Play();
465
466            Timer.SingleShot(1, delegate
467            {
468                FadeOut();
469                if (OnFadingOut != null) OnFadingOut.Invoke();
470                Timer.SingleShot(FadeSpeed + 1, delegate
471                {
472                    RandomEncounterAnim = false;
473
474                    Camera.ZoomFactor = 1.0f;
475                    UseShaders = false;
476                    WarpAmount = 0.0f;
477
478                    FadeIn();
479
480                    Camera.StayInLevel = false;
481                    Camera.Position = Player.Position;
482                   
483                    if (AfterAnimation != null) AfterAnimation.Invoke();
484                });
485            });
486        }
487       
488    }
489
490    protected override void Update(Time time)
491    {
492        base.Update(time);
493
494        if (Flags.InBattle)
495        {
496            BattleView.Update(time);
497        }
498
499        if (RandomEncounterAnim)
500        {
501            float deltaTime = (float)Time.SinceLastUpdate.TotalSeconds;
502            WarpAmount += deltaTime * 3;
503
504            Camera.ZoomFactor += deltaTime * 3;
505        }
506    }
507
508    protected override void Draw(GameTime gameTime)
509    {
510        GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.Black);
511
512        if (UseShaders) GraphicsDevice.SetRenderTarget(renderTarget);
513
514        base.Draw(gameTime);
515        if (UseShaders) GraphicsDevice.SetRenderTarget(null);
516
517        if (UseShaders)
518        {
519            GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.Black);
520           
521            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
522            if (Twirl == Content.Load<Effect>("Twirl"))
523            {
524                Twirl.Parameters["Rotation"].SetValue(new Vector2(WarpAmount, WarpAmount));
525                Twirl.Parameters["CenterRadius"].SetValue(new Vector4(0.5f, 0.5f, 0.5f, 0.5f));
526                Twirl.CurrentTechnique.Passes[0].Apply();
527            }
528            else if (Shader == Content.Load<Effect>("Shader") || Shader == Content.Load<Effect>("Wave"))
529            {
530                Shader.Parameters["parameter1"].SetValue((float)Time.SinceStartOfGame.TotalMilliseconds);
531                Shader.CurrentTechnique.Passes[0].Apply();
532            }
533            spriteBatch.Draw(renderTarget, new Vector2(0, 0), Microsoft.Xna.Framework.Color.White);
534            spriteBatch.End();
535        }
536    }
537}
Note: See TracBrowser for help on using the repository browser.