Пример #1
0
//
// ACS_funcThingCountName
//
static void ACS_funcThingCountName(ACS_FUNCARG)
{
   mobjtype_t type = E_ThingNumForName(ACSVM::GetString(args[0]));
   int32_t    tid  = args[1];

   *retn++ = ACS_thingCount(type, tid);
}
Пример #2
0
//
// ACS_funcThingCountNameSector
//
static void ACS_funcThingCountNameSector(ACS_FUNCARG)
{
   int32_t    tag  = args[0];
   mobjtype_t type = E_ThingNumForName(ACSVM::GetString(args[1]));
   int32_t    tid  = args[2];

   *retn++ = ACS_thingCountSector(tag, type, tid);
}
Пример #3
0
//
// ACS_funcCheckThingType
//
static void ACS_funcCheckThingType(ACS_FUNCARG)
{
   Mobj *mo = P_FindMobjFromTID(args[0], NULL, thread->trigger);

   if(mo)
      *retn++ = E_ThingNumForName(ACSVM::GetString(args[1])) == mo->type;
   else
      *retn++ = 0;
}
Пример #4
0
//
// ACS_spawnPoint
//
static void ACS_spawnPoint(ACS_FUNCARG, bool forced)
{
   mobjtype_t type  = E_ThingNumForName(ACSVM::GetString(args[0]));
   fixed_t    x     = args[1];
   fixed_t    y     = args[2];
   fixed_t    z     = args[3];
   int        tid   = args[4];
   angle_t    angle = args[5] << 24;

   *retn++ = !!ACS_spawn(type, x, y, z, tid, angle, forced);
}
Пример #5
0
//
// ACS_spawnSpotAngle
//
static void ACS_spawnSpotAngle(ACS_FUNCARG, bool forced)
{
   mobjtype_t type   = E_ThingNumForName(ACSVM::GetString(args[0]));
   int        spotid = args[1];
   int        tid    = args[2];
   Mobj      *spot   = NULL;

   *retn = 0;

   while((spot = P_FindMobjFromTID(spotid, spot, thread->trigger)))
      *retn += !!ACS_spawn(type, spot->x, spot->y, spot->z, tid, spot->angle, forced);

   ++retn;
}
Пример #6
0
//
// ACS_funcSpawnProjectile
//
static void ACS_funcSpawnProjectile(ACS_FUNCARG)
{
   int32_t    spotid  = args[0];
   mobjtype_t type    = E_ThingNumForName(ACSVM::GetString(args[1]));
   angle_t    angle   = args[2] << 24;
   int32_t    speed   = args[3] * 8;
   int32_t    vspeed  = args[4] * 8;
   bool       gravity = args[5] ? true : false;
   int32_t    tid     = args[6];
   Mobj      *spot    = NULL;
   fixed_t    momx    = speed * finecosine[angle >> ANGLETOFINESHIFT];
   fixed_t    momy    = speed * finesine[  angle >> ANGLETOFINESHIFT];
   fixed_t    momz    = vspeed << FRACBITS;

   while((spot = P_FindMobjFromTID(spotid, spot, thread->trigger)))
      ACS_spawnProjectile(type, spot->x, spot->y, spot->z, momx, momy, momz,
                          tid, spot, angle, gravity);
}
Пример #7
0
//
// E_processGamePropsBlock
//
// Process a single gameproperties block.
//
static void E_processGamePropsBlock(cfg_t *props)
{
   // Flags

   if(IS_SET(ITEM_GPROP_FLAGSADD))
   {
      const char *flagstr = cfg_getstr(props, ITEM_GPROP_FLAGSADD);

      GameModeInfo->flags |= E_ParseFlags(flagstr, &gmi_flagset);
   }

   if(IS_SET(ITEM_GPROP_FLAGSREM))
   {
      const char *flagstr = cfg_getstr(props, ITEM_GPROP_FLAGSREM);
      unsigned int curFlags = GameModeInfo->flags;

      GameModeInfo->flags &= ~E_ParseFlags(flagstr, &gmi_flagset);

      GameModeInfo->flags |= (curFlags & GIF_SHAREWARE); // nice try, bitch.
   }

   if(IS_SET(ITEM_GPROP_MFLAGSADD))
   {
      const char *flagstr = cfg_getstr(props, ITEM_GPROP_MFLAGSADD);

      GameModeInfo->missionInfo->flags |= E_ParseFlags(flagstr, &mission_flagset);
   }

   if(IS_SET(ITEM_GPROP_MFLAGSREM))
   {
      const char *flagstr = cfg_getstr(props, ITEM_GPROP_MFLAGSREM);

      GameModeInfo->missionInfo->flags &= ~E_ParseFlags(flagstr, &mission_flagset);
   }

   // Demo Loop Properties

   if(IS_SET(ITEM_GPROP_TITLETICS))
      GameModeInfo->titleTics = cfg_getint(props, ITEM_GPROP_TITLETICS);
   
   if(IS_SET(ITEM_GPROP_ADVISORTICS))
      GameModeInfo->advisorTics = cfg_getint(props, ITEM_GPROP_ADVISORTICS);
   
   if(IS_SET(ITEM_GPROP_PAGETICS))
      GameModeInfo->pageTics = cfg_getint(props, ITEM_GPROP_PAGETICS);

   // Menu Properties
   if(IS_SET(ITEM_GPROP_MENUBKGND))
   {
      E_setDynamicString(GameModeInfo->menuBackground, GI_STR_MENUBKGND, 
                         cfg_getstr(props, ITEM_GPROP_MENUBKGND));
   }

   if(IS_SET(ITEM_GPROP_TRANSFRAME))
   {
      int stateNum = E_StateNumForName(cfg_getstr(props, ITEM_GPROP_TRANSFRAME));

      if(stateNum >= 0 &&
         (states[stateNum]->dehnum >= 0 || E_AutoAllocStateDEHNum(stateNum)))
      {
         GameModeInfo->transFrame = states[stateNum]->dehnum;
      }
   }

   if(IS_SET(ITEM_GPROP_MENUSKVASND))
   {
      sfxinfo_t *snd = E_SoundForName(cfg_getstr(props, ITEM_GPROP_MENUSKVASND));

      if(snd && (snd->dehackednum >= 0 || E_AutoAllocSoundDEHNum(snd)))
         GameModeInfo->skvAtkSound = snd->dehackednum;
   }

   if(IS_SET(ITEM_GPROP_MENUOFFSET))
      GameModeInfo->menuOffset = cfg_getint(props, ITEM_GPROP_MENUOFFSET);

   if(IS_SET(ITEM_GPROP_MENUPTR1) && GameModeInfo->menuCursor->numpatches >= 1)
   {
      E_setDynamicString(GameModeInfo->menuCursor->patches[0], GI_STR_MENUPTR1,
                         cfg_getstr(props, ITEM_GPROP_MENUPTR1));
   }

   if(IS_SET(ITEM_GPROP_MENUPTR2) && GameModeInfo->menuCursor->numpatches >= 2)
   {
      E_setDynamicString(GameModeInfo->menuCursor->patches[1], GI_STR_MENUPTR2,
                         cfg_getstr(props, ITEM_GPROP_MENUPTR2));
   }

   // Border Properties

   if(IS_SET(ITEM_GPROP_BORDERFLAT))
   {
      E_setDynamicString(GameModeInfo->borderFlat, GI_STR_BORDERFLAT,
                         cfg_getstr(props, ITEM_GPROP_BORDERFLAT));
   }

   if(IS_SET(ITEM_GPROP_BORDERTL))
   {
      E_setDynamicString(GameModeInfo->border->c_tl, GI_STR_BORDERTL, 
                         cfg_getstr(props, ITEM_GPROP_BORDERTL));
   }

   if(IS_SET(ITEM_GPROP_BORDERTOP))
   {
      E_setDynamicString(GameModeInfo->border->top, GI_STR_BORDERTOP, 
                         cfg_getstr(props, ITEM_GPROP_BORDERTOP));
   }

   if(IS_SET(ITEM_GPROP_BORDERTR))
   {
      E_setDynamicString(GameModeInfo->border->c_tr, GI_STR_BORDERTR, 
                         cfg_getstr(props, ITEM_GPROP_BORDERTR));
   }

   if(IS_SET(ITEM_GPROP_BORDERLEFT))
   {
      E_setDynamicString(GameModeInfo->border->left, GI_STR_BORDERLEFT, 
                         cfg_getstr(props, ITEM_GPROP_BORDERLEFT));
   }

   if(IS_SET(ITEM_GPROP_BORDERRIGHT))
   {
      E_setDynamicString(GameModeInfo->border->right, GI_STR_BORDERRIGHT, 
                         cfg_getstr(props, ITEM_GPROP_BORDERRIGHT));
   }

   if(IS_SET(ITEM_GPROP_BORDERBL))
   {
      E_setDynamicString(GameModeInfo->border->c_bl, GI_STR_BORDERBL, 
                         cfg_getstr(props, ITEM_GPROP_BORDERBL));
   }

   if(IS_SET(ITEM_GPROP_BORDERBOTT))
   {
      E_setDynamicString(GameModeInfo->border->bottom, GI_STR_BORDERBOTT, 
                         cfg_getstr(props, ITEM_GPROP_BORDERBOTT));
   }

   if(IS_SET(ITEM_GPROP_BORDERBR))
   {
      E_setDynamicString(GameModeInfo->border->c_br, GI_STR_BORDERBR, 
                         cfg_getstr(props, ITEM_GPROP_BORDERBR));
   }

   // Console Properties

   if(IS_SET(ITEM_GPROP_CCHARSPERLN))
      GameModeInfo->c_numCharsPerLine = cfg_getint(props, ITEM_GPROP_CCHARSPERLN);

   if(IS_SET(ITEM_GPROP_CBELLSOUND))
   {
      sfxinfo_t *snd = E_SoundForName(cfg_getstr(props, ITEM_GPROP_CBELLSOUND));

      if(snd && (snd->dehackednum >= 0 || E_AutoAllocSoundDEHNum(snd)))
         GameModeInfo->c_BellSound = snd->dehackednum;
   }

   if(IS_SET(ITEM_GPROP_CCHATSOUND))
   {
      sfxinfo_t *snd = E_SoundForName(cfg_getstr(props, ITEM_GPROP_CCHATSOUND));

      if(snd && (snd->dehackednum >= 0 || E_AutoAllocSoundDEHNum(snd)))
         GameModeInfo->c_ChatSound = snd->dehackednum;
   }

   if(IS_SET(ITEM_GPROP_CBACKDROP))
   {
      E_setDynamicString(GameModeInfo->consoleBack, GI_STR_CBACKDROP,
                         cfg_getstr(props, ITEM_GPROP_CBACKDROP));
   }

   // HUD Properties

   if(IS_SET(ITEM_GPROP_PAUSEPATCH))
   {
      E_setDynamicString(GameModeInfo->pausePatch, GI_STR_PAUSEPATCH,
                         cfg_getstr(props, ITEM_GPROP_PAUSEPATCH));
   }

   // Gamesim Properties

   if(IS_SET(ITEM_GPROP_PUFFTYPE))
   {
      const char *name = cfg_getstr(props, ITEM_GPROP_PUFFTYPE);

      if(E_ThingNumForName(name) >= 0)
         E_setDynamicString(GameModeInfo->puffType, GI_STR_PUFFTYPE, name);
   }

   if(IS_SET(ITEM_GPROP_TELEFOGTYPE))
   {
      const char *name = cfg_getstr(props, ITEM_GPROP_TELEFOGTYPE);

      if(E_ThingNumForName(name) >= 0)
         E_setDynamicString(GameModeInfo->teleFogType, GI_STR_TELEFOGTYPE, name);
   }

   if(IS_SET(ITEM_GPROP_TELEFOGHT))
   {
      int num = cfg_getint(props, ITEM_GPROP_TELEFOGHT);

      GameModeInfo->teleFogHeight = num * FRACUNIT;
   }

   if(IS_SET(ITEM_GPROP_TELESOUND))
   {
      sfxinfo_t *snd = E_SoundForName(cfg_getstr(props, ITEM_GPROP_TELESOUND));

      if(snd && (snd->dehackednum >= 0 || E_AutoAllocSoundDEHNum(snd)))
         GameModeInfo->teleSound = snd->dehackednum;
   }

   if(IS_SET(ITEM_GPROP_THRUSTFACTR))
      GameModeInfo->thrustFactor = (int16_t)cfg_getint(props, ITEM_GPROP_THRUSTFACTR);

   if(IS_SET(ITEM_GPROP_DEFPCLASS))
   {
      E_setDynamicString(GameModeInfo->defPClassName, GI_STR_DEFPCLASS,
                         cfg_getstr(props, ITEM_GPROP_DEFPCLASS));
   }

   // Determines behavior of the Teleport_EndGame line special when LevelInfo
   // has not provided a specific finale type for the level.
   if(IS_SET(ITEM_GPROP_FINTYPE))
   {
      int finaleType = E_StrToNumLinear(finaleTypeStrs, FINALE_NUMFINALES, 
                                        cfg_getstr(props, ITEM_GPROP_FINTYPE));

      if(finaleType >= 0 && finaleType < FINALE_NUMFINALES)
         GameModeInfo->teleEndGameFinaleType = finaleType;
   }

   // Finale Properties

   if(IS_SET(ITEM_GPROP_FINALEX))
      GameModeInfo->fTextPos->x = cfg_getint(props, ITEM_GPROP_FINALEX);

   if(IS_SET(ITEM_GPROP_FINALEY))
      GameModeInfo->fTextPos->y = cfg_getint(props, ITEM_GPROP_FINALEY);

   if(IS_SET(ITEM_GPROP_CASTTITLEY))
      GameModeInfo->castTitleY = cfg_getint(props, ITEM_GPROP_CASTTITLEY);

   if(IS_SET(ITEM_GPROP_CASTNAMEY))
      GameModeInfo->castNameY = cfg_getint(props, ITEM_GPROP_CASTNAMEY);

   // Intermission Properties

   if(IS_SET(ITEM_GPROP_INTERPIC))
   {
      E_setDynamicString(GameModeInfo->interPic, GI_STR_INTERPIC,
                         cfg_getstr(props, ITEM_GPROP_INTERPIC));
   }

   // Sound Properties

   if(IS_SET(ITEM_GPROP_DEFMUSNAME))
   {
      E_setDynamicString(GameModeInfo->defMusName, GI_STR_DEFMUSNAME,
                         cfg_getstr(props, ITEM_GPROP_DEFMUSNAME));
   }

   if(IS_SET(ITEM_GPROP_DEFSNDNAME))
   {
      E_setDynamicString(GameModeInfo->defSoundName, GI_STR_DEFSNDNAME,
                         cfg_getstr(props, ITEM_GPROP_DEFSNDNAME));
   }
   
   // Credit Screen Properties

   if(IS_SET(ITEM_GPROP_CREDITBKGND))
   {
      E_setDynamicString(GameModeInfo->creditBackground, GI_STR_CREDITBKGND,
                         cfg_getstr(props, ITEM_GPROP_CREDITBKGND));
   }

   if(IS_SET(ITEM_GPROP_CREDITY))
      GameModeInfo->creditY = cfg_getint(props, ITEM_GPROP_CREDITY);

   if(IS_SET(ITEM_GPROP_CREDITTSTEP))
      GameModeInfo->creditTitleStep = cfg_getint(props, ITEM_GPROP_CREDITTSTEP);
   
   // Exit Properties
   
   if(IS_SET(ITEM_GPROP_ENDTEXTNAME))
   {
      E_setDynamicString(GameModeInfo->endTextName, GI_STR_ENDTEXTNAME,
                         cfg_getstr(props, ITEM_GPROP_ENDTEXTNAME));
   }
}
Пример #8
0
//
// ACS_ChkThingVar
//
bool ACS_ChkThingVar(Mobj *thing, uint32_t var, int32_t val)
{
   if(!thing) return false;

   switch(var)
   {
   case ACS_THINGVAR_Health:       return thing->health == val;
   case ACS_THINGVAR_Speed:        return thing->info->speed == val;
   case ACS_THINGVAR_Damage:       return thing->damage == val;
   case ACS_THINGVAR_Alpha:        return thing->translucency == val;
   case ACS_THINGVAR_RenderStyle:  return false;
   case ACS_THINGVAR_SeeSound:     return false;
   case ACS_THINGVAR_AttackSound:  return false;
   case ACS_THINGVAR_PainSound:    return false;
   case ACS_THINGVAR_DeathSound:   return false;
   case ACS_THINGVAR_ActiveSound:  return false;
   case ACS_THINGVAR_Ambush:       return !!(thing->flags & MF_AMBUSH) == !!val;
   case ACS_THINGVAR_Invulnerable: return !!(thing->flags2 & MF2_INVULNERABLE) == !!val;
   case ACS_THINGVAR_JumpZ:        return false;
   case ACS_THINGVAR_ChaseGoal:    return false;
   case ACS_THINGVAR_Frightened:   return false;
   case ACS_THINGVAR_Friendly:     return !!(thing->flags & MF_FRIEND) == !!val;
   case ACS_THINGVAR_SpawnHealth:  return thing->info->spawnhealth == val;
   case ACS_THINGVAR_Dropped:      return !!(thing->flags & MF_DROPPED) == !!val;
   case ACS_THINGVAR_NoTarget:     return false;
   case ACS_THINGVAR_Species:      return false;
   case ACS_THINGVAR_NameTag:      return false;
   case ACS_THINGVAR_Score:        return false;
   case ACS_THINGVAR_NoTrigger:    return false;
   case ACS_THINGVAR_DamageFactor: return false;
   case ACS_THINGVAR_MasterTID:    return false;
   case ACS_THINGVAR_TargetTID:    return thing->target ? thing->target->tid == val : false;
   case ACS_THINGVAR_TracerTID:    return thing->tracer ? thing->tracer->tid == val : false;
   case ACS_THINGVAR_WaterLevel:   return false;
   case ACS_THINGVAR_ScaleX:       return M_FloatToFixed(thing->xscale) == val;
   case ACS_THINGVAR_ScaleY:       return M_FloatToFixed(thing->yscale) == val;
   case ACS_THINGVAR_Dormant:      return !!(thing->flags2 & MF2_DORMANT) == !!val;
   case ACS_THINGVAR_Mass:         return thing->info->mass == val;
   case ACS_THINGVAR_Accuracy:     return false;
   case ACS_THINGVAR_Stamina:      return false;
   case ACS_THINGVAR_Height:       return thing->height == val;
   case ACS_THINGVAR_Radius:       return thing->radius == val;
   case ACS_THINGVAR_ReactionTime: return thing->reactiontime == val;
   case ACS_THINGVAR_MeleeRange:   return MELEERANGE == val;
   case ACS_THINGVAR_ViewHeight:   return false;
   case ACS_THINGVAR_AttackZOff:   return false;
   case ACS_THINGVAR_StencilColor: return false;
   case ACS_THINGVAR_Friction:     return false;
   case ACS_THINGVAR_DamageMult:   return false;

   case ACS_THINGVAR_Angle:          return thing->angle >> 16 == (uint32_t)val;
   case ACS_THINGVAR_Armor:          return thing->player ? thing->player->armorpoints == val : false;
      // ioanch 20160116: extreme sector (portal aware)
   case ACS_THINGVAR_CeilingTexture: return P_ExtremeSectorAtPoint(thing, true)->ceilingpic == R_FindWall(ACSVM::GetString(val));
   case ACS_THINGVAR_CeilingZ:       return thing->ceilingz == val;
   case ACS_THINGVAR_FloorTexture:   return P_ExtremeSectorAtPoint(thing, false)->floorpic == R_FindWall(ACSVM::GetString(val));
   case ACS_THINGVAR_FloorZ:         return thing->floorz == val;
   case ACS_THINGVAR_Frags:          return thing->player ? thing->player->totalfrags == val : false;
   case ACS_THINGVAR_LightLevel:     return thing->subsector->sector->lightlevel == val;
   case ACS_THINGVAR_MomX:           return thing->momx == val;
   case ACS_THINGVAR_MomY:           return thing->momy == val;
   case ACS_THINGVAR_MomZ:           return thing->momz == val;
   case ACS_THINGVAR_Pitch:          return thing->player ? thing->player->pitch >> 16 == val : false;
   case ACS_THINGVAR_PlayerNumber:   return thing->player ? thing->player - players == val : false;
   case ACS_THINGVAR_SigilPieces:    return false;
   case ACS_THINGVAR_TID:            return thing->tid == val;
   case ACS_THINGVAR_Type:           return thing->type == E_ThingNumForName(ACSVM::GetString(val));
   case ACS_THINGVAR_X:              return thing->x == val;
   case ACS_THINGVAR_Y:              return thing->y == val;
   case ACS_THINGVAR_Z:              return thing->z == val;

   default: return false;
   }
}
Пример #9
0
//
// E_ProcessBossTypes
//
// Gets the thing type entries in the boss_spawner_types list,
// for use by the SpawnFly codepointer.
//
// modified by schepe to remove 11-type limit
//
static void E_ProcessBossTypes(cfg_t *cfg)
{
   int i, a = 0;
   int numTypes = cfg_size(cfg, SEC_BOSSTYPES);
   int numProbs = cfg_size(cfg, SEC_BOSSPROBS);
   bool useProbs = true;

   E_EDFLogPuts("\t* Processing boss spawn types\n");

   if(!numTypes)
   {
      // haleyjd 05/31/06: allow zero boss types
      E_EDFLogPuts("\t\tNo boss types defined\n");
      return;
   }

   // haleyjd 11/19/03: allow defaults for boss spawn probs
   if(!numProbs)
      useProbs = false;

   if(useProbs ? numTypes != numProbs : numTypes != 11)
   {
      E_EDFLoggedErr(2, 
         "E_ProcessBossTypes: %d boss types, %d boss probs\n",
         numTypes, useProbs ? numProbs : 11);
   }

   // haleyjd 11/21/11: allow multiple runs
   if(BossSpawnTypes)
   {
      efree(BossSpawnTypes);
      BossSpawnTypes = NULL;
   }
   if(BossSpawnProbs)
   {
      efree(BossSpawnProbs);
      BossSpawnProbs = NULL;
   }

   NumBossTypes = numTypes;
   BossSpawnTypes = ecalloc(int *, numTypes, sizeof(int));
   BossSpawnProbs = ecalloc(int *, numTypes, sizeof(int));

   // load boss spawn probabilities
   for(i = 0; i < numTypes; ++i)
   {
      if(useProbs)
      {
         a += cfg_getnint(cfg, SEC_BOSSPROBS, i);
         BossSpawnProbs[i] = a;
      }
      else
         BossSpawnProbs[i] = BossDefaults[i];
   }

   // check that the probabilities total 256
   if(useProbs && a != 256)
   {
      E_EDFLoggedErr(2, 
         "E_ProcessBossTypes: boss spawn probs do not total 256\n");
   }

   for(i = 0; i < numTypes; ++i)
   {
      const char *typeName = cfg_getnstr(cfg, SEC_BOSSTYPES, i);
      int typeNum = E_ThingNumForName(typeName);

      if(typeNum == -1)
      {
         E_EDFLoggedWarning(2, "Warning: invalid boss type '%s'\n", typeName);

         typeNum = UnknownThingType;
      }

      BossSpawnTypes[i] = typeNum;

      E_EDFLogPrintf("\t\tAssigned type %s(#%d) to boss type %d\n",
                     mobjinfo[typeNum]->name, typeNum, i);
   }
}
Пример #10
0
//
// E_ProcessCast
//
// Creates the DOOM II cast call information
//
static void E_ProcessCast(cfg_t *cfg)
{
   static bool firsttime = true;
   int i, numcastorder = 0, numcastsections = 0;
   cfg_t **ci_order;

   E_EDFLogPuts("\t* Processing cast call\n");
   
   // get number of cast sections
   numcastsections = cfg_size(cfg, SEC_CAST);

   if(firsttime && !numcastsections) // on main parse, at least one is required.
      E_EDFLoggedErr(2, "E_ProcessCast: no cast members defined.\n");

   firsttime = false;

   E_EDFLogPrintf("\t\t%d cast member(s) defined\n", numcastsections);

   // haleyjd 11/21/11: allow multiple runs
   if(castorder)
   {
      int i;

      // free names
      for(i = 0; i < max_castorder; i++)
      {
         if(castorder[i].name)
            efree(castorder[i].name);
      }
      // free castorder
      efree(castorder);
      castorder = NULL;
      max_castorder = 0;
   }

   // check if the "castorder" array is defined for imposing an
   // order on the castinfo sections
   numcastorder = cfg_size(cfg, SEC_CASTORDER);

   E_EDFLogPrintf("\t\t%d cast member(s) in castorder\n", numcastorder);

   // determine size of castorder
   max_castorder = (numcastorder > 0) ? numcastorder : numcastsections;

   // allocate with size+1 for an end marker
   castorder = estructalloc(castinfo_t, max_castorder + 1);
   ci_order  = ecalloc(cfg_t **, sizeof(cfg_t *), max_castorder);

   if(numcastorder > 0)
   {
      for(i = 0; i < numcastorder; ++i)
      {
         const char *title = cfg_getnstr(cfg, SEC_CASTORDER, i);         
         cfg_t *section    = cfg_gettsec(cfg, SEC_CAST, title);

         if(!section)
         {
            E_EDFLoggedErr(2, 
               "E_ProcessCast: unknown cast member '%s' in castorder\n", 
               title);
         }

         ci_order[i] = section;
      }
   }
   else
   {
      // no castorder array is defined, so use the cast members
      // in the order they are encountered (for backward compatibility)
      for(i = 0; i < numcastsections; ++i)
         ci_order[i] = cfg_getnsec(cfg, SEC_CAST, i);
   }


   for(i = 0; i < max_castorder; ++i)
   {
      int j;
      const char *tempstr;
      int tempint = 0;
      cfg_t *castsec = ci_order[i];

      // resolve thing type
      tempstr = cfg_getstr(castsec, ITEM_CAST_TYPE);
      if(!tempstr || 
         (tempint = E_ThingNumForName(tempstr)) == -1)
      {
         E_EDFLoggedWarning(2, "Warning: cast %d: unknown thing type %s\n",
                            i, tempstr);

         tempint = UnknownThingType;
      }
      castorder[i].type = tempint;

      // get cast name, if any -- the first seventeen entries can
      // default to using the internal string editable via BEX strings
      tempstr = cfg_getstr(castsec, ITEM_CAST_NAME);
      if(cfg_size(castsec, ITEM_CAST_NAME) == 0 && i < 17)
         castorder[i].name = NULL; // set from DeHackEd
      else
         castorder[i].name = estrdup(tempstr); // store provided value

      // get stopattack flag (used by player)
      castorder[i].stopattack = cfg_getbool(castsec, ITEM_CAST_SA);

      // process sound blocks (up to four will be processed)
      tempint = cfg_size(castsec, ITEM_CAST_SOUND);
      for(j = 0; j < 4; ++j)
      {
         castorder[i].sounds[j].frame = 0;
         castorder[i].sounds[j].sound = 0;
      }
      for(j = 0; j < tempint && j < 4; ++j)
      {
         int num;
         sfxinfo_t *sfx;
         const char *name;
         cfg_t *soundsec = cfg_getnsec(castsec, ITEM_CAST_SOUND, j);

         // if these are invalid, just fill them with zero rather
         // than causing an error, as they're very unimportant

         // name of sound to play
         name = cfg_getstr(soundsec, ITEM_CAST_SOUNDNAME);
         
         // haleyjd 03/22/06: modified to support dehnum auto-allocation
         if((sfx = E_EDFSoundForName(name)) == NULL)
         {
            E_EDFLoggedWarning(2, "Warning: cast member references invalid sound %s\n",
                               name);
            castorder[i].sounds[j].sound = 0;
         }
         else
         {
            if(sfx->dehackednum != -1 || E_AutoAllocSoundDEHNum(sfx))
               castorder[i].sounds[j].sound = sfx->dehackednum;
            else
            {
               E_EDFLoggedWarning(2, "Warning: could not auto-allocate a DeHackEd number "
                                     "for sound %s\n", name);
               castorder[i].sounds[j].sound = 0;
            }
         }

         // name of frame that triggers sound event
         name = cfg_getstr(soundsec, ITEM_CAST_SOUNDFRAME);
         if((num = E_StateNumForName(name)) < 0)
            num = 0;
         castorder[i].sounds[j].frame = num;
      }
   }

   // initialize the end marker to all zeroes
   memset(&castorder[max_castorder], 0, sizeof(castinfo_t));

   // free the ci_order table
   efree(ci_order);
}