====== 9. Finetuning the game ====== Before we go further, we need to fix a few things we didn't pay attention in the previous chapter and demonstrate a few little concepts along the way. This will be only a little relaxing chapter, so we could gather some energy for the upcoming chapters. First problem is that our character should walk from the correct spot depending on the previous scene. If you now for example exit through the door in the Room, you appear next to the car. Let's fix it! Open the **scene_init.script** in the Street scene and change the beginning to read: if (Game.PrevScene == "Room") { actor.SkipTo(680, 340); actor.Direction = DI_LEFT; } else { actor.SkipTo(253, 571); actor.Direction = DI_RIGHT; } actor.Active = true; What we're doing here is referencing **Game.PrevScene** which contains the name of the room we've just left (previous scene). If it's room, we appear next to the door, otherwise we appear next to the car. You can of course add as many previous scenes as you like. Another little thing we add is a small hint animation which would draw attention to the painting. Let's make his eye twinkle. Inside of the folder Room create another folder called **Twinkle** and copy files eye1 - eye5.jpg there. Then open Sprite Edit and create a sprite which would have delay 70 for all frames. Save this sprite as a **twinkle.sprite** and open the Room scene in the scene edit. Create the Sprite entity called **Twinkle** and place the twinkle.sprite there. Position it in the eye of the painting. Now modify the **scene_init.script** of the Room scene. #include "scripts\base.inc" // here comes the stuff which initializes the scene actor.SkipTo(706, 405); actor.Direction = DI_DOWN; actor.Active = true; //////////////////////////////////////////////////////////////////////////////// // scene state global StateRoom; // default values if(StateRoom==null) { StateRoom.Visited = false; // add scene states here } //////////////////////////////////////////////////////////////////////////////// // setup scene according to state variables //////////////////////////////////////////////////////////////////////////////// if(!StateRoom.Visited) { StateRoom.Visited = true; Game.Interactive = false; var e = Scene.GetNode("Twinkle"); e.Active = true; var sp = e.GetSpriteObject(); while (!sp.Finished) Sleep(1); e.Active = false; Game.Interactive = true; // this is our first visit in this scene... } actor.GoTo(635,459); What we've done here needs a bit of explaining. The code takes place only when player enters the room for the first time. First we display the sprite. var e = Scene.GetNode("Twinkle"); e.Active = true; Then we get it's **sprite object**. Sprite objects are the means how we can manipulate with sprite entities on the lower level. The code lines provides that the script will wait until the animation is over. In this case it's not that useful, but many times you need to synchronize your game with animation and this is the way how we can do it: var sp = e.GetSpriteObject(); // e is the Node and we get it's low level sprite while (!sp.Finished) Sleep(1); // Finished is set whenever the animation is over OOPS. We can't see it because the inventory is in the way. Let's return to the very original sliding inventory concept! Open the game_loop.script and modify the end so it'll read: if(Game.Interactive && Game.MouseY < 45) Game.InventoryVisible = true; else if(Game.MouseY > 100) Game.InventoryVisible = false; // go to sleep for 20 miliseconds to allow the engine to perform other tasks // it is important for the "endless" scripts to call the Sleep command, otherwise the game will get stuck Sleep(20); } Now it works! If our game is interactive and we slide our mouse to the top of the screen (Y coordinate is lesser than 45) we make the inventory visible and if we move the mouse down, the inventory is invisible again. Let's check again our animation. Hmm, it does something but it's not exactly very obvious, let's play it twice. Again the **scene_init.script**: ... if(!StateRoom.Visited) { StateRoom.Visited = true; Game.Interactive = false; var e = Scene.GetNode("Twinkle"); e.Active = true; var sp = e.GetSpriteObject(); while (!sp.Finished) Sleep(1); sp.Reset(); sp.Play(); while (!sp.Finished) Sleep(1); Game.Interactive = true; // this is our first visit in this scene... } ... Method **Reset()** of the sprite objects resets the sprite position to frame 0 and **Play()** makes the sprite play again. Now it looks way more convincing. Last two changes are in the **circle.scene**. First change is when you do the transition from one scene to the other, our captions are a bit off from the skeleton. If you remember we are using the skeleton region entity for talking but with a new image, the skeleton is at different place while the entity remains, which causes the problem. To fix it, I'll introduce 3 new attributes of entities in general: **entity.SubtitlesPosRelative** - either true or false specifies if we define (true) relative coordinates from the current position or (false) absolute coordinates on the screen. **entity.SubtitlesPosX** - Caption X position on the screen (either relative or absolute, see above) **entity.SubtitlesPosY** - Caption Y position on the screen (either relative or absolute, see above) So we'll fix our **servant.script** like this (place it above the talk lines: this.SubtitlesPosRelative = false; this.SubtitlesPosY = 257; this.SubtitlesPosX = 330; // this is already in your file this.Talk("It can't be like this!"); this.Talk("Please save me someone, anyone???? Hello???"); this.Talk("Wait what did he said? Influencing other people dreams?"); And last change and also the last point of this chapter is quite common trick - image crossfade, which is achieved by fading in the second image through animating its alpha value. Although it's a very simple trick, it looks much better than mere image flip. Alpha value defines transparency - you can do many different tricks with alpha, just remember that 0 = completely transparent and 255 is fully solid. First we need one new Attribute and one new Method: **entity.AlphaColor** - accepts RGBA value and sets the alpha according to it. **MakeRGBA** - global method accepting 4 numbers in the range of 0-255. As we're going to use only alpha color, we'd change the last value only (R=red,G=green,B=blue,A=alpha). so we modify the code to read: var e = Scene.GetNode("Ending"); e.AlphaColor = MakeRGBA(255,255,255,0); e.Active = true; for (var n=0;n<255;n=n+1) { e.AlphaColor = MakeRGBA(255,255,255,n); Sleep(1); } First we set the new image to fully transparent = invisible. Then in the loop from 0 to 255 we make it less and less transparent which means that it'll crossfade slowly with the original image and finally takes over. Full servant script goes here. #include "scripts\base.inc" on "LeftClick" { Game.Interactive = false; this.Talk("Finally you arrived! I was waiting here for you for so long!"); actor.Talk("Who are you?"); this.Talk("I'm the servant of the WME circle and I have to serve here until someone changes a place with me."); actor.Talk("But how could that be? Who was the one in the alternate universe?"); this.Talk("I have only very little power but one of my skills is to influence other's dreams. I tried this on many people but you're the one who caught up."); actor.Talk("I don't believe you. You are supposed to tell me the secrets of everything."); this.Talk("The secret is not to get your legs wooden. HAHAHA!"); Scene.FadeOut(400,255,255,255,255); actor.Active = false; Sleep(1000); Scene.FadeIn(400); this.Talk("NOOOOOOOOOOOOOOOOOOOOOOOOO!"); var e = Scene.GetNode("Ending"); e.AlphaColor = MakeRGBA(255,255,255,0); e.Active = true; for (var n=0;n<255;n=n+1) { e.AlphaColor = MakeRGBA(255,255,255,n); Sleep(1); } this.SubtitlesPosRelative = false; this.SubtitlesPosY = 257; this.SubtitlesPosX = 330; this.Talk("It can't be like this!"); this.Talk("Please save me someone, anyone???? Hello???"); this.Talk("Wait what did he said? Influencing other people dreams?"); Game.FadeOut(5000,0,0,0,255); Game.QuitGame(); Game.Interactive = true; }