PDA

View Full Version : Scripting quesitons: Functional NES-style doors in Interior type DMaps



CaRmAgE
11-14-2020, 08:47 PM
I remember an issue that was brought up about NES doors in Interior type rooms in 2015 (I think? I can't find the thread.), where even though the doors are technically only supposed to work in NES dungeons, they still worked in some Interior DMaps, but not others? I'm not trying to bring the topic back up, but rather, I'd like to script the doors as a workaround. My goal is to have full control of walkability and layering on these screens (which is not possible in NES dungeons), but still have the possibility of using the doors. This is why I'm using the Interior DMap type instead. Anyway, this is less a script request and more a general "is it possible", as I'm sure I can script most of it myself; I'm just stuck on a few things.

1) Is there a general pattern to which Interior DMaps will have working doors and which won't, so I can detect it? The plan is to create a global script to mimic the behavior, so I'm not copy/pasting FFCs everywhere.
2) For locked doors, if I change the door type for a particular direction on a screen, is the change permanent?
3) For shutters, how would I detect if Link is coming from a shutter side only during the first moment he enters the screen, and then subsequently, make him walk inward before shutting the door? Use a variable to keep track of the frames remaining, I guess, but can I make him look like he's walking?

Saffith
11-17-2020, 04:43 PM
I've actually done this already, though I did it as an FFC script. It's not too big a deal to set up if you don't have many doors. Even if it's just for reference, here it is:


const int BD_SHUTTER=1;
const int BD_1WAYSHUTTER=2;
const int BD_LOCKED=3;
const int BD_BOSSLOCKED=4;
const int BD_BOMBABLE=5;

