Migraine Door Challenge!
#1
Posté 13 avril 2012 - 04:33
Imagine, if you will, 6 square areas, each area has doors at uniform intervals on all sides.
The areas are arranged as if they were faces on a cube.
In the center of each area, is a switch that when triggered, theoretically rotates the area the switch is in, although only the door/transitions would change targets.
Furthermore, some switches would cause the unoccupied faces of the cube to 'rotate'.
Now, what is required for this?
I am thinking of a matrix that is the underlying 'cube', with the location of each door interface marked by a node, and given a unique identity. Those unique nodes would carry 2 variables, the identies of the doors that interface to it.
Then, as areas 'rotate', the door ID variables stored with each of the 'nodes' is changed accordingly. Each switch would have a list of nodes that it influenced, so the managing script would know which nodes 'rotate' door ID's for.
Sadly, I know how I would do all this on a Commodore 64 using BASIC, but that doesn't really help me in NWN.
I will conceed, that the use of the intermediate nodes may not be a hard requirement to make this particular concept work, but there is another layer to this onion that requires the nodes.
#2
Posté 13 avril 2012 - 04:50
Tesseract! The heart of Greenvenom's lair in the Iron Mountain is a tesseract =)
A bit more complicated than 6 faces, and requires a Klein bottle to... <boss!>
Uh, right. can't cross that Teaser line, can we?
Your node framework makes sense to me.
Each node belongs to two faces.
Each face has 4 nodes.
Each door references a node.
On transition, the node is polled to determine the matching door *at that moment*, and a jump to wp moves the party. When switch is pulled, the 4 doors for that face rotate node references (placeholder=a=b=c=d=placeholder).
The other half of each door-node-door set is unchanged.
What would be *really* tricky (spoiler) is to have 4 versions of each face rotated by 90 degrees so that the compass directions swing wildly about...
<...tesseract horripilations!>
Modifié par Rolo Kipp, 13 avril 2012 - 05:08 .
#3
Posté 13 avril 2012 - 05:12
#4
Posté 13 avril 2012 - 05:23
#5
Posté 13 avril 2012 - 06:32
...
Your node framework makes sense to me.
Each node belongs to two faces.
Each face has 4 nodes.
Each door references a node.
On transition, the node is polled to determine the matching door *at that moment*, and a jump to wp moves the party. When switch is pulled, the 4 doors for that face rotate node references (placeholder=a=b=c=d=placeholder).
The other half of each door-node-door set is unchanged.
Just so... except I have no clue how to implement such a tangle in NWN.
What would be *really* tricky (spoiler) is to have 4 versions of each face rotated by 90 degrees so that the compass directions swing wildly about...
I am totally disinterested in the compass... In a tesseract, with space bending in on itself, I doubt magnetic forces would fair much better.
Unless there is a script to disable or disorient the compass, it's probably best just to ignore it.
I don't think knowing which side of the map was 'up' when it was drawn will do anybody much good if the map keeps getting cut up, spun around, and reoriented... particularly if the map is a legitimate labyrinth in it's own right.
#6
Posté 13 avril 2012 - 06:43
PlasmaJohn wrote...
Don't link the doors, use the OnAreaTransitionClick event then have the script choose the jump target.
Got it, then these functions ought to work perfectly no matter how you do the transition.
For this script to work each side transition tag must be of the form FACEPREFIX + "_" + SIDE# and the local variable (on the module) for that tag should designate the tag of the target object. Also set the number of sides for each face using the local integer (on the module) designated by the face prefix.
object GetTargetForTransition()
{
return GetObjectByTag(GetLocalString(GetModule(), GetTag(OBJECT_SELF)));
}
void RotateFaces(string sFacePrefix, int nShift = 1)
{
int nSidesOnFace = GetLocalInt(GetModule(), sFacePrefix);
int n = nSidesOnFace +1;
int nSide = 1;
int nPosition;
string sSide1;
string sSide2;
while(--n)
{
sSide1 = sFacePrefix + "_" + IntToString(n);
nPosition = 1 + FindSubString(sSide1, "_");
nSide = StringToInt(GetSubString(sSide1, nPosition, GetStringLength(sSide1) - nPosition));
nSide = ((nSide + nShift - 1) % nSidesOnFace) + 1;
sSide2 = GetLocalString(GetModule(), GetStringLeft(sSide1, nPosition) + IntToString(nSide));
DelayCommand(0.5, SetLocalString(GetModule(), sSide2, sSide1));
DelayCommand(0.5, SetLocalString(GetModule(), sSide1, sSide2));
}
}
Edit fixed a script problem by resetting n, (should have used for loop to avoid this) .
Second edit, I feel ashamed for having to resort to delay command, but I really didn't want to redo this from scratch again with another parameter stored, so I have inserted delayed commands.
Modifié par WhiZard, 13 avril 2012 - 07:13 .
#7
Posté 13 avril 2012 - 07:08
Sweet! And elegant. I like it!
Wait... <looking confused>
WhiZard wrote...
object GetTargetForTransition() { return GetObjectByTag(GetLocalString(GetModule(), GetTag(OBJECT_SELF))); } void RotateFaces(string sFacePrefix, int nShift = 1) { int nSidesOnFace = GetLocalInt(GetModule(), sFacePrefix); int n = nSidesOnFace +1; int nSide = 1; int nPosition; string sSide1; string sSide2; while(--n) { sSide1 = sFacePrefix + "_" + IntToString(n); nPosition = 1 + FindSubString(sSide1, "_"); nSide = StringToInt(GetSubString(sSide1, nPosition, GetStringLength(sSide1) - nPosition)); nSide = ((nSide + nShift - 1) % nSidesOnFace) + 1; sSide2 = GetLocalString(GetModule(), GetStringLeft(sSide1, nPosition) + IntToString(nSide)); SetLocalString(GetModule(), sSide2, sSide1); SetLocalString(GetModule(), sSide1, sSide2); } }Edit fixed a script problem by resetting n, (should have used for loop to avoid this) .
nShift is the number of sides to rotate?
So while there are still sides to do (n)
assemble the name of the face_side (sFacePrefix + _ +(n))
Find out where the underscore is so you can extract the number part (What am I missing? Wouldn't this be n?)
...
Where are you retreiving the sSide1 target from the local variable (module)? Er, I mean the sSide2 to swap into sSide1?
<...a map>
#8
Posté 13 avril 2012 - 07:16
Rolo Kipp wrote...
Where are you retreiving the sSide1 target from the local variable (module)? Er, I mean the sSide2 to swap into sSide1?
<...a map>
The local variables would all be set from the module so that each variable name (tag of transition) links it to the tag of the target transition.
I did make a second edit above, because I found that the shift would displace the sides I had already set further down the loop, and didn't want any compounding problems.
#9
Posté 13 avril 2012 - 07:38
WhiZard wrote...
PlasmaJohn wrote...
Don't link the doors, use the OnAreaTransitionClick event then have the script choose the jump target.
Got it, then these functions ought to work perfectly no matter how you do the transition.
For this script to work each side transition tag must be of the form FACEPREFIX + "_" + SIDE# and the local variable (on the module) for that tag should designate the tag of the target object. Also set the number of sides for each face using the local integer (on the module) designated by the face prefix.
object GetTargetForTransition()
{
return GetObjectByTag(GetLocalString(GetModule(), GetTag(OBJECT_SELF)));
}
void RotateFaces(string sFacePrefix, int nShift = 1)
{
int nSidesOnFace = GetLocalInt(GetModule(), sFacePrefix);
int n = nSidesOnFace +1;
int nSide = 1;
int nPosition;
string sSide1;
string sSide2;
while(--n)
{
sSide1 = sFacePrefix + "_" + IntToString(n);
nPosition = 1 + FindSubString(sSide1, "_");
nSide = StringToInt(GetSubString(sSide1, nPosition, GetStringLength(sSide1) - nPosition));
nSide = ((nSide + nShift - 1) % nSidesOnFace) + 1;
sSide2 = GetLocalString(GetModule(), GetStringLeft(sSide1, nPosition) + IntToString(nSide));
DelayCommand(0.5, SetLocalString(GetModule(), sSide2, sSide1));
DelayCommand(0.5, SetLocalString(GetModule(), sSide1, sSide2));
}
}
So for face D and side 1 of a 6 sided cube...
While 1 is more than 0:
sSide1 = "D+ "_" + "1" //sSide1 = "D_1"
nPosition = 1+1 //nPosition = 2
nSide = StringToInt(GetSubString("D_1", nPosition, GetStringLength("D_1")-nPosition) // nSide =1
nSide = ((1+nShift-1)%nSidesOnFace+1 //nSide = 1%7 ? Not understanding this line... :-(
sSide2 = Get the string stored in the variable "D_" + (1%7)
Swap sSide1 ("D_1") into the variable "D_" + (1%7) //Are you storing the variable name for sSide1 into sSide2?
Swap sSide2 ("D_" + 1%7) into variable named "D_1"
I can *feel* this script is right, but I'm just not following it :-(
#10
Posté 13 avril 2012 - 07:45
Right. Got that far. The transition tags would be paired up, and changing the tags when a face rotates is what we are talking about.WhiZard wrote...
The local variables would all be set from the module so that each variable name (tag of transition) links it to the tag of the target transition.
I did make a second edit above, because I found that the shift would displace the sides I had already set further down the loop, and didn't want any compounding problems.
But somehow my brain is frazzling with that middle bit in there. Isn't the modulus operator going to give strange integers when dividing by the number of faces + 1? I.e. 1%7 is 1428571428571429 (depending on precision). How does that give us the new side tag?
Edit, the 1 is added after the modulus... duh! But still (1%6) + 1 = 1.1666...
<...bread crumbs>
Modifié par Rolo Kipp, 13 avril 2012 - 07:47 .
#11
Posté 13 avril 2012 - 07:54
EDIT:Redid the second loop what was there before worked only when the shift amount and number of sides were relatively prime. Thus a shift of 2 on 4 sides did not work as only half of the sides would make it through the second loop. Now this should work without delayed command as it uses the "#Temp" designation as a deletable placeholder.
object GetTargetForTransition()
{
return GetObjectByTag(GetLocalString(GetModule(), GetTag(OBJECT_SELF)));
}
void RotateFaces(string sFacePrefix, int nShift = 1)
{
int nSidesOnFace = GetLocalInt(GetModule(), sFacePrefix);
int n = nSidesOnFace + 1;
int nSide = 1;
int nPosition;
string sSide1;
string sSide2;
while(--n)
{
sSide1 = sFacePrefix + "_" + IntToString(n);
nPosition = 1 + FindSubString(sSide1, "_");
nSide = StringToInt(GetSubString(sSide1, nPosition, GetStringLength(sSide1) - nPosition));
nSide = ((nSide + nShift - 1) % nSidesOnFace) + 1;
sSide2 = GetLocalString(GetModule(), GetStringLeft(sSide1, nPosition) + IntToString(nSide));
SetLocalString(GetModule(), sSide2, sSide1);
SetLocalString(GetModule(), sSide1 + "#Temp", sSide2);
}
while(++n <= nSidesOnFace)
{
sSide1 = sFacePrefix + "_" + IntToString(n);
SetLocalString(GetModule(), sSide1, GetLocalString(GetModule(), sSide1 + "#Temp"));
DeleteLocalString(GetModule(), sSide1 + "#Temp");
}
}
Modifié par WhiZard, 13 avril 2012 - 08:13 .
#12
Posté 13 avril 2012 - 07:57
Rolo Kipp wrote...
But still (1%6) + 1 = 1.1666...
<...bread crumbs>
1%6 is 1 and 7%6 is 1. The operation returns an integer.
#13
Posté 13 avril 2012 - 08:29
WhiZard wrote...
Rolo Kipp wrote...
But still (1%6) + 1 = 1.1666...
<...bread crumbs>
1%6 is 1 and 7%6 is 1. The operation returns an integer.
1%6 is 0 and 7%6 is 1. The operation returns an integer.
Edit: sorry long day.
Modifié par Lightfoot8, 13 avril 2012 - 08:44 .
#14
Posté 13 avril 2012 - 08:42
Ok, I am running on 2 hours of sleep and a second pot of coffee, but I can't really blame that, you guys have blown right past the 6 scripting tutorials I have choked down so far.
I see the rotation for the switch taking place, but what is being modified by that, and how do you stitch it all together for the desired effect?
It seems that you will need to modify the node associated with the door, as well as modify the rotated door associated with the node, so that coming back from the other side, you land in the right place, not just land in the right place on the outbound trip. Then of course, a script will need to stitch the door to the node (and on to the door), or maybe that can all be handled by just having a generic call from the door, and let the node management script handle it all?!
Ok, I have tied my synapses into a knot again. I am going to get more coffee, or maybe a nap, and let the experts handle this. Carry on and disregard me.
#15
Posté 13 avril 2012 - 08:48
1 Do all doors on the outside of the 6x6 square head out of the square?
2 Is this for multi-player or single player? If it is single player, You could do it all with just two areas. if all the areas look alike.
#16
Posté 13 avril 2012 - 08:49
Lightfoot8 wrote...
1%6 is 0 and 7%6 is 1. The operation returns an integer.
Edit: sorry long day.
Well, looks like all of us have committed noob like errors today.
#17
Posté 13 avril 2012 - 08:54
Quite right, I was thinking along the lines of different looking faces, perhaps multiple in the same area, and extending the possibility to faces with different number of sides.Lightfoot8 wrote...
2 Is this for multi-player or single player? If it is single player, You could do it all with just two areas. if all the areas look alike.
#18
Posté 13 avril 2012 - 09:07
Leurnid wrote...
It seems that you will need to modify the node associated with the door, as well as modify the rotated door associated with the node, so that coming back from the other side, you land in the right place, not just land in the right place on the outbound trip. Then of course, a script will need to stitch the door to the node (and on to the door), or maybe that can all be handled by just having a generic call from the door, and let the node management script handle it all?!
So long as the door nodes work properly initially, the changing of doors should have no effect on the side you enter one and exit the other through.
#19
Posté 13 avril 2012 - 09:23
It makes it simple to just lay the areas out in rows of your cube size. Where the first row would be areas numbered 0 to nCube size-1. Second row wound be areas nCubeSize to 2*nCubeSize -1..
and the last row being (nCubeSize-1) * nCubeSize to (nCubeSize* nCubeSize) -1;
Where nCubeSize is the number of areas along one side of the cube.
So basicly if you are in area number x of the cube. You are in row x/nCubesize and collumn x%nCubeSize.
Sorry if that is basicly what you have above, and I am sure it is, I just have not read through it all yet.
#20
Posté 13 avril 2012 - 11:32
Lightfoot8 wrote...
I was thinking of keeping it more simplistic with it always being a cube grid with 4 doors always.
It makes it simple to just lay the areas out in rows of your cube size. Where the first row would be areas numbered 0 to nCube size-1. Second row wound be areas nCubeSize to 2*nCubeSize -1..
and the last row being (nCubeSize-1) * nCubeSize to (nCubeSize* nCubeSize) -1;
Where nCubeSize is the number of areas along one side of the cube.
So basicly if you are in area number x of the cube. You are in row x/nCubesize and collumn x%nCubeSize.
Sorry if that is basicly what you have above, and I am sure it is, I just have not read through it all yet.
I understand what you are doing, and we are tackling the problem differently. It would be up to the OP to find out whether he/she wishes to use areas/faces which are all similar and then script the differences, or if he/she wants to use fundamentally different layouts for one face to the next. The way I have mine working all faces would be distinct and thus remembered distinctly (including map exploration, map pins, creatures remaining and all the rest). What you propose will use a lot less resources but will have limitations with regard to textures and tiles.
Both are quite feasible directions, and I doubt the OP will try an icosahedron or any other weird shape, though my script can handle these because I am trying to do it broadly.
One thing your solution does handle that mine does not is having mulitple doors along the side of a single face (though you are accomplishing this through each face being composed of multiple areas, (though technically only two areas are there)). I could add this functionality, but am not sure, given the circumstance that it is needed, how the OP would want to have the multiplicity of doors per side handled.
#21
Posté 14 avril 2012 - 02:45
The idea is for each face to be a unique area, with traps, monsters, and puzzles (traditional PnP dungeon crawl). Ideally, some areas of said dungeon would be inaccessible unless/until certain faces are aligned correctly. I have seen a number of PnP dungeons over the years that feature tesseract puzzles, usually as the safeguard or access method to the final boss or big ticket item of the crawl. I prefer making the entire crawl the tesseract, and present it as the macro-puzzle that must be solved for the big pay-off.
With that said:
- The solution will need to handle more than 1 door per side. The number *must* be consistent within the module, but ideally, the script would allow that number to be defined, and not hardcoded.
- This sould support multiplayer, but a unique key should be reqquired to use the switches and perimeter doors.
- Doors will close and lock shortly after opening, and will be forced closed when the switches are accessed.
The other reqs are easily handled with cut'n'paste from the vault.
I have a little demo-mod (using one area to save time) with six isolated square areas, each with 4 doors. Currentlly, they are assigned transitions between the doors, so it acts as a 'fixed' tesseract. My efforts at figuring this scripting issue out so far have been limited by my ignorance. As I said previously, I know exactly how I would code the swaps and array the nodes in BASIC, but this isn't a C=64.
Vaguely related bonus tale:
I DM'ed a local tournament dungeon one year that had as a penultimate challenge a 'maze' of 20 identical triangular rooms, with murals and statuary in each that would subtly indicate the number of the room. About 6 rooms in, most teams caught on. Every group discovered while trying to meta-game the maze with their d20's, that there are 3 common and many not so common number arrangements for d20's, and the module didn't use any of those.
#22
Posté 14 avril 2012 - 03:22
Leurnid wrote...
WhiZard's solution so far as I grasp it, will work for more doors, as all you need to do is increment the number of spaces the door nodes 'swap' places by defining a 'number of doors per face' in the switch's script.
Quite true. Multiply both nSidesPerFace and nShift by nDoorsPerSide, and you get it working when the sides have multiple doors. I was fearing you might go a little more complex and actually ask for a system where the sides would expand and door entrances would increase as the PW grew.
While I was working at this script, I was also having fun drawing a Klein's Bottle model where the 2D surface, though continuous in all directions, could not be mapped in 3-space. For fun I even added a rotating feature about the "center" of the weird shape.
Modifié par WhiZard, 14 avril 2012 - 03:34 .
#23
Posté 14 avril 2012 - 04:29
This appears to be scripted to a testable state, if so, what gets attached to what? Spell it out like I am a 5 year old for bonus points.
I am still perplexed at how/where the node-door action is getting handled, but I have found the scripts I ook at often seem to be missing critical bits but work perfectly.
#24
Posté 14 avril 2012 - 10:00
Leurnid wrote...
I am not a big fan of personal public displays of ignorance, but I am doing so well, I thought I would double down:
This appears to be scripted to a testable state, if so, what gets attached to what? Spell it out like I am a 5 year old for bonus points.
I am still perplexed at how/where the node-door action is getting handled, but I have found the scripts I ook at often seem to be missing critical bits but work perfectly.
Doors have their direction set when placed in the Toolkit. The arrow points in the direction that creatures going into the area will be placed. What happens when you click on the area transition and your character approaches it is governed by the OnAreaTransitionClick event. The default script for this is at_corridore_001. You will be copying this script into a new blank script changing only the line:
object oTarget = GetTransitionTarget();
The rotating doors will instead use the object I defined by the first declaration:
object GetTargetForTransition()
{
return GetObjectByTag(GetLocalString(GetModule(), GetTag(OBJECT_SELF)));
}
You can either make this declaration above void main() and then write in the replacement
object oTarget = GetTargetForTransition()
Or you could just integrate the single line
object oTarget = GetObjectByTag(GetLocalString(GetModule(), GetTag(OBJECT_SELF)));
For the doors to use this object correctly, you will need to set the local variables in the module for the doors.
A simple way to accomplish this is to have the door that is linked in the "Destination Tag" field, and then have an opening script cycle through all doors on each face getting the door they are linked to (by GetTransitionTarget()) and setting the module variable using the GetTag() function to get the variable name and its value).
#25
Posté 14 avril 2012 - 11:06
WhiZard wrote...
The default script for this is at_corridore_001.
Hmm, I have never heard of that script. In Fact it is not even in the vanilla x3 scripts.
the Default door transistion script is x3_g0_transition.nss
Before the x3 expansion it was: nw_g0_transition.nss
Modifié par Lightfoot8, 14 avril 2012 - 11:07 .





Retour en haut






