Player Character doesn't change room, but other characters do

Started by humaldo, Tue 09/12/2014 23:49:24

Previous topic - Next topic

humaldo

Hi there!

After hours of googling and searching the AGS board I finally decided to post my question here:

I used the cCharacter.ChangeRoom() function a dozen times, but it somehow doesn't work in one specific room anymore.
Here's the code:

Code: ags

function on_call (int value) {
  if (value == 0) {
    disableBooks();
  }
  else if (value == 10){ //Returning from a Dialog
    Display ("End Of Act 1");
    Wait(40);
    Display("Now it should change to Act 2...");
    cCharacter.ChangeRoom(4); //This character disappears
    Wait(40);
    cPlayerCharacter.ChangeRoom(4); //but the player character stays in the same room
  }
}


I'm returning from a dialog (with CallRoomScript(10); ) and just want that both characters change to room 4.
But only cCharacter changes (he disappears) and after the Wait(40); a quick fade-out and fade-in occurs, but my
cPlayerCharacter is still in the same room. No error message, nothing.

I've tried various things, like changing to another room, but it seems my Player Character can't leave this room anymore.
Accessing Room 4 from other places is no problem, so there's no problem with Room4.

Is there anything I don't know? I found this here "This means that you should not use any other commands which rely on the new room (object positionings, and so on) after this command within the same function." but i don't know of any other commands which are currently running...

I would be endlessly helpful if anyone could give me a hint, this problem already cost me 4 hours...

Thank you!!
Cheers,
Christian

RetroJay

Hi Humaldo.

I have had lots of grief, in the past, with Brackets.
I'm not sure about this but give this a go and see what happens.
I made a few changes.

Code: ags
function on_call (int value) {
  if (value == 0) {
    disableBooks();
  }
  
  else {
    if (value == 10) { //Returning from a Dialog
      Display ("End Of Act 1");
      Wait(40);
      Display("Now it should change to Act 2...");
      cCharacter.ChangeRoom(4); //This character disappears
      Wait(40);
      Player.ChangeRoom(4);
    }
  }
}


EDIT:
If you have created your own 'Function' for your Room.
Then you may want to put THIS into your Rooms 'function room_RepExec() {'
Code: ags
on_call();


I hope this helps.

Yours.
Jay.

humaldo

Hey Jay, thank you for your answer!!

But unfortunately it doesn't have any effect. It's exactly the same as before.
I changed the brackets exactly as you described (else { if { instead of else if {)
and i changed cPlayer to Player.

I have no room_RepExec() function in my room, and neither do I in any other room.
As I said, the ChangeRoom works in dozens of other cases in my game...

Maybe the steps I did before could help:

  • I finished my Room2 Script, everything worked.
  • I already had the on_call function working, but with "quitgame(0)" which worked
  • Then I wanted to duplicate Room2 for my next Act
  • So I created a template out of Room2
  • I created the new Room4, using the template of Room2
  • I renamed and modified Room4 properly
  • Room2 Script: I deleted "quitgame(0)" and put both ChangeRooms into the script
  • Then I tested the transition to Room 4
  • -> Player Character stays in Room2, the other character disappears, the fades are very short.

Could it be an AGS bug?

Thank you very much!!

AnasAbdin

Quote from: humaldo on Tue 09/12/2014 23:49:24I'm returning from a dialog (with CallRoomScript(10); ) and just want that both characters change to room 4.
But only cCharacter changes (he disappears) and after the Wait(40); a quick fade-out and fade-in occurs, but my
cPlayerCharacter is still in the same room. No error message, nothing.

The fade-out/fade-in means the current player character has moved to another room then back to the same room. Check the code for (in your case room 4: "before fade in" and "after fade in" functions) and make sure nothing there makes the character move back.

humaldo

Thanks for your answer. But I just checked it again, there's nothing in Room4 yet which could send my player character back. And as i said, changing to any other room doesn't work either.

But I tried something else: In the on_call(10) i set the Act Global var to "Act = 2".
Then I added an else_if (Act == 2) when the player character leaves the room (room_LeaveLeft() )
...and there it works!! Exact the same cPlayerCharacter.ChangeRoom(4) function than in the
on_call function.

To sum it up:
1) The on_call(10) after returning from a dialog works and all functions are executed...
2) ...except of the ChangeRoom of the Player Character
3) The ChangeRoom works with NO other room of my game
3) Exactly the same ChangeRoom function works when the character leaves the room with room_LeaveLeft()

Mandle

Quote from: humaldo on Wed 10/12/2014 06:31:10
So I created a template out of Room2
I created the new Room4, using the template of Room2
I renamed and modified Room4 properly

I'm suspecting that your problem lies herein somewhere. I have also worked on a game where I used extensive templates to add new rooms and I found that I had to be a real human robot to make sure every....single....thing was changed properly to make the new rooms behave.

humaldo

Hi Mandle!
I already deleted room4 (which i created out of the template of room2), created a new, empty room and tried to get my characters there via ChangeRoom - without success. Could it be that i kina "destroyed" something in my Room2 when i made a template out of it?

And if this is the case - who could I solve it?

