Here's a chunk from a massive switch that replicates a column from a dozen different 2das:
//--------------------------------DECLARATIONS--------------------------------//
// This function returns the level at which the specified class gets the specified feat.
// If the feat is epic only, it will return -1 in most cases, though it will also return
// 21 on occasion, as with Epic Spells, which actually require 21 levels of the class rather
// than just epic character status. It replicates EXACTLY the cls_feat 2da for each of the
// respective classes, though the order of feats has been shuffled to accomodate the switch
// statements, and a few duplicate cases in the 2das were removed. If the feat input is not
// a class feat (and therefore not found in the 2da), this function will return -2. If the
// class input is not a valid class int, the function returns -3. Although Harper Scout could
// not possibly be a character's control class for legendary levels, it is included at the end
// of the switch so that the function can be of broader use to the community. Several feats were
// added as class feats for Palemasters, including Spell Focuses and Greater Spell Focuses
// (since they get Epic Focuses). They are marked in the function. The class switches are
// in aphabetical order by 2da file (which is slightly different than class name).
int GetclassLevelReqForFeat(int nFeat, int nclass, object oPC);
// This function returns TRUE if the feat specified is a general feat, avalable to all classes.
int GetIsGeneralFeat(int nFeat);
//----------------------------------FUNCTIONS---------------------------------//
int GetclassLevelReqForFeat(int nFeat, int nclass, object oPC)
{
int nInt = -2, nQuasi;
switch(nclass)
{
case class_TYPE_ARCANE_ARCHER:
switch(nFeat/250)
{
case 0:
switch(nFeat/25)
{
case 0: //0-24
switch(nFeat){
case 1 : nInt = -1 ; break;
case 3 : nInt = 1 ; break;
case 4 : nInt = 1 ; break;
default : nInt = -2 ; break;}; break;
case 1: //25-49
switch(nFeat){
case 32 : nInt = 1 ; break;
case 41 : nInt = -1 ; break;
case 44 : nInt = -1 ; break;
case 45 : nInt = 1 ; break;
case 46 : nInt = 1 ; break;
default : nInt = -2 ; break;}; break;
case 2: //50-74
switch(nFeat){
The full thing is 6596 lines long, but it runs quite quickly, because of the switch structure. Here's the same thing, redone to use two 2da-based functions:
int GetIsFeatExceptionFrom2da(int nFeat, int nclass, object oPC) {
int nRace = GetRacialType(oPC);
switch(nFeat) {
case 424: if (GetLocalInt(oPC, "Quasiclass") == QUASIclass_DWARVEN_WARCHANTER) return -1; break;//lingering song
case 870: if (GetLocalInt(oPC, "Quasiclass") == QUASIclass_DWARVEN_WARCHANTER) return -1; break;//lasting inspiration
//spell feats below:
case 35:case 166:case 167:case 168:case 169:case 170:case 171:case 172: //spell foci
case 36:case 401:case 618: if (GetLocalInt(oPC, "Quasiclass")) return -1; break;//spell penetration feats
//general feats below:
case 290: //Creature Weapon Spec
case 694: if (GetLevelByclass(class_TYPE_DRUID, oPC) > 0) return -1; break; //Epic CW Spec
case 311:case 313:case 314:case 315:case 316:case 317:case 321:case 322:case 325: if (GetKnowsFeat(FEAT_EPIC_PLANAR_TURNING, oPC)) return -1; break; //cleric domains
case HGFEAT_LEG_SKILL_AFFINITY_LISTEN:
case HGFEAT_LEG_SKILL_AFFINITY_SPOT: if (GetRacialType(oPC) == RACIAL_TYPE_HALFELF) return -1; break;
case HGFEAT_LEG_SKILL_AFFINITY_TAUNT: if (GetLocalInt(oPC, "SubraceID") == 37) return -1; break;
}
return -2;
}
int GetclassLevelReqForFeat_2da(int nFeat, int nclass, object oPC) {
string s2da = Getclass2daName(nclass);
string sVal = "Nothing";
int nCount = 0, nVal, nRet;
while (sVal != "") {
sVal = Get2DAString(s2da, "FeatIndex", nCount);
nVal = StringToInt(sVal);
if (nVal == nFeat) {
nRet = StringToInt(Get2DAString(s2da, "GrantedOnLevel", nCount));
return nRet;
}
nCount++;
}
nRet = GetIsFeatExceptionFrom2da(nFeat, nclass, oPC);
return nRet;
}
In this case, after the improvement in 2da caching, the 2da approach is by far the better one. Despite that, that massive function didn't take all that long to create, thanks to excel scripting. In your case, since you're able to determine structure up front, you can structure your code so that either option will work well, and you won't wind up with huge switch functions unless you REALLY have a lot of data to enter - it just doesn't sound like you do, far from it.
If I were coding functions for your puzzles, I would probably do something like:
string GetPuzzleName(int nPuzzle);
string GetIntroduction(int nPuzzle);
string GetPuzzleClue(int nPuzzle, int nClueNumber);
(solution)
You're going to wind up with the same functions either way; the only thing to decide is whether they're going to contain switches or 2da reads (or database, but I really don't recommend that). There are, of course, other options, like variable pseudohashes - look at OldManWhistler's spell system, for example (no good for strings like yours) - or real hashsets (only if using NWNX, not a good option for SP).
In summary, it sounds like you need to figure out EXACTLY what your puzzles will look like. My guess is you'll want a scripted solution, which is more flexible. You'll be able to hotdrop more easily as well. 2das, by contrast, will shine if your relational data is minimalistic and easy organization is more important.
Funky