Statstrings

From UFOpaedia
Jump to navigation Jump to search

Statstrings allow to automatically rename your soldiers in such a way that some of their stats (such as firing accuracy, reactions, strength...) are reflected in their name. For example, "Anton Miller /wmr" would be a weak good marksman of decent reactions. However, like almost everything in XcomUtil, this is fully configurable. Custom sets of statstring definitions can be found here.

Syntax

> Note: There is the original XcomUtils syntax. And there is the reimplementation of this mod within OpenXcom (https://github.com/OpenXcom/). The reimplementation uses a slightly different Syntax (see below).

The entry w s:-25 would add the a "w" to the name of any soldier whose strength is 25 or less. As you can guess, "w" means "weak".

A statstrings entry consists of two parts:

  1. The statstring that will be added to a soldiers' name. It can be anything you like, but should better be short.
  2. The condition to trigger that string which itself is made up of two parts: a one-letter handle for the stat, and the experience range.

Note that these are ranges: lower boundary, dash, upper boundary. Either one can be omitted if the range should be open-end. So 30-50 means "from thirty to fifty", -25 means "anything below 26" (and is equal to 0-25) and 60- means "sixty or higher" (equal to 60-255). 42, unsurprisingly, would mean "a value of exactly 42".

Single character strings accumulate. But any string with more than one character will end the search right there and no further strings will be added to the name. The strings are parsed in the order they are written in the XcomUtil.cfg file.

Displaying the exact stat

In some cases you may want to display the exact stat in your statstrings. Here's an example for strength:

2 s:20-29
3 s:30-39
4 s:40-49
5 s:50-59
6 s:60-69
7 s:70-71

0 s:20-20
1 s:21-21
2 s:22-22
3 s:23-23
4 s:24-24
5 s:25-25
6 s:26-26
7 s:27-27
8 s:28-28
9 s:29-29
0 s:30-30
1 s:31-31
2 s:32-32
3 s:33-33
4 s:34-34
5 s:35-35
6 s:36-36
7 s:37-37
8 s:38-38
9 s:39-39
0 s:40-40
1 s:41-41
2 s:42-42
3 s:43-43
4 s:44-44
5 s:45-45
6 s:46-46
7 s:47-47
8 s:48-48
9 s:49-49
0 s:50-50
1 s:51-51
2 s:52-52
3 s:53-53
4 s:54-54
5 s:55-55
6 s:56-56
7 s:57-57
8 s:58-58
9 s:59-59
0 s:60-60
1 s:61-61
2 s:62-62
3 s:63-63
4 s:64-64
5 s:65-65
6 s:66-66
7 s:67-67
8 s:68-68
9 s:69-69
0 s:70-70
1 s:71-71


Working Examples

The default configuration:

Open XcomUtil.cfg with any text editor. The section on Statstrings is at around line 1200 (and can be easisly found if you search for "statstrings"). This is the default configuration as it comes out of the box. The comments have been added to make it more readable, but the actual data has not been changed. In case you messed things up and forgot to make a backup, you can copy-and-pase the following code:

NameStats
StatStrings

x p:-30        //Psi strength (resistance) 30 or less
P p:80-        //virtually immune to Psi attacks
p p:60-79      //reasonably safe from Psi
K k:60-        //high Psi Skill
k k:30-59      //good Psi Skill
b b:60-        //very brave
c b:-10        //coward
w s:-25	//weak

Snpr f:60- r:60-

M f:70-        //Marksman
m f:60-69

Sct  r:50- d:60-

R r:60-        //Reactions
r r:50-59


The main problem with the default scheme is that sooner or later, most of your soldiers will become a "Snpr". The same string for everyone is like having no string at all.


Simple Numeric StatStrings

Instead of Mnemnonics, many people prefer to have actual figures displayed. Like: firing accuracy from 45-54 becomes a "5", from 55-64 is a "6" and so on. The following statstrings will do just that, first digit is Firing Accuracy, second reactions, so "Anton Miller /45" would a Soldier whose firing accuracy is about 40, and reactions about 50. Just copy and paste this into the statstrings section of you XcomUtil.cfg file:

1 f:0-14
2 f:15-24
3 f:25-34
4 f:35-44
5 f:45-54
6 f:55-64
7 f:65-74
8 f:75-84
9 f:85-94

1 r:0-14
2 r:15-24
3 r:25-34
4 r:35-44
5 r:45-54
6 r:55-64
7 r:65-74
8 r:75-84
9 r:85-94


Detailed numeric strings

I've been using the above scheme for a while, but noticed that I don't really care what the actual firing accuracy of any given soldier is – all I want to know is who's better or worse than the next man. In the following example, it's no longer easy to map the strings back to actual stats; higher numbers indicate better soldiers, that's all.

I habitually only hire sodliers with 50+ in both reactions and firing accuracy: anyone who doesn't meet these requirements will have a dash added to the name.

Again, you can copy this into your config file if you want to give it a try.


NameStats
StatStrings

- f:0-49
1 f:50-54
2 f:55-60
3 f:61-64
4 f:65-70
5 f:71-75
6 f:76-80
7 f:81-85
8 f:86-90
9 f:91-95
a f:96-100
b f:101-105
c f:106-110
d f:111-115
e f:116-

- r:0-45
0 r:46-49 
1 r:50-53
2 r:54-57
3 r:58-61
4 r:62-65
5 r:66-69
6 r:70-73
7 r:74-77
8 r:78-81
9 r:82-85
a r:86-89
b r:90-93
c r:94-97
d r:98-100
e r:101-

W s:0-19    //weak. can only carry Rifle + clips OR Laser Rifle + grenade
w s:20-25   //additional Medikit or Stun Rod or grenades possible
n s:26-30   //normal. no problems with standard gear
h s:31-40   //heavy cannon or auto-cannon, each plus spare clip
H s:41-     //can handle 4 Large Rockets + Launcher

1 p:0-15      
2 p:16-25    
3 p:26-35
4 p:36-44
5 p:45-54
6 p:55-64
7 p:65-74
8 p:75-84
9 p:85-

0 k:1-15     
2 k:16-24
3 k:25-34
4 k:35-44
5 k:45-54
6 k:55-64
7 k:65-74
8 k:75-84
9 k:85-

This results in Soldier names like "Anton Karas /13n" (before Psionic Training) or "Grace Kelly /41H81" (after Psi abilities have been unveiled). The scheme is:
firing accuracy - reactions - strength - Psi strength (resistance) - Psi skill

Notes:

  • I wanted to use W-w-n-s-S for weak-normal-strong, but it's impossible to distiguish an S from a 5 which really looks weird. So I settled on H for "heavy gear" instead.
  • values for Firing accuracy and reactions are pseudo-hexadecimal, 1-9 then a-e; it may be that the sudden appearance of letters where you're used to numbers may look strange. I don't know, because my soldiers never become that good.


Mix of the exact stat and detailed numeric strings

I've been using the previous scheme and I've found out that I don't really care about the accuracy and the psyonic skill of my soldiers, only about the reaction and their psionic power. I also found out that I want to see the exact strength of each soldier, so I can give him as much equipment as possible. Sg2002

So here's my statstrings scheme:

2 s:20-29
3 s:30-39
4 s:40-49
5 s:50-59
6 s:60-69
7 s:70-71

0 s:20-20
1 s:21-21
2 s:22-22
3 s:23-23
4 s:24-24
5 s:25-25
6 s:26-26
7 s:27-27
8 s:28-28
9 s:29-29
0 s:30-30
1 s:31-31
2 s:32-32
3 s:33-33
4 s:34-34
5 s:35-35
6 s:36-36
7 s:37-37
8 s:38-38
9 s:39-39
0 s:40-40
1 s:41-41
2 s:42-42
3 s:43-43
4 s:44-44
5 s:45-45
6 s:46-46
7 s:47-47
8 s:48-48
9 s:49-49
0 s:50-50
1 s:51-51
2 s:52-52
3 s:53-53
4 s:54-54
5 s:55-55
6 s:56-56
7 s:57-57
8 s:58-58
9 s:59-59
0 s:60-60
1 s:61-61
2 s:62-62
3 s:63-63
4 s:64-64
5 s:65-65
6 s:66-66
7 s:67-67
8 s:68-68
9 s:69-69
0 s:70-70
1 s:71-71

- r:0-45
0 r:46-49 
1 r:50-53
2 r:54-57
3 r:58-61
4 r:62-65
5 r:66-69
6 r:70-73
7 r:74-77
8 r:78-81
9 r:82-85
a r:86-89
b r:90-93
c r:94-97
d r:98-100
e r:101-
 
1 p:0-15      
2 p:16-25    
3 p:26-35
4 p:36-44
5 p:45-54
6 p:55-64
7 p:65-74
8 p:75-84
9 p:85-

This results in Soldier names like "Anton Karas /321" (before Psionic Training) or "Grace Kelly /4131" (after Psi abilities have been unveiled). The scheme is: strength(2 first values is the full strength of the soldier) - reactions - Psi strength (resistance)

Another example

Here's a string that I've used in the past:

Ace r:100- f:120- t:120- k:100- d:80- h:60- s:70- e:100-

I place it between x (low psi strength) and P (high psi strength) in the order. That results in "/xAce" for a superman who needs to be kept away from psi warfare, and "/Ace" for any other superman.

This designates a soldier who's maxed out; one who won't benefit from experience gains. In combat, I move these guys last of all to make sure the others get the benefit of the mission's experience. If you set up a [Experience_Training|"live" firing range] to train your troops, leave these guys out - they're a waste of pistol clips.

If you've got most of your soldiers to this level (and you're not going to Cydonia) then you may want to reverse the requirements so the minority stands out. Do so like this:

