const int CT_MV_WARP = 142;//Combo type for MetroidVania warp portals. Script 1 by default
const int MIDI_MW_WARP_TELEPORT = 6;//MIDI that plays during teleportation Must be a non-looping one.
const int MW_WARP_TELEPORT_DURATION = 240;//Duration of teleporting animation
const int FONT_MW_WARP_UI = 0;//Font used to render in-game instruction manual for warp portals.
const int COLOR_MV_WARP_UI_FONT_SHADOW = 16;//Color of shadow to render for UI strings.
const int FONT_MV_WARP_TEXT_X_OFFSET = 0;//UI text X offset, in pixels. NEW!!
const int TILE_MW_WARP_FRAME = 20052;//Top left corner of 3x3 frame used to render around the screen when user chooses warp destination
const int CSET_MW_WARP_FRAME = 7;//CSet used to render frame around the screen when user chooses warp destination
const int CMB_MW_WARP_TELEPORT_CLOUD = 912;//Combo ID used to render full-screen teleport animation.
const int CSET_MW_WARP_TELEPORT_CLOUD = 5;//CSet used to render full-screen teleport animation.
const int CMB_MW_WARP_PROMPT = 911;//Combo ID used to render prompt, when Link steps on warp spot.
const int CSET_MW_WARP_PROMPT = 11;//CSet used to render prompt, when Link steps on warp spot.
const int SFX_MV_WARP_RECORD = 7;//Sound that plays when a new warp spot is recorded into portal network system.
const int COLOR_MW_WARP_IMAGE_SHADOW = 1;//Color used to render shadow of preview image in warping UI.
const int MW_WARP_IMAGE_TILE_WIDTH = 4;//Tile width and tile height used to render preview images for warps.
const int MW_WARP_IMAGE_TILE_HEIGHT = 3;
int MV_WARP_DMAP[512];//Global arrays. 7 arrays used.
int MV_WARP_SCREEN[512];
int MV_WARP_LEVEL[512];
int MV_WARP_MAP[512];
int MV_WARP_MAPSCR[512];
int MV_WARP_PREVIEW_TILE[512];
int MV_WARP_PREVIEW_CSET[512];
//Metroidvania Warp Network System
//A set of portals, usually scattered across entire quest, that allows Link to warp between previously visited places for fast travel.
//Visit 2 or more screens with those warp spots, stand on warp spot FFC, press A to open menu with all available destinations. Cycle between warp spots with Left/Right then press A to confirm teleportation.
//Uses up 7 global arrays.
//1. Set up 3x3 frame for warp menu.
//2. Set up 2 combos, 1 for prompt combo (a speech bubble that appears above Link, when he steps on warp spot) and 1 for full screen teleportation animation.
//3. Check out constants inside the script file, especially CMB_MW_WARP_TELEPORT_CLOUD, CMB_MW_WARP_PROMPT and CMB_MW_WARP_PROMPT.
//4. Import and compile the script. Assign FFC script.
//5. Construct warp screens using only palette-independant CSets. That`s because Screen->DrawScreen uses current palette for rendering warp position selecting and there is no way to change that.
//6. Place warp spot - A combo with CT_MV_WARP combo type, warp return point A (or legacy warp arrival square, if used) and FFC with script onto the same position.
// D0 - Id of top left tile for screen screen preview image.
// D1 - CSet used for drawing that image.
//7. Repeat step 6 for each warp point in the quest.
global script Init{
void run(){
InitMetroidvaniaWarpData();
}
}
void InitMetroidvaniaWarpData(){
for (int i=0;i<512;i++){
MV_WARP_DMAP[i]=-1;
MV_WARP_SCREEN[i]=-1;
MV_WARP_LEVEL[i]=-1;
MV_WARP_MAP[i]=-1;
MV_WARP_MAPSCR[i]=-1;
MV_WARP_PREVIEW_TILE[i]=-1;
MV_WARP_PREVIEW_CSET[i]=-1;
}
}
ffc script MetroidvaniaWarpSpot{
void run( int tile, int cset){
if (tile==0) tile=-1;
int origcmb = ComboAt(CenterX(this), CenterY(this));
while(Screen->ComboT[origcmb]!=CT_MV_WARP) Waitframe();
if (WarpHasRecoeded()<0)RecordWarpPoint(tile,cset);
int cmb=-1;
int state = 0;
int curid=-1;
int teletimer = MW_WARP_TELEPORT_DURATION;
int str1[] = "Select Warp Destination";
int str2[] = "Left/Right - Cycle";
int str3[] = "A - Confirm, B - Cancel";
while(true){
if (state==0){
cmb = ComboAt(CenterLinkX(), CenterLinkY());
if (cmb==origcmb){
Screen->FastCombo(6, Link->X,Link->Y-15,CMB_MW_WARP_PROMPT, CSET_MW_WARP_PROMPT,OP_OPAQUE);
if (Link->PressA){
curid = WarpHasRecoeded();
state=1;
}
}
}
else if (state==1){
if (Link->PressRight){
curid++;
if (curid>=512)curid=0;
while(MV_WARP_DMAP[curid]<0){
curid++;
if (curid>=512)curid=0;
}
}
if (Link->PressLeft){
curid--;
if (curid<0)curid=511;
while(MV_WARP_DMAP[curid]<0){
curid--;
if (curid<0)curid=511;
}
}
if (Link->PressB){
state=0;
}
if (Link->PressA){
Game->PlayMIDI(MIDI_MW_WARP_TELEPORT);
state=2;
}
Screen->Rectangle(6, 0, 0, 256, 176, 0, 1, 1, 1, 0, true, OP_OPAQUE);
if (MV_WARP_PREVIEW_TILE[curid]>=0){
if (COLOR_MW_WARP_IMAGE_SHADOW>0)Screen->Rectangle(6, 130-MW_WARP_IMAGE_TILE_WIDTH*8, 74, 130+MW_WARP_IMAGE_TILE_WIDTH*8, 74+MW_WARP_IMAGE_TILE_HEIGHT*16, COLOR_MW_WARP_IMAGE_SHADOW, 1, 1, 1, 0, true, OP_OPAQUE);
Screen->DrawTile (6, 128-MW_WARP_IMAGE_TILE_WIDTH*8, 72, MV_WARP_PREVIEW_TILE[curid], MW_WARP_IMAGE_TILE_WIDTH, MW_WARP_IMAGE_TILE_HEIGHT, MV_WARP_PREVIEW_CSET[curid], -1, -1,0 ,0 , 0, 0, false, OP_OPAQUE);
}
else Screen->DrawScreen(6,MV_WARP_MAP[curid], MV_WARP_MAPSCR[curid],0,0,0);
DrawFrame(6,TILE_MW_WARP_FRAME,0,0,16,10,CSET_MW_WARP_FRAME,OP_OPAQUE);
if (COLOR_MV_WARP_UI_FONT_SHADOW>0){
Screen->DrawString(6, 33+FONT_MV_WARP_TEXT_X_OFFSET, 17, FONT_MW_WARP_UI, COLOR_MV_WARP_UI_FONT_SHADOW, -1, 0, str1, OP_OPAQUE);
Screen->DrawString(6, 33+FONT_MV_WARP_TEXT_X_OFFSET, 25, FONT_MW_WARP_UI, COLOR_MV_WARP_UI_FONT_SHADOW, -1, 0, str2, OP_OPAQUE);
Screen->DrawString(6, 33+FONT_MV_WARP_TEXT_X_OFFSET, 33, FONT_MW_WARP_UI, COLOR_MV_WARP_UI_FONT_SHADOW, -1, 0, str3, OP_OPAQUE);
}
Screen->DrawString(6, 32+FONT_MV_WARP_TEXT_X_OFFSET, 16, FONT_MW_WARP_UI, 1, -1, 0, str1, OP_OPAQUE);
Screen->DrawString(6, 32+FONT_MV_WARP_TEXT_X_OFFSET, 24, FONT_MW_WARP_UI, 1, -1, 0, str2, OP_OPAQUE);
Screen->DrawString(6, 32+FONT_MV_WARP_TEXT_X_OFFSET, 32, FONT_MW_WARP_UI, 1, -1, 0, str3, OP_OPAQUE);
NoAction();
}
else if (state==2){
teletimer--;
for (int i=0; i<176;i++){
Screen->FastCombo(6, ComboX(i),ComboY(i),CMB_MW_WARP_TELEPORT_CLOUD, CSET_MW_WARP_TELEPORT_CLOUD,OP_OPAQUE);
}
if (teletimer<=0){
Screen->Rectangle(6, 0, 0, 256, 176, 1, 1, 1, 1, 0, true, OP_OPAQUE);
Waitframe();
Link->Warp(MV_WARP_DMAP[curid],MV_WARP_SCREEN[curid]);
Quit();
}
NoAction();
}
Waitframe();
}
}
}
int WarpHasRecoeded(){
int dmap = Game->GetCurDMap();
int scrn = Game->GetCurDMapScreen();
int lvl = Game->GetCurLevel();
for (int i=0;i<512;i++){
if (MV_WARP_DMAP[i]!=dmap)continue;
if (MV_WARP_SCREEN[i]!=scrn)continue;
if (MV_WARP_LEVEL[i]!=lvl)continue;
return i;
}
return -1;
}
void RecordWarpPoint(int tile, int cset){
Game->PlaySound(SFX_MV_WARP_RECORD);
for (int i=0;i<512;i++){
if (MV_WARP_DMAP[i]>=0)continue;
MV_WARP_DMAP[i] = Game->GetCurDMap();
MV_WARP_SCREEN[i] = Game->GetCurDMapScreen();
MV_WARP_LEVEL[i]= Game->GetCurLevel();
MV_WARP_MAP[i]=Game->GetCurMap();
MV_WARP_MAPSCR[i]=Game->GetCurScreen();
MV_WARP_PREVIEW_TILE[i] = tile;
MV_WARP_PREVIEW_CSET[i] = cset;
return;
}
}
void DrawFrame(int layer, int tile, int posx, int posy, int sizex, int sizey, int CSet, int opacity){
int drawx = posx;
int drawy = posy;
int xoffset=0;
int yoffset=0;
for (int w=0; w<sizex; w++){
drawx = posx+16*w;
xoffset=0;
if (w>0)xoffset=1;
if (w==sizex-1) xoffset=2;
for (int h=0; h<sizey; h++){
drawy = posy+16*h;
yoffset=0;
if (h>0)yoffset=1;
if (h==sizey-1) yoffset=2;
Screen->FastTile(layer, drawx, drawy, tile +xoffset+20*yoffset, CSet, opacity);
}
}
}