Not sure where to start, but I'm needing a way to make characters change rooms and be positioned to a relative coordinate from the previous room. (for example: When cEGO is leaving the right edge of Room #1 at (317, 125), cEGO should appear at (3, 125) and so on... I'm guessing I need to define this in the globalscript, since I need this for any/all rooms. I'm familiar with the basic cEGO.ChangeRoom and cEGO.ChangeRoomAutoPosition commands, but I'm guessing that there's an easier way to do this than to have up to 200 cEGO.ChangeRoom.(#,x,y) commands...
I would assume that this has been implemented in other projects, however my searches are not producing anything helpful. I'm 99% sure I'm not using the right criteria to find it, so if anyone could at least point me in the right direction, I would appreciate it.
Thanks in advance!!
-j
Just how many rooms you have? :)
anyway
Basically you need to:
a) remember coords of the room player exited, and the direction he went.
b) use these values when changing room.
I would suggest writing a function for that. That may be put into GlobalScript or separate module, depending on what you like more.
Sort of this:
In the GlobalScript.ash:
enum ExitDirection
{
// (or East, North, etc - depending on how you like this more)
eExitDirLeft,
eExitDirRight,
eExitDirTop,
eExitDirBottom
}
import function ChangeRoomAutoCoords(int new_room, ExitDirection exit_dir);
In the GlobalScript.asc:
function ChangeRoomAutoCoords(int new_room, ExitDirection exit_dir)
{
// put here coords of desired room borders (depends on your game really)
int roomBorderLeft = ...
int roomBorderRight = ...
int roomBorderTop = ...
int roomBorderBottom = ...
int lastRoomX = player.x;
int lastRoomY = player.y;
int nextRoomX;
int nextRoomY;
if (exit_dir == eExitDirLeft)
{
nextRoomX = roomBorderRight;
nextRoomY = lastRoomY;
}
else if (exit_dir == eExitDirRight)
{
nextRoomX = roomBorderLeft;
nextRoomY = lastRoomY;
}
else if (exit_dir == eExitDirTop)
{
nextRoomX = lastRoomX;
nextRoomY = roomBorderBottom;
}
else if (exit_dir == eExitDirBottom)
{
nextRoomX = lastRoomX;
nextRoomY = roomBorderTop;
}
player.ChangeRoom(new_room, nextRoomX, nextRoomY);
}
export function ChangeRoomAutoCoords(int new_room, ExitDirection exit_dir);
Then call it from room script like:
ChangeRoomAutoCoords(# of room, eExitDirLeft);
That's the right approach, but it can be done with even less code.
I assume your game uses rooms that are positioned on a uniform grid, right? Like the first few King's Quest and Quest for Glory games?
// add to global script
#define border 10
bool map_room_change;
void NextRoom() {
if (player.x < border) player.ChangeRoom(GetRoomProperty("west"));
if (player.x > Room.Width - border) player.ChangeRoom(GetRoomProperty("east"));
if (player.y < border) player.ChangeRoom(GetRoomProperty("north"));
if (player.y > Room.Height - border) player.ChangeRoom(GetRoomProperty("south"));
map_room_change = true;
}
void on_event(EventType event, int data) {
if (event == eEventEnterRoomBeforeFadein) {
if (!map_room_change) return; // exit here if intro / menu / other room
int pr = player.PreviousRoom;
if (pr == GetRoomProperty("west")) player.x = border+2;
if (pr == GetRoomProperty("east")) player.x = Room.Width - (border+2);
if (pr == GetRoomProperty("north")) player.y = border+2;
if (pr == GetRoomProperty("south")) player.y = Room.Height - (border+2);
map_room_change = false;
}
}
As you can see in the second function, the player is positioned after they've arrived in the new room, so the game can easily place them at the edge, even if the room is wider or higher than others.
Now add custom properties to the rooms.
-Open any room, and in the properties, click Properties, then the [...] button.
-in the window that opens, click "Edit schema..."
-right click inside the new window and select "Add new property..."
-Name: north, Description: room to the north (or whatever, not needed), Type: Number, Default Value: -1, Applies to: check only "Rooms"
-Repeat for the other three directions, then close the schema editor.
Now you can conveniently set all the room connections in here, without going into/adding several functions for each room.
To trigger the room change, I'd use a single region. Draw a rectangle at each edge of the room, using one color. Make sure it is narrower than the value of border. In its "player walks on region" event field, simply write "NextRoom", without quotes.
For special cases, like for instance when the player goes south from a 400 pixels wide room into a 320 pixels wide room, just use a second region and put the code in manually:
player.ChangeRoom(17, (player.x*320)/400, 12);
Just a quick side question on your code Khris, if I may.
I follow it fine and it seems an elegant solution to the problem to my inexperienced eyes. If I had tried to do this (and been clever enough to work out the same solution) I would probably have done
int border = 10;
rather than #define. Not in any way saying that would be better, but I wonder what the benefit of #define is? Is it simply better practice as the value of border will never change and therefore does not need to be a "variable" as such? Or is there something i'm missing?
It's funny that you'd bring this up because while I was writing the code I was wondering the exact same thing. I guess I took to doing it like that because a) the lines stand out, b) no memory is wasted because like you said, the value will never change.
Both aren't that good reasons though, and of course using an int instead will work just as well.
Thanks for the help guys. Using my vast ignorance in scripting, I'm still a bit confused with a couple of items with the suggestions.
@Khris: Since I've been digging through the forums for all of my questions, I've noticed that my reply in this case should be "Oh yeah, I meant to say that is what I planned to do...I happened to forget that part." :P
However, I'll just stick to "I have no idea what I'm doing here." I've taken each line and attempted to breakdown and dissect each line, so I can get everything out of it. Here's what "I think" I know about the code:
// add to global script
#define border 10 //setting border size to 10
bool map_room_change; //sets boolean for map_room_change (false by default)? Yes, that's a question.. :P
void NextRoom() { //having issues with this one. Not sure if I need to configure NextRoom somewhere. void states that I don't need a value returned, correct???
if (player.x < border) player.ChangeRoom(GetRoomProperty("west")); //if cEGO x coord is less than border (10), cEGO goes west
if (player.x > Room.Width - border) player.ChangeRoom(GetRoomProperty("east")); //if cEGO's x coord is more than the Room's width (320 - 10 = 310), cEGO goes east
if (player.y < border) player.ChangeRoom(GetRoomProperty("north")); //if cEGO's y coord is less than 10 (border), cEGO goes north
if (player.y > Room.Height - border) player.ChangeRoom(GetRoomProperty("south")); //if cEGO's y coord is 190 (200-10), cEGO goes south
map_room_change = true; //
}
void on_event(EventType event, int data) { //not sure here either...is this defining the return for all on_events for the map_room_change?
if (event == eEventEnterRoomBeforeFadein) { //if any room event occurs before fade in..????
if (!map_room_change) return; // exit here if intro / menu / other room -- Not exactly sure what this is doing
int pr = player.PreviousRoom; //defining int pr
if (pr == GetRoomProperty("west")) player.x = border+2; //if cEGO's previous room was west of current room, cEGO will be placed at previous x coord +12 (10 for border + 2)
if (pr == GetRoomProperty("east")) player.x = Room.Width - (border+2); //if cEGO's prev room was east of current room. cEGO will be placed at 308 (320 for roomWidth - 12)
if (pr == GetRoomProperty("north")) player.y = border+2; //if cEGO's prev room was north of current room, cEGO will be placed at previous y coord +12
if (pr == GetRoomProperty("south")) player.y = Room.Height - (border+2); //if cEGO's prev room was south of current room, cEGO will be placed at 188 (200 for roomHeight - 12)
map_room_change = false;
}
}
So how far off am I? Hopefully not too far....Also all of my rooms are the standard 320x200, so I wont have any custom ones, at least not now...
Thanks again for all of the help!
-j
Sorry, completely missed that regions are looking for room functions. Unfortunately, that means you need at least one additional function per room.
Open GlobalScript.ash and add this line anywhere:
import void NextRoom();
After drawing the region and adding the "player walks onto event", put NextRoom(); inside the function:
function region1_WalksOnto()
{
NextRoom();
}
As for on_event (http://www.adventuregamestudio.co.uk/manual/TextScriptEvents.htm), it's a function that's called by AGS if it exists, when certain events take place.
In this case, I want to do something in every room's before fadein event, so instead of adding that to every single room, I'll use the function to have the code in one place.
Basically, when your character walks onto a region and NextRoom is called, I set map_room_change to true to later find out whether the room change was called in NextRoom. (I don't want the character repositioned after every room change, just after the automatic ones.)
So assuming the next room is room 2, the current room fades out, AGS loads the new room and now calls on_event(eEventEnterRoomBeforeFadein, 2);
And what I did was provide the code that's executed in that case.
So first of all, I check whether map_room_change is true, or rather, if it is false, (! = NOT) and if it is, exit the function at this point.
If the current room change WAS triggered by NextRoom(), we now reposition the player in the room, using the previous room to do that.
Thank again Khris. Makes sense and I'm able to follow it. One last question is on the schema changes. I followed your directions, but can you explain them? I'm getting this error when changing rooms "AGS had a problem running your game, etc...etc... NewRoom: room change requested to invalid room number -1.
Doing some testing with it (since I'm not familiar with the schema settings), I changed the east from -1 to 1, just to see and that makes cEGO go back to Room1 from any room that I have the NextRoom(); in the region's walksOnto event. I'm going back through the code again to see if I may have missed something. However, even though I have cEGO moving to Room 1 from any other room, the placement is working correctly. :)
Thanks for your patience.
-j
I thought this was self-explanatory, so I didn't mention it explicitly: You have to enter the room numbers in the properties. If you look at the created schema, each direction leads to room -1 by default, which of course doesn't exist.
Don't just change it to 1, you have to enter the actual room number. And you have to enter every connection in both rooms.
Say this is your map:
1--2--3
|
4
1: east = 2
2: west = 1, east = 3, south = 4
3: west = 2
4: north = 2
Each room has to "know" all adjacent ones, otherwise the code won't work.
These are the values that are read back by
GetRoomProperty("east") and the other three.
To recap, we've attached a small database to each room that stores the adjacent rooms and read its data during the room change.
Thanks Khris. I understand now...(thanks for the db analogy). I was unclear on setting the custom properties, but I have figured it out now. I was going into the schema and trying to change them there. The room changes are now working. Something I did has now caused cEGO to change his starting point, but I'm working on the fix now. Next, I'll be working on the exceptions for the room to room transition that needs to go from the edge of current room to the middle of the next room, etc... I'm guessing that this will need to be done manually from a separate region for that particular direction...(if I read it correctly from your first reply).
You've given me a great base to start with. Can't thank you enough!! Sorry for needing it spelled out more.
-j
No problem, I've become so familiar with the inner workings of AGS that I tend to leave out a few steps in my explanations :D
And yes, using a separate region and a manual room change is the correct way to handle exceptions.
Btw, one could also store the room connections inside global variables. Besides being able to change them in-game (which isn't possible with custom properties), that would also allow to calculate them automatically for huge, uniform maps. If you look at Kings Quest I's map (http://vgmaps.com/Atlas/PC/King%27sQuest-QuestForTheCrown-Daventry.png) for instance, and number the rooms from left to right and then top to bottom, the room to the east is almost always the current_room + 1, and the room to the south current_room + map_width.
So in case you do have a big, rectangular map, it might be better to switch over to that now, unless you've already entered all the connections.
Well at my current coding level, you'll have to assume that I know nothing... :P But I'm working to change that...heh.
I was thinking that a variable would be possible, in fact my main problem with the schema earlier was because I thought that the -1 was a variable of sort, taking the player from -1 of current room, but it made alot more sense once I realized that it was just the default (invalid) property. :)
My current project's map isn't that uniform, so I think what you gave me earlier will work the best.
Thanks again! Another problem solved!!