PDA

View Full Version : my method of adding multi-character switching



Colin
22-09-09, 05:45 PM
here is the info and code to add multi-character switching into a game
as used in summer blast dizzy.

firstly you need to add those characters to your map!
place them all in their start positions. Dizzy's exact start location isn't as important,
as the player initial start position, is still controlled by beginx and beginy in gamedef.gs
each one is an actionable dynamic object and for the code to work with the right tiles,
you need to set them up with the right ID numbers.

1 Dizzy
2 Daisy
3 Dylan
4 Denzil
5 Dora
6 Dozy
7 Grand Dizzy

you don't need to use all of them(except for dizzy)
***BUT***
those that you do, must have the right id

once that is done, you can start adding the switching functions that will
be called by your game,into game.gs

The first function will save the existing player id and inventory, then paste the character
as an actionable object again



func OldChar()
{
i=0;
PlayerSet(P_DISABLE,1); //pause player
OldCharScuba(); //checks to see if wearing scuba
count = InventoryCount();
chrx = ObjFind(chr); //
ObjSet(ObjFind(1),O_USER+10,chr); //saves last character id
x = PlayerGet(P_X);
y = PlayerGet(P_Y); //finds player x and y location
ObjSet(chrx,O_X,x-12); //
ObjSet(chrx,O_Y,y-14); //replaces player with last character tile
ObjSet(chrx,O_DISABLE,0); //
ObjPresent(chrx); //
ObjSet(chrx,O_USER+11,count); //saves number of items in inventory
if(count>0)
{
for(i=count-1;i>=0;i--) //
{
idx = InventoryGet(i); //saves each item id to
chrx = ObjFind(chr); //that characters id
ObjSet(chr,O_USER+i,idx); //user variables
}
}

InventoryClear(); //wipes current inventory ready for new character
return;
}


The next function will load the new player inventory and set the costume to the new character



func NewChar()
{
i=0;
chrx = ObjFind(chr); //
count = ObjGet(chrx,O_USER+11); //calls up number of items new character holds
x = ObjGet(chrx,O_X); //
y = ObjGet(chrx,O_Y); //finds new character x and y location
scb = ObjFind(chr);
chrf = ObjGet(scb,O_TILE); //finds new characters idle tile
ObjSet(ObjFind(1),O_USER+10,chr);//saves new player id

PlayerSet(P_TILE,chrf); //switches player tile to new idle tile
c=PlayerGet(P_COSTUME);
if(c==330)
{
PlayerSet(P_TILE,340); //initially switches to scuba tile if previously wearing
}
PlayerSet(P_COSTUME,chrf);
PlayerSet(P_X,x+12); //
PlayerSet(P_Y,y+14); //moves to new characters location


if(count>0)
{
for(i=count-1;i>=0;i--) //loads each item id from
{
idx = ObjGet(chr,O_USER+i); //that characters id
GameSet(G_INVENTORY+i,idx); //user variables
}
}
chrx = ObjFind(chr);

ObjSet(chrx,O_DISABLE,1); //disables new characters action tile
GamePause(0);
PlayerSet(P_DISABLE,0); //re enable player control
return;
}


This next function checks if the player is carrying the scuba, if so then the actionable object will appear
still wearing it!



func OldCharScuba()
{
scuba = 0;
if(ID_SCUBA!=-1) scuba = InventoryHasItem(ID_SCUBA) ? 30 : 0;//offset tiles by 30 with scuba
if(chr==1) scuba+=10; //dizzy tile set start from 10 onwards
if(chr==2) scuba+=310; //daisy tile set start from 310 onwards
if(chr==3) scuba+=360; //dylan tile set start from 360 onwards
if(chr==4) scuba+=410; //denzil tile set start from 410 onwards
if(chr==5) scuba+=460; //dora tile set start from 460 onwards
if(chr==6) scuba+=510; //dozy tile set start from 510 onwards
if(chr==7) scuba+=560; //g dizzy tile set start from 560 onwards
scb = ObjFind(chr);
ObjSet(scb,O_TILE,scuba); //switches action tile to the right costume
}


Now in the func BeginNewGame() in game.gs
you need to add 4 lines to initialize the player start id



