Jump to content
The Dark Mod Forums

AI stats Wiki page


Destined

Recommended Posts

Hi guys,

 

I am considering making a Wiki page with AI stats, so mappers can decide more easily, which AI (apart from the setting) would fit for specific game situations. These properties can all be looked up in the def-files, of course, but these are (from my personal experience) not that pleasant to read and I think it would be better to have the relevant stats in one place. I think this could save some time playtesting, when you know the most relevant properties beforehand and are not surprised, that the AI you chose behaves differently than expected. I would want this page to include properties like health, field of vision, KOable, fighting difficulty, spotting chances, damage reductions etc.

Now, I have two questions:

 

1. Is there an actual interest in this or is it something that would most likely see no use at all?

2. Who do I have to ask, to get writing access to the Wiki?

 

If there actually is an interest, I would later on make a list of properties I would include. Then, if anyone thinks anything is missing, I can expand the list, of course.

  • Like 4
Link to comment
Share on other sites

People are shy these days...

 

I say this would be quite nice. Args and what not, what they mean, how many are there, etc, is one of those things you have to learn on the go by mostly guessing, while you are mapping, and it would be nice to have more of this stuff documented. Wiki work is a noble endevour, but often thankless, Im afraid. People just study it, and get on with what theyre doing. I know Ive never thanked people for a wiki article, though Ive always felt happy somebody took the time to put the information that saved me in writting. The knowledge of doing something useful that will help someone, someday, is the reward you get. ;)

  • Like 1
Link to comment
Share on other sites

Ok, then I will first collect the data in an Excel Sheet. After that it will only be some writing effort.

 