ffc script BasicDoors
{
// D0-D3: Door type (BD_* constant)
// D4: Combo number of switch. If the combo at this position changes,
// any 1-way shutters will open.
// D5: If nonzero, the switch is permanent.
void run(int north, int south, int west, int east, int switchPos, int switchPermanent)
{
bool playSound=false;
int switchStart=Screen->ComboD[switchPos];
bool switched=false;
bool checkNorth=north>0;
bool checkSouth=south>0;
bool checkWest=west>0;
bool checkEast=east>0;

InitDoor(DIR_UP, north, ST_DOORUP);
InitDoor(DIR_DOWN, south, ST_DOORDOWN);
InitDoor(DIR_LEFT, west, ST_DOORLEFT);
InitDoor(DIR_RIGHT, east, ST_DOORRIGHT);

// North or south
if(Link->X==120)
{
// Coming through north door

// The way positions are handled has changed since this was written, but it still seems to work well enough.
if((Game->GetCurDMap()==prevDMap && Link->Y>=160) || (Game->GetCurDMap()!=prevDMap && Link->Y<=0))
{
// Shutter - walk all the way into the room, then close the door
if(north==BD_SHUTTER || north==BD_1WAYSHUTTER)
{
while(Link->Y<32 || Link->Y>=160)
{
NoAction();
Link->InputDown=true;
Waitframe();
}
enterX=Clamp(Link->X, 0, 240);
enterY=Clamp(Link->Y, 0, 160);
}
// Locked- must have been unlocked, so open it up and be sure it's set properly
else if(north==BD_LOCKED)
{
Screen->Door[DIR_UP]=D_UNLOCKED;
Screen->State[ST_DOORUP]=true;
checkNorth=false;
}
else if(north==BD_BOSSLOCKED)
{
Screen->Door[DIR_UP]=D_BOSSUNLOCKED;
Screen->State[ST_DOORUP]=true;
checkNorth=false;
}
}
// Coming through south door
else if((Game->GetCurDMap()==prevDMap && Link->Y<=0) || (Game->GetCurDMap()!=prevDMap && Link->Y>=160))
{
if(south==BD_SHUTTER || south==BD_1WAYSHUTTER)
{
while(Link->Y>128 || Link->Y<=0)
{
NoAction();
Link->InputUp=true;
Waitframe();
}
enterX=Clamp(Link->X, 0, 240);
enterY=Clamp(Link->Y, 0, 160);
}
else if(south==BD_LOCKED)
{
Screen->Door[DIR_DOWN]=D_UNLOCKED;
Screen->State[ST_DOORDOWN]=true;
checkSouth=false;
}
else if(south==BD_BOSSLOCKED)
{
Screen->Door[DIR_DOWN]=D_BOSSUNLOCKED;
Screen->State[ST_DOORDOWN]=true;
checkSouth=false;
}
}
}
// East or west
else if(Link->Y==80)
{
// Coming through west door
if((Game->GetCurDMap()==prevDMap && Link->X>=240) || (Game->GetCurDMap()!=prevDMap && Link->X<=0))
{
if(west==BD_SHUTTER || west==BD_1WAYSHUTTER)
{
while(Link->X<32 || Link->X>=240)
{
NoAction();
Link->InputRight=true;
Waitframe();
}
enterX=Clamp(Link->X, 0, 240);
enterY=Clamp(Link->Y, 0, 160);
}
else if(west==BD_LOCKED)
{
Screen->Door[DIR_LEFT]=D_UNLOCKED;
Screen->State[ST_DOORLEFT]=true;
checkWest=false;
}
else if(west==BD_BOSSLOCKED)
{
Screen->Door[DIR_LEFT]=D_BOSSUNLOCKED;
Screen->State[ST_DOORLEFT]=true;
checkWest=false;
}
}
// Coming through east door
else if((Game->GetCurDMap()==prevDMap && Link->X<=0) || (Game->GetCurDMap()!=prevDMap && Link->X>=240))
{
if(east==BD_SHUTTER || east==BD_1WAYSHUTTER)
{
while(Link->X>208 || Link->X<=0)
{
NoAction();
Link->InputLeft=true;
Waitframe();
}
enterX=Clamp(Link->X, 0, 240);
enterY=Clamp(Link->Y, 0, 160);
}
else if(east==BD_LOCKED)
{
Screen->Door[DIR_RIGHT]=D_UNLOCKED;
Screen->State[ST_DOORRIGHT]=true;
checkEast=false;
}
else if(east==BD_BOSSLOCKED)
{
Screen->Door[DIR_RIGHT]=D_BOSSUNLOCKED;
Screen->State[ST_DOORRIGHT]=true;
checkEast=false;
}
}
}

Waitframe();

if(north==BD_SHUTTER)
{
Screen->Door[DIR_UP]=D_SHUTTER;
playSound=true;
}
else if(north==BD_1WAYSHUTTER)
{
Screen->Door[DIR_UP]=D_1WAYSHUTTER;
playSound=true;
}

if(south==BD_SHUTTER)
{
Screen->Door[DIR_DOWN]=D_SHUTTER;
playSound=true;
}
else if(south==BD_1WAYSHUTTER)
{
Screen->Door[DIR_DOWN]=D_1WAYSHUTTER;
playSound=true;
}

if(west==BD_SHUTTER)
{
Screen->Door[DIR_LEFT]=D_SHUTTER;
playSound=true;
}
else if(west==BD_1WAYSHUTTER)
{
Screen->Door[DIR_LEFT]=D_1WAYSHUTTER;
playSound=true;
}

if(east==BD_SHUTTER)
{
Screen->Door[DIR_RIGHT]=D_SHUTTER;
playSound=true;
}
else if(east==BD_1WAYSHUTTER)
{
Screen->Door[DIR_RIGHT]=D_1WAYSHUTTER;
playSound=true;
}

if(playSound)
Game->PlaySound(SFX_DOORCLOSE);

Waitframes(8);

while(true)
{
if(checkNorth)
{
if((north==BD_LOCKED || north==BD_BOSSLOCKED || north==BD_BOMBABLE) && Screen->ComboS[23]!=15)
{
Screen->State[ST_DOORUP]=true;
Game->SetScreenState(Game->GetCurMap(), Game->GetCurScreen()-16, ST_DOORDOWN, true);
checkNorth=false;
if(north==BD_BOMBABLE)
Screen->Door[DIR_UP]=D_BOMBED;
}
else if(north==BD_1WAYSHUTTER && Screen->State[ST_DOORUP])
{
Screen->Door[DIR_UP]=D_OPENSHUTTER;
Game->PlaySound(SFX_DOOROPEN);
checkNorth=false;
}
}

if(checkSouth)
{
if((south==BD_LOCKED || south==BD_BOSSLOCKED || south==BD_BOMBABLE) && Screen->ComboS[151]!=15)
{
Screen->State[ST_DOORDOWN]=true;
Game->SetScreenState(Game->GetCurMap(), Game->GetCurScreen()+16, ST_DOORUP, true);
checkSouth=false;
if(south==BD_BOMBABLE)
Screen->Door[DIR_DOWN]=D_BOMBED;
}
else if(south==BD_1WAYSHUTTER && Screen->State[ST_DOORDOWN])
{
Screen->Door[DIR_DOWN]=D_OPENSHUTTER;
Game->PlaySound(SFX_DOOROPEN);
checkSouth=false;
}
}

if(checkWest)
{
if((west==BD_LOCKED || west==BD_BOSSLOCKED || west==BD_BOMBABLE) && Screen->ComboS[81]!=15)
{
Screen->State[ST_DOORLEFT]=true;
Game->SetScreenState(Game->GetCurMap(), Game->GetCurScreen()-1, ST_DOORRIGHT, true);
checkWest=false;
if(west==BD_BOMBABLE)
Screen->Door[DIR_LEFT]=D_BOMBED;
}
else if(west==BD_1WAYSHUTTER && Screen->State[ST_DOORLEFT])
{
Screen->Door[DIR_LEFT]=D_OPENSHUTTER;
Game->PlaySound(SFX_DOOROPEN);
checkWest=false;
}
}

if(checkEast)
{
if((east==BD_LOCKED || east==BD_BOSSLOCKED || east==BD_BOMBABLE) && Screen->ComboS[94]!=15)
{
Screen->State[ST_DOORRIGHT]=true;
Game->SetScreenState(Game->GetCurMap(), Game->GetCurScreen()+1, ST_DOORLEFT, true);
checkEast=false;
if(east==BD_BOMBABLE)
Screen->Door[DIR_RIGHT]=D_BOMBED;
}
else if(east==BD_1WAYSHUTTER && Screen->State[ST_DOORRIGHT])
{
Screen->Door[DIR_RIGHT]=D_OPENSHUTTER;
Game->PlaySound(SFX_DOOROPEN);
checkEast=false;
}
}

if(switchPos>0 && !switched && Screen->ComboD[switchPos] != switchStart)
{
switched=true;

if(north==BD_1WAYSHUTTER)
OpenDoor(DIR_UP, BD_1WAYSHUTTER, switchPermanent>0);
if(south==BD_1WAYSHUTTER)
OpenDoor(DIR_DOWN, BD_1WAYSHUTTER, switchPermanent>0);
if(west==BD_1WAYSHUTTER)
OpenDoor(DIR_LEFT, BD_1WAYSHUTTER, switchPermanent>0);
if(east==BD_1WAYSHUTTER)
OpenDoor(DIR_RIGHT, BD_1WAYSHUTTER, switchPermanent>0);

Game->PlaySound(SFX_DOOROPEN);

checkNorth=false;
checkSouth=false;
checkWest=false;
checkEast=false;
}

Waitframe();
}
}

void InitDoor(int dir, int type, int state)
{
if(Screen->State[state])
{
if(type==BD_LOCKED)
Screen->Door[dir]=D_UNLOCKED;
else if(type==BD_BOSSLOCKED)
Screen->Door[dir]=D_BOSSUNLOCKED;
else if(type==BD_BOMBABLE)
Screen->Door[dir]=D_BOMBED;
}
else if(type==BD_BOMBABLE)
Screen->Door[dir]=D_BOMB;
}

void OpenDoor(int dir, int startState, bool permanent)
{
if(startState==0)
return;

if(startState==BD_LOCKED)
{
Screen->Door[dir]=D_UNLOCKED;
Screen->State[dir]=true;
Game->SetScreenState(Game->GetCurMap(), NextScreen(dir), Opposite(dir), true);
}
else if(startState==BD_BOSSLOCKED)
{
Screen->Door[dir]=D_BOSSUNLOCKED;
Screen->State[dir]=true;
Game->SetScreenState(Game->GetCurMap(), NextScreen(dir), Opposite(dir), true);
}
else if(startState==BD_BOMBABLE)
{
Screen->Door[dir]=D_BOMBED;
Screen->State[dir]=true;
Game->SetScreenState(Game->GetCurMap(), NextScreen(dir), Opposite(dir), true);
}
else // BD_SHUTTER
{
Screen->Door[dir]=D_OPENSHUTTER;
if(permanent)
Screen->State[dir]=true;
}
}

int NextScreen(int dir)
{
if(dir==DIR_UP)
return Game->GetCurScreen()-16;
else if(dir==DIR_DOWN)
return Game->GetCurScreen()+16;
else if(dir==DIR_LEFT)
return Game->GetCurScreen()-1;
else
return Game->GetCurScreen()+1;
}
}


1) Is there a general pattern to which Interior DMaps will have working doors and which won't, so I can detect it? The plan is to create a global script to mimic the behavior, so I'm not copy/pasting FFCs everywhere.
It's been a while, but if I remember correctly, doors only work in dungeon DMaps. It's probably a DMap flag in 2.55, though.


2) For locked doors, if I change the door type for a particular direction on a screen, is the change permanent?
If you also set the appropriate item in Screen->State[] to true, e.g. Screen->State[ST_DOORLEFT]. The ST_DOOR* constants are actually the same as the corresponding DIR_ constants, so you can just use the same value for each if it's easier.


3) For shutters, how would I detect if Link is coming from a shutter side only during the first moment he enters the screen, and then subsequently, make him walk inward before shutting the door? Use a variable to keep track of the frames remaining, I guess, but can I make him look like he's walking?
Again, I may not remember correctly, but... If the screen scrolls, Link's X or Y position will be beyond the edge of the screen when he first enters a room. From a non-scrolling warp, he'll appear right at the edge. In any case, if you try tracing his position, you should be able to work it out pretty easily.
Making him walk in is simple: press the appropriate direction key and unpress everything else until he's far enough into the room.