chr = 1; //initialises dizzy as start character
chrx = ObjFind(chr);
ObjSet(chrx,O_DISABLE,1); //disables dizzys action tile
ObjSet(ObjFind(1),O_USER+10,chr); //initialises dizzy for load/save


also in game.gs you need to add the inventory saving to the load/save game
so that you will continue with the right items on reloading.
in func SaveUserData( file ), add



func SaveUserData( file )
{
//..
return 1;
}


and func LoadUserData( file )



func LoadUserData( file )
{
chr = ObjGet(ObjFind(1),O_USER+10); //calls switch to that character
return 1;
}


that is it for the game.gs for the moment!

you now need to add some code into the handlers.gs file
this code will always make sure the player tile is set to the right character id's

look for the func HandlerPlayerUpdate()
and replace the // Player set costume section with



costume = 0;
if(chr==1)
{
if(ID_SCUBA!=-1) costume = InventoryHasItem(ID_SCUBA) ? 30 : 0;
}
if(chr==2) costume+=300;
if(chr==3) costume+=350;
if(chr==4) costume+=400;
if(chr==5) costume+=450;
if(chr==6) costume+=500;
if(chr==7) costume+=550;
PlayerSet(P_COSTUME,costume);


this is where dizzyage updates the player costume!

also important, in gamedef.gs
you will need to set a global variable for chr so all the scripts can see it

so add



#def chr 7


i added mine at the top of the list



now back to game.gs and actually switching between characters in game!

say you were another character and you wanted to switch to daisy
here is the basic code to switch.



/////////////////////////////////////////////////////////////////////
//daisy switch

func ActionObject_2()
{
OldChar(); //calls the old character save function
chr=2; //sets chr variable to the daisy id
NewChar(); //loads new character chr id as player
}


this you can expand upon, so it can display messages when switching.
you can also for instance, use status checks to disable or enable
the switching to each character as your game progresses.

have fun!

Grandad
23-09-09, 02:30 PM
Brilliant Colin ...I've actually been ploughing through all the scripts of Summer Blast for details of character switiching and keeping the inventories separate, so this should help a lot. Thanks for posting this.

Grandad

Meph
23-09-09, 04:38 PM
Cool,

But is it possible to switch to a character thats not in the same room?
Dizzy could be in room 1 and Dozy could be in room 10, and you wanted to switch.

Colin
23-09-09, 05:08 PM
yes, the code does do that. it takes the new player position from where they were standing. When I originally wrote the code in a test map. I had the switching performed from a menu (if you look in the sbd game script in multicharacter.gs, the menu code is still there, it's just deactivated for the game.

Meph
23-09-09, 05:48 PM
Excellent.
:dizzy_cool:

Grandad
18-06-10, 10:52 AM
I'm having trouble getting this to work. I'm only using two characters. The main character (Daisy) tiles start at 10 and the second character (Annie) start at 310, so have set the handler PlayerSetCostume as:


// Player set costume
costume = 0;
if (chr == 2) costume +=300;
PlayerSet(P_COSTUME, costume);

I've ignored the scuba stuff as I'm not using a scuba ...and can't be bothered to draw the scuba tiles for the second character. Initially I screwed this right up cos I thought that 1 Dizzy, 2 Daisy etc were just the list numbers, not the ID you have to give them.

Everything is working fine except the costume change. The inactive player's tile is fine (both for Daisy and Annie), but the active player's costume remains as Daisy even though the inventory switches correctly. The tiles follow the same sequence, ie idle = 10 and 310, walk = 11 and 311, up is 12 and 312 etc. Really not sure what I'm doing wrong as I've double checked the coding for old/new char functions.

Grandad

Colin
18-06-10, 12:44 PM
Hmm! are you sure you have placed a dizzy idle tile, just off your game map, with an object ID set to 1?
as explained at the beginning, even if you are not having dizzy in the game, you still need it

it is important that you have this!!
when the script switches characters, it saves the current character info into one of ID 1's user variables. this is so it knows which character to switch to, when you reload a save game.
at game start it writes the start character id into this. as it can't find this object id to write into, it shows an error

if you are starting with daisy for instance. in begin new game, you would set chr = 2
and also call a
NewChar();
so that you are now switched into the right character at the beginning.

I also noticed that your using
ObjSet(ObjFind(10000), O_STATUS, 1);
to find which character you are

you can find this out anytime by checking up the chr variable

Grandad
18-06-10, 01:22 PM
Thanks Colin, yet again I'm being an idiot and looking in the wrong place. Because I was having problems with the engine not finding the scuba stuff I disabled all the references to scuba ...and have just realised that I also disabled the costume change in the Player Set Costume in the Handler Player Update.

I don't think I need a Dizzy brush as for the last couple of games I've made, the Daisy tiles always start at 10 and the engine seems to accept this with no problem even when saving and loading. I suppose that the default for Dizzy normally starts with a id of 10 for the idle tile, but I will check out saving for this new game and if there is a problem I'll add a Dizzy tile and change the ids of Daisy and Annie to '2' and '3'.

regarding my 'check' brush (10000), I set this as I want Annie to have different reactions to Daisy for the same object Are you saying that instead of adding ..ObjGet(ObjFind(10000), O_STATUS) == 1) to the end of an 'if' function I could just add ...&& chr = 2) to check that it is Annie not Daisy as that would save a lot of typing. Or even better something like:


func ActionObject_888(idx)
{
if (chr == 2)
{
Message1 (4,4, "MESSAGE");
MessagePop();
}

else
{
ObjSet(ObjFind(666), O_DISABLE, 1);
}
}

By the way, I've managed to get your climbing code off pat now, so have included a section of lots of ivy hanging off rocks that Daisy has to climb ..and jump to. Thanks for creating it.

Grandad

PS - loading after saving a game does bring the correct character into play, but no matter at what position you save the game, it always loads at the place where you switched characters.

PPS - same happens when add a 'dizzy' as char 1 and switch daisy to 2 and annie to 3, plus amend the def, handler and switch functions

Grandad
18-06-10, 03:27 PM
Now it's really acting silly. OK I've added a new actionable object with an ID of 1 (to be Dizzy), switched Daisy to '2' and Annie to '3'. Changed the Def.gs to #def chr 3. Changed the Handler Player Update to :


// Player set costume
costume = 0;
if (chr == 2) costume+= 0;
if (chr == 3) costume+= 300;
PlayerSet(P_COSTUME, costume);

and amended the switch function so that Daisy is '2' and Annie is '3'. Looking at the 'new char' coding it should 'find' the location of the current active 'costume' then set it:


func NewChar() // load new player inventory and set costume
{
i = 0;
chrx = ObjFind(chr);
count = ObjGet(chrx, O_USER +11);
x = ObjGet(chrx, O_X);
y = ObjGet(chrx, O_Y);
scb = ObjFind(chr);
chrf = ObjGet(scb, O_TILE);
ObjSet(ObjFind(1), O_USER +10, chr);
PlayerSet(P_TILE, chrf);
PlayerSet(P_COSTUME, chrf);
PlayerSet(P_X, x+12);
PlayerSet(P_Y, y+14);
if (count >0)
{
for (i = count -1; i >=0; i--)
{
idx = ObjGet(chr, O_USER+i);
GameSet(G_INVENTORY +i, idx);
}
}
chrx = ObjFind(chr);
ObjSet(chrx, O_DISABLE, 1);
GamePause(0);
PlayerSet(P_DISABLE, 0);
return;
}

If I save the game after switching characters the screen flips back to the position of where the switch took place and saves there. Loading starts at the place the switch took place but the correct character is active and the relevant inventories are correct. However, if I try to save the game before switching characters it crashes with this error message:


GS_ERROR: USER, fn=ObjGet, line=-1, file="unknown"
GS_DESC: -1, 853, "invalid object index"
CALLSTACK: ObjGet:-1 < NewChar:311 < SaveUserData:159 < SaveGame:145 < OpenDialogFiles:348 < OpenDialogGameMenu:164 < .

APP: activate off
APP: activate on
ERROREXIT: GS9 ERROR
_gserrorexit < GSErrHandler < gsVM::Error < gsObjGet < gsVM::Run < cDizScript::Update < cDizGame::Update < cDizApp::Update < AppOnRun < e9App::Run < main
APP: activate off
APP: activate on

