I'm working in the latest version of Zquest with the Dance of Remembrance (Hybrid) tileset. I came up with this idea based upon two other scripts. The first is the authentic dungeon barriers script which I've included below.
Spoiler
import "std.zh"
import "string.zh"
import "ffcscript.zh"
const int SCRIPT_BARRIERS = 1; // Must refer to "Barrier"'s ffc script slot in the quest
// ID's of barrier-related combos
// Barriers in raised state
const int BARRIER_A_RAISED = 32;
const int BARRIER_B_RAISED = 33;
// Barriers in lowered state
const int BARRIER_A_LOWERED = 34;
const int BARRIER_B_LOWERED = 35;
// Barriers animating to raised state
const int BARRIER_A_ANIMRAISE = 36;
const int BARRIER_B_ANIMRAISE = 37;
// Barriers animating to lowered state
const int BARRIER_A_ANIMLOWER = 38;
const int BARRIER_B_ANIMLOWER = 39;
// Raised barriers that Link can walk on
const int BARRIER_A_WALKABLE = 40;
const int BARRIER_B_WALKABLE = 41;
// Barrier switches
const int BARRIER_A_SWITCH = 42;
const int BARRIER_B_SWITCH = 43;
const int BARRIER_SWITCH_DUMMY = 177; // ID of a switch hit detection dummy enemy
const int BARRIER_SWITCH_DUMMY_HP = 32767;
// Global array to store the state of barriers per dmap
// If you have more than 16 dmaps you can change the capacity in the []'s
// You may change the states in other scripts, but the changes will not be visible
// until there is a new screen, so set them before Barriers_NewScreen() is called.
bool barriers[16];
global script slot2 {
void run() {
// Initialize variables used to listen on screen changes
int curscreen = -1;
while (true) {
// Keep track of screen changes
// Run a Barrier script on every screen change
if (Game->GetCurScreen() != curscreen) {
curscreen = Game->GetCurScreen();
Barriers_NewScreen();
}
Waitframe();
}
}
}
// Function that makes preparations for barriers on each screen and starts an FFC script
void Barriers_NewScreen() {
// Search for a barrier-related combo
for (int i = 0; i <= 175; i++) {
int cd = Screen->ComboD[i];
if (cd == BARRIER_A_RAISED || cd == BARRIER_A_LOWERED || cd == BARRIER_A_SWITCH ||
cd == BARRIER_B_RAISED || cd == BARRIER_B_LOWERED || cd == BARRIER_B_SWITCH) {
// A barrier-related combo was found
int curdmap = Game->GetCurDMap();
// Make initial changes to combos
if (barriers[curdmap]) {
for (int j = i; j <= 175; j++) {
int cd = Screen->ComboD[j];
if (cd == BARRIER_A_RAISED) Screen->ComboD[j] = BARRIER_A_LOWERED;
else if (cd == BARRIER_B_LOWERED) Screen->ComboD[j] = BARRIER_B_RAISED;
else if (cd == BARRIER_A_SWITCH) Screen->ComboD[j] = BARRIER_B_SWITCH;
}
}
else {
for (int j = i; j <= 175; j++) {
int cd = Screen->ComboD[j];
if (cd == BARRIER_B_RAISED) Screen->ComboD[j] = BARRIER_B_LOWERED;
else if (cd == BARRIER_A_LOWERED) Screen->ComboD[j] = BARRIER_A_RAISED;
else if (cd == BARRIER_B_SWITCH) Screen->ComboD[j] = BARRIER_A_SWITCH;
}
}
// So run FFCscript to control barriers
int args[] = {curdmap,i,0,0,0,0,0,0};
RunFFCScript(SCRIPT_BARRIERS, args);
break;
}
}
}
// This lets you toggle barriers on any dmap
bool ToggleBarriers(int dmap) {
if (dmap == Game->GetCurDMap()) ToggleBarriers();
else barriers[dmap] = !barriers[dmap];
return barriers[dmap];
}
// This toggles barriers on the current dmap
bool ToggleBarriers() {
int curdmap = Game->GetCurDMap();
if (!barriers[curdmap]) {
barriers[curdmap] = true;
for (int i = 0; i <= 175; i++) {
int cd = Screen->ComboD[i];
if (cd == BARRIER_A_RAISED || cd == BARRIER_A_WALKABLE || cd == BARRIER_A_ANIMRAISE) {
Screen->ComboD[i] = BARRIER_A_ANIMLOWER;}
else if (cd == BARRIER_B_LOWERED || cd == BARRIER_B_ANIMLOWER) {
Screen->ComboD[i] = BARRIER_B_ANIMRAISE;}
else if (cd == BARRIER_A_SWITCH) {Screen->ComboD[i] = BARRIER_B_SWITCH;}
}
}
else {
barriers[curdmap] = false;
for (int i = 0; i <= 175; i++) {
int cd = Screen->ComboD[i];
if (cd == BARRIER_B_RAISED || cd == BARRIER_B_WALKABLE || cd == BARRIER_B_ANIMRAISE) {
Screen->ComboD[i] = BARRIER_B_ANIMLOWER;}
else if (cd == BARRIER_A_LOWERED || cd == BARRIER_A_ANIMLOWER) {
Screen->ComboD[i] = BARRIER_A_ANIMRAISE;}
else if (cd == BARRIER_B_SWITCH) {Screen->ComboD[i] = BARRIER_A_SWITCH;}
}
}
return barriers[curdmap];
}
// This script controls barriers on the screen
// The FFC is automatically created by Barriers_NewScreen()
ffc script Barriers {
void run(int first) {
// Initialize storage for bswitch hit dummies
int bswitch_count;
npc bswitch[8];
for (int i = first; i <= 175; i++) {
if (Screen->ComboD[i] == BARRIER_A_SWITCH || Screen->ComboD[i] == BARRIER_B_SWITCH) {
npc bs = CreateNPCAt(BARRIER_SWITCH_DUMMY, ComboX(i), ComboY(i));
bs->HitWidth = 8; // Smaller hit box to avoid annoying collisions with Link
bs->HitHeight = 8;
bs->HP = BARRIER_SWITCH_DUMMY_HP;
bswitch[bswitch_count++] = bs;
}
}
// Change raised barriers to walkable ones if Link enters screen on a raised barrier
int lcombo = LinkOnComboD();
bool onbarrier = (lcombo == BARRIER_A_RAISED || lcombo == BARRIER_B_RAISED);
if (onbarrier) for (int i = 0; i < 176; i++) {
if (Screen->ComboD[i] == BARRIER_A_RAISED) {Screen->ComboD[i] = BARRIER_A_WALKABLE;}
else if (Screen->ComboD[i] == BARRIER_B_RAISED) {Screen->ComboD[i] = BARRIER_B_WALKABLE;}
}
while (true) {
// Detect hits on bswitches, and change combos accordingly
for (int j = 0; j < bswitch_count; j++) {
if (bswitch[j]->HP < BARRIER_SWITCH_DUMMY_HP) {
bswitch[j]->HP = BARRIER_SWITCH_DUMMY_HP;
ToggleBarriers();
break;
}
} //break so that only one bswitch hit may register per frame
// Make barriers walkable if Link is on raised barriers, or unwalkable if not
lcombo = LinkOnComboD();
if (!onbarrier && (lcombo == BARRIER_A_RAISED || lcombo == BARRIER_B_RAISED)) {
onbarrier = true;
for (int i = 0; i <= 175; i++) {
if (Screen->ComboD[i] == BARRIER_A_RAISED) {Screen->ComboD[i] = BARRIER_A_WALKABLE;}
else if (Screen->ComboD[i] == BARRIER_B_RAISED) {Screen->ComboD[i] = BARRIER_B_WALKABLE;}
}
}
else if (onbarrier && !(lcombo == BARRIER_A_WALKABLE || lcombo == BARRIER_B_WALKABLE)) {
onbarrier = false;
for (int i = 0; i <= 175; i++) {
if (Screen->ComboD[i] == BARRIER_A_WALKABLE) {Screen->ComboD[i] = BARRIER_A_RAISED;}
else if (Screen->ComboD[i] == BARRIER_B_WALKABLE) {Screen->ComboD[i] = BARRIER_B_RAISED;}
}
}
Waitframe();
}
}
}
// A utility function that returns the ID of the combo that Link appears to stand on
int LinkOnComboD() {
return Screen->ComboD[ComboAt(Link->X+8, Link->Y+13)];
}
The idea I have seems like it would be a simple one, but so far my efforts to code it myself have hit against a giant wall. Namely, I can tell the script how to set up the dummy enemys, count the number of switches on screen and even reset the switches if you get the order wrong. But I'm a little hung up on telling the script what the right order is supposed to be.
My instincts tell me that this would probably have to either have an FFC which could have the right order placed as its arguments, or one FFC per switch. If it was the former, then I imagine doing something like this.
ffc script SwitchOrder {
void run(int SwitchPos1, int SwitchPos2, int SwitchPos3, int SwitchPos4){//Etc up to 8 switches.
The code in the topic above is sufficiently different from what I'm after that I don't think it would work. Anyways, theoretically the ffc would detect which switch you just tried to hit (using the dummy enemy's location) and if it wasn't the right one; any switches that you had already correctly hit would be returned to normal. Sort of like a reset room combo, except where you have to hit it with a weapon to activate it.
My own code does nothing and I even know why... the FFC has no clue what the order is supposed to be. By using this, I could set up puzzles where you couldn't just randomly hit switches in the hope of figuring it out but had to read clues to get past. (I'd post what I came up with, but it isn't that different from the authentic barriers script; just changing names of variables and eliminating stuff that I don't need.)
You could use the ffc number as the order. So gotta hit ffc1 first, then ffc2, etc.
Each ffc when hit checks if the previous one has been hit (use the misc array, or check the combo under it to see if it is the triggered version), if it hasn't been hit then it cycles through all and resets them. If the previous has been triggered then trigger this one.
Instead of using the dummy enemy, I would just use a standard strike-> next combo, and watch for it to change.
I knew that there was something that I forgot to mention whenever I created this thread. The reason I'm trying to reset the room is because it is a tiered secret. By having it where hitting the wrong switch resets the room, that keeps the player from just randomly hitting switches until one works.
Plus, that means I can't check for the combo changing. (I even tried that.) Since the combo won't change until the switches are hit in order, that means that it has to be active before the combo changes. I don't know of anything else I might be forgetting, but I'll post it if I think of it.
i don't get exactly what you mean about it being a tiered secret.
you can still have the combo change, just change it into a combo that looks exactly like the first one. the combo# is different for the scripting purpose, but the player sees the same tile.
On the screen in question, only one of the switches is marked with a flag that triggers a secret, changing it to a blue switch and activating one of the other three switches, which then activates the next. The way that the barrier script works is by detecting how many of the switch combos are on screen, assigning an unkillable dummy enemy to it and then detecting whether that enemy has been hit.
My intention was to create a script that would do that, then you could tell it what order the switches were supposed to be in and if you hit the wrong one all of the switches would reset to normal. (Basically, warping you back to the same screen in the same spot without any secrets triggered.)
I think I know how to use the FFC Misc Array as you mentioned. As for each FFC detecting the previous, I can't really make heads or tails of the code from the crystal switches. (At least the barriers one makes sense to me, even if it works differently than what I want.) I'll probably look over both codes more thoroughly tomorrow and see if I can work something out. If you happen to come solution or any other tips, I'd appreciate them.
This was designed to let you step on switches in a certain order, so hitting them in order shouldn't be that different.
The main problem I have comes with resetting the room.
There are several different methods I can think of to warp you back to the same room in the same spot. One I've tried and failed to get working is the one used by the Hole/Lava script. Just for reference, here's the script I'm talking about.
Spoiler
const int WSP_FALLING = 100; //Weapon/Misc. sprite for Link falling down a hole
const int WSP_LAVA = 89; //Weapon/Misc. sprite for Link drowning in lava
const int SFX_FALLING = 38; //SFX for falling down a hole
const int SFX_LAVA = 19; //SFX for drowning in lava
const int CM_AUTOWARPD = 1427; //Combo ID of a transparent AutoWarp D combotype
const int CT_HOLELAVA = 11; //Combotype to give hole functionality to (default is Left Statue)
// D0: Whether this screen has pits or lava. 0 for pits, 1 for lava
// D1: Whether to warp Link after falling (example: pits that take you to a lower floor in a dungeon)
// D2: Damage Link takes from falling, in 16ths of a heart. Default damage is half a heart
bool Falling;
ffc script HoleLava{
void run(int lava, int warpto, int warptype, int damage){
int graphic = WSP_FALLING; int sfx = SFX_FALLING;
if(lava){ graphic = WSP_LAVA; sfx = SFX_LAVA; }
if(this->X == 0 && this->Y == 0){
Waitframes(5);
this->X = Link->X; this->Y = Link->Y;
}
if(damage == 0) damage = 8;
while(true){
while(!OnPitCombo()) Waitframe();
int pitclk = 0;
while(OnPitCombo() && pitclk++ < 4) WaitCancelFeather();
if(pitclk >= 5) Fall(this,sfx,graphic,damage,warpto,warptype);
}
}
void Fall(ffc pos, int sfx, int graphic, int damage, int warpto, int warptype){
Falling = true;
Game->PlaySound(sfx);
for(int i=1;i<=Screen->NumLWeapons();i++){
lweapon l = Screen->LoadLWeapon(i);
if(l->ID == LW_SWORD) l->DeadState = WDS_DEAD;
}
int wait = CreateGraphic(graphic);
Link->CollDetection = false; Link->Invisible = true;
for(int i=0;i<30;i++) WaitNoAction();
Link->CollDetection = true; Link->Invisible = false;
if(warpto) Warp(pos, warptype);
Link->X = pos->X; Link->Y = pos->Y;
Link->HP -= damage;
Game->PlaySound(SFX_OUCH);
Falling = false;
}
void Warp(ffc Warp, int warptype){
int orig = Warp->Data;
Warp->Data = CM_AUTOWARPD+warptype;
Warp->Flags[FFCF_CARRYOVER] = true;
Waitframe();
Warp->Data = orig;
Warp->Flags[FFCF_CARRYOVER] = false;
Link->Z = Link->Y;
Quit();
}
bool OnPitCombo(){
return (Screen->ComboT[ComboAt(Link->X+8,Link->Y+8)] == CT_HOLELAVA && Link->Z <= 0 && Link->Action != LA_FROZEN);
}
int CreateGraphic(int sprite){
lweapon l = Screen->CreateLWeapon(LW_SCRIPT1);
l->HitXOffset = 500;
l->UseSprite(sprite);
l->DeadState = l->NumFrames*l->ASpeed;
l->X = Link->X; l->Y = Link->Y;
return l->DeadState;
}
void WaitCancelFeather(){
if(GetEquipmentA() == I_ROCSFEATHER && Link->InputA) Link->InputA = false;
if(GetEquipmentB() == I_ROCSFEATHER && Link->InputB) Link->InputB = false;
Waitframe();
}
}
The easiest way I can think of to combine these is to use a second FFC script that is activated by the first. So, I worked up my own script to accomplish this. Instead of doing what I intended, it warps you to the top right corner and causes you to be invisible and visible every frame. I can even determine why it is doing this. Here's the script.
Spoiler
import "std.zh"
const int A_SWITCH = 29340;
const int SWITCH_DUMMY = 116; // ID of a switch hit detection dummy enemy
const int SWITCH_DUMMY_HP = 32767;
bool WrongSwitch;
ffc script orderedSwitch{
void run(int d, int number, int total){
// Reset puzzle state when entering room
Screen->D[d] = 0;
npc bs = CreateNPCAt(SWITCH_DUMMY, this->X, this->Y);
bs->HitWidth = 8; // Smaller hit box to avoid annoying collisions with Link
bs->HitHeight = 8;
bs->HP = SWITCH_DUMMY_HP;
// Until permanent secrets are triggered, or all switches are hit
while(!Screen->State[ST_SECRET] && Screen->D[d] < total){
//Detect Hit on Switch
if (bs->HP < SWITCH_DUMMY_HP) {
bs->HP = SWITCH_DUMMY_HP;
if(number > Screen->D[d]){
if(number == Screen->D[d] + 1){
// Right switch
Screen->D[d]++;
if(number == total){
Game->PlaySound(SFX_SECRET);
return;
}
}
else{
Screen->D[d] = 0;
WrongSwitch = true;
}
}
}
Waitframe();
}
}
}
ffc script ResetRoom{
void run(){
if(WrongSwitch== true){
if(this->X == 0 && this->Y == 0){
Waitframes(5);
this->X = Link->X; this->Y = Link->Y;
}
}
while(true){
int pitclk = 0;
while(pitclk++ < 4) WaitCancelFeather();
if(pitclk >= 5) Reset(this);
Waitframe();
}
}
void Reset(ffc pos){
Link->CollDetection = false;
Link->Invisible = true;
for(int i=0;i<30;i++) WaitNoAction();
Link->CollDetection = true;
Link->Invisible = false;
Link->X = pos->X;
Link->Y = pos->Y;
WrongSwitch = false;
}
void WaitCancelFeather(){
if(GetEquipmentA() == I_ROCSFEATHER && Link->InputA) Link->InputA = false;
if(GetEquipmentB() == I_ROCSFEATHER && Link->InputB) Link->InputB = false;
Waitframe();
}
}
My instincts tell me that the Screen->D variable starts at zero, which then activates the while loop. Then the next if is being activated, even though it shouldn't be unless the enemy's HP isn't at max. (Maybe because there is more than one of them?) And since there are multiple switches, the number variable is never right, so the bool WrongSwitch is being activated constantly.
It may also be that the placement of commands is incorrect somehow. I know that WrongSwitch needs to be set to false, but I'm not sure where to put that. It is also possible that I need to place the if that detects its condition inside the while loop for ResetRoom, perhaps only activating the Reset() if WrongSwitch is true. I'm going to continue to work with it, but if anyone can point me in the right direction or suggest an alternative; I'd appreciate it.
I think you're making this too complicated. I'm going to write the script when I get to a computer later today.
try this, uncompiled untested. setup three combos. the first two should look exactly the same. the first one has a strike->next or step->next or whatever on it. so when hit it turns into the next combo which looks exactly the same (so the player has no idea anything has happened). create a third combo that is the version that looks triggered. put those combo#s into the constants in the script.
place your combos onscreen and place the ffc script over each one. for the first ffc running the script set D0 to 1. have the ffcs in subsequent consecutive order, the order that they should be hit. on the last ffc of the puzzle set D0 to 2.
the script looks for the combo to have changed to the 2nd combo (the triggered but look-alike). if changed it checks whether the combo before it is triggered. if not, all the combos get reset to the 1st combo. if the previous one is triggered then we know all the ones before were triggered in correct order. once the last gets triggered, and the one before is triggered then we trigger secrets.
import "std.zh"
const int FFC_MISC_PUZZLEORDER = 0; // set to an unused FFC->Misc[] array position
const int PUZZLEORDER_COMBO_UNSET = 0; // combo of the unhit trigger
const int PUZZLEORDER_COMBO_ISSET = 0; // combo of a hit trigger, should look the exact same as above
const int PUZZLEORDER_COMBO_ALLSET = 0; // combo of the hit trigger showing the puzzle is complete
// D0: 1 = is the first ffc in the puzzle, 2 = the last ffc in the puzzle.
// only the first and last need this set, the others don't
ffc script PuzzleOrder
{
void run(int isFirstorLast)
{
int loc = ComboAt(this->X,this->Y);
int thisNum = FFCNum(this);
ffc f;
while(true)
{
if( Screen->ComboD[loc] == PUZZLEORDER_COMBO_ISSET && this->Misc[FFC_MISC_PUZZLEORDER] == 0)
{
if(isFirstorLast == 1) // first
{
this->Misc[FFC_MISC_PUZZLEORDER] = 1;
}
else
{
f = Screen->LoadFFC(thisNum-1);
if( f->Misc[FFC_MISC_PUZZLEORDER] == 1 )
{
this->Misc[FFC_MISC_PUZZLEORDER] = 1;
if(isFirstorLast == 2) // last
{
PuzzleOrderSetAll(this->Script);
Screen->TriggerSecrets();
Screen->State[ST_SECRET] = true;
}
}
else
{
PuzzleOrderUnset(this->Script);
}
}
}
Waitframe();
}
}
void PuzzleOrderSetAll(int scriptNum)
{
ffc f;
for(int i = 1; i <= 32; i++)
{
f = Screen->LoadFFC(i);
if(f->Script != scriptNum) continue;
Screen->ComboD[ ComboAt(f->X,f->Y) ] = PUZZLEORDER_COMBO_ALLSET;
}
}
void PuzzleOrderUnset(int scriptNum)
{
ffc f;
for(int i = 1; i <= 32; i++)
{
f = Screen->LoadFFC(i);
if(f->Script != scriptNum) continue;
if(f->Misc[FFC_MISC_PUZZLEORDER] == 1)
{
Screen->ComboD[ ComboAt(f->X,f->Y) ] = PUZZLEORDER_COMBO_UNSET;
f->Misc[FFC_MISC_PUZZLEORDER] = 0;
}
}
}
}
Okay, I'm not sure if I'm doing something wrong with the setup or if the code isn't working properly. I set up the FFCs that run the code on this screen.
FFC one is at bottom left, with D0 set to one. Top right is FFC 2, Top left is FFC 3 and bottom right is FFC 4 which has D0 at 2. The three combo idea is similar to something I had thought of, using the Hamiltonian Path script as a model; so I'd already created Slash-Next Combos, along with one to the right that was identical and type none and one right of that which had the graphics of the hit switch and type none. So, i changed the constants at the top of the script to their combo numbers.
Also, I made the combos for a switch that was hit but not solved different so I could tell that it was doing something.
If hit in the right order, the secrets are triggered like they are supposed to. I can use Screen->D to make that permanent so you never have to solve the puzzle again. The problem is that now they can be hit in the wrong order too. For example, if I hit the first switch; then the third switch; the first switch changes back to normal. Then if I hit the second switch, it doesn't reset the puzzle. If I then hit the 4th switch, it acts as if the puzzle has been solved correctly.
I'm sure that there is a perfectly logical reason why this is happening, but sadly it eludes me.
Still doesn't seem to be working.... I've tested the following methods.
Hit the switches in the right order- almost perfect. Secrets are triggered, switches change to right combo. However, if you solve the puzzle again (which since the secrets stay triggered it isn't logical to do, but lets say somebody did) the secrets are untriggered. Best solution I can think of is saving to the Screen->D variable when solved and make the script not run and the combo's change to their hit status if that variable has been set.
Hit all the switches in any order- still solves the puzzle, strangely. Not only that, but the only trigger that is reset to its unhit state is the one covered by FFC one. If I start at any other switch, no triggers are reset and all I must do is hit all the switches to solve the puzzle. Whereas, if I start at the correct first switch, then hit a wrong switch; the first one resets but the puzzle can still be solved if I hit all of the other switches in any order.
One reason this might be is that script is checking for the value of FFC_MISC_PUZZLEORDER == 1 to reset them and that's only being set for the first switch. I can think of a few ways to modify it to check each FFC, but working it all out will probably take me a while. (Based on my past efforts.) Thanks for everything you've done so far.
ok, this worked for me. could only hit them in the correct order. by using permanent secrets they are set as soon as you return to the room (don't use nes dungeon dmap).
import "std.zh"
const int FFC_MISC_PUZZLEORDER = 0; // set to an unused FFC->Misc[] array position
const int PUZZLEORDER_COMBO_UNSET = 896; // combo of the unhit trigger
const int PUZZLEORDER_COMBO_ISSET = 897; // combo of a hit trigger, should look the exact same as above
const int PUZZLEORDER_COMBO_ALLSET = 898; // combo of the hit trigger showing the puzzle is complete
// D0: 1 = is the first ffc in the puzzle, 2 = the last ffc in the puzzle.
// only the first and last need this set, the others don't
ffc script PuzzleOrder
{
void run(int isFirstorLast)
{
if(Screen->State[ST_SECRET])
{
PuzzleOrderSetAll(this->Script);
Quit();
}
int loc = ComboAt(this->X,this->Y);
int thisNum = FFCNum(this);
ffc f;
while(true)
{
if(Screen->State[ST_SECRET]) Quit();
if( Screen->ComboD[loc] == PUZZLEORDER_COMBO_ISSET && this->Misc[FFC_MISC_PUZZLEORDER] != 1)
{
if(isFirstorLast == 1) // first
{
this->Misc[FFC_MISC_PUZZLEORDER] = 1;
}
else
{
f = Screen->LoadFFC(thisNum-1);
if( f->Misc[FFC_MISC_PUZZLEORDER] != 1 ) PuzzleOrderUnset(this->Script);
else
{
this->Misc[FFC_MISC_PUZZLEORDER] = 1;
if(isFirstorLast == 2) // last
{
PuzzleOrderSetAll(this->Script);
Screen->TriggerSecrets();
Screen->State[ST_SECRET] = true;
}
}
}
}
Waitframe();
}
}
void PuzzleOrderSetAll(int scriptNum)
{
ffc f;
for(int i = 1; i <= 32; i++)
{
f = Screen->LoadFFC(i);
if(f->Script != scriptNum) continue;
Screen->ComboD[ ComboAt(f->X,f->Y) ] = PUZZLEORDER_COMBO_ALLSET;
}
}
void PuzzleOrderUnset(int scriptNum)
{
ffc f;
for(int i = 1; i <= 32; i++)
{
f = Screen->LoadFFC(i);
if(f->Script != scriptNum) continue;
Screen->ComboD[ ComboAt(f->X,f->Y) ] = PUZZLEORDER_COMBO_UNSET;
f->Misc[FFC_MISC_PUZZLEORDER] = 0;
}
}
}
The setup for this one is the same as the old one, right? That is, place the FFCs over the PUZZLEORDER_COMBO_UNSET combos, starting with the first and ending with the last. (First switch is FFC one, second is FFC two, etc.) Because when I loaded this version, if hit in the wrong order no switch is unset. And when hit in the right order, the puzzle isn't solved. FFC one's D0 is set to one and FFC 4's D0 is set to two, as per the instructions in the script. There are no other FFC's on the screen.
ya the exact same setup. did you change the constants?
i tested this, and it worked exactly as intended. i can send you my test quest, or you can send me yours.
Silly me... I forgot to set the constants. (I actually wondered about this before you suggested it, but didn't have a chance to test it until now.) The script works perfectly, keeping the switches and secrets triggered once all of them are hit in order and resetting if not hit in order. This puzzle is an interesting variation on the Lights Out Script I've seen in the database, so there's a chance that some other people might want to use it.
One way this could be done is by using the following code.
Spoiler
import "std.zh"
const int FFC_MISC_PUZZLEORDER = 0; // set to an unused FFC->Misc[] array position
// D0: 1 = is the first ffc in the puzzle, 2 = the last ffc in the puzzle.
// only the first and last need this set, the others don't
// D1: combo of the unhit trigger
// D2: combo of a hit trigger, should look the exact same as below.
// D3: combo of the hit trigger showing the puzzle is complete
ffc script PuzzleOrder
{
void run(int isFirstorLast, int PUZZLEORDER_COMBO_UNSET, int PUZZLEORDER_COMBO_ISSET, int PUZZLEORDER_COMBO_ALLSET )
{
if(Screen->State[ST_SECRET])
{
PuzzleOrderSetAll(this->Script);
Quit();
}
int loc = ComboAt(this->X,this->Y);
int thisNum = FFCNum(this);
ffc f;
while(true)
{
if(Screen->State[ST_SECRET]) Quit();
if( Screen->ComboD[loc] == PUZZLEORDER_COMBO_ISSET && this->Misc[FFC_MISC_PUZZLEORDER] != 1)
{
if(isFirstorLast == 1) // first
{
this->Misc[FFC_MISC_PUZZLEORDER] = 1;
}
else
{
f = Screen->LoadFFC(thisNum-1);
if( f->Misc[FFC_MISC_PUZZLEORDER] != 1 ) PuzzleOrderUnset(this->Script);
else
{
this->Misc[FFC_MISC_PUZZLEORDER] = 1;
if(isFirstorLast == 2) // last
{
PuzzleOrderSetAll(this->Script);
Screen->TriggerSecrets();
Screen->State[ST_SECRET] = true;
}
}
}
}
Waitframe();
}
}
void PuzzleOrderSetAll(int scriptNum)
{
ffc f;
for(int i = 1; i <= 32; i++)
{
f = Screen->LoadFFC(i);
if(f->Script != scriptNum) continue;
Screen->ComboD[ ComboAt(f->X,f->Y) ] = PUZZLEORDER_COMBO_ALLSET;
}
}
void PuzzleOrderUnset(int scriptNum)
{
ffc f;
for(int i = 1; i <= 32; i++)
{
f = Screen->LoadFFC(i);
if(f->Script != scriptNum) continue;
Screen->ComboD[ ComboAt(f->X,f->Y) ] = PUZZLEORDER_COMBO_UNSET;
f->Misc[FFC_MISC_PUZZLEORDER] = 0;
}
}
}
I haven't tested this idea myself, but the Hamiltonian Path Script uses a similar method so that you can make the combo's any number you want rather than hard coding them.
Thanks for the script! I plan to use it extensively in my current project.