But I have already encountered the first problem: I have created an account at MediaWiki, but when I try to log in at the DarkMod Wiki, it says that my user name does not exist :( Do I need an own account for the DarkMod Wiki? If so, where can I create it? I only see the log in button...

Link to comment
Share on other sites

There are quite a few. This csv file has just the spawnargs that are defined in each entity def. It doesn't include inherited spawnargs but I can tweak the script to include them if you want.
ai_spawnargs.csv.txt

How: I use python scripts to search TDM asset files and published FMs for compatibility problems when we do changes to the game. They parse text using regular expressions. I'll paste the code in case you want to mess with it. This was a quick fix because I just had to adapt a procedure I already had.

The first file mapparse_general.py searches a TDM installation including inside pk4s and FMs, and generates the filenames and their contents. The second one parses .def files and constructs an inheritance tree of entity defs, then uses that to print out the spawnargs of all entities that inherit from tdm_ai_base.

mapparse_general.py

 

"""Search text files in FMs and TDM assets"""
import re, os, fnmatch, zipfile
 
 
PATHS = [ r"C:\darkmod" ] #, r"E:\dm-dev\campaign"]  
FILE_EXTS = ['.mtr'] # '.mtr', '.prt', ,'.skin','.fx','.md5mesh','.pfb', '.anim', '.md5anim','.gui','.map','.script','.gui','.def'
 
def searchPk4(pk4, exts):
    """Look in a pk4 archive and yield files with any of the extensions 
    in exts. Returns ( filename, filecontents ). filename includes any
    directory path internal to the archive.
    
    pk4: full path, e.g. r'E:\darkmod\tdm_defs01.pk4'
    exts: list or tuple of bare extensions, e.g. ['def', 'mtr']
    """
    try:
        f = zipfile.ZipFile(pk4, 'r')
    except zipfile.BadZipfile:
        print "*** Error reading ", pk4
        return
    for member in f.infolist():
        ext = (os.path.splitext(member.filename)[1]).lower()
        if ext in exts:
            yield member.filename, f.read(member)
 
def findTDMFiles(paths, exts):
    """Yield tuples: (filepath, filecontents) of all files 
    found in paths with any of the extensions in exts. Looks inside
    pk4 archives, but not nested archives.
    
    paths, exts are sequences containing one or more paths / extensions.
    """
    # Make sure params are both lists else we'll get weird results from a string
    assert(paths.append and exts.append)
    for path in set(paths):
        for root, dirnames, filenames in os.walk(path):
            for ext in set( ['.pk4'] + exts):
                for filename in fnmatch.filter(filenames, '*'+ext):
                    fpath = os.path.join(root, filename)
                    if ext == '.pk4':
                        for internalname, content in searchPk4(fpath, exts):
                            yield ( fpath + ' -> ' + internalname,   content )
                    else:
                         yield fpath, file(fpath).read()
 

 




ai_spawnarg.py

 

"""Re-useable functions:
Parse entityDefs in unpacked FMs and TDM assets and compile class tree.
Make spawnargs searchable including inherited ones.

Currently set up to:
Compile a CSV of AI spawnargs
"""
 
import re, os, fnmatch
from collections import Counter
import mapparse_general
 
GAMEPATH = r"C:\darkmod"
FMPATH = r""
DEBUGMODE = True
 
# regexes
NAME_PTN = re.compile(r'entityDef\s+(\S+)\s*\{.*?\}', re.IGNORECASE | re.DOTALL)
ENT_PTN = re.compile(r'(entityDef\s+(\S+)\s*\{.*?\})', re.IGNORECASE | re.DOTALL)
ARGS_PTN = re.compile(r'\{([^}]*)\}', re.IGNORECASE)
SPAWNARG_PTN = re.compile(r'("[^"\n]*"|[^\s\n"]+)')
 
 
def debugprint(text):
    if DEBUGMODE:
        print text
 
def stripComments(text):
    """
    Strip text of /* block comments */ and // line comments, 
    except for those in a "string".
    """
    result = ''
    context = 'normal'
    pos = 0
    while pos < len(text):
        token = text[pos]
        next  = text[pos+1] if pos < len(text)-1 else ''
        skip = 0
        if context == 'string':
            result += token
            if token == '"':
                context = 'normal'
        elif context == 'line comment':
            if token == '\n':
                context = 'normal'
                result += token
        elif context == 'block comment':
            if token + next == '*/':
                context = 'normal'
                skip = 1
        else:
            assert(context == 'normal')
            if token + next == '//':
                context = 'line comment'
                skip = 1
            elif token + next == '/*':
                context = 'block comment'
                skip = 1
            else:
                result += token
                if token == '"':
                    context = 'string'
        pos += (1 + skip)
    return result
 
 
class entityDef:
    def __init__(self, name, filename, parentname='', spawnargs={}, inherited={}):
        self.name = name
        self.file = filename
        self.parentname = parentname
        self.parents = []      # Set by calling setParents() once all defs constructed
        self.children = set()  # Set by calling setParents() on all defs once constructed
        self.spawnargs=spawnargs
        self.inherited=inherited
 
    def _findParents(self, defs):
        """Return a list of parents, starting with the immediate parent."""
        parent = defs.get( self.spawnargs.get('inherit') )
        if parent:
            return [parent] + parent._findParents(defs)
        else:
            return []
 
    def setParents(self, defs):
        """Discover our ancestors and collect their spawnargs. 
        Make an inheritance list then work down it
        so spawnargs get overridden right, finally adding the our own."""
        self.parents = self._findParents(defs)
        for p in reversed(self.parents):
            self.inherited.update(p.spawnargs)
            p.children.add(self)
 
 
def getFileDefs(fname, text):
    """Return a dict: { name : entityDef }"""
    text = stripComments(text)
    entdefs = ENT_PTN.findall(text) # [ ( deftext, name ), ... ]
    defs = {}
    for deftext, name in entdefs:
        spawnargs = {}
        sp_text = ARGS_PTN.search(deftext).group(1)
        #debugprint(sp_text)
        strings = SPAWNARG_PTN.findall(sp_text)
        #debugprint('%s Strings found: %d' % (name, len(strings)))
        for idx in range(0, len(strings), 2):
            spawnargs[strings[idx].strip('"').lower()] = strings[idx+1].lower().strip('"')
        parentname = spawnargs.get('inherit')
        defs[name] = entityDef(name, fname, parentname, spawnargs)
    return defs
 
 
def getDefs(path, exclude_fms=False):
    """Compile a dict of entity defs from path: { name : entityDef }"""
    debugprint('Checking path ' + path)
    defs = {}   # { name : entityDef }
    for fname, text in mapparse_general.findTDMFiles( [path] , exts=['.def']):
        if exclude_fms and '\\fms\\' in fname:
            pass
        else:
            debugprint('Checking file ' + fname)
            defs.update(getFileDefs(fname, text))
            #debugprint('Def count %d' % len(defs))
    # Now we have all defs, populate ancestor details and inherited spawnargs
    for name, e in defs.items():
        e.setParents(defs)
    return defs
    
if __name__ == '__main__':
    defs = getDefs(GAMEPATH, exclude_fms=True)
    filecount = len( set( d.file for d in defs.values() ) )
    print "Found %d files with %d defs" % (filecount, len(defs))
    # Print out a csv table of AI (anything inheriting from atdm:ai_base
    results = ['"Name","Spawnarg","Default value"']
    ai_base = defs["atdm:ai_base"]
    for ent in ai_base.children:
        sp = ent.spawnargs
        for s in sp.keys():
            results.append('"'+ent.name+'","'+s+'","'+sp[s]+'"')
    for row in results:
        print row
    outfile = file('ai_spawnargs.csv', 'w')
    outfile.writelines(r + '\n' for r in results)
    outfile.close()

 

 

Link to comment
Share on other sites

Thanks for the scripts and the explanation. I just remembered, I already have a python script, that you made, for searching various file types for specific terms. I used it for the transparent AIs (that I also have not completely finished,yet :wacko: I should probably not get any more projects to work on here... Well, we'll see). If the output files look similar to the one you posted before, it should be no problem to import them into excel, which will save a lot of time :smile: So, thanks again!

Link to comment
Share on other sites

  • 3 months later...

Since I am currently stuck with my WIP, I decided to continue this project. However, the scripts SteveL posted do not seem to work. When I run them with IDLE I get the following error message:

 

For the mapparse script:

Traceback (most recent call last):
File "C:\Users\Destined\Desktop\mapparse_general.py", line 131, in <module>
defs = getDefs(GAMEPATH, exclude_fms=True)
File "C:\Users\Destined\Desktop\mapparse_general.py", line 118, in getDefs
for fname, text in mapparse_general.findTDMFiles( [path] , exts=['.def']):
AttributeError: 'module' object has no attribute 'findTDMFiles'

 

And for the ai_spawnarg:

raceback (most recent call last):
File "C:\Users\Destined\Desktop\ai_spawnarg.py", line 131, in <module>
defs = getDefs(GAMEPATH, exclude_fms=True)
File "C:\Users\Destined\Desktop\ai_spawnarg.py", line 118, in getDefs
for fname, text in mapparse_general.findTDMFiles( [path] , exts=['.def']):
AttributeError: 'module' object has no attribute 'findTDMFiles'

 

How can I fix that? Thanks in advance for the help!

Edited by Destined
Link to comment
Share on other sites

I don't know why it can't reach the mapparse function. Maybe I screwed up the syntax somehow, although I usually make it clear if I post code without testing it.

You could avoid the problem by merging the two scripts.

Start with ai_spawnarg,py
Add zipfile to the list of imports
Paste the 2 functions from mapparse_general above the functions in the ai script.

I mean like this (not tested but hopefully ok):

 

import re, os, fnmatch, zipfile
from collections import Counter
 
GAMEPATH = r"C:\darkmod"
FMPATH = r""
DEBUGMODE = True
 
# regexes
NAME_PTN = re.compile(r'entityDef\s+(\S+)\s*\{.*?\}', re.IGNORECASE | re.DOTALL)
ENT_PTN = re.compile(r'(entityDef\s+(\S+)\s*\{.*?\})', re.IGNORECASE | re.DOTALL)
ARGS_PTN = re.compile(r'\{([^}]*)\}', re.IGNORECASE)
SPAWNARG_PTN = re.compile(r'("[^"\n]*"|[^\s\n"]+)')
 
 
def searchPk4(pk4, exts):
    """Look in a pk4 archive and yield files with any of the extensions 
    in exts. Returns ( filename, filecontents ). filename includes any
    directory path internal to the archive.
    
    pk4: full path, e.g. r'E:\darkmod\tdm_defs01.pk4'
    exts: list or tuple of bare extensions, e.g. ['def', 'mtr']
    """
    try:
        f = zipfile.ZipFile(pk4, 'r')
    except zipfile.BadZipfile:
        print "*** Error reading ", pk4
        return
    for member in f.infolist():
        ext = (os.path.splitext(member.filename)[1]).lower()
        if ext in exts:
            yield member.filename, f.read(member)
 
def findTDMFiles(paths, exts):
    """Yield tuples: (filepath, filecontents) of all files 
    found in paths with any of the extensions in exts. Looks inside
    pk4 archives, but not nested archives.
    
    paths, exts are sequences containing one or more paths / extensions.
    """
    # Make sure params are both lists else we'll get weird results from a string
    assert(paths.append and exts.append)
    for path in set(paths):
        for root, dirnames, filenames in os.walk(path):
            for ext in set( ['.pk4'] + exts):
                for filename in fnmatch.filter(filenames, '*'+ext):
                    fpath = os.path.join(root, filename)
                    if ext == '.pk4':
                        for internalname, content in searchPk4(fpath, exts):
                            yield ( fpath + ' -> ' + internalname,   content )
                    else:
                         yield fpath, file(fpath).read()
 
def debugprint(text):
    if DEBUGMODE:
        print text
 
def stripComments(text):
    """
    Strip text of /* block comments */ and // line comments, 
    except for those in a "string".
    """
    result = ''
    context = 'normal'
    pos = 0
    while pos < len(text):
        token = text[pos]
        next  = text[pos+1] if pos < len(text)-1 else ''
        skip = 0
        if context == 'string':
            result += token
            if token == '"':
                context = 'normal'
        elif context == 'line comment':
            if token == '\n':
                context = 'normal'
                result += token
        elif context == 'block comment':
            if token + next == '*/':
                context = 'normal'
                skip = 1
        else:
            assert(context == 'normal')
            if token + next == '//':
                context = 'line comment'
                skip = 1
            elif token + next == '/*':
                context = 'block comment'
                skip = 1
            else:
                result += token
                if token == '"':
                    context = 'string'
        pos += (1 + skip)
    return result
 
class entityDef:
    def __init__(self, name, filename, parentname='', spawnargs={}, inherited={}):
        self.name = name
        self.file = filename
        self.parentname = parentname
        self.parents = []      # Set by calling setParents() once all defs constructed
        self.children = set()  # Set by calling setParents() on all defs once constructed
        self.spawnargs=spawnargs
        self.inherited=inherited
 
    def _findParents(self, defs):
        """Return a list of parents, starting with the immediate parent."""
        parent = defs.get( self.spawnargs.get('inherit') )
        if parent:
            return [parent] + parent._findParents(defs)
        else:
            return []
 
    def setParents(self, defs):
        """Discover our ancestors and collect their spawnargs. 
        Make an inheritance list then work down it
        so spawnargs get overridden right, finally adding the our own."""
        self.parents = self._findParents(defs)
        for p in reversed(self.parents):
            self.inherited.update(p.spawnargs)
            p.children.add(self)
 
def getFileDefs(fname, text):
    """Return a dict: { name : entityDef }"""
    text = stripComments(text)
    entdefs = ENT_PTN.findall(text) # [ ( deftext, name ), ... ]
    defs = {}
    for deftext, name in entdefs:
        spawnargs = {}
        sp_text = ARGS_PTN.search(deftext).group(1)
        #debugprint(sp_text)
        strings = SPAWNARG_PTN.findall(sp_text)
        #debugprint('%s Strings found: %d' % (name, len(strings)))
        for idx in range(0, len(strings), 2):
            spawnargs[strings[idx].strip('"').lower()] = strings[idx+1].lower().strip('"')
        parentname = spawnargs.get('inherit')
        defs[name] = entityDef(name, fname, parentname, spawnargs)
    return defs
 
def getDefs(path, exclude_fms=False):
    """Compile a dict of entity defs from path: { name : entityDef }"""
    debugprint('Checking path ' + path)
    defs = {}   # { name : entityDef }
    for fname, text in findTDMFiles( [path] , exts=['.def']):
        if exclude_fms and '\\fms\\' in fname:
            pass
        else:
            debugprint('Checking file ' + fname)
            defs.update(getFileDefs(fname, text))
            #debugprint('Def count %d' % len(defs))
    # Now we have all defs, populate ancestor details and inherited spawnargs
    for name, e in defs.items():
        e.setParents(defs)
    return defs
 
if __name__ == '__main__':
    defs = getDefs(GAMEPATH, exclude_fms=True)
    filecount = len( set( d.file for d in defs.values() ) )
    print "Found %d files with %d defs" % (filecount, len(defs))
    # Print out a csv table of AI (anything inheriting from atdm:ai_base
    results = ['"Name","Spawnarg","Default value"']
    ai_base = defs["atdm:ai_base"]
    for ent in ai_base.children:
        sp = ent.spawnargs
        for s in sp.keys():
            results.append('"'+ent.name+'","'+s+'","'+sp[s]+'"')
    for row in results:
        print row
    outfile = file('ai_spawnargs.csv', 'w')
    outfile.writelines(r + '\n' for r in results)
    outfile.close()

 

 

Edited by SteveL
Link to comment
Share on other sites

I just tried the new script, but I still get an error message:

 

Traceback (most recent call last):
File "C:\Users\Destined\Desktop\ai_spawnarg_v2.py", line 157, in <module>
defs = getDefs(GAMEPATH, exclude_fms=True)
File "C:\Users\Destined\Desktop\ai_spawnarg_v2.py", line 144, in getDefs
for fname, text in mapparse_general.findTDMFiles( [path] , exts=['.def']):
NameError: global name 'mapparse_general' is not defined

 

By the way: The path for TDM is "E:\Spiele\Stealth\TDM" for my PC, so I have changed line 4 to GAMEPATH = r"E:\Spiele\Stealth\TDM". I don't know, if this might cause problems.

 

If it won't work, I will just export the def files into excel, as I originally planned.

Link to comment
Share on other sites

I finally tried this code myself, and I've added a couple of new settings at the top of the script:

  • DEBUGMODE = False
  • INCLUDE_INHERITED_SPAWNARGS = True
  • INCLUDE_DR_HELP_TEXT = False
  • Setting DEBUGMODE to False suppresses screen output. You just get a single-line confirmation at the end, plus the .csv file that you can load in your spreadsheet. That makes the script run much faster.
  • Setting INCLUDE_INHERITED_SPAWNARGS to True will show all inherited spawnargs, so you can see the full list that each AI def uses. There's a fourth column in the output that tells you whether the spawnarg is inherited or not so you can filter them in your s/sheet.
  • Setting INCLUDE_DR_HELP_TEXT to False will hide any "editor_*" spawnargs.
Warning: including inherited s/args and help text means you'll get 500k records! Without the help text but including inherited spawnargs, it's about 250k.

 

 

 

import re, os, fnmatch, zipfile
from collections import Counter
 
GAMEPATH = r"C:\darkmod"
FMPATH = r""
DEBUGMODE = False
INCLUDE_INHERITED_SPAWNARGS = True
INCLUDE_DR_HELP_TEXT = False
 
# regexes
NAME_PTN = re.compile(r'entityDef\s+(\S+)\s*\{.*?\}', re.IGNORECASE | re.DOTALL)
ENT_PTN = re.compile(r'(entityDef\s+(\S+)\s*\{.*?\})', re.IGNORECASE | re.DOTALL)
ARGS_PTN = re.compile(r'\{([^}]*)\}', re.IGNORECASE)
SPAWNARG_PTN = re.compile(r'("[^"\n]*"|[^\s\n"]+)')
 
 
def searchPk4(pk4, exts):
    """Look in a pk4 archive and yield files with any of the extensions 
    in exts. Returns ( filename, filecontents ). filename includes any
    directory path internal to the archive.
    
    pk4: full path, e.g. r'E:\darkmod\tdm_defs01.pk4'
    exts: list or tuple of bare extensions, e.g. ['def', 'mtr']
    """
    try:
        f = zipfile.ZipFile(pk4, 'r')
    except zipfile.BadZipfile:
        print "*** Error reading ", pk4
        return
    for member in f.infolist():
        ext = (os.path.splitext(member.filename)[1]).lower()
        if ext in exts:
            yield member.filename, f.read(member)
 
def findTDMFiles(paths, exts):
    """Yield tuples: (filepath, filecontents) of all files 
    found in paths with any of the extensions in exts. Looks inside
    pk4 archives, but not nested archives.
    
    paths, exts are sequences containing one or more paths / extensions.
    """
    # Make sure params are both lists else we'll get weird results from a string
    assert(paths.append and exts.append)
    for path in set(paths):
        for root, dirnames, filenames in os.walk(path):
            for ext in set( ['.pk4'] + exts):
                for filename in fnmatch.filter(filenames, '*'+ext):
                    fpath = os.path.join(root, filename)
                    if ext == '.pk4':
                        for internalname, content in searchPk4(fpath, exts):
                            yield ( fpath + ' -> ' + internalname,   content )
                    else:
                         yield fpath, file(fpath).read()
 
def debugprint(text):
    if DEBUGMODE:
        print text
 
def stripComments(text):
    """
    Strip text of /* block comments */ and // line comments, 
    except for those in a "string".
    """
    result = ''
    context = 'normal'
    pos = 0
    while pos < len(text):
        token = text[pos]
        next  = text[pos+1] if pos < len(text)-1 else ''
        skip = 0
        if context == 'string':
            result += token
            if token == '"':
                context = 'normal'
        elif context == 'line comment':
            if token == '\n':
                context = 'normal'
                result += token
        elif context == 'block comment':
            if token + next == '*/':
                context = 'normal'
                skip = 1
        else:
            assert(context == 'normal')
            if token + next == '//':
                context = 'line comment'
                skip = 1
            elif token + next == '/*':
                context = 'block comment'
                skip = 1
            else:
                result += token
                if token == '"':
                    context = 'string'
        pos += (1 + skip)
    return result
 
class entityDef:
    def __init__(self, name, filename, parentname='', spawnargs={}, inherited={}):
        self.name = name
        self.file = filename
        self.parentname = parentname
        self.parents = []      # Set by calling setParents() once all defs constructed
        self.children = set()  # Set by calling setParents() on all defs once constructed
        self.spawnargs=spawnargs
        self.inherited=inherited
 
    def _findParents(self, defs):
        """Return a list of parents, starting with the immediate parent."""
        parent = defs.get( self.spawnargs.get('inherit') )
        if parent:
            return [parent] + parent._findParents(defs)
        else:
            return []
 
    def setParents(self, defs):
        """Discover our ancestors and collect their spawnargs. 
        Make an inheritance list then work down it
        so spawnargs get overridden right, finally adding the our own."""
        self.parents = self._findParents(defs)
        for p in reversed(self.parents):
            self.inherited.update(p.spawnargs)
            p.children.add(self)
 
def getFileDefs(fname, text):
    """Return a dict: { name : entityDef }"""
    text = stripComments(text)
    entdefs = ENT_PTN.findall(text) # [ ( deftext, name ), ... ]
    defs = {}
    for deftext, name in entdefs:
        spawnargs = {}
        sp_text = ARGS_PTN.search(deftext).group(1)
        #debugprint(sp_text)
        strings = SPAWNARG_PTN.findall(sp_text)
        #debugprint('%s Strings found: %d' % (name, len(strings)))
        for idx in range(0, len(strings), 2):
            spawnargs[strings[idx].strip('"').lower()] = strings[idx+1].lower().strip('"')
        parentname = spawnargs.get('inherit')
        defs[name] = entityDef(name, fname, parentname, spawnargs)
    return defs
 
def getDefs(path, exclude_fms=False):
    """Compile a dict of entity defs from path: { name : entityDef }"""
    debugprint('Checking path ' + path)
    defs = {}   # { name : entityDef }
    for fname, text in findTDMFiles( [path] , exts=['.def']):
        if exclude_fms and '\\fms\\' in fname:
            pass
        else:
            debugprint('Checking file ' + fname)
            defs.update(getFileDefs(fname, text))
            #debugprint('Def count %d' % len(defs))
    # Now we have all defs, populate ancestor details and inherited spawnargs
    for name, e in defs.items():
        e.setParents(defs)
    return defs
 
if __name__ == '__main__':
    defs = getDefs(GAMEPATH, exclude_fms=True)
    filecount = len( set( d.file for d in defs.values() ) )
    print "Found %d files with %d defs" % (filecount, len(defs))
    # Print out a csv table of AI (anything inheriting from atdm:ai_base
    results = ['"Name","Spawnarg","Default value","Inherited"']
    ai_base = defs["atdm:ai_base"]
    for ent in ai_base.children:
        sp_args = [s for s in ent.spawnargs if INCLUDE_DR_HELP_TEXT or not s.startswith('editor_')]
        for s in sp_args:
            results.append('"'+ent.name+'","'+s+'","'+ent.spawnargs[s]+'","N"')
        active_inherited_spawnargs = [s for s in ent.inherited
                                      if INCLUDE_INHERITED_SPAWNARGS
                                      and s not in ent.spawnargs
                                      and (INCLUDE_DR_HELP_TEXT or not s.startswith('editor_'))]
        for s in active_inherited_spawnargs:
            results.append('"'+ent.name+'","'+s+'","'+ent.inherited[s]+'","Y"')
    for row in results:
        debugprint(row)
    outfile = file('ai_spawnargs.csv', 'w')
    outfile.writelines(r + '\n' for r in results)
    outfile.close()

 

Link to comment
Share on other sites

Thanks for the modifications! I have already started sorting, but I think the modifications make sense, so I will start over again (was only half an hour so far). That way, I maybe get the consistent lists for most of the characters.

 

So far my idea was to make the Wiki article two parts: The first is intended for players and only includes stats that are relevant for them (e.g. health, damage, etc). The second part is intended for map authors and includes other spawnargs (e.g. attach positions, niticing chances etc.). But first I will have to sort a lot of data...

And if I am motivated enough I will also update the Spawnargs list in the Wiki, including explanations. Maybe categorise them, so they are easier to find. But first things first.

Link to comment
Share on other sites

Cool. I haven't a clue how to put all this information into a readable article tbh, and it might be better to leave the inherited spawnargs out. They mean 100s of 1000s of duplicated spawnargs. Maybe you could structure your article around the inheritance tree, so you need only quote each distinct spawnarg once.

 

If you find yourself faced with a massive manual task at any point that could be automated, feel free to ask for a script change :)

Link to comment
Share on other sites

I know what you mean. With the last script I got 1800 spawnargs for each AI. Still, for now I will leave the inherited stats and maybe we can somehow provide the whole list for the interested reader. For the Wiki article it might be a good idea to have a section with "general stats" that are identical for each AI of a subset (e.g. "Team" should be identical for each faction, as this is basically its definition...). But this will also come later.

 

This evening I will sort the AI by faction. Then I will alphabetise the spawnargs and see that I can get the same order for all AIs. When I have done that I only need one column with spawnarg names, so it should be a lot clearer. After that I can have a look, which spawnargs I deem of higher interest (e.g. the footstep sound is not that important for players or mappers). With that I will see how many are left and from there I can start thinking about how to best structure them. I am not really positive that I can get that all done this evening, but it sounds doable. Also, this weekend I will visit my mother in law, so there will not be too much progress then. Anyway, I will keep you posted on how it goes and ask for your opinion on the spawnargs I want to include.

 

Thanks for your offer, but I believe scripts won't help for the work that comes now. But they already helped a lot! Having to manually extract the spawnargs from the def files would have been horrible (especially after I saw how many there actually are).

Link to comment
Share on other sites

So, I am done sorting the AIs by faction. It is interesting to see how many different AI there are. Very impressive guys! I have divided them into the following factions:

Builders (9 AIs)

Townsfolk (24 AIs)

Undead (14 AIs)

Thieves (10 AIs)

Monsters/Animals (15 AIs)

Guards (17 AIs)

Steambots (3 AIs)

Trainer (22 AIs)

Pagans (3 AIs)

Mages (4 AIs)

Moors (3 AIs)

 

These are each seperately defined AIs and do not inclide different skins. As I said: very impressive :)

  • Like 1