TRAINME r:-100 f:-120 t:-120 k:-100- d:-80 h:-60 s:-70 e:-100

Anyone who hasn't hit the cap yet will have a "/TRAINME" string. Or possibly "/xkcwTRAINME" if you put it at the end of the Statstrings section.

OpenXcom Syntax

OpenXcom uses yaml based configuration files. You find the configuration in data/standard/XcomUtil_Statstrings/XcomUtil_Statstrings.rul

The rule w s:-25 would translate into the following:

 statStrings:
 - string: "w"
   strength: [0, 25]

> Note: first Line starts with 2 spaces indent. Then 2 spaces per indent-level. No tabs!


The following python code will translate the examples from above to the yaml file syntax (filter out the Lines starting with #DEBUG):

#!/bin/python3
import sys
import re

print("  statStrings:")

#
# statid = a = armor (front)
# 				 b = bravery
# 				 d = time units (dexterity)
# 				 e = stamina (endurance)
# 				 f = firing accuracy
# 				 h = health
# 				 k = psi/mc skill
# 				 p = psi/mc strength (if skill > 0)
# 				 q = psi/mc strength (regardless of skill)
# 				 r = reactions
# 				 s = strength
# 				 t = throwing accuracy
#
skill_map = {
    "b": "bravery",
    "d": "tu",
    "e": "stamina",
    "f": "firing",
    "h": "health",
    "k": "psiSkill",
    "p": "psiStrength",
    "q": "dunno",
    "r": "reactions",
    "s": "strength",
    "t": "throwing",
}


def parse_range(s):
    m = s.split("-")
    if len(m) == 1:
        m.append(m[0])
    if m[0].strip() == "":
        m[0] = "0"
    if m[1].strip() == "":
        m[1] = "255"

    ret = ", ".join(m)
    return f"[{ret}]"


for line in sys.stdin:
    print(f"#DEBUG> got line: '{line.strip()}'")
    line = line.split("//")[0].strip()
    if line == "":
        continue

    rule_string = ""
    tokens = re.split(r"\s+", line)
    if len(tokens) <= 1:
        print("#DEBUG> ignoring")
        continue
    rule_string = tokens[0]
    rules = []
    for t in tokens[1:]:
        rule = t.split(":")
        if 1 <= len(rule) < 2:
            print("#DEBUG> faulty rule?")
            continue
        else:
            rules.append((rule[0], rule[1]))
    try:
        rules = list(map(lambda t: (skill_map[t[0]], parse_range(t[1])), rules))
        print(f"#DEBUG> Rule found: String {rule_string}, conditions: {rules}")
        print(f'  - string: "{rule_string}"')
        for r in rules:
            print(f"    {r[0]}: {r[1]}")
    except Exception as r:
        print(str(r))
        print("#DEBUG> error parsing rule, ignoring")
print("#DEBUG> Done")

See Also