humaldo


Snarky

Quote from: humaldo on Tue 09/12/2014 23:49:24
I'm returning from a dialog (with CallRoomScript(10); )

I would strongly suspect this is the problem. You're calling CallRoomScript() from within a dialog? Try what happens if you call it from a room script event instead. If it works, you can move the call outside of the dialog (by wrapping the dialog call in a function, ending the dialog and then calling your room script).

humaldo

Thanks, Snarky :cheesy:! That sounds reasonable, I'll try it asap!
But the strange thing is that calling the CallRoomScript() in dialogues AND ChangeRoom afterwards works in other situations of my game...
I'll keep you informed!

humaldo

Hallelujah, it worked, thanks so much!! But I just doublecked, I did it exactly the same way as before in other parts of my game, and there it still works... 

The more i work with AGS I less understand some things. There seem to be plenty of strange behaviors if something is not 100% done the way as intended. The most time I've spent so far in AGS was to find ways around those things... Or is it just me?

Monsieur OUXX

#11
Snarky got it right once again!
I'd like to add some details. Humaldo, I guarantee you that 99.9% of the time AGS works as expected.

But there is something that is hard to understand in AGS: the order in which scripts are executed. It's hard to understand not because it's complicated, but instead because it seems so simple, that most AGS scripters overlook the details, and then sometimes get confused. You could say it's not hard but it's subtle.

The most important thing to understand in AGS is that it's basically split in two : 1) all the things that are done by the inner engine at each game cycle, and 2) all the things that are done by your custom scripts.
Most of the time, what you do in your script will take effect (at least visually, on-screen) during the next game cycle. For example, everything you do in repeatedly_execute. But there are other things that are meant to be executed immediately, no matter what. They're meant to say "fuck you I'm not waiting" to the internal AGS engine. That's the kind of scripting that you would put into "repeatedly_execute_always". And because that second category of scripts is so blocking, then it's forbidden to put blocking functions in them (Wait(); Display(); etc.)

Where am I getting at? You should imagine that all the scripting in AGS is a big heap of instructions to execute. AGS takes them from the heap in the same order as you put them, and sometimes interrupts that task to execute its internal code, once at the beginning of each game loop. Some of your scripting goes at the top of the heap, that's what happens with regular, non-blocking instructions. But there is a handful of other "special" instructions that will take effect later, at the next game cycle, because they have the courtesy of letting AGS do its stuff inbetween. The trouble is : when the game cycle is over, AGS will not "resume" the interrupted scripts during the next game cycle. They're gone.

That's precisely what happens with "CallRoomScript". You're telling AGS "don't do it now, but remember to call on_call at the next game cycle". Therefore you cannot base your thinking on the values of the variables of the current game loop. They will probably have changed during the same game loop.

(For the record I got that from the help article) :
Quote
The function doesn't get called immediately; instead, the engine will run it in due course, probably during the next game loop, so you can't use any values set by it immediately.

Once the on_call function has executed (or not if there isn't one), the game.roomscript_finished variable will be set to 1, so you can check for that in your repeatedly_execute script if you need to do something afterwards.
That's probably what happens in your case. You get mixed up between the game loops and the variables change values inbetween. And that's probably why Snarky's solution fixes the issue : you're piling up too many queued instructions (Dialogs are a whole world of their own), so it's better to call CallRoomScript only after you're sure the dialog is over. By the way CallRoomScript does not "exit" the dialog. It just piles up more things to do inside the dialog. If you're lucky they will unpile in the order you thought was to be expected. If you're unlucky something else within the dialog interrupts the game loop, hence skipping to the next game loop, and your CallRoomScript does not get called at all or causes trouble.

Conclusion: whenever you write some script in AGS, always ask yourself:
- Is it meant to be executed right now and block everything? (which means the game cannot continue until this script and all the script it calls are completely finished)
  OR
- Is is meant to be executed right now and forbidden to block anything? (which means, for example if you put a Wait inside, that the execution of this script might potentially be split over several game loops -- thus allowing the internal game engine to update the display and such, but not to run some of your other custom scripts. Your own local variables will keep their values, but all graphic variables such as player.x, etc. might change during the execution of that script)
  OR
- Is it meant to be run later? (the engine will remember to do it at next game cycle, but then you cannot rely on any of your own variables since they might change inbetween).

 

humaldo

Wow, thank you for the great answer and explanation!! I admit I have to study it a bit longer to fully understand it, but it starts to make more sense to me now :)

Snarky

Glad that solved it! I don't understand all the rules either, I just have a vague feeling that certain things are likely to break. The upshot of what Monsieur OUXX is saying, for this particular situation, is (I think) that this won't work:

-from a dialog script
-call CallRoomScript()
-which runs a script that calls player.ChangeRoom()