Link to comment
Share on other sites

I think we have. These are the names and they sound like melee training:

 

 

 

atdm:ai_trainer_melee_dummy_citywatch

atdm:ai_trainer_melee_dummy_elite

atdm:ai_trainer_melee_stophit_evade_fast

atdm:ai_trainer_melee_stophit_evade

atdm:ai_trainer_melee_dummy_builder_guard_lesser

atdm:ai_trainer_melee_parry atdm:ai_trainer_melee_attack

atdm:ai_trainer_melee_stophit_parry_riposte

atdm:ai_trainer_melee_parry_fast

atdm:ai_trainer_melee_slow_base

atdm:ai_trainer_melee_parry_riposte

atdm:ai_trainer_melee_fast_base

atdm:ai_trainer_melee_dummy_base

atdm:ai_trainer_melee_stophit_parry_feint

atdm:ai_trainer_melee_dummy_commoner

atdm:ai_trainer_melee_dummy_builder_guard

atdm:ai_trainer_melee_parry_feint_fast

atdm:ai_trainer_melee_parry_feint

atdm:ai_trainer_melee_feint_fast

atdm:ai_trainer_melee_feint

atdm:ai_trainer_melee_parry_riposte_fast

atdm:ai_trainer_melee_dummy_proguard

 

 

