]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/q3compat_sanity
authorMario <mario.mario@y7mail.com>
Fri, 6 Nov 2020 14:12:18 +0000 (00:12 +1000)
committerMario <mario.mario@y7mail.com>
Fri, 6 Nov 2020 14:12:18 +0000 (00:12 +1000)
1  2 
qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh

diff --combined qcsrc/common/mapinfo.qc
index 4f23862d2e8979a48596f257f8fbc27f4c8e935b,facfe02b75c8b145180b8553947e0b77fbeea33d..a166d82602b0d1a7828b735b42739d808d857b64
@@@ -8,9 -8,6 +8,9 @@@
        #include <common/monsters/_mod.qh>
  #endif
  
 +bool autocvar_g_mapinfo_arena_compat = true;
 +bool autocvar_g_mapinfo_arena_generate = false;
 +
  #ifdef MENUQC
  #define WARN_COND false
  #else
@@@ -299,25 -296,6 +299,25 @@@ float _MapInfo_Generate(string pFilenam
        mapMins = '0 0 0';
        mapMaxs = '0 0 0';
  
 +      if(autocvar_g_mapinfo_arena_generate)
 +      {
 +              // try for .arena or .defi files, as they may have more accurate information
 +              bool isdefi = false;
 +              string arena_fn = _MapInfo_FindArenaFile(pFilename, ".arena");
 +              int arena_fh = fopen(arena_fn, FILE_READ);
 +              if(arena_fh < 0)
 +              {
 +                      isdefi = true;
 +                      arena_fn = _MapInfo_FindArenaFile(pFilename, ".defi");
 +                      arena_fh = fopen(arena_fn, FILE_READ);
 +              }
 +              if(arena_fh >= 0)
 +              {
 +                      _MapInfo_ParseArena(arena_fn, arena_fh, pFilename, NULL, isdefi, true);
 +                      fclose(arena_fh);
 +              }
 +      }
 +
        for (;;)
        {
                if (!((s = fgets(fh))))
@@@ -608,10 -586,9 +608,10 @@@ void _MapInfo_Map_ApplyGametypeEx(strin
        }
  }
  
 -Gametype MapInfo_Type_FromString(string gtype, bool dowarn)
 +Gametype MapInfo_Type_FromString(string gtype, bool dowarn, bool is_q3compat)
  {
        string replacement = "";
 +      bool do_warn = true;
        switch (gtype)
        {
                case "nexball":   replacement = "nb"; break;
                case "invasion":  replacement = "inv"; break;
                case "assault":   replacement = "as"; break;
                case "race":      replacement = "rc"; break;
 +              // quake 3 compat
 +              case "ffa":       replacement = "dm"; do_warn = false; break;
 +              case "cctf":
 +              case "oneflag":   replacement = "ctf"; do_warn = false; break;
 +              case "tourney":   replacement = "duel"; do_warn = false; break;
 +              case "arena":     if(is_q3compat) { replacement = "ca"; do_warn = false; } break;
        }
        if (replacement != "")
        {
@@@ -780,193 -751,6 +780,193 @@@ float MapInfo_isRedundant(string fn, st
        return false;
  }
  
 +bool _MapInfo_ParseArena(string arena_filename, int fh, string pFilename, Gametype pGametypeToSet, bool isdefi, bool isgenerator)
 +{
 +      // NOTE: .arena files can hold more than 1 map's information!
 +      // to handle this, we're going to store gathered information in local variables and save it if we encounter the correct map name
 +      bool in_brackets = false; // testing a potential mapinfo section (within brackets)
 +      bool dosave = (arena_filename == strcat("scripts/", pFilename, ((isdefi) ? ".defi" : ".arena"))); // if the map is using the fallback, just accept the first found mapinfo (it's probably correct!)
 +      string stored_Map_description = "";
 +      string stored_Map_title = "";
 +      string stored_Map_author = "";
 +      int stored_supportedGametypes = 0;
 +      int stored_supportedFeatures = 0;
 +      int stored_flags = 0;
 +      string t, s;
 +      for (;;)
 +      {
 +              if (!((s = fgets(fh))))
 +                      break;
 +
 +              // catch different sorts of comments
 +              if(s == "")                    // empty lines
 +                      continue;
 +              if(substring(s, 0, 1) == "#")  // UNIX style
 +                      continue;
 +              if(substring(s, 0, 2) == "//") // C++ style
 +                      continue;
 +              if(substring(s, 0, 1) == "_")  // q3map style
 +                      continue;
 +              if(strstrofs(s, "{", 0) >= 0)
 +              {
 +                      if(in_brackets)
 +                              return false; // edge case? already in a bracketed section!
 +                      in_brackets = true;
 +                      continue;
 +              }
 +              else if(!in_brackets)
 +              {
 +                      // if we're not inside a bracket, don't process map info
 +                      continue;
 +              }
 +              if(strstrofs(s, "}", 0) >= 0)
 +              {
 +                      if(!in_brackets)
 +                              return false; // no starting bracket! let the mapinfo generation system handle it
 +                      in_brackets = false;
 +                      if(dosave)
 +                      {
 +                              MapInfo_Map_description = stored_Map_description;
 +                              if(stored_Map_title != "")
 +                                      MapInfo_Map_title = stored_Map_title;
 +                              MapInfo_Map_author = stored_Map_author;
 +                              if(isgenerator)
 +                                      MapInfo_Map_supportedGametypes = stored_supportedGametypes;
 +                              else
 +                              {
 +                                      FOREACH(Gametypes, it.m_flags & stored_supportedGametypes,
 +                                      {
 +                                              _MapInfo_Map_ApplyGametype ("", pGametypeToSet, it, true);
 +                                      });
 +                              }
 +                              MapInfo_Map_supportedFeatures = stored_supportedFeatures;
 +                              MapInfo_Map_flags = stored_flags;
 +                              return true; // no need to continue through the file, we have our map!
 +                      }
 +                      else
 +                      {
 +                              // discard any gathered locals, we're not using the correct map!
 +                              stored_Map_description = "";
 +                              stored_Map_title = "";
 +                              stored_Map_author = "";
 +                              stored_supportedGametypes = 0;
 +                              stored_supportedFeatures = 0;
 +                              stored_flags = 0;
 +                              continue;
 +                      }
 +              }
 +
 +              s = strreplace("\t", " ", s);
 +
 +              float p = strstrofs(s, "//", 0);
 +              if(p >= 0)
 +                      s = substring(s, 0, p);
 +
 +              // perform an initial trim to ensure the first argument is properly obtained
 +              //   remove leading spaces
 +              while(substring(s, 0, 1) == " ")
 +                      s = substring(s, 1, -1);
 +
 +              t = car(s); s = cdr(s);
 +              t = strtolower(t); // apparently some q3 maps use capitalized parameters
 +
 +              //   remove trailing spaces
 +              while(substring(t, -1, 1) == " ")
 +                      t = substring(t, 0, -2);
 +
 +              //   remove trailing spaces
 +              while(substring(s, -1, 1) == " ")
 +                      s = substring(s, 0, -2);
 +              //   remove leading spaces
 +              while(substring(s, 0, 1) == " ")
 +                      s = substring(s, 1, -1);
 +              // limited support of ""
 +              //   remove trailing and leading " of s
 +              if(substring(s, 0, 1) == "\"")
 +              {
 +                      if(substring(s, -1, 1) == "\"")
 +                              s = substring(s, 1, -2);
 +              }
 +              if(t == "longname")
 +                      stored_Map_title = s;
 +              else if(t == "author")
 +                      stored_Map_author = s;
 +              else if(t == "type")
 +              {
 +                      // if there is a valid gametype in this .arena file, include it in the menu
 +                      stored_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;
 +                      // type in quake 3 holds all the supported gametypes, so we must loop through all of them
 +                      string types = strreplace("team", "tdm ft", s); // TODO: handle support here better to include more Xonotic teamplay modes
 +                      FOREACH_WORD(types, true,
 +                      {
 +                              Gametype f = MapInfo_Type_FromString(it, false, true);
 +                              if(f)
 +                                      stored_supportedGametypes |= f.m_flags;
 +                      });
 +              }
 +              else if(t == "style" && isdefi)
 +              {
 +                      // we have a defrag map on our hands, add CTS!
 +                      // TODO: styles
 +                      stored_supportedGametypes |= MAPINFO_TYPE_CTS.m_flags;
 +              }
 +              else if(t == "map")
 +              {
 +                      if(strtolower(s) == strtolower(pFilename))
 +                              dosave = true; // yay, found our map!
 +              }
 +              else if(t == "quote")
 +                      stored_Map_description = s;
 +              // TODO: fraglimit
 +      }
 +
 +      // if the map wasn't found in the .arena, fall back to generated .mapinfo
 +      return false;
 +}
 +
 +#if defined(CSQC) || defined(MENUQC)
 +string(string filename) whichpack = #503;
 +#endif
 +string _MapInfo_FindArenaFile(string pFilename, string extension)
 +{
 +      string fallback = strcat("scripts/", pFilename, extension);
 +      if(!checkextension("DP_QC_FS_SEARCH_PACKFILE"))
 +              return fallback;
 +      string base_pack = whichpack(strcat("maps/", pFilename, ".bsp"));
 +      if(base_pack == "") // this map isn't packaged!
 +              return fallback;
 +
 +      int glob = search_packfile_begin(strcat("scripts/*", extension), true, true, base_pack);
 +      if(glob < 0)
 +              return fallback;
 +      int n = search_getsize(glob);
 +      for(int j = 0; j < n; ++j)
 +      {
 +              string file = search_getfilename(glob, j);
 +
 +              int fh = fopen(file, FILE_READ);
 +              if(fh < 0)
 +                      continue; // how?
 +              for(string s; (s = fgets(fh)); )
 +              {
 +                      int offset = strstrofs(s, "map", 0);
 +                      if(offset >= 0)
 +                      {
 +                              if(strstrofs(strtolower(s), strcat("\"", strtolower(pFilename), "\""), offset) >= 0) // quake 3 is case insensitive
 +                              {
 +                                      fclose(fh);
 +                                      search_end(glob);
 +                                      return file; // FOUND IT!
 +                              }
 +                      }
 +              }
 +              fclose(fh);
 +      }
 +
 +      search_end(glob);
 +      return fallback; // if we get here, a valid .arena file could not be found
 +}
 +
  // load info about a map by name into the MapInfo_Map_* globals
  float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gametype pGametypeToSet)
  {
        string s, t;
        float fh;
        int f, i;
 -      float r, n, p;
 +      float r, n;
        string acl;
  
        acl = MAPINFO_SETTEMP_ACL_USER;
        fh = fopen(fn, FILE_READ);
        if(fh < 0)
        {
 +              if(autocvar_g_mapinfo_arena_compat)
 +              {
 +                      // try for .arena or .defi files if no .mapinfo exists
 +                      bool isdefi = false;
 +                      fn = _MapInfo_FindArenaFile(pFilename, ".arena");
 +                      fh = fopen(fn, FILE_READ);
 +                      if(fh < 0)
 +                      {
 +                              isdefi = true;
 +                              fn = _MapInfo_FindArenaFile(pFilename, ".defi");
 +                              fh = fopen(fn, FILE_READ);
 +                      }
 +                      if(fh >= 0)
 +                      {
 +                              _MapInfo_Map_Reset();
 +                              if(_MapInfo_ParseArena(fn, fh, pFilename, pGametypeToSet, isdefi, false))
 +                                      goto mapinfo_handled; // skip generation
 +                      }
 +              }
 +
                fn = strcat("maps/autogenerated/", pFilename, ".mapinfo");
                fh = fopen(fn, FILE_READ);
                if(fh < 0)
                if(substring(s, 0, 1) == "_")  // q3map style
                        continue;
  
 -              p = strstrofs(s, "//", 0);
 +              float p = strstrofs(s, "//", 0);
                if(p >= 0)
                        s = substring(s, 0, p);
  
                else if(t == "type")
                {
                        t = car(s); s = cdr(s);
 -                      Gametype f = MapInfo_Type_FromString(t, true);
 +                      Gametype f = MapInfo_Type_FromString(t, true, false);
                        //if(WARN_COND)
                                //LOG_WARN("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'.");
                        if(f)
                else if(t == "gametype")
                {
                        t = car(s); s = cdr(s);
 -                      Gametype f = MapInfo_Type_FromString(t, true);
 +                      Gametype f = MapInfo_Type_FromString(t, true, false);
                        if(f)
                                _MapInfo_Map_ApplyGametypeEx (s, pGametypeToSet, f);
                        else if(WARN_COND)
                        t = car(s); s = cdr(s);
                        bool all = t == "all";
                        Gametype f = NULL;
 -                      if(all || (f = MapInfo_Type_FromString(t, true)))
 +                      if(all || (f = MapInfo_Type_FromString(t, true, false)))
                        {
                                if((all ? MAPINFO_TYPE_ALL : f.m_flags) & pGametypeToSet.m_flags)
                                {
                        t = car(s); s = cdr(s);
                        bool all = t == "all";
                        Gametype f = NULL;
 -                      if(all || (f = MapInfo_Type_FromString(t, true)))
 +                      if(all || (f = MapInfo_Type_FromString(t, true, false)))
                        {
                                if((all ? MAPINFO_TYPE_ALL : f.m_flags) & pGametypeToSet.m_flags)
                                {
                else if(WARN_COND)
                        LOG_WARN("Map ", pFilename, " provides unknown info item ", t, ", ignored");
        }
 +      LABEL(mapinfo_handled)
        fclose(fh);
  
        if(MapInfo_Map_title == "<TITLE>")
@@@ -1374,7 -1137,7 +1374,7 @@@ int MapInfo_CurrentFeatures(
  {
        int req = 0;
      // TODO: find a better way to check if weapons are required on the map
-       if(!(cvar("g_instagib") || cvar("g_overkill") || cvar("g_nix") || cvar("g_weaponarena") || !cvar("g_pickup_items") 
+       if(!(cvar("g_instagib") || cvar("g_overkill") || cvar("g_nix") || cvar("g_weaponarena") || !cvar("g_pickup_items") || !cvar("g_melee_only") 
                || cvar("g_race") || cvar("g_cts") || cvar("g_nexball") || cvar("g_ca") || cvar("g_freezetag") || cvar("g_lms")))
                req |= MAPINFO_FEATURE_WEAPONS;
        return req;
  
  Gametype MapInfo_CurrentGametype()
  {
 -      Gametype prev = MapInfo_Type_FromString(cvar_string("gamecfg"), false);
 +      Gametype prev = MapInfo_Type_FromString(cvar_string("gamecfg"), false, false);
        FOREACH(Gametypes, cvar(it.netname) && it != prev, return it);
        return prev ? prev : MAPINFO_TYPE_DEATHMATCH;
  }
diff --combined qcsrc/common/mapinfo.qh
index 7b1bfc2ba3be1ff2c4594c6755d7a6c49d7c0d8e,be1a4ef7c686dd8bb7861b8edb764ff4cbac3c3f..89ffdb9c25a0c3bec3572899f4c8a5ac187217f7
@@@ -21,6 -21,7 +21,7 @@@ const int GAMETYPE_FLAG_USEPOINT
  const int GAMETYPE_FLAG_PREFERRED       = BIT(2); // preferred (when available) in random selections
  const int GAMETYPE_FLAG_PRIORITY        = BIT(3); // priority selection when preferred gametype isn't available in random selections
  const int GAMETYPE_FLAG_HIDELIMITS      = BIT(4); // don't display a score limit needed for winning the match in the scoreboard
+ const int GAMETYPE_FLAG_WEAPONARENA     = BIT(5); // gametype has a forced weapon arena, weapon arena mutators should disable themselves when this is set
  
  int MAPINFO_TYPE_ALL;
  .int m_flags;
@@@ -41,6 -42,8 +42,8 @@@ CLASS(Gametype, Object
      ATTRIB(Gametype, frags, bool, true);
      /** should this gametype display a score limit in the scoreboard? */
      ATTRIB(Gametype, m_hidelimits, bool, false);
+     /** does this gametype enforce its own weapon arena? */
+     ATTRIB(Gametype, m_weaponarena, bool, false);
      /** game type defaults */
      ATTRIB(Gametype, model2, string);
      /** game type description */
          this.frags = (gflags & GAMETYPE_FLAG_USEPOINTS);
          this.m_priority = ((gflags & GAMETYPE_FLAG_PREFERRED) ? 2 : ((gflags & GAMETYPE_FLAG_PRIORITY) ? 1 : 0));
          this.m_hidelimits = (gflags & GAMETYPE_FLAG_HIDELIMITS);
+         this.m_weaponarena = (gflags & GAMETYPE_FLAG_WEAPONARENA);
  
          // same as `1 << m_id`
          MAPINFO_TYPE_ALL |= this.items = this.m_flags = (MAPINFO_TYPE_ALL + 1);
@@@ -175,7 -179,7 +179,7 @@@ string MapInfo_ListAllAllowedMaps(floa
  // gets a gametype from a string
  string _MapInfo_GetDefaultEx(Gametype t);
  float _MapInfo_GetTeamPlayBool(Gametype t);
 -Gametype MapInfo_Type_FromString(string t, bool dowarn);
 +Gametype MapInfo_Type_FromString(string t, bool dowarn, bool is_q3compat);
  string MapInfo_Type_Description(Gametype t);
  string MapInfo_Type_ToString(Gametype t);
  string MapInfo_Type_ToText(Gametype t);
@@@ -189,10 -193,6 +193,10 @@@ void MapInfo_Cache_Destroy(); // disabl
  void MapInfo_Cache_Create(); // enable caching
  void MapInfo_Cache_Invalidate(); // delete cache if any, but keep enabled
  
 +bool _MapInfo_ParseArena(string arena_filename, int fh, string pFilename, Gametype pGametypeToSet, bool isdefi, bool isgenerator);
 +
 +string _MapInfo_FindArenaFile(string pFilename, string extension);
 +
  void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse);
  
  void MapInfo_ClearTemps(); // call this when done with mapinfo for this frame