You might be able to do any two of those things (I'm not sure), or do ChangeRoom() for any character other than the player, but not all three in one go with the player character.

humaldo

Hello again!

Aparently I didn't understand anything, I have another strange problem now:
After finishing a Puzzle I want to send my player character to another room and
let him walk to the middle of this room. But the screen stays black until all actions
are finished and the fade-in happens afterwards!

I tried a lot of variations, but without any success... I have no idea what I could try
else to make the fade-in happen directly at the beginning of the room change...

Here's the code:

FIRST ROOM:
Code: ags

// 1/6 - CallRoomScript (2); when the puzzle is finished
function on_call (int value){
if (value == 1){ 
    cPlayer.Say("Finished!");
    Wait(20);
    g2_Progression = 1;
    cPlayer.ChangeRoom(4, -10,  172);
    Wait(40);
  }
  else if (value == 2){
      FadeOut(1);
      // <--2/6 - Fade out happens here
      g2_Progression = 3;
      cPlayer.ChangeView(1);
      cPlayer.ChangeRoom(4, -10,  172); 
      cCharacter.ChangeRoom(4, 280, 160); // 3/6 - Characters are moved to other location
      Wait(30);
  }
}


NEW ROOM:
The "Enters the Room before Fade-In" Event:
Code: ags

function room_Load(){
  stopRemainingSounds();
  if(g2_Progression == 1){
    CallRoomScript(1);
  }
  else if(g2_Progression == 3){ // 4/6 - startScene(); Function is started
    startScene();
  }
}

Code: ags

function startScene(){
  if(g2_Progression == 0){ 
    Wait(20);
    cPlayer.Say("Start");
    CallRoomScript(0); 
  }
  else if(g2_Progression == 1){
    cPlayer.Say("Puzzle 2.2");
  }
  else if(g2_Progression == 2){
    cPlayer.Say("Cheat Shortcut to Practice 2");
    CallRoomScript(0);
  }
  else if(g2_Progression == 3){ // 5/6 - this else if loop is called
    cPlayer.Walk(20, 175, eBlock, eAnywhere); 
    cPlayer.Walk(185, 165, eBlock, eWalkableAreas);
    cPlayer.FaceCharacter(cCharacter, eBlock);
    // <-- 6/6 - The Fade-in happens only here!
    dAct2_Review2.Start();
  }
}


Thank you so much in advance!!

Snarky

First of all, I really question your overuse of on_call() and CallRoomScript(). There's absolutely no reason to do that from another room script: just call the method you want directly! CallRoomScript is only needed to run room code from dialog scripts or the global script, and even then you can often use ProcessClick or RunInteraction instead, or simply use a global shared variable to let the room know the game state and any particular code it needs to run when the room loads.

CallRoomScript is bad because it makes the code unreadable and hard to debug, and (the way you're using it) relies on "magic numbers" ("if 1 do this, if 2 do that" etc.). In the very few cases where you do need to use it, I would strongly suggest you replace the numbers by meaningfully named constants or enums, so that instead of "CallRoomScript(0);" which you need to know on_call() by heart to understand, you would at least have something like "CallRoomScrip(RUN_PUZZLE2COMPLETE);" which gives you some useful information.

OK, for your specific problem: well, you call all of these commands from the "Enters the room before fade-in" event, so what did you expect to happen? If you want the actions to happen after fade-in, put them in the "Enters room after fade-in" event handler!

Calin Leafshade

I've made several AGS games now and have *never* used CallRoomScript.

humaldo

Good Morning!
Thank you for your answers. I lied in bed when the soltution came to my mind: It's the "Enters the room before fade-in". So far I only used "...before fade-in" OR "after fade-in". I didn't use both at the same time because I thougt there are either-or events. But apparently they are not and it makes sense to place the things that have to happen instantly and unseen (e.g. disabling objects, starting sounds etc) with the "before" event and everything else with the "after" event.

@on_call: The code above is the outcome of countless of tries to get the problem solved. Now that i know where the problem lies, I will get rid of them again and make the code more readable again.

Thank you very much!

humaldo

So, I tried it out and it works perfectly now! Funny, how such a simple thing can cost me so much time :-)

@Calin Leafshade: Hm, the following function doesn't seem to work correctly without CallRoomScript (or calling another function), because the dialog is started AFTER the function finished.

This function would show "Dialog Start", followed  by "Dialog End" BEFORE the dialog script even starts.
Code: ags

function startScene(){
  if(g2_Progression == 0){
    Wait(20);
    cPlayer.Say("Dialog Start"); 
    dAct2_Dialog.Start(); // <- Dialog doesn't start here but...
    // CallRoomScript(1); 
    Wait(5);
    cPlayer.Say("Dialog End");
    FadeOut(1);
    cPlayer.ChangeRoom(5, 327, 121);
    Wait(50);
  }
//<-- ...dAct2_Dialog.Start(); starts here.

Snarky

Hmm, so CallRoomScript(), uniquely among script functions, blocks until a queued dialog is run? That is an interesting trick.

I assume you absolutely need this to be a dialog (i.e. you're using multiple-choice branching), because otherwise you could just write it as a series of character.Say() commands.

The conventional way to continue a function after a dialog is as Khris describes here: http://www.adventuregamestudio.co.uk/forums/index.php?topic=50803.msg636493896#msg636493896

Remember, you can call functions directly from inside your dialog script as well, you don't need to do it indirectly with CallRoomScript().

SMF spam blocked by CleanTalk