Edited by Destined
Link to comment
Share on other sites

  • 9 months later...

Being reminded through another thread that I started this project quite some time ago (OMG, it is already a year) and having forgotten about it :unsure: , I went through the documents I already made for this and found an excel table, that contains all the spawargs and one example of spawnargs I would include. I have to admit that for a lot of spawnargs I have no idea what they actually mean, but came to the conclusion that most players will most likely also not know, which is why I simply ignored them. My current plan would be to make a table with an explanation of the individual spawnargs and after that transfer the structure to the AI sorted by faction. I already know, that the page will be very long, but still I will try my best to make it as clearly as possible. For the explanations, I most likely will need help from someone who knows the mechanics (like grayman) so I can explain the exact influence of some spawnargs (like acuity). For now I have picked the following 70 spawnargs to be included in the table. If anyone thinks something is missing or superfluous, just tell me.

 

 

acuity_aud

acuity_env

acuity_other

acuity_tact

acuity_vis

alert_aud_thresh

ammo armor

attack_accuracy

attack_cone

bleed blind_time

blind_time_fuzziness

can_be_flatfooted

can_drown

can_parry

can_parry_all

canlighttorches

canoperatedoors

canoperateelevators

canoperateswitchlights

canremainopen

cansearch

cansearchcooperatively

damage

drunk_acuity_factor

fov fov_rotation

fov_vert gas_immune

headturn_chance_idle

headturn_factor_alerted

ignore_alerts is_civilian

is_mantleable

ko_alert_immune

ko_alert_immune_state

ko_alert_state

ko_angle_alert_horiz

ko_angle_alert_vert

ko_angle_horiz

ko_angle_vert

ko_immune

ko_rotation

ko_spot_offset

ko_zone max_ammo

maxarmor

maxhealth

melee_chance_to_counter

melee_damage

melee_damage_mod melee_range

melee_rep_attack_time move_speed notpushable

outofreach_projectile_delay_max

outofreach_projectile_delay_min

outofreach_projectile_enabled

pickpocket_alert

pickpocket_delay_max

pickpocket_delay_min

push_player sleep

sneak_attack_alert_state

sneak_attack_mult

stamina team

unarmed_melee

unarmed_ranged

 

 

Edited by Destined
Link to comment
Share on other sites

Thanks, Springheel, I found a couple of formulas, but I am not 100% sure if I include them, as I in part don't really understand what's going on (maybe I am also missing some part). I will use the "editor_bool" descriptions as they usually give a good insight into what the respective spawnarg does.

 

While working on this list, I noticed a small mistake in one description: for "is_mantleable", the editor_bool states: "If set to 1, this entity is not mantleable by the player. Defaults to 0.", but I think it should read "If set to 1, this entity is not mantleable by the player. Defaults to 0." I know this is just nitpicking, but I thought if I find mistakes I might as well point them out (especially if this is just such small stuff that needs deleting a word in one def-file).

Link to comment
Share on other sites

I have just saved the first part of the AI stats page http://wiki.thedarkmod.com/index.php?title=AI_stats with the explanations of the spawnargs I want to cover and the following structure I would appreciate any criticism regarding the classification of the spawnargs (if I have classified all correctly or if someone finds a better classification)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recent Status Updates

    • OrbWeaver

      Does anyone actually use the Normalise button in the Surface inspector? Even after looking at the code I'm not quite sure what it's for.
      · 5 replies
    • Ansome

      Turns out my 15th anniversary mission idea has already been done once or twice before! I've been beaten to the punch once again, but I suppose that's to be expected when there's over 170 FMs out there, eh? I'm not complaining though, I love learning new tricks and taking inspiration from past FMs. Best of luck on your own fan missions!
      · 4 replies
    • The Black Arrow

      I wanna play Doom 3, but fhDoom has much better features than dhewm3, yet fhDoom is old, outdated and probably not supported. Damn!
      Makes me think that TDM engine for Doom 3 itself would actually be perfect.
      · 6 replies
    • Petike the Taffer

      Maybe a bit of advice ? In the FM series I'm preparing, the two main characters have the given names Toby and Agnes (it's the protagonist and deuteragonist, respectively), I've been toying with the idea of giving them family names as well, since many of the FM series have named protagonists who have surnames. Toby's from a family who were usually farriers, though he eventually wound up working as a cobbler (this serves as a daylight "front" for his night time thieving). Would it make sense if the man's popularly accepted family name was Farrier ? It's an existing, though less common English surname, and it directly refers to the profession practiced by his relatives. Your suggestions ?
      · 9 replies
    • nbohr1more

      Looks like the "Reverse April Fools" releases were too well hidden. Darkfate still hasn't acknowledge all the new releases. Did you play any of the new April Fools missions?
      · 5 replies
×
×
  • Create New...