and yet there is definitely an actionable object with an ID of 1 on the saved map. My brain hurts!

Grandad

Colin
18-06-10, 05:04 PM
when I said you need a tile of dizzy idle with an id of 1, this would be because i didn't read that you weren't using the original character tilesets

if you can, revert back to when it was working.
place a tile of annie idle with ID 1
This is the tile that will appear when you switch to the new character. so needs to be of the right tile.

to explain how the switch works (original tilesets)

placing the dizzy idle tile ID (1)
as you normally start with dizzy, I place this off the game area. you can place it where you start, but make sure it is disabled

when you action on another character say daisy ID (2)
it checks current character ID from chr
it then counts how many items in the inventory and saves the amount and the item ids into the old character ID(1) user variables
then it moves the dizzy idle ID(1) to the current player position and reenables it
it sets the chr to the new character ID(2)
now saves the new chr to ID(1) user variable, so it knows which character to save game
it then reads the new character ID(2) user variables back into the inventory
disables the ID(2) daisy idle tile from view
switches the player tile from dizzy to daisy

Colin
18-06-10, 05:19 PM
do you want me to take a look at it?

there shouldn't be any need to change any of the original script
swapping the dizzy tileset over to the annie one, won't affect the code.
anything that was dizzy, instead will show as annie.

Grandad
18-06-10, 06:54 PM
Thanks Colin. I can follow the logic of the func NewChar, and it seems that it shouldn't make any difference by disabling the lines:

c=PlayerGet(P_COSTUME);
if (c == 330)
{
PlayerSet(P_TILE, 340);
}

and just keeping PlayerGet(P_COSTUME), as that only seems to be a check for whether the character has a scuba outfit on or not.

What I'll do over the weekend is to set up a new 'minigame' and copy the relevant parts of the scripts into it and play around to see if I can figure out the problem ...it's always a good learning curve if you can work it out yourself. If not then I'll be back here pleading with you to take a look.

Thanks again

Grandad

Grandad
19-06-10, 09:31 AM
I'm Sorry Colin, but it seems that your script is at fault. I've opened up the template from Dizzyage2.4 added an actionable dizzy (id 1) outside the play area and an actionable Annie (id 2) in the play area. Added one actionable object (id 100) to give a different message when either player actions it. Then rather than make any typing errors I cut and pasted all the coding from your first thread and only added a switch function from Annie back to dizzy.

It plays exactly the same as in my current game ie, saving after switching characters takes you back to the original switch point and crashes if you try and save without switching.

What I did notice was that in my printed version of your coding from when you first posted it, has adding #def 7 to def.gs, whereas the current coding has adding this to gamedef.gs????

Just in case, I've tried adding #def 7 to both files with the same result - separately of course.

Sorry

Grandad

delta
19-06-10, 09:38 AM
What I did notice was that in my printed version of your coding from when you first posted it, has adding #def 7 to def.gs, whereas the current coding has adding this to gamedef.gs????

it doesn't really matter which file you add it to, the game will pick it up from either. def.gs and gamedef.gs could quite easily be just one file, but Alex tried to make things easy for people in DizzyAGE v2, and moved all the defines that players were likely to change into their own file - gamedef.gs - with the idea being that beginners would only need to change things in two files: game.gs and gamedef.gs

Colin
19-06-10, 11:36 AM
can you send me a zip of the template copy

colin.page33@ntlworld.com

Colin
19-06-10, 01:52 PM
Sorry Grandad! looks like the save/load code wasn't working
I have now fixed it

here is a test version
multichar_demo.zip (http://homepage.ntlworld.com/colin.page33/multichar_demo.zip)

Grandad
19-06-10, 03:28 PM
Sorry about this Colin, but the save game after switching works fine, but even in your test game if you save the game before you switch to another character it saves, but still crashes when you try and load it.

Grandad

Colin
19-06-10, 06:44 PM
:dizzy_mad:
forgot a line!

add
ObjSet(ObjFind(1),O_USER+10,chr);
into the BeginNewGame() section after the disable line

Grandad
20-06-10, 08:11 AM
As Pop Larkin would say 'Perfick'! Thanks a lot Colin for taking the time to sort this out. Much appreciated.

Grandad
:v2_dizzy_thumbsup: