Update pb-to-cpp.uc

Update pb-to-cpp.uc

Words that conflict with C++ keywords are renamed

# pb-to-cpp.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 4/18/2014 5:15:23 PM
# Comment: Converts PB source code to C++; modified by Daniel Corbier

ExternalKeywords: Exclude, Comment, Selected, ParentChild, FindMode, InputFile, OutputFile, BatchAction, SEND
ExternalKeywords: Highlight, ForeColor, BackColor, FontName, FontSize, FontStyle
ExternalKeywords: FilterEndText, FilterSeparator, FilterSort, FilterSortFunc, FilterStartText, FilterUnique, FilterTally

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: Converts PB source code to C++; modified by Daniel Corbier
Selected: False
Highlight: False
ForeColor: ControlText
BackColor: Aqua
FontName:
FontSize:
FontStyle:
CaseSensitive: False
QuoteSensitive: True
CodeBlockSensitive: True
FilterEndText:
FilterSeparator: {#10}
FilterSort: False
FilterSortFunc:
FilterStartText:
FilterUnique: False
FilterTally: False
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
InputFile:
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find:
Replace: {@Define:
            Var: BitType As String
            Var: Case1
         }
         {@Define::
            Token: \xFF[^\xFF]*\xFF\n ~~ Properties: ucWhiteSpace
         }

Criteria: 2
Highlight: True
Find: {@Start}
Replace: ' This file ({@Eval: Extract(ShortName(InputFile), ".")}.cpp) was converted from {@Eval: Extract(ShortName(InputFile), ".")}.Bas
         ' with {@Eval: AppName} on {@Eval: TimeStamp("MM/dd/yy")} using the Open Source
         ' PowerBASIC to C++ converter found at https://github.com/uCalc/powerbasic-to-cpp

         #include "pbOS.h"
         #include "pbMisc.h"
         #include <cstdarg>

Criteria: 3
Comment: Renames words that conflict w/ C++ keywords
Pass: 1

Criteria: 4
Find: { auto|bitand|bitor|bool|break|char|compl|constexpr|
      continue|default|delete|explicit|extern|false|float|
      friend|inline|mutable|new|nullptr|operator|private|
      protected|public|short|signed|struct|template|this|
      throw|true|typename|unsigned|virtual|void|volatile }
Replace: {Self}_Renamed

Criteria: 5
SkipOver: True
Find: { ' | _ } {Comment:".*"}
Replace: [Skip over]

Criteria: 6
Comment: Handles long line break (underscore _)
Pass: 2

Criteria: 7
Highlight: True
PassOnce: False
Find: {nl}{line} _ [{" +"}] [{comment:".+"}]{nl}

Replace: {nl}{line} {#255}{comment}{#255}{nl}

Criteria: 8
Highlight: True
SkipOver: True
Find: {"[\(\)]"} {@Note: These are ( and ); must be skipped}
Replace: [Skip over]

Criteria: 9
Highlight: True
SkipOver: True
Find: ' [{comment:".*"}]
Replace: [Skip over]

Criteria: 10
Comment: Functions, Subs, =
Pass: 3

Criteria: 11
Highlight: True
Find: =
Replace: ==

Criteria: 12
SkipOver: True
Find: { + | - | * | / | % | & | ^ | ! }=
Replace: [Skip over]

Criteria: 13
Highlight: True
SkipOver: True
Find: { {nl}[{ %|$ }]|Then|Else|For|: } {var:" *[a-z\@\.\_]+"}[({index})] =
Replace: [Skip over]

Criteria: 14
Highlight: True
SkipOver: True
Find: { { #If | #ElseIf } Not | ' [{comment:".*"}] }
Replace: [Skip over]

Criteria: 15
Highlight: True
SkipOver: True
Find: Macro {name}[({params})] =
Replace: [Skip over]

Criteria: 16
Highlight: True
PassOnce: False
Find: Sub {name:1} [Alias {alias}]([{args}])[{exp: Export}] [{comment:" *'.*"}] {nl}
Replace: {exp:extern "C" __declspec(dllexport)} void {exp:__stdcall} {name}({args}) { {comment}

Criteria: 17
Highlight: True
BackColor: DarkKhaki
PassOnce: False
Find: Function {name:1} [Alias {alias}]([{args}])[{exp: Export}] As {type}[{comment:" *'.*"}]{nl}
Replace: {exp:extern "C" __declspec(dllexport)} {type} {exp:__stdcall} {name}({args}) { {comment}
         {@Define:: Syntax: {name} = ::= return{#32}}

Criteria: 18
Highlight: True
PassOnce: False
Find: Function PBMain [()] [As Long]
Replace: Function main() As Long

Criteria: 19
Highlight: True
PassOnce: False
Find: Declare Function {name:1} [Lib {lib}] [Alias {alias}]
      [([{args}])] As {type}
Replace: {lib: extern __declspec(dllimport)} {type} {name}({args})

Criteria: 20
Highlight: True
Find: Declare Sub {name:1} [Lib {lib}] [Alias {alias}]
      [([{args}])]
Replace: {lib: extern __declspec(dllimport)} void {name}({args})

Criteria: 21
Highlight: True
Find: Static
Replace: static dim

Criteria: 22
Comment: For type member access (temp stage)
Find: {typevar:"@*[a-z_][a-z_0-9@]+\.[a-z_0-9@\.]+"}
Replace: `{typevar}`

Criteria: 23
Comment: Misc
Pass: 4

Criteria: 24
Highlight: True
SkipOver: True
Find: // [{comment:".*"}]
Replace: [Skip over]

Criteria: 25
Highlight: True
Find: ' [{comment:".*"}]
Replace: //{comment}

Criteria: 26
Highlight: True
BackColor: Green
PassOnce: False
Find: {nl}{ {num: %} | {str: $} }{equate} = {value}
Replace: {nl}const{num: int}{str: string} {equate} = {value};

Criteria: 27
Highlight: True
BackColor: Green
Find: { % | $ }{equate:"[a-z]+"}
Replace: {equate}

Criteria: 28
Highlight: True
BackColor: SandyBrown
Find: {"&h"}
Replace: 0x

Criteria: 29
Comment: Single-line IF
Highlight: True
BackColor: Green
PassOnce: False
Find: If {cond} Then {statement} [Else {else}]{nl}
Replace: if ({cond}) {{statement}{nl}}{else: else {{else}{nl}}}{nl}

Criteria: 30
Comment: Multi-line IF
Highlight: True
PassOnce: False
Find: If {cond} Then [{comment:" *'.*"}] {nl}
Replace: if ({cond}) { {comment}{nl}

Criteria: 31
Find: {nl}Else
Replace: {nl} } else {

Criteria: 32
Find: {nl}ElseIf {cond} Then
Replace: {nl} } else if ({cond}) {

Criteria: 33
Highlight: True
PassOnce: False
Find: For {x} = {start} To {stop} [Step {inc=1}][{comment:" *'.*"}]
Replace: for ({x}={start}; {x}{@Eval: IIF(sgn({inc})>0, '<', '>')}={stop}; {x} += {inc}) { {comment}

Criteria: 34
Highlight: True
PassOnce: False
Find: {nl}While {cond} [{comment:" *'.*"}] {nl}
Replace: {nl}while ({cond}) { {comment} {nl}

Criteria: 35
Highlight: True
PassOnce: False
Find: Do { {while: While} | {until: Until} } {cond} [{comment:" *'.*"}]{nl}
Replace: while ({while: {cond}}{until:!({cond})}) { {comment}

Criteria: 36
Find: { End { Sub|Function|If|Type|Union } | Wend|Next|Loop }
Replace: }

Criteria: 37
Find: do{nl}
Replace: do {

Criteria: 38
Highlight: True
PassOnce: False
Find: Loop { {while: While} | {until: Until} } {cond} [{comment:" *'.*"}]
Replace: } while ({while: {cond}}{until:!({cond})}) {comment}

Criteria: 39
Find: Select Case [As] [Long | Const | Const$] {expr} {nl}
Replace: { auto CASE_VAR = {expr}
         {@Execute: Case1 = 1}

Criteria: 40
Find: End Select
Replace: }}

Criteria: 41
Find: Case [IS] {test%} [:] [' {comment:".*"}]
Replace: {@Evaluate:
            IIf(Case1==1, Case1=0; "", "} else")
         } if (CASE_VAR == {test}) {{comment: // {comment}}

Criteria: 42
PassOnce: False
Find: Case [IS] {item}, {more}
Replace: Case {item} || CASE_VAR = {more}

Criteria: 43
Find: Case Else
Replace: } else {

Criteria: 44
Highlight: True
BackColor: Orange
PassOnce: False
Find: { Dim | {member: {nl}}} {var:1} As {type} [{ptr: Ptr}]
Replace: {member: {nl}}{type} {ptr:*}{var};

Criteria: 45
Highlight: True
PassOnce: False
Find: {nl} {bitfield:1} As Bit * {size:1} [{in: In {type:1}}]
Replace: {nl}{@Eval:
            IIf("{type}" <> "", SetVar(BitType, "{type}"))
            BitType
         } {bitfield} : {size};

Criteria: 46
Highlight: True
PassOnce: False
Find: Dim {array}([{size}]) As {type}
Replace: std::vector<{type}> {array}{size: ({size}+1)};{@Define::
             Syntax: {array}([{sz}]) ::= {array}[{sz}]}

Criteria: 47
Find: Erase {array:1}[()]
Replace: {array}.clear();

Criteria: 48
PassOnce: False
Find: ReDim {array}({size}) [As {type}] [{more: , {etc}}]
Replace: {array}.clear();
         {array}.resize({size}+1); {more: {nl} ReDim {etc}}

Criteria: 49
PassOnce: False
Find: ReDim Preserve {array}({size}) [As {type}] [{more: , {etc}}]
Replace: {array}.resize({size}+1); {more: {nl} ReDim Preserve {etc}}

Criteria: 50
Find: UBound({array})
Replace: ({array}.size()-1)

Criteria: 51
Highlight: True
BackColor: SandyBrown
PassOnce: False
Find: Dim {var1}, {more}
Replace: Dim {var1}
         Dim {more}

Criteria: 52
Highlight: True
BackColor: Gold
PassOnce: False
Find: { Local | Global | Register }
Replace: Dim

Criteria: 53
Find: `{typevar}.@{last:1}`
Replace: *`{typevar}.{last}`

Criteria: 54
Highlight: True
BackColor: Silver
Find: VarPtr({var})
Replace: &{var}

Criteria: 55
Find: Iterate
Replace: continue

Criteria: 56
Find: Exit
Replace: break

Criteria: 57
Highlight: True
Find: Exit Sub
Replace: return

Criteria: 58
Highlight: True
BackColor: Lime
Find: Long
Replace: int

Criteria: 59
Highlight: True
BackColor: Red
Find: Single
Replace: float

Criteria: 60
Find: Double
Replace: double

Criteria: 61
Highlight: True
Find: Byte
Replace: unsigned char

Criteria: 62
Highlight: True
Find: Integer
Replace: short

Criteria: 63
Highlight: True
Find: Word
Replace: unsigned short

Criteria: 64
Highlight: True
Find: Dword
Replace: unsigned int

Criteria: 65
Find: AsciiZ
Replace: LPCSTR

Criteria: 66
Highlight: True
Find: Quad
Replace: __int64

Criteria: 67
SkipOver: True
Find: long double
Replace: [Skip over]

Criteria: 68
Highlight: True
Find: { Extended | Ext }
Replace: long double

Criteria: 69
Highlight: True
BackColor: SlateBlue
PassOnce: False
Find: ByRef {arg} As {type:1}
Replace: {type}& {arg}

Criteria: 70
PassOnce: False
Find: ByRef {array}() As {type:1}
Replace: {type} {array}[]{@Define::
            Syntax: {array}([{index}]) ::= {array}[{index}]
         }

Criteria: 71
Highlight: True
BackColor: Pink
PassOnce: False
Find: ByVal {arg} As {type:1} [{ptr: Ptr}]
Replace: {type} {ptr:*}{arg}

Criteria: 72
Find: Type {name:1}
Replace: struct {name} {

Criteria: 73
Find: Union {name:1}
Replace: union {name} {

Criteria: 74
Highlight: True
Find: Macro {name} [{params: ({paramlist})}] = {replacement%}
Replace: #define {name}{params} {replacement}

Criteria: 75
Highlight: True
Find: #ElseIf
Replace: #elif

Criteria: 76
Find: #{directive: If | Else | EndIf }
Replace: #{@Eval: LCase("{directive}", "{'.*'}")}

Criteria: 77
Highlight: True
PassOnce: False
Find: [{NOT: Not }] %Def({const})
Replace: {NOT:!}defined {const}

Criteria: 78
Find: Choose[&]({index}, {choice})
Replace: IIf({index}, {choice}, 0)

Criteria: 79
Find: Choose$({index}, {choice})
Replace: IIf({index}, {choice}, "")

Criteria: 80
PassOnce: False
Find: Choose[{type: $ | & }]({index}, {choice1}, {more})
Replace: IIf({index}, {choice1}, Choose{type}({index}, {more}))

Criteria: 81
Find: StdOut {text} { {sameline: ;} | {NewLine: } }
Replace: cout << {text} {NewLine: << endl;}

Criteria: 82
Find: Sleep {milliseconds}
Replace: PB_SLEEP({milliseconds})

Criteria: 83
Find: {keyword:"CV(BYT|DWD|D|E|I|L|Q|S|WRD)"}({string} [, {offset=1}])
Replace: PB_{@Eval:UCase("{keyword}", "{'.*'}")}({string}, {offset})

Criteria: 84
PassOnce: False
Find: Line Input [{prompt: {q}{str}{q} | ${equate:1}}][,] {StrVariable:1}
Replace: {prompt: cout << {prompt};}getline(cin, {StrVariable});

Criteria: 85
Comment: Adds semi-colons to statements, etc
Pass: 5

Criteria: 86
Highlight: True
SkipOver: True
Find: { "{" | "}" | ; | //[{".*"}] | #{".*"} } {nl} [{"[ \n]+"}]
Replace: [Skip over]

Criteria: 87
Comment: Skips over so that colons in bit fields are not affected
Highlight: True
SkipOver: True
Find: { struct | union } {name:1} "{" {members+} "}"
Replace: [Skip over]

Criteria: 88
SkipOver: True
Find: ({cond} ? {this} :
Replace: [Skip over]

Criteria: 89
Highlight: True
Find: :
Replace: ;

Criteria: 90
SkipOver: True
Find: ::
Replace: [Skip over]

Criteria: 91
Highlight: True
PassOnce: False
Find: {nl}
Replace: ;{nl}

Criteria: 92
PassOnce: False
Find: IIf[$]({cond%}, {this%}, {that%})
Replace: ({cond} ? {this} : {that})

Criteria: 93
Find: _char([{char= }])
Replace: '{char}'

Criteria: 94
Find: {" \xFF\xFF\n"}
Replace: {nl}

Criteria: 95
Find: {" \xFF"}{comment:"[^\xFF]+"}{"\xFF\n"}
Replace:  /* {comment} */{nl}

Criteria: 96
Highlight: True
BackColor: DeepSkyBlue
PassOnce: False
Find: Function = {value}
Replace: return {value}

Criteria: 97
Find: PB_ASC({q}{char}{q}, {n:" *[0-9]+"})
Replace: '{@Eval:Mid("{char}", {n}, 1, ucChar)}'

Criteria: 98
Find: @
Replace: *

Criteria: 99
Selected: True
Highlight: True
Find: @{pointer:1}.
Replace: {pointer}->

Criteria: 100
Find: `{@Comment: removes temp char for type member access}
Replace: {Nothing}

# End Search

# pb-to-cpp.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 4/14/2014 6:18:21 PM
# Comment: Converts PB source code to C++; modified by Daniel Corbier

ExternalKeywords: Exclude, Comment, Selected, ParentChild, FindMode, InputFile, OutputFile, BatchAction, SEND
ExternalKeywords: Highlight, ForeColor, BackColor, FontName, FontSize, FontStyle
ExternalKeywords: FilterEndText, FilterSeparator, FilterSort, FilterSortFunc, FilterStartText, FilterUnique, FilterTally

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: Converts PB source code to C++; modified by Daniel Corbier
Selected: False
Highlight: False
ForeColor: ControlText
BackColor: Aqua
FontName:
FontSize:
FontStyle:
CaseSensitive: False
QuoteSensitive: True
CodeBlockSensitive: True
FilterEndText:
FilterSeparator: {#10}
FilterSort: False
FilterSortFunc:
FilterStartText:
FilterUnique: False
FilterTally: False
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
InputFile:
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find:
Replace: {@Define:
            Var: BitType As String
            Var: Case1
         }
         {@Define::
            Token: \xFF[^\xFF]*\xFF\n ~~ Properties: ucWhiteSpace
         }

Criteria: 2
Highlight: True
Find: {@Start}
Replace: ' This file ({@Eval: Extract(ShortName(InputFile), ".")}.cpp) was converted from {@Eval: Extract(ShortName(InputFile), ".")}.Bas
         ' with {@Eval: AppName} on {@Eval: TimeStamp()} using the Open Source
         ' PowerBASIC to C++ converter found at https://github.com/uCalc/powerbasic-to-cpp

         #include "pbOS.h"
         #include "pbMisc.h"
         #include <cstdarg>

Criteria: 3
Comment: Handles long line break (underscore _)
Pass: 1

Criteria: 4
Highlight: True
PassOnce: False
Find: {nl}{line} _ [{" +"}] [{comment:".+"}]{nl}

Replace: {nl}{line} {#255}{comment}{#255}{nl}

Criteria: 5
Highlight: True
SkipOver: True
Find: {"[\(\)]"} {@Note: These are ( and ); must be skipped}
Replace: [Skip over]

Criteria: 6
Highlight: True
SkipOver: True
Find: ' [{comment:".*"}]
Replace: [Skip over]

Criteria: 7
Comment: Functions, Subs, =
Pass: 2

Criteria: 8
Highlight: True
Find: =
Replace: ==

Criteria: 9
SkipOver: True
Find: { + | - | * | / | % | & | ^ | ! }=
Replace: [Skip over]

Criteria: 10
Highlight: True
SkipOver: True
Find: { {nl}[{ %|$ }]|Then|Else|For|: } {var:" *[a-z\@\.\_]+"}[({index})] =
Replace: [Skip over]

Criteria: 11
Highlight: True
SkipOver: True
Find: { { #If | #ElseIf } Not | ' [{comment:".*"}] }
Replace: [Skip over]

Criteria: 12
Highlight: True
SkipOver: True
Find: Macro {name}[({params})] =
Replace: [Skip over]

Criteria: 13
Highlight: True
PassOnce: False
Find: Sub {name:1} [Alias {alias}]([{args}])[{exp: Export}] [{comment:" *'.*"}] {nl}
Replace: {exp:extern "C" __declspec(dllexport)} void {exp:__stdcall} {name}({args}) { {comment}

Criteria: 14
Highlight: True
BackColor: DarkKhaki
PassOnce: False
Find: Function {name:1} [Alias {alias}]([{args}])[{exp: Export}] As {type}[{comment:" *'.*"}]{nl}
Replace: {exp:extern "C" __declspec(dllexport)} {type} {exp:__stdcall} {name}({args}) { {comment}
         {@Define:: Syntax: {name} = ::= return{#32}}

Criteria: 15
Highlight: True
PassOnce: False
Find: Function PBMain [()] [As Long]
Replace: Function main() As Long

Criteria: 16
Highlight: True
PassOnce: False
Find: Declare Function {name:1} [Lib {lib}] [Alias {alias}]
      [([{args}])] As {type}
Replace: {lib: extern __declspec(dllimport)} {type} {name}({args})

Criteria: 17
Highlight: True
Find: Declare Sub {name:1} [Lib {lib}] [Alias {alias}]
      [([{args}])]
Replace: {lib: extern __declspec(dllimport)} void {name}({args})

Criteria: 18
Highlight: True
Find: Static
Replace: static dim

Criteria: 19
Comment: For type member access (temp stage)
Find: {typevar:"@*[a-z_][a-z_0-9@]+\.[a-z_0-9@\.]+"}
Replace: `{typevar}`

Criteria: 20
Comment: Misc
Pass: 3

Criteria: 21
Highlight: True
SkipOver: True
Find: // [{comment:".*"}]
Replace: [Skip over]

Criteria: 22
Highlight: True
Find: ' [{comment:".*"}]
Replace: //{comment}

Criteria: 23
Highlight: True
BackColor: Green
PassOnce: False
Find: {nl}{ {num: %} | {str: $} }{equate} = {value}
Replace: {nl}const{num: int}{str: string} {equate} = {value};

Criteria: 24
Highlight: True
BackColor: Green
Find: { % | $ }{equate:"[a-z]+"}
Replace: {equate}

Criteria: 25
Highlight: True
BackColor: SandyBrown
Find: {"&h"}
Replace: 0x

Criteria: 26
Comment: Single-line IF
Highlight: True
BackColor: Green
PassOnce: False
Find: If {cond} Then {statement} [Else {else}]{nl}
Replace: if ({cond}) {{statement}{nl}}{else: else {{else}{nl}}}{nl}

Criteria: 27
Comment: Multi-line IF
Highlight: True
PassOnce: False
Find: If {cond} Then [{comment:" *'.*"}] {nl}
Replace: if ({cond}) { {comment}{nl}

Criteria: 28
Find: {nl}Else
Replace: {nl} } else {

Criteria: 29
Find: {nl}ElseIf {cond} Then
Replace: {nl} } else if ({cond}) {

Criteria: 30
Highlight: True
PassOnce: False
Find: For {x} = {start} To {stop} [Step {inc=1}][{comment:" *'.*"}]
Replace: for ({x}={start}; {x}{@Eval: IIF(sgn({inc})>0, '<', '>')}={stop}; {x} += {inc}) { {comment}

Criteria: 31
Highlight: True
PassOnce: False
Find: {nl}While {cond} [{comment:" *'.*"}] {nl}
Replace: {nl}while ({cond}) { {comment} {nl}

Criteria: 32
Highlight: True
PassOnce: False
Find: Do { {while: While} | {until: Until} } {cond} [{comment:" *'.*"}]{nl}
Replace: while ({while: {cond}}{until:!({cond})}) { {comment}

Criteria: 33
Find: { End { Sub|Function|If|Type|Union } | Wend|Next|Loop }
Replace: }

Criteria: 34
Find: do{nl}
Replace: do {

Criteria: 35
Highlight: True
PassOnce: False
Find: Loop { {while: While} | {until: Until} } {cond} [{comment:" *'.*"}]
Replace: } while ({while: {cond}}{until:!({cond})}) {comment}

Criteria: 36
Find: Select Case [As] [Long | Const | Const$] {expr} {nl}
Replace: { auto CASE_VAR = {expr}
         {@Execute: Case1 = 1}

Criteria: 37
Find: End Select
Replace: }}

Criteria: 38
Selected: True
Find: Case [IS] {test%} [:] [' {comment:".*"}]
Replace: {@Evaluate:
            IIf(Case1==1, Case1=0; "", "} else")
         } if (CASE_VAR == {test}) {{comment: // {comment}}

Criteria: 39
PassOnce: False
Find: Case [IS] {item}, {more}
Replace: Case {item} || CASE_VAR = {more}

Criteria: 40
Find: Case Else
Replace: } else {

Criteria: 41
Highlight: True
BackColor: Orange
PassOnce: False
Find: { Dim | {member: {nl}}} {var:1} As {type} [{ptr: Ptr}]
Replace: {member: {nl}}{type} {ptr:*}{var};

Criteria: 42
Highlight: True
PassOnce: False
Find: {nl} {bitfield:1} As Bit * {size:1} [{in: In {type:1}}]
Replace: {nl}{@Eval:
            IIf("{type}" <> "", SetVar(BitType, "{type}"))
            BitType
         } {bitfield} : {size};

Criteria: 43
Highlight: True
PassOnce: False
Find: Dim {array}([{size}]) As {type}
Replace: std::vector<{type}> {array}{size: ({size}+1)};{@Define::
             Syntax: {array}([{sz}]) ::= {array}[{sz}]}

Criteria: 44
Find: Erase {array:1}[()]
Replace: {array}.clear();

Criteria: 45
PassOnce: False
Find: ReDim {array}({size}) [As {type}] [{more: , {etc}}]
Replace: {array}.clear();
         {array}.resize({size}+1); {more: {nl} ReDim {etc}}

Criteria: 46
PassOnce: False
Find: ReDim Preserve {array}({size}) [As {type}] [{more: , {etc}}]
Replace: {array}.resize({size}+1); {more: {nl} ReDim Preserve {etc}}

Criteria: 47
Find: UBound({array})
Replace: ({array}.size()-1)

Criteria: 48
Highlight: True
BackColor: SandyBrown
PassOnce: False
Find: Dim {var1}, {more}
Replace: Dim {var1}
         Dim {more}

Criteria: 49
Highlight: True
BackColor: Gold
PassOnce: False
Find: { Local | Global | Register }
Replace: Dim

Criteria: 50
Find: `{typevar}.@{last:1}`
Replace: *`{typevar}.{last}`

Criteria: 51
Highlight: True
BackColor: Silver
Find: VarPtr({var})
Replace: &{var}

Criteria: 52
Find: Iterate
Replace: continue

Criteria: 53
Find: Exit
Replace: break

Criteria: 54
Highlight: True
Find: Exit Sub
Replace: return

Criteria: 55
Highlight: True
BackColor: Lime
Find: Long
Replace: int

Criteria: 56
Highlight: True
BackColor: Red
Find: Single
Replace: float

Criteria: 57
Find: Double
Replace: double

Criteria: 58
Highlight: True
Find: Byte
Replace: unsigned char

Criteria: 59
Highlight: True
Find: Integer
Replace: short

Criteria: 60
Highlight: True
Find: Word
Replace: unsigned short

Criteria: 61
Highlight: True
Find: Dword
Replace: unsigned int

Criteria: 62
Find: AsciiZ
Replace: LPCSTR

Criteria: 63
Highlight: True
Find: Quad
Replace: __int64

Criteria: 64
SkipOver: True
Find: long double
Replace: [Skip over]

Criteria: 65
Highlight: True
Find: { Extended | Ext }
Replace: long double

Criteria: 66
Highlight: True
BackColor: SlateBlue
PassOnce: False
Find: ByRef {arg} As {type:1}
Replace: {type}& {arg}

Criteria: 67
PassOnce: False
Find: ByRef {array}() As {type:1}
Replace: {type} {array}[]{@Define::
            Syntax: {array}([{index}]) ::= {array}[{index}]
         }

Criteria: 68
Highlight: True
BackColor: Pink
PassOnce: False
Find: ByVal {arg} As {type:1} [{ptr: Ptr}]
Replace: {type} {ptr:*}{arg}

Criteria: 69
Find: Type {name:1}
Replace: struct {name} {

Criteria: 70
Find: Union {name:1}
Replace: union {name} {

Criteria: 71
Highlight: True
Find: Macro {name} [{params: ({paramlist})}] = {replacement%}
Replace: #define {name}{params} {replacement}

Criteria: 72
Highlight: True
Find: #ElseIf
Replace: #elif

Criteria: 73
Find: #{directive: If | Else | EndIf }
Replace: #{@Eval: LCase("{directive}", "{'.*'}")}

Criteria: 74
Highlight: True
PassOnce: False
Find: [{NOT: Not }] %Def({const})
Replace: {NOT:!}defined {const}

Criteria: 75
Find: Choose[&]({index}, {choice})
Replace: IIf({index}, {choice}, 0)

Criteria: 76
Find: Choose$({index}, {choice})
Replace: IIf({index}, {choice}, "")

Criteria: 77
PassOnce: False
Find: Choose[{type: $ | & }]({index}, {choice1}, {more})
Replace: IIf({index}, {choice1}, Choose{type}({index}, {more}))

Criteria: 78
Find: StdOut {text} { {sameline: ;} | {NewLine: } }
Replace: cout << {text} {NewLine: << endl;}

Criteria: 79
Find: Sleep {milliseconds}
Replace: PB_SLEEP({milliseconds})

Criteria: 80
Find: {keyword:"CV(BYT|DWD|D|E|I|L|Q|S|WRD)"}({string} [, {offset=1}])
Replace: PB_{@Eval:UCase("{keyword}", "{'.*'}")}({string}, {offset})

Criteria: 81
PassOnce: False
Find: Line Input [{prompt: {q}{str}{q} | ${equate:1}}][,] {StrVariable:1}
Replace: {prompt: cout << {prompt};}getline(cin, {StrVariable});

Criteria: 82
Comment: Adds semi-colons to statements, etc
Pass: 4

Criteria: 83
Highlight: True
SkipOver: True
Find: { "{" | "}" | ; | //[{".*"}] | #{".*"} } {nl} [{"[ \n]+"}]
Replace: [Skip over]

Criteria: 84
Comment: Skips over so that colons in bit fields are not affected
Highlight: True
SkipOver: True
Find: { struct | union } {name:1} "{" {members+} "}"
Replace: [Skip over]

Criteria: 85
SkipOver: True
Find: ({cond} ? {this} :
Replace: [Skip over]

Criteria: 86
Highlight: True
Find: :
Replace: ;

Criteria: 87
SkipOver: True
Find: ::
Replace: [Skip over]

Criteria: 88
Highlight: True
PassOnce: False
Find: {nl}
Replace: ;{nl}

Criteria: 89
PassOnce: False
Find: IIf[$]({cond%}, {this%}, {that%})
Replace: ({cond} ? {this} : {that})

Criteria: 90
Find: _char([{char= }])
Replace: '{char}'

Criteria: 91
Find: {" \xFF\xFF\n"}
Replace: {nl}

Criteria: 92
Find: {" \xFF"}{comment:"[^\xFF]+"}{"\xFF\n"}
Replace:  /* {comment} */{nl}

Criteria: 93
Highlight: True
BackColor: DeepSkyBlue
PassOnce: False
Find: Function = {value}
Replace: return {value}

Criteria: 94
Find: PB_ASC({q}{char}{q}, {n:" *[0-9]+"})
Replace: '{@Eval:Mid("{char}", {n}, 1, ucChar)}'

Criteria: 95
Find: @
Replace: *

Criteria: 96
Highlight: True
Find: @{pointer:1}.
Replace: {pointer}->

Criteria: 97
Find: `{@Comment: removes temp char for type member access}
Replace: {Nothing}

# End Search


Fix for issue #359

Fix for issue #359

class XGCharacterGenerator extends Actor
	dependson(X2StrategyGameRulesetDataStructures) config(NameList);

// American
var localized array<string> m_arrAmMFirstNames;
var localized array<string> m_arrAmFFirstNames;
var localized array<string> m_arrAmLastNames;
// Arab
var localized array<string> m_arrAbMFirstNames;
var localized array<string> m_arrAbFFirstNames;
var localized array<string> m_arrAbLastNames;
// Israel
var localized array<string> m_arrIsMFirstNames;
var localized array<string> m_arrIsFFirstNames;
var localized array<string> m_arrIsLastNames;
// Russia
var localized array<string> m_arrRsMFirstNames;
var localized array<string> m_arrRsFFirstNames;
var localized array<string> m_arrRsMLastNames;
var localized array<string> m_arrRsFLastnames;
// England
var localized array<string> m_arrEnMFirstNames;
var localized array<string> m_arrEnFFirstNames;
var localized array<string> m_arrEnLastNames;
// French
var localized array<string> m_arrFrMFirstNames;
var localized array<string> m_arrFrFFirstNames;
var localized array<string> m_arrFrLastNames;
// African
var localized array<string> m_arrAfMFirstNames;
var localized array<string> m_arrAfFFirstNames;
var localized array<string> m_arrAfLastNames;
// Japanese
var localized array<string> m_arrJpMFirstNames;
var localized array<string> m_arrJpFFirstNames;
var localized array<string> m_arrJpLastNames;
// Chinese
var localized array<string> m_arrChMFirstNames;
var localized array<string> m_arrChFFirstNames;
var localized array<string> m_arrChLastNames;
// Indian
var localized array<string> m_arrInMFirstNames;
var localized array<string> m_arrInFFirstNames;
var localized array<string> m_arrInLastNames;
// German
var localized array<string> m_arrGmMFirstNames;
var localized array<string> m_arrGmFFirstNames;
var localized array<string> m_arrGmLastNames;
// Italian
var localized array<string> m_arrItMFirstNames;
var localized array<string> m_arrItFFirstNames;
var localized array<string> m_arrItLastNames;
// Mexican
var localized array<string> m_arrMxMFirstNames;
var localized array<string> m_arrMxFFirstNames;
var localized array<string> m_arrMxLastNames;
// Australian
var localized array<string> m_arrAuMFirstNames;
var localized array<string> m_arrAuFFirstNames;
var localized array<string> m_arrAuLastNames;
// Spanish
var localized array<string> m_arrEsMFirstNames;
var localized array<string> m_arrEsFFirstNames;
var localized array<string> m_arrEsLastNames;
// Greece
var localized array<string> m_arrGrMFirstNames;
var localized array<string> m_arrGrFFirstNames;
var localized array<string> m_arrGrLastNames;
// Norway
var localized array<string> m_arrNwMFirstNames;
var localized array<string> m_arrNwFFirstNames;
var localized array<string> m_arrNwLastNames;
// Ireland
var localized array<string> m_arrIrMFirstNames;
var localized array<string> m_arrIrFFirstNames;
var localized array<string> m_arrIrLastNames;
// South Korea
var localized array<string> m_arrSkMFirstNames;
var localized array<string> m_arrSkFFirstNames;
var localized array<string> m_arrSkLastNames;
// The Netherlands
var localized array<string> m_arrDuMFirstNames;
var localized array<string> m_arrDuFFirstNames;
var localized array<string> m_arrDuLastNames;
// Scotland
var localized array<string> m_arrScMFirstNames;
var localized array<string> m_arrScFFirstNames;
var localized array<string> m_arrScLastNames;
// Belgium
var localized array<string> m_arrBgMFirstNames;
var localized array<string> m_arrBgFFirstNames;
var localized array<string> m_arrBgLastNames;
// Poland
var localized array<string> m_arrPlMFirstNames;
var localized array<string> m_arrPlMLastnames;
var localized array<string> m_arrPlFFirstNames;
var localized array<string> m_arrPlFLastNames;

struct LocToSoldierLanguageElement
{
	var string LocString;
	var string GameLanguage;
};

var config array<LocToSoldierLanguageElement> LocToSoldierLanguage; //Allows the game to map the Loc string to an in-game language when picking soldiers

var config float NewSoldier_HatChance;
var config float NewSoldier_UpperFacePropChance;
var config float NewSoldier_LowerFacePropChance;
var config float NewSoldier_BeardChance;
var config bool  NewSoldier_ForceColors; //Forces units to use color selections 0-6 when being spawned

var config float NewCivlian_HatChance;
var config float NewCivlian_UpperFacePropChance;
var config float NewCivlian_LowerFacePropChance;
var config float NewCivlian_BeardChance;

var config float DLCPartPackDefaultChance;

// Temporary variable for uniquely randomized hair so artists
// can see potentially all hair types in a squad.
var int m_iHairType;

// temporary variable for soldier creation, used by TemplateMgr filter functions
var protected TSoldier kSoldier;
var protected X2BodyPartTemplate kTorsoTemplate;
var protected name MatchCharacterTemplateForTorso;
var protected name MatchArmorTemplateForTorso;
var protected array<name> DLCNames; //List of DLC packs to pull parts from for the currently generating soldier.

// Store a country name to be use in bios for soldiers that force a unique country
var name BioCountryName;

// Character groups that use soldier voices
var const config array<name> SoldierVoiceCharacterGroups;
// Variable for Issue #384
var X2CharacterTemplate m_CharTemplate;

// New variable for issue #397
var config(Content) int iDefaultWeaponTint;

function GenerateName( int iGender, name CountryName, out string strFirst, out string strLast, optional int iRace = -1 )
{
	local X2StrategyElementTemplateManager StratMgr;
	local X2CountryTemplate CountryTemplate;
	local array<CountryNames> AllNames, GeneralNames, RaceSpecificNames;
	local int idx, iRoll;

	strFirst = "";
	strLast = "";
	StratMgr = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager();
	CountryTemplate = X2CountryTemplate(StratMgr.FindStrategyElementTemplate(CountryName));

	for(idx = 0; idx < CountryTemplate.Names.Length; idx++)
	{
		// Add to list of all names
		AllNames.AddItem(CountryTemplate.Names[idx]);

		if(!CountryTemplate.Names[idx].bRaceSpecific)
		{
			// Add to general list of names
			GeneralNames.AddItem(CountryTemplate.Names[idx]);
		}
		else if(CountryTemplate.Names[idx].bRaceSpecific && CountryTemplate.Names[idx].Race == iRace)
		{
			// Add to list of applicable race specific names
			RaceSpecificNames.AddItem(CountryTemplate.Names[idx]);
		}
	}

	// Roll for race specific name
	iRoll = `SYNC_RAND(100);
	while(RaceSpecificNames.Length > 0 && (strFirst == "" || strLast == ""))
	{
		if(RaceSpecificNames.Length == 1 || iRoll < RaceSpecificNames[0].PercentChance)
		{
			if(iGender == eGender_Female)
			{
				strFirst = RaceSpecificNames[0].FemaleNames[`SYNC_RAND(RaceSpecificNames[0].FemaleNames.Length)];
				strLast = RaceSpecificNames[0].FemaleLastNames[`SYNC_RAND(RaceSpecificNames[0].FemaleLastNames.Length)];
			}
			else
			{
				strFirst = RaceSpecificNames[0].MaleNames[`SYNC_RAND(RaceSpecificNames[0].MaleNames.Length)];
				strLast = RaceSpecificNames[0].MaleLastNames[`SYNC_RAND(RaceSpecificNames[0].MaleLastNames.Length)];
			}
		}
		else
		{
			iRoll -= RaceSpecificNames[0].PercentChance;
			RaceSpecificNames.Remove(0, 1);
		}
	}

	// Roll for general name
	iRoll = `SYNC_RAND(100);
	while(GeneralNames.Length > 0 && (strFirst == "" || strLast == ""))
	{
		if(GeneralNames.Length == 1 || iRoll < GeneralNames[0].PercentChance)
		{
			if(iGender == eGender_Female)
			{
				strFirst = GeneralNames[0].FemaleNames[`SYNC_RAND(GeneralNames[0].FemaleNames.Length)];
				strLast = GeneralNames[0].FemaleLastNames[`SYNC_RAND(GeneralNames[0].FemaleLastNames.Length)];
			}
			else
			{
				strFirst = GeneralNames[0].MaleNames[`SYNC_RAND(GeneralNames[0].MaleNames.Length)];
				strLast = GeneralNames[0].MaleLastNames[`SYNC_RAND(GeneralNames[0].MaleLastNames.Length)];
			}
		}
		else
		{
			iRoll -= GeneralNames[0].PercentChance;
			GeneralNames.Remove(0, 1);
		}
	}

	// Roll on all names (could happen if only race specific names were configured for the country, and input race doesn't match)
	iRoll = `SYNC_RAND(100);
	while(AllNames.Length > 0 && (strFirst == "" || strLast == ""))
	{
		if(AllNames.Length == 1 || iRoll < AllNames[0].PercentChance)
		{
			if(iGender == eGender_Female)
			{
				strFirst = AllNames[0].FemaleNames[`SYNC_RAND(AllNames[0].FemaleNames.Length)];
				strLast = AllNames[0].FemaleLastNames[`SYNC_RAND(AllNames[0].FemaleLastNames.Length)];
			}
			else
			{
				strFirst = AllNames[0].MaleNames[`SYNC_RAND(AllNames[0].MaleNames.Length)];
				strLast = AllNames[0].MaleLastNames[`SYNC_RAND(AllNames[0].MaleLastNames.Length)];
			}
		}
		else
		{
			iRoll -= AllNames[0].PercentChance;
			AllNames.Remove(0, 1);
		}
	}

	// Fall through case
	if(strFirst == "" || strLast == "")
	{
		if(iGender == eGender_Female)
		{
			strFirst = m_arrAmFFirstNames[`SYNC_RAND(m_arrAmFFirstNames.Length)];
		}
		else
		{
			strFirst = m_arrAmMFirstNames[`SYNC_RAND(m_arrAmMFirstNames.Length)];
		}

		strLast = m_arrAmLastNames[`SYNC_RAND(m_arrAmLastNames.Length)];
	}
}

function name PickOriginCountry()
{
	local X2StrategyElementTemplateManager StratMgr;
	local array<X2StrategyElementTemplate> CountryTemplates;
	local array<name> arrCountries;
	local int idx, i;

	StratMgr = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager();
	CountryTemplates = StratMgr.GetAllTemplatesOfClass(class'X2CountryTemplate');

	for(idx = 0; idx < CountryTemplates.Length; idx++)
	{
		for(i = 0; i < X2CountryTemplate(CountryTemplates[idx]).UnitWeight; i++)
		{
			arrCountries.AddItem(CountryTemplates[idx].DataName);
		}
	}

	if(arrCountries.Length > 0)
	{
		return arrCountries[`SYNC_RAND(arrCountries.Length)];
	}

	return 'Country_USA';
}

function name DefaultGetRandomUberTemplate_WarnAboutFilter(string PartType)
{
	`log("WARNING:"@ `ShowVar(self) $": Unable to find a GetRandomUberTemplate for '"$PartType$"' given Torso '"$kSoldier.kAppearance.nmTorso$"' Armor '"$MatchArmorTemplateForTorso$"' Character '"$MatchCharacterTemplateForTorso$"'", ,'XCom_Templates');
	return '';
}

//In some cases we already have unit that we want to just want to randomize the appearance of. This method is used for that purpose. UsedGameState is the game state
//that contains Unit
function TSoldier CreateTSoldierFromUnit( XComGameState_Unit Unit, XComGameState UseGameState )
{
	local XComGameState_Item ArmorItem;
	local name ArmorName;

	ArmorItem = Unit.GetItemInSlot(eInvSlot_Armor, UseGameState, true);
	ArmorName = ArmorItem == none ? '' : ArmorItem.GetMyTemplateName();
	return CreateTSoldier( Unit.GetMyTemplateName(), EGender(Unit.kAppearance.iGender), Unit.kAppearance.nmFlag, Unit.kAppearance.iRace, ArmorName );
}

delegate bool FilterCallback(X2BodyPartTemplate Template);
function X2BodyPartTemplate SetBodyPartToFirstInArray(X2BodyPartTemplateManager PartTemplateManager, out name DataName, string PartTypeName, delegate<FilterCallback> CallbackFn)
{
	local array<X2BodyPartTemplate> listPartTemplate;
	PartTemplateManager.GetFilteredUberTemplates(PartTypeName, `XCOMGAME.SharedBodyPartFilter, CallbackFn, listPartTemplate);
	DataName = (listPartTemplate.length > 0) ? listPartTemplate[0].DataName : DefaultGetRandomUberTemplate_WarnAboutFilter(PartTypeName);
	return (listPartTemplate.length > 0) ? listPartTemplate[0] : none;
}
function X2BodyPartTemplate RandomizeSetBodyPart(X2BodyPartTemplateManager PartTemplateManager, out name DataName, string PartTypeName, delegate<FilterCallback> CallbackFn, optional bool bCanBeEmpty = false)
{
	local X2BodyPartTemplate RandomPart;

	//Clear the template name if it is allowed to be empty
	if(bCanBeEmpty)
	{
		DataName = '';
	}

	RandomPart = PartTemplateManager.GetRandomUberTemplate(PartTypeName, `XCOMGAME.SharedBodyPartFilter, CallbackFn);
	if(!bCanBeEmpty || (RandomPart != none))
	{
		DataName = (RandomPart != none) ? RandomPart.DataName : DefaultGetRandomUberTemplate_WarnAboutFilter(PartTypeName);
	}
	return RandomPart;
}

function UpdateDLCPackFilters()
{
	// Issue #155 follow-up Start
	DLCNames = class'CHHelpers'.static.GetAcceptablePartPacks();
	// Issue #155 End
}

function TSoldier CreateTSoldier( optional name CharacterTemplateName, optional EGender eForceGender, optional name nmCountry = '', optional int iRace = -1, optional name ArmorName )
{
	local XComLinearColorPalette HairPalette;
	local X2SimpleBodyPartFilter BodyPartFilter;
	local X2CharacterTemplate CharacterTemplate;
	local TAppearance DefaultAppearance;

	kSoldier.kAppearance = DefaultAppearance;

	CharacterTemplate = SetCharacterTemplate(CharacterTemplateName, ArmorName);
	// Single Line for Issue #384
	m_CharTemplate = CharacterTemplate;

	if (nmCountry == '')
		nmCountry = PickOriginCountry();

	SetCountry(nmCountry);
	SetRace(iRace);
	SetGender(eForceGender);
	kSoldier.iRank = 0;

	BodyPartFilter = `XCOMGAME.SharedBodyPartFilter;

	//When generating new characters, consider the DLC pack filters.
	//Use the player's settings from Options->Game Options to pick which DLC / Mod packs this generated soldier should draw from
	UpdateDLCPackFilters();

	SetTorso(BodyPartFilter, CharacterTemplateName);
	SetArmsLegsAndDeco(BodyPartFilter);
	SetHead(BodyPartFilter, CharacterTemplate);
	SetAccessories(BodyPartFilter, CharacterTemplateName);
	SetUnderlay(BodyPartFilter);
	SetArmorTints(CharacterTemplate);

	HairPalette = `CONTENT.GetColorPalette(ePalette_HairColor);
	kSoldier.kAppearance.iHairColor = ChooseHairColor(kSoldier.kAppearance, HairPalette.BaseOptions); // Only generate with base options
	kSoldier.kAppearance.iEyeColor = 0; //right now initialized to the first one, todo when we decides which are the base colors for the different races.

	// SHIP HACK: There are at least 5 base options for every palette. I am not proud. -- jboswell
	kSoldier.kAppearance.iSkinColor = Rand(5);

	SetVoice(CharacterTemplateName, nmCountry);
	SetAttitude();
	GenerateName( kSoldier.kAppearance.iGender, kSoldier.nmCountry, kSoldier.strFirstName, kSoldier.strLastName, kSoldier.kAppearance.iRace );

	BioCountryName = kSoldier.nmCountry;

	return kSoldier;
}

static function Name GetLanguageByString(optional string strLanguage="")
{
	if (len(strLanguage) == 0)
		strLanguage = GetLanguage();

	switch (strLanguage)
	{
	case "DEU":
		return 'german';
	case "ESN":
		return 'spanish';
	case "FRA":
		return 'french';
	case "ITA":
		return 'italian';
	}

	return 'english';
}

function Name GetLanguageByCountry(name CountryName)
{
	local X2StrategyElementTemplateManager StratMgr;
	local X2CountryTemplate CountryTemplate;
	local name LanguageName;
	local string LanguageString;
	local string LocString;
	local int Index;
	local bool bWantsForeignLanguages;
	local XComOnlineProfileSettings ProfileSettings;

	StratMgr = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager();
	CountryTemplate = X2CountryTemplate(StratMgr.FindStrategyElementTemplate(CountryName));
	if(CountryTemplate != none)
	{
		LanguageName = CountryTemplate.Language;
	}

	ProfileSettings = `XPROFILESETTINGS;
	bWantsForeignLanguages = ProfileSettings != none && ProfileSettings.Data != none && ProfileSettings.Data.m_bForeignLanguages;
	//Try to get a language from the LOC settings if the player has an option set to do so or something is wrong with the country settings
	if(!bWantsForeignLanguages || LanguageName == '')
	{
		LocString = GetLanguage(); //Get the three letter code for our language
		for(Index = 0; Index < LocToSoldierLanguage.Length; ++Index)
		{
			if(LocString == LocToSoldierLanguage[Index].LocString)
			{
				LanguageString = string(LanguageName);

				//Do str in str check so that we can match language accents, ie english matching englishUK
				if(InStr(LanguageString, LocToSoldierLanguage[Index].GameLanguage) == -1)
				{
					//The country language did not match our loc settings, change it
					LanguageName = name(LocToSoldierLanguage[Index].GameLanguage);
				}
			}
		}
	}

	//If we STILL don't have a language, pick english
	if(LanguageName == '')
	{
		LanguageName = 'english';
	}

	return LanguageName;
}

function name GetVoiceFromCountryAndGender(name CountryName, int iGender)
{
	local name nmLanguage;

	local X2BodyPartTemplateManager PartTemplateManager;
	local X2BodyPartTemplate SelectedVoiceTemplate;

	local array<X2BodyPartTemplate> AllVoiceTemplates;
	local array<X2BodyPartTemplate> FilteredVoiceTemplates;
	local X2BodyPartTemplate VoiceTemplate;
	local X2SimpleBodyPartFilter BodyPartFilter;

	local int iRoll;

	// Get a language spoken in the given country. If the player doesn't want foreign languages, will return a language that matches their loc settings
	nmLanguage = GetLanguageName(CountryName);

	// Get all the Voice body part templates
	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();
	PartTemplateManager.GetUberTemplates("Voice", AllVoiceTemplates);

	// Filter Voice templates, selecting ones that match the determined language and given gender.
	foreach AllVoiceTemplates(VoiceTemplate)
	{
		if (VoiceTemplate.Gender == iGender && VoiceTemplate.Language == nmLanguage && !VoiceTemplate.SpecializedType)
			FilteredVoiceTemplates.AddItem(VoiceTemplate);
	}

	// From the filtered list, select and return a random template.
	if (FilteredVoiceTemplates.Length > 0)
	{
		iRoll = `SYNC_RAND( FilteredVoiceTemplates.Length );
		SelectedVoiceTemplate = FilteredVoiceTemplates[iRoll];

		if (SelectedVoiceTemplate != none)
			return SelectedVoiceTemplate.DataName;
	}

	BodyPartFilter = `XCOMGAME.SharedBodyPartFilter;
	BodyPartFilter.Set(EGender(iGender), ECharacterRace(0), ''); //Just picking voice, only need gender
	VoiceTemplate = PartTemplateManager.GetRandomUberTemplate("Voice", BodyPartFilter, BodyPartFilter.FilterByGenderAndCharacterAndNonSpecialized);

	return VoiceTemplate.DataName;
}

function name GetVoiceFromCountryAndGenderAndCharacter(name CountryName, int Gender, name CharacterTemplateName)
{
	local X2BodyPartTemplateManager PartTemplateManager;
	local array<X2BodyPartTemplate> AllVoiceTemplates;
	local array<X2BodyPartTemplate> FilteredVoiceTemplates;
	local X2BodyPartTemplate VoiceTemplate;
	local name nmLanguage;

	// Get a language spoken in the given country. If the player doesn't want foreign languages, will return a language that matches their loc settings
	nmLanguage = GetLanguageName(CountryName);

	// Get all the Voice body part templates
	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();
	PartTemplateManager.GetUberTemplates("Voice", AllVoiceTemplates);

	// Filter Voice templates, selecting ones that match the determined language, gender, and character.
	foreach AllVoiceTemplates(VoiceTemplate)
	{
		if (VoiceTemplate.Gender == kSoldier.kAppearance.iGender && VoiceTemplate.Language == nmLanguage && VoiceTemplate.CharacterTemplate == CharacterTemplateName)
		{
			FilteredVoiceTemplates.AddItem(VoiceTemplate);
		}
	}

	// From the filtered list, select and return a random template.
	if (FilteredVoiceTemplates.Length > 0)
	{
		VoiceTemplate = FilteredVoiceTemplates[`SYNC_RAND( FilteredVoiceTemplates.Length )];
	}

	return VoiceTemplate.DataName;
}

function int ChooseHairColor( const out TAppearance kAppearance, int iNumBaseOptions )
{
	local int iHairChoice;
	local array<int> arrChoices;

	switch( kAppearance.iRace )
	{
	case eRace_African:
		arrChoices.AddItem(11);
		arrChoices.AddItem(12);
		arrChoices.AddItem(13);
		iHairChoice = arrChoices[Rand(arrChoices.Length)];
		break;
	case eRace_Hispanic:
		arrChoices.AddItem(11);
		arrChoices.AddItem(12);
		arrChoices.AddItem(13);
		iHairChoice = arrChoices[Rand(arrChoices.Length)];
		break;
	case eRace_Asian:
		arrChoices.AddItem(11);
		arrChoices.AddItem(12);
		arrChoices.AddItem(13);
		iHairChoice = arrChoices[Rand(arrChoices.Length)];
		break;
	default:
		iHairChoice = Rand(iNumBaseOptions);
		break;
	}

	if( iHairChoice >= iNumBaseOptions )
		iHairChoice = Rand(iNumBaseOptions);

	return iHairChoice;
}

function int ChooseFacialHair( const out TAppearance kAppearance, name nmOrigin, int iNumBaseOptions )
{
	local int iHairChoice;

	if( Rand(4) == 0 )
		iHairChoice = Rand(iNumBaseOptions);
	else
		iHairChoice = 0;

	return iHairChoice;
}

function bool IsSoldier(name CharacterTemplateName)
{
	return (CharacterTemplateName == 'Soldier' || CharacterTemplateName == '');
}

function Name GetLanguageName(name CountryName)
{
	return GetLanguageByCountry(CountryName);
}

function X2CharacterTemplate SetCharacterTemplate(name CharacterTemplateName, name ArmorName)
{
	local X2CharacterTemplate CharacterTemplate;

	if (IsSoldier(CharacterTemplateName))
	{
		MatchArmorTemplateForTorso = (ArmorName == '') ? 'KevlarArmor' : ArmorName;
		MatchCharacterTemplateForTorso = 'NoCharacterTemplateName'; //Force the selector to use the armor type to filter torsos
	}
	else
	{
		MatchArmorTemplateForTorso = 'NoArmor'; //Default armor match value
		MatchCharacterTemplateForTorso = CharacterTemplateName;
	}

	CharacterTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate(CharacterTemplateName);
	if (CharacterTemplate == None)
	{
		CharacterTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate('Soldier');
	}

	return CharacterTemplate;
}

function SetCountry(name nmCountry)
{
	kSoldier.nmCountry = nmCountry;
	kSoldier.kAppearance.nmFlag = kSoldier.nmCountry; // needs to be copied here for pawns -- jboswell
}

function SetRace(int iRace)
{
	if (iRace == -1)
		kSoldier.kAppearance.iRace = GetRandomRaceByCountry(kSoldier.nmCountry);
	else
		kSoldier.kAppearance.iRace = iRace;
}

function SetGender(EGender eForceGender)
{
	if (eForceGender != eGender_None)
		kSoldier.kAppearance.iGender = eForceGender;
	else
		kSoldier.kAppearance.iGender = (Rand(2) == 0) ? eGender_Female : eGender_Male;
}

function SetTorso(out X2SimpleBodyPartFilter BodyPartFilter, name CharacterTemplateName)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	//Set up for the torso selection
	BodyPartFilter.Set(EGender(kSoldier.kAppearance.iGender), ECharacterRace(kSoldier.kAppearance.iRace), '', , , DLCNames); //Don't have a torso yet
	BodyPartFilter.SetTorsoSelection(MatchCharacterTemplateForTorso, MatchArmorTemplateForTorso);
	kTorsoTemplate = RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmTorso, "Torso", BodyPartFilter.FilterTorso);

	//Set again now that we have picked our torso
	BodyPartFilter.Set(EGender(kSoldier.kAppearance.iGender), ECharacterRace(kSoldier.kAppearance.iRace), kSoldier.kAppearance.nmTorso, !IsSoldier(CharacterTemplateName), , DLCNames);
}

function SetArmsLegsAndDeco(X2SimpleBodyPartFilter BodyPartFilter)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLegs, "Legs", BodyPartFilter.FilterByTorsoAndArmorMatch);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmArms, "Arms", BodyPartFilter.FilterByTorsoAndArmorMatch, true);

	//If no arm selection, check the left / right arms
	if (kSoldier.kAppearance.nmArms == '')
	{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLeftArm, "LeftArm", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmRightArm, "RightArm", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLeftArmDeco, "LeftArmDeco", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmRightArmDeco, "RightArmDeco", BodyPartFilter.FilterByTorsoAndArmorMatch);
	}

	// Start Issue #384
	// XPack Hero Deco
	if (!m_CharTemplate.bForceAppearance)
	{
		// Start Issue #359
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLeftForearm, "LeftForearm", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmRightForearm, "RightForearm", BodyPartFilter.FilterByTorsoAndArmorMatch);
		// End Issue #359
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmThighs, "Thighs", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmShins, "Shins", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmTorsoDeco, "TorsoDeco", BodyPartFilter.FilterByTorsoAndArmorMatch);
	}
	// End Issue #384
}

function SetHead(X2SimpleBodyPartFilter BodyPartFilter, X2CharacterTemplate CharacterTemplate)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	if (IsSoldier(CharacterTemplate.DataName))
	{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHaircut, "Hair", BodyPartFilter.FilterByGenderAndNonSpecialized);
	}
	else
	{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHaircut, "Hair", BodyPartFilter.FilterByGenderAndNonSpecializedCivilian);
	}

	BodyPartFilter.AddCharacterFilter(CharacterTemplate.DataName, CharacterTemplate.bHasCharacterExclusiveAppearance); // Make sure heads get filtered properly
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHead, "Head", BodyPartFilter.FilterByGenderAndRaceAndCharacterAndClass);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmEye, "Eyes", BodyPartFilter.FilterAny);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmTeeth, "Teeth", BodyPartFilter.FilterAny);
}

function SetAccessories(X2SimpleBodyPartFilter BodyPartFilter, name CharacterTemplateName)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	//Custom settings depending on whether the unit is a soldier or not
	SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmPatterns, "Patterns", BodyPartFilter.FilterAny);
	SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmWeaponPattern, "Patterns", BodyPartFilter.FilterAny);
	SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmTattoo_LeftArm, "Tattoos", BodyPartFilter.FilterAny);
	SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmTattoo_RightArm, "Tattoos", BodyPartFilter.FilterAny);

	if (IsSoldier(CharacterTemplateName))
	{
		if (`SYNC_FRAND() < NewSoldier_BeardChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmBeard, "Beards", BodyPartFilter.FilterByGenderAndNonSpecialized);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmBeard, "Beards", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewSoldier_HatChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHelmet, "Helmets", BodyPartFilter.FilterByGenderAndNonSpecializedAndClassAndTechAndArmor);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmHelmet, "Helmets", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewSoldier_LowerFacePropChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmFacePropLower, "FacePropsLower", BodyPartFilter.FilterByGenderAndNonSpecialized);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmFacePropLower, "FacePropsLower", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewSoldier_UpperFacePropChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmFacePropUpper, "FacePropsUpper", BodyPartFilter.FilterByGenderAndNonSpecialized);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmFacePropUpper, "FacePropsUpper", BodyPartFilter.FilterAny);
		}
	}
	else
	{
		if (`SYNC_FRAND() < NewCivlian_BeardChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmBeard, "Beards", BodyPartFilter.FilterByGenderAndNonSpecialized);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmBeard, "Beards", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewCivlian_HatChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHelmet, "Helmets", BodyPartFilter.FilterByGenderAndNonSpecializedAndTechAndArmor);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmHelmet, "Helmets", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewCivlian_LowerFacePropChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmFacePropLower, "FacePropsLower", BodyPartFilter.FilterByGenderAndNonSpecializedCivilian);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmFacePropLower, "FacePropsLower", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewCivlian_UpperFacePropChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmFacePropUpper, "FacePropsUpper", BodyPartFilter.FilterByGenderAndNonSpecializedCivilian);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmFacePropUpper, "FacePropsUpper", BodyPartFilter.FilterAny);
		}
	}
}

function SetUnderlay(out X2SimpleBodyPartFilter BodyPartFilter)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	//All character templates that use the character generator get an underlay definition, reset the filter for underlay part selection
	BodyPartFilter.Set(EGender(kSoldier.kAppearance.iGender), ECharacterRace(kSoldier.kAppearance.iRace), '');
	BodyPartFilter.SetTorsoSelection(MatchCharacterTemplateForTorso, 'Underlay');
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmTorso_Underlay, "Torso", BodyPartFilter.FilterTorso);
	BodyPartFilter.Set(EGender(kSoldier.kAppearance.iGender), ECharacterRace(kSoldier.kAppearance.iRace), kSoldier.kAppearance.nmTorso_Underlay);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLegs_Underlay, "Legs", BodyPartFilter.FilterByTorsoAndArmorMatch);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmArms_Underlay, "Arms", BodyPartFilter.FilterByTorsoAndArmorMatch);
}

function SetArmorTints(X2CharacterTemplate CharacterTemplate)
{
	local TAppearance DefaultAppearance;
	local XComLinearColorPalette ArmorPalette;
	local int DefaultColors, SkipColors;

	//Randomly select colors for new soldiers
	ArmorPalette = `CONTENT.GetColorPalette(ePalette_ArmorTint);
	SkipColors = 10; //The last colors in the palette are rainbow colors, so skip those
	DefaultColors = 7; //Military default colors are 0-7

	//Use the default defined by the character template if set
	if (CharacterTemplate.DefaultAppearance.iArmorTint != DefaultAppearance.iArmorTint)
	{
		kSoldier.kAppearance.iArmorTint = CharacterTemplate.DefaultAppearance.iArmorTint;
	}
	else
	{
		if (NewSoldier_ForceColors)
		{
			kSoldier.kAppearance.iArmorTint = `SYNC_RAND(DefaultColors);
		}
		else
		{
			kSoldier.kAppearance.iArmorTint = `SYNC_RAND(ArmorPalette.Entries.length - SkipColors);
		}
	}

	//Use the default defined by the character template if set
	if (CharacterTemplate.DefaultAppearance.iArmorTintSecondary != DefaultAppearance.iArmorTintSecondary)
	{
		kSoldier.kAppearance.iArmorTintSecondary = CharacterTemplate.DefaultAppearance.iArmorTintSecondary;
	}
	else
	{
		if (NewSoldier_ForceColors)
		{
			kSoldier.kAppearance.iArmorTintSecondary = `SYNC_RAND(DefaultColors);
		}
		else
		{
			kSoldier.kAppearance.iArmorTintSecondary = `SYNC_RAND(ArmorPalette.Entries.length - SkipColors);
		}
	}

	// Begin issue #397
	kSoldier.kAppearance.iWeaponTint = iDefaultWeaponTint;
	// End issue #397
	kSoldier.kAppearance.iTattooTint = `SYNC_RAND(ArmorPalette.Entries.length - SkipColors);
}

private function bool SetCivilianVoice(const name CharTemplateName)
{
	local X2CharacterTemplate CharTemplate;

	if (CharTemplateName == 'Civilian')
	{
		CharTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate(CharTemplateName);
		if (CharTemplate != none)
		{
			kSoldier.kAppearance.nmVoice = CharTemplate.DefaultAppearance.nmVoice;
			return true;
		}
	}
	return false;
}

private function bool NeedsSoldierVoice(const name CharTemplateName)
{
	local X2CharacterTemplate CharTemplate;

	CharTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate(CharTemplateName);
	return CharTemplate != none && SoldierVoiceCharacterGroups.Find(CharTemplate.CharacterGroupName) != INDEX_NONE;
}

function SetVoice(name CharacterTemplateName, name CountryName)
{
	local X2CharacterTemplate CharTemplate;

	// Determine voice
	// If a civilian is being generated, set its voice according to its editor setting to prevent it from using soldier voices.
	// Otherwise, only set voices for soldiers and specific character groups that use soldier voices.
	if (!SetCivilianVoice(CharacterTemplateName) && (IsSoldier(CharacterTemplateName) || NeedsSoldierVoice(CharacterTemplateName)))
	{
		kSoldier.kAppearance.nmVoice = GetVoiceFromCountryAndGender(CountryName, kSoldier.kAppearance.iGender);

		if (kSoldier.kAppearance.nmVoice == '')
		{
			if (kSoldier.kAppearance.iGender == eGender_Male)
			{
				kSoldier.kAppearance.nmVoice = 'MaleVoice1_English_US';
			}
			else
			{
				kSoldier.kAppearance.nmVoice = 'FemaleVoice1_English_US';
			}
		}
	}
	else
	{
		CharTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate( CharacterTemplateName );
		if ((CharTemplate != none) && CharTemplate.DefaultAppearance.nmVoice != '')
		{
			kSoldier.kAppearance.nmVoice = CharTemplate.DefaultAppearance.nmVoice;
		}
	}
}

function SetAttitude()
{
	local array<X2StrategyElementTemplate> PersonalityTemplates;

	local XComOnlineProfileSettings ProfileSettings;
	local int BronzeScore, HighScore, Choice;

	// Give Random Personality
	PersonalityTemplates = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager().GetAllTemplatesOfClass(class'X2SoldierPersonalityTemplate');

	ProfileSettings = `XPROFILESETTINGS;
	BronzeScore = class'XComGameState_LadderProgress'.static.GetLadderMedalThreshold( 4, 0 );
	HighScore = ProfileSettings.Data.GetLadderHighScore( 4 );

	if (BronzeScore > HighScore)
	{
		do { // repick until we choose something not from TLE
			Choice = `SYNC_RAND(PersonalityTemplates.Length);
		} until (PersonalityTemplates[Choice].ClassThatCreatedUs.Name != 'X2StrategyElement_TLESoldierPersonalities');
	}
	else // anything will do
	{
		Choice = `SYNC_RAND(PersonalityTemplates.Length);
	}

	kSoldier.kAppearance.iAttitude = Choice;
}

function int GetRandomRaceByCountry(name CountryName)
{
	local X2StrategyElementTemplateManager StratMgr;
	local X2CountryTemplate CountryTemplate;
	local int iRoll;

	StratMgr = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager();
	CountryTemplate = X2CountryTemplate(StratMgr.FindStrategyElementTemplate(CountryName));

	if(CountryTemplate != none)
	{
		iRoll = `SYNC_RAND(CountryTemplate.Races.iCaucasian + CountryTemplate.Races.iAfrican +
			CountryTemplate.Races.iHispanic + CountryTemplate.Races.iAsian);

		if(CountryTemplate.Races.iCaucasian > 0 && iRoll < CountryTemplate.Races.iCaucasian)
			return eRace_Caucasian;
		else
			iRoll -= CountryTemplate.Races.iCaucasian;

		if(CountryTemplate.Races.iAfrican > 0 && iRoll < CountryTemplate.Races.iAfrican)
			return eRace_African;
		else
			iRoll -= CountryTemplate.Races.iAfrican;

		if(CountryTemplate.Races.iHispanic > 0 && iRoll < CountryTemplate.Races.iHispanic)
			return eRace_Hispanic;
		else
			iRoll -= CountryTemplate.Races.iHispanic;

		if(CountryTemplate.Races.iAsian > 0 && iRoll < CountryTemplate.Races.iAsian)
			return eRace_Asian;
		else
			iRoll -= CountryTemplate.Races.iAsian;
	}

	return eRace_Caucasian;
}

function string GenerateNickname(X2SoldierClassTemplate Template, int iGender)
{
	local int iNumChoices, iChoice;

	iNumChoices = Template.RandomNickNames.Length;

	if(iGender == eGender_Female)
	{
		iNumChoices += Template.RandomNickNames_Female.Length;
	}
	else if(iGender == eGender_Male)
	{
		iNumChoices += Template.RandomNickNames_Male.Length;
	}

	iChoice = `SYNC_RAND(iNumChoices);

	if(iChoice < Template.RandomNickNames.Length)
	{
		return Template.RandomNickNames[iChoice];
	}
	else
	{
		iChoice -= Template.RandomNickNames.Length;
	}

	if(iGender == eGender_Female)
	{
		return Template.RandomNickNames_Female[iChoice];
	}
	else if(iGender == eGender_Male)
	{
		return Template.RandomNickNames_Male[iChoice];
	}

	return "";
}

class XGCharacterGenerator extends Actor
	dependson(X2StrategyGameRulesetDataStructures) config(NameList);

// American
var localized array<string> m_arrAmMFirstNames;
var localized array<string> m_arrAmFFirstNames;
var localized array<string> m_arrAmLastNames;
// Arab
var localized array<string> m_arrAbMFirstNames;
var localized array<string> m_arrAbFFirstNames;
var localized array<string> m_arrAbLastNames;
// Israel
var localized array<string> m_arrIsMFirstNames;
var localized array<string> m_arrIsFFirstNames;
var localized array<string> m_arrIsLastNames;
// Russia
var localized array<string> m_arrRsMFirstNames;
var localized array<string> m_arrRsFFirstNames;
var localized array<string> m_arrRsMLastNames;
var localized array<string> m_arrRsFLastnames;
// England
var localized array<string> m_arrEnMFirstNames;
var localized array<string> m_arrEnFFirstNames;
var localized array<string> m_arrEnLastNames;
// French
var localized array<string> m_arrFrMFirstNames;
var localized array<string> m_arrFrFFirstNames;
var localized array<string> m_arrFrLastNames;
// African
var localized array<string> m_arrAfMFirstNames;
var localized array<string> m_arrAfFFirstNames;
var localized array<string> m_arrAfLastNames;
// Japanese
var localized array<string> m_arrJpMFirstNames;
var localized array<string> m_arrJpFFirstNames;
var localized array<string> m_arrJpLastNames;
// Chinese
var localized array<string> m_arrChMFirstNames;
var localized array<string> m_arrChFFirstNames;
var localized array<string> m_arrChLastNames;
// Indian
var localized array<string> m_arrInMFirstNames;
var localized array<string> m_arrInFFirstNames;
var localized array<string> m_arrInLastNames;
// German
var localized array<string> m_arrGmMFirstNames;
var localized array<string> m_arrGmFFirstNames;
var localized array<string> m_arrGmLastNames;
// Italian
var localized array<string> m_arrItMFirstNames;
var localized array<string> m_arrItFFirstNames;
var localized array<string> m_arrItLastNames;
// Mexican
var localized array<string> m_arrMxMFirstNames;
var localized array<string> m_arrMxFFirstNames;
var localized array<string> m_arrMxLastNames;
// Australian
var localized array<string> m_arrAuMFirstNames;
var localized array<string> m_arrAuFFirstNames;
var localized array<string> m_arrAuLastNames;
// Spanish
var localized array<string> m_arrEsMFirstNames;
var localized array<string> m_arrEsFFirstNames;
var localized array<string> m_arrEsLastNames;
// Greece
var localized array<string> m_arrGrMFirstNames;
var localized array<string> m_arrGrFFirstNames;
var localized array<string> m_arrGrLastNames;
// Norway
var localized array<string> m_arrNwMFirstNames;
var localized array<string> m_arrNwFFirstNames;
var localized array<string> m_arrNwLastNames;
// Ireland
var localized array<string> m_arrIrMFirstNames;
var localized array<string> m_arrIrFFirstNames;
var localized array<string> m_arrIrLastNames;
// South Korea
var localized array<string> m_arrSkMFirstNames;
var localized array<string> m_arrSkFFirstNames;
var localized array<string> m_arrSkLastNames;
// The Netherlands
var localized array<string> m_arrDuMFirstNames;
var localized array<string> m_arrDuFFirstNames;
var localized array<string> m_arrDuLastNames;
// Scotland
var localized array<string> m_arrScMFirstNames;
var localized array<string> m_arrScFFirstNames;
var localized array<string> m_arrScLastNames;
// Belgium
var localized array<string> m_arrBgMFirstNames;
var localized array<string> m_arrBgFFirstNames;
var localized array<string> m_arrBgLastNames;
// Poland
var localized array<string> m_arrPlMFirstNames;
var localized array<string> m_arrPlMLastnames;
var localized array<string> m_arrPlFFirstNames;
var localized array<string> m_arrPlFLastNames;

struct LocToSoldierLanguageElement
{
	var string LocString;
	var string GameLanguage;
};

var config array<LocToSoldierLanguageElement> LocToSoldierLanguage; //Allows the game to map the Loc string to an in-game language when picking soldiers

var config float NewSoldier_HatChance;
var config float NewSoldier_UpperFacePropChance;
var config float NewSoldier_LowerFacePropChance;
var config float NewSoldier_BeardChance;
var config bool  NewSoldier_ForceColors; //Forces units to use color selections 0-6 when being spawned

var config float NewCivlian_HatChance;
var config float NewCivlian_UpperFacePropChance;
var config float NewCivlian_LowerFacePropChance;
var config float NewCivlian_BeardChance;

var config float DLCPartPackDefaultChance;

// Temporary variable for uniquely randomized hair so artists
// can see potentially all hair types in a squad.
var int m_iHairType;

// temporary variable for soldier creation, used by TemplateMgr filter functions
var protected TSoldier kSoldier;
var protected X2BodyPartTemplate kTorsoTemplate;
var protected name MatchCharacterTemplateForTorso;
var protected name MatchArmorTemplateForTorso;
var protected array<name> DLCNames; //List of DLC packs to pull parts from for the currently generating soldier.

// Store a country name to be use in bios for soldiers that force a unique country
var name BioCountryName;

// Character groups that use soldier voices
var const config array<name> SoldierVoiceCharacterGroups;
// Variable for Issue #384
var X2CharacterTemplate m_CharTemplate;

// New variable for issue #397
var config(Content) int iDefaultWeaponTint;

function GenerateName( int iGender, name CountryName, out string strFirst, out string strLast, optional int iRace = -1 )
{
	local X2StrategyElementTemplateManager StratMgr;
	local X2CountryTemplate CountryTemplate;
	local array<CountryNames> AllNames, GeneralNames, RaceSpecificNames;
	local int idx, iRoll;

	strFirst = "";
	strLast = "";
	StratMgr = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager();
	CountryTemplate = X2CountryTemplate(StratMgr.FindStrategyElementTemplate(CountryName));

	for(idx = 0; idx < CountryTemplate.Names.Length; idx++)
	{
		// Add to list of all names
		AllNames.AddItem(CountryTemplate.Names[idx]);

		if(!CountryTemplate.Names[idx].bRaceSpecific)
		{
			// Add to general list of names
			GeneralNames.AddItem(CountryTemplate.Names[idx]);
		}
		else if(CountryTemplate.Names[idx].bRaceSpecific && CountryTemplate.Names[idx].Race == iRace)
		{
			// Add to list of applicable race specific names
			RaceSpecificNames.AddItem(CountryTemplate.Names[idx]);
		}
	}

	// Roll for race specific name
	iRoll = `SYNC_RAND(100);
	while(RaceSpecificNames.Length > 0 && (strFirst == "" || strLast == ""))
	{
		if(RaceSpecificNames.Length == 1 || iRoll < RaceSpecificNames[0].PercentChance)
		{
			if(iGender == eGender_Female)
			{
				strFirst = RaceSpecificNames[0].FemaleNames[`SYNC_RAND(RaceSpecificNames[0].FemaleNames.Length)];
				strLast = RaceSpecificNames[0].FemaleLastNames[`SYNC_RAND(RaceSpecificNames[0].FemaleLastNames.Length)];
			}
			else
			{
				strFirst = RaceSpecificNames[0].MaleNames[`SYNC_RAND(RaceSpecificNames[0].MaleNames.Length)];
				strLast = RaceSpecificNames[0].MaleLastNames[`SYNC_RAND(RaceSpecificNames[0].MaleLastNames.Length)];
			}
		}
		else
		{
			iRoll -= RaceSpecificNames[0].PercentChance;
			RaceSpecificNames.Remove(0, 1);
		}
	}

	// Roll for general name
	iRoll = `SYNC_RAND(100);
	while(GeneralNames.Length > 0 && (strFirst == "" || strLast == ""))
	{
		if(GeneralNames.Length == 1 || iRoll < GeneralNames[0].PercentChance)
		{
			if(iGender == eGender_Female)
			{
				strFirst = GeneralNames[0].FemaleNames[`SYNC_RAND(GeneralNames[0].FemaleNames.Length)];
				strLast = GeneralNames[0].FemaleLastNames[`SYNC_RAND(GeneralNames[0].FemaleLastNames.Length)];
			}
			else
			{
				strFirst = GeneralNames[0].MaleNames[`SYNC_RAND(GeneralNames[0].MaleNames.Length)];
				strLast = GeneralNames[0].MaleLastNames[`SYNC_RAND(GeneralNames[0].MaleLastNames.Length)];
			}
		}
		else
		{
			iRoll -= GeneralNames[0].PercentChance;
			GeneralNames.Remove(0, 1);
		}
	}

	// Roll on all names (could happen if only race specific names were configured for the country, and input race doesn't match)
	iRoll = `SYNC_RAND(100);
	while(AllNames.Length > 0 && (strFirst == "" || strLast == ""))
	{
		if(AllNames.Length == 1 || iRoll < AllNames[0].PercentChance)
		{
			if(iGender == eGender_Female)
			{
				strFirst = AllNames[0].FemaleNames[`SYNC_RAND(AllNames[0].FemaleNames.Length)];
				strLast = AllNames[0].FemaleLastNames[`SYNC_RAND(AllNames[0].FemaleLastNames.Length)];
			}
			else
			{
				strFirst = AllNames[0].MaleNames[`SYNC_RAND(AllNames[0].MaleNames.Length)];
				strLast = AllNames[0].MaleLastNames[`SYNC_RAND(AllNames[0].MaleLastNames.Length)];
			}
		}
		else
		{
			iRoll -= AllNames[0].PercentChance;
			AllNames.Remove(0, 1);
		}
	}

	// Fall through case
	if(strFirst == "" || strLast == "")
	{
		if(iGender == eGender_Female)
		{
			strFirst = m_arrAmFFirstNames[`SYNC_RAND(m_arrAmFFirstNames.Length)];
		}
		else
		{
			strFirst = m_arrAmMFirstNames[`SYNC_RAND(m_arrAmMFirstNames.Length)];
		}

		strLast = m_arrAmLastNames[`SYNC_RAND(m_arrAmLastNames.Length)];
	}
}

function name PickOriginCountry()
{
	local X2StrategyElementTemplateManager StratMgr;
	local array<X2StrategyElementTemplate> CountryTemplates;
	local array<name> arrCountries;
	local int idx, i;

	StratMgr = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager();
	CountryTemplates = StratMgr.GetAllTemplatesOfClass(class'X2CountryTemplate');

	for(idx = 0; idx < CountryTemplates.Length; idx++)
	{
		for(i = 0; i < X2CountryTemplate(CountryTemplates[idx]).UnitWeight; i++)
		{
			arrCountries.AddItem(CountryTemplates[idx].DataName);
		}
	}

	if(arrCountries.Length > 0)
	{
		return arrCountries[`SYNC_RAND(arrCountries.Length)];
	}

	return 'Country_USA';
}

function name DefaultGetRandomUberTemplate_WarnAboutFilter(string PartType)
{
	`log("WARNING:"@ `ShowVar(self) $": Unable to find a GetRandomUberTemplate for '"$PartType$"' given Torso '"$kSoldier.kAppearance.nmTorso$"' Armor '"$MatchArmorTemplateForTorso$"' Character '"$MatchCharacterTemplateForTorso$"'", ,'XCom_Templates');
	return '';
}

//In some cases we already have unit that we want to just want to randomize the appearance of. This method is used for that purpose. UsedGameState is the game state
//that contains Unit
function TSoldier CreateTSoldierFromUnit( XComGameState_Unit Unit, XComGameState UseGameState )
{
	local XComGameState_Item ArmorItem;
	local name ArmorName;

	ArmorItem = Unit.GetItemInSlot(eInvSlot_Armor, UseGameState, true);
	ArmorName = ArmorItem == none ? '' : ArmorItem.GetMyTemplateName();
	return CreateTSoldier( Unit.GetMyTemplateName(), EGender(Unit.kAppearance.iGender), Unit.kAppearance.nmFlag, Unit.kAppearance.iRace, ArmorName );
}

delegate bool FilterCallback(X2BodyPartTemplate Template);
function X2BodyPartTemplate SetBodyPartToFirstInArray(X2BodyPartTemplateManager PartTemplateManager, out name DataName, string PartTypeName, delegate<FilterCallback> CallbackFn)
{
	local array<X2BodyPartTemplate> listPartTemplate;
	PartTemplateManager.GetFilteredUberTemplates(PartTypeName, `XCOMGAME.SharedBodyPartFilter, CallbackFn, listPartTemplate);
	DataName = (listPartTemplate.length > 0) ? listPartTemplate[0].DataName : DefaultGetRandomUberTemplate_WarnAboutFilter(PartTypeName);
	return (listPartTemplate.length > 0) ? listPartTemplate[0] : none;
}
function X2BodyPartTemplate RandomizeSetBodyPart(X2BodyPartTemplateManager PartTemplateManager, out name DataName, string PartTypeName, delegate<FilterCallback> CallbackFn, optional bool bCanBeEmpty = false)
{
	local X2BodyPartTemplate RandomPart;

	//Clear the template name if it is allowed to be empty
	if(bCanBeEmpty)
	{
		DataName = '';
	}

	RandomPart = PartTemplateManager.GetRandomUberTemplate(PartTypeName, `XCOMGAME.SharedBodyPartFilter, CallbackFn);
	if(!bCanBeEmpty || (RandomPart != none))
	{
		DataName = (RandomPart != none) ? RandomPart.DataName : DefaultGetRandomUberTemplate_WarnAboutFilter(PartTypeName);
	}
	return RandomPart;
}

function UpdateDLCPackFilters()
{
	// Issue #155 follow-up Start
	DLCNames = class'CHHelpers'.static.GetAcceptablePartPacks();
	// Issue #155 End
}

function TSoldier CreateTSoldier( optional name CharacterTemplateName, optional EGender eForceGender, optional name nmCountry = '', optional int iRace = -1, optional name ArmorName )
{
	local XComLinearColorPalette HairPalette;
	local X2SimpleBodyPartFilter BodyPartFilter;
	local X2CharacterTemplate CharacterTemplate;
	local TAppearance DefaultAppearance;

	kSoldier.kAppearance = DefaultAppearance;

	CharacterTemplate = SetCharacterTemplate(CharacterTemplateName, ArmorName);
	// Single Line for Issue #384
	m_CharTemplate = CharacterTemplate;

	if (nmCountry == '')
		nmCountry = PickOriginCountry();

	SetCountry(nmCountry);
	SetRace(iRace);
	SetGender(eForceGender);
	kSoldier.iRank = 0;

	BodyPartFilter = `XCOMGAME.SharedBodyPartFilter;

	//When generating new characters, consider the DLC pack filters.
	//Use the player's settings from Options->Game Options to pick which DLC / Mod packs this generated soldier should draw from
	UpdateDLCPackFilters();

	SetTorso(BodyPartFilter, CharacterTemplateName);
	SetArmsLegsAndDeco(BodyPartFilter);
	SetHead(BodyPartFilter, CharacterTemplate);
	SetAccessories(BodyPartFilter, CharacterTemplateName);
	SetUnderlay(BodyPartFilter);
	SetArmorTints(CharacterTemplate);

	HairPalette = `CONTENT.GetColorPalette(ePalette_HairColor);
	kSoldier.kAppearance.iHairColor = ChooseHairColor(kSoldier.kAppearance, HairPalette.BaseOptions); // Only generate with base options
	kSoldier.kAppearance.iEyeColor = 0; //right now initialized to the first one, todo when we decides which are the base colors for the different races.

	// SHIP HACK: There are at least 5 base options for every palette. I am not proud. -- jboswell
	kSoldier.kAppearance.iSkinColor = Rand(5);

	SetVoice(CharacterTemplateName, nmCountry);
	SetAttitude();
	GenerateName( kSoldier.kAppearance.iGender, kSoldier.nmCountry, kSoldier.strFirstName, kSoldier.strLastName, kSoldier.kAppearance.iRace );

	BioCountryName = kSoldier.nmCountry;

	return kSoldier;
}

static function Name GetLanguageByString(optional string strLanguage="")
{
	if (len(strLanguage) == 0)
		strLanguage = GetLanguage();

	switch (strLanguage)
	{
	case "DEU":
		return 'german';
	case "ESN":
		return 'spanish';
	case "FRA":
		return 'french';
	case "ITA":
		return 'italian';
	}

	return 'english';
}

function Name GetLanguageByCountry(name CountryName)
{
	local X2StrategyElementTemplateManager StratMgr;
	local X2CountryTemplate CountryTemplate;
	local name LanguageName;
	local string LanguageString;
	local string LocString;
	local int Index;
	local bool bWantsForeignLanguages;
	local XComOnlineProfileSettings ProfileSettings;

	StratMgr = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager();
	CountryTemplate = X2CountryTemplate(StratMgr.FindStrategyElementTemplate(CountryName));
	if(CountryTemplate != none)
	{
		LanguageName = CountryTemplate.Language;
	}

	ProfileSettings = `XPROFILESETTINGS;
	bWantsForeignLanguages = ProfileSettings != none && ProfileSettings.Data != none && ProfileSettings.Data.m_bForeignLanguages;
	//Try to get a language from the LOC settings if the player has an option set to do so or something is wrong with the country settings
	if(!bWantsForeignLanguages || LanguageName == '')
	{
		LocString = GetLanguage(); //Get the three letter code for our language
		for(Index = 0; Index < LocToSoldierLanguage.Length; ++Index)
		{
			if(LocString == LocToSoldierLanguage[Index].LocString)
			{
				LanguageString = string(LanguageName);

				//Do str in str check so that we can match language accents, ie english matching englishUK
				if(InStr(LanguageString, LocToSoldierLanguage[Index].GameLanguage) == -1)
				{
					//The country language did not match our loc settings, change it
					LanguageName = name(LocToSoldierLanguage[Index].GameLanguage);
				}
			}
		}
	}

	//If we STILL don't have a language, pick english
	if(LanguageName == '')
	{
		LanguageName = 'english';
	}

	return LanguageName;
}

function name GetVoiceFromCountryAndGender(name CountryName, int iGender)
{
	local name nmLanguage;

	local X2BodyPartTemplateManager PartTemplateManager;
	local X2BodyPartTemplate SelectedVoiceTemplate;

	local array<X2BodyPartTemplate> AllVoiceTemplates;
	local array<X2BodyPartTemplate> FilteredVoiceTemplates;
	local X2BodyPartTemplate VoiceTemplate;
	local X2SimpleBodyPartFilter BodyPartFilter;

	local int iRoll;

	// Get a language spoken in the given country. If the player doesn't want foreign languages, will return a language that matches their loc settings
	nmLanguage = GetLanguageName(CountryName);

	// Get all the Voice body part templates
	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();
	PartTemplateManager.GetUberTemplates("Voice", AllVoiceTemplates);

	// Filter Voice templates, selecting ones that match the determined language and given gender.
	foreach AllVoiceTemplates(VoiceTemplate)
	{
		if (VoiceTemplate.Gender == iGender && VoiceTemplate.Language == nmLanguage && !VoiceTemplate.SpecializedType)
			FilteredVoiceTemplates.AddItem(VoiceTemplate);
	}

	// From the filtered list, select and return a random template.
	if (FilteredVoiceTemplates.Length > 0)
	{
		iRoll = `SYNC_RAND( FilteredVoiceTemplates.Length );
		SelectedVoiceTemplate = FilteredVoiceTemplates[iRoll];

		if (SelectedVoiceTemplate != none)
			return SelectedVoiceTemplate.DataName;
	}

	BodyPartFilter = `XCOMGAME.SharedBodyPartFilter;
	BodyPartFilter.Set(EGender(iGender), ECharacterRace(0), ''); //Just picking voice, only need gender
	VoiceTemplate = PartTemplateManager.GetRandomUberTemplate("Voice", BodyPartFilter, BodyPartFilter.FilterByGenderAndCharacterAndNonSpecialized);

	return VoiceTemplate.DataName;
}

function name GetVoiceFromCountryAndGenderAndCharacter(name CountryName, int Gender, name CharacterTemplateName)
{
	local X2BodyPartTemplateManager PartTemplateManager;
	local array<X2BodyPartTemplate> AllVoiceTemplates;
	local array<X2BodyPartTemplate> FilteredVoiceTemplates;
	local X2BodyPartTemplate VoiceTemplate;
	local name nmLanguage;

	// Get a language spoken in the given country. If the player doesn't want foreign languages, will return a language that matches their loc settings
	nmLanguage = GetLanguageName(CountryName);

	// Get all the Voice body part templates
	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();
	PartTemplateManager.GetUberTemplates("Voice", AllVoiceTemplates);

	// Filter Voice templates, selecting ones that match the determined language, gender, and character.
	foreach AllVoiceTemplates(VoiceTemplate)
	{
		if (VoiceTemplate.Gender == kSoldier.kAppearance.iGender && VoiceTemplate.Language == nmLanguage && VoiceTemplate.CharacterTemplate == CharacterTemplateName)
		{
			FilteredVoiceTemplates.AddItem(VoiceTemplate);
		}
	}

	// From the filtered list, select and return a random template.
	if (FilteredVoiceTemplates.Length > 0)
	{
		VoiceTemplate = FilteredVoiceTemplates[`SYNC_RAND( FilteredVoiceTemplates.Length )];
	}

	return VoiceTemplate.DataName;
}

function int ChooseHairColor( const out TAppearance kAppearance, int iNumBaseOptions )
{
	local int iHairChoice;
	local array<int> arrChoices;

	switch( kAppearance.iRace )
	{
	case eRace_African:
		arrChoices.AddItem(11);
		arrChoices.AddItem(12);
		arrChoices.AddItem(13);
		iHairChoice = arrChoices[Rand(arrChoices.Length)];
		break;
	case eRace_Hispanic:
		arrChoices.AddItem(11);
		arrChoices.AddItem(12);
		arrChoices.AddItem(13);
		iHairChoice = arrChoices[Rand(arrChoices.Length)];
		break;
	case eRace_Asian:
		arrChoices.AddItem(11);
		arrChoices.AddItem(12);
		arrChoices.AddItem(13);
		iHairChoice = arrChoices[Rand(arrChoices.Length)];
		break;
	default:
		iHairChoice = Rand(iNumBaseOptions);
		break;
	}

	if( iHairChoice >= iNumBaseOptions )
		iHairChoice = Rand(iNumBaseOptions);

	return iHairChoice;
}

function int ChooseFacialHair( const out TAppearance kAppearance, name nmOrigin, int iNumBaseOptions )
{
	local int iHairChoice;

	if( Rand(4) == 0 )
		iHairChoice = Rand(iNumBaseOptions);
	else
		iHairChoice = 0;

	return iHairChoice;
}

function bool IsSoldier(name CharacterTemplateName)
{
	return (CharacterTemplateName == 'Soldier' || CharacterTemplateName == '');
}

function Name GetLanguageName(name CountryName)
{
	return GetLanguageByCountry(CountryName);
}

function X2CharacterTemplate SetCharacterTemplate(name CharacterTemplateName, name ArmorName)
{
	local X2CharacterTemplate CharacterTemplate;

	if (IsSoldier(CharacterTemplateName))
	{
		MatchArmorTemplateForTorso = (ArmorName == '') ? 'KevlarArmor' : ArmorName;
		MatchCharacterTemplateForTorso = 'NoCharacterTemplateName'; //Force the selector to use the armor type to filter torsos
	}
	else
	{
		MatchArmorTemplateForTorso = 'NoArmor'; //Default armor match value
		MatchCharacterTemplateForTorso = CharacterTemplateName;
	}

	CharacterTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate(CharacterTemplateName);
	if (CharacterTemplate == None)
	{
		CharacterTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate('Soldier');
	}

	return CharacterTemplate;
}

function SetCountry(name nmCountry)
{
	kSoldier.nmCountry = nmCountry;
	kSoldier.kAppearance.nmFlag = kSoldier.nmCountry; // needs to be copied here for pawns -- jboswell
}

function SetRace(int iRace)
{
	if (iRace == -1)
		kSoldier.kAppearance.iRace = GetRandomRaceByCountry(kSoldier.nmCountry);
	else
		kSoldier.kAppearance.iRace = iRace;
}

function SetGender(EGender eForceGender)
{
	if (eForceGender != eGender_None)
		kSoldier.kAppearance.iGender = eForceGender;
	else
		kSoldier.kAppearance.iGender = (Rand(2) == 0) ? eGender_Female : eGender_Male;
}

function SetTorso(out X2SimpleBodyPartFilter BodyPartFilter, name CharacterTemplateName)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	//Set up for the torso selection
	BodyPartFilter.Set(EGender(kSoldier.kAppearance.iGender), ECharacterRace(kSoldier.kAppearance.iRace), '', , , DLCNames); //Don't have a torso yet
	BodyPartFilter.SetTorsoSelection(MatchCharacterTemplateForTorso, MatchArmorTemplateForTorso);
	kTorsoTemplate = RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmTorso, "Torso", BodyPartFilter.FilterTorso);

	//Set again now that we have picked our torso
	BodyPartFilter.Set(EGender(kSoldier.kAppearance.iGender), ECharacterRace(kSoldier.kAppearance.iRace), kSoldier.kAppearance.nmTorso, !IsSoldier(CharacterTemplateName), , DLCNames);
}

function SetArmsLegsAndDeco(X2SimpleBodyPartFilter BodyPartFilter)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLegs, "Legs", BodyPartFilter.FilterByTorsoAndArmorMatch);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmArms, "Arms", BodyPartFilter.FilterByTorsoAndArmorMatch, true);

	//If no arm selection, check the left / right arms
	if (kSoldier.kAppearance.nmArms == '')
	{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLeftArm, "LeftArm", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmRightArm, "RightArm", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLeftArmDeco, "LeftArmDeco", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmRightArmDeco, "RightArmDeco", BodyPartFilter.FilterByTorsoAndArmorMatch);
	}

	// Start Issue #384
	// XPack Hero Deco
	if (!m_CharTemplate.bForceAppearance)
	{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLeftForearm, "LeftForearm", BodyPartFilter.FilterByTorsoAndArmorMatch);
			RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmRightForearm, "RightForearm", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmThighs, "Thighs", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmShins, "Shins", BodyPartFilter.FilterByTorsoAndArmorMatch);
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmTorsoDeco, "TorsoDeco", BodyPartFilter.FilterByTorsoAndArmorMatch);
	}
	// End Issue #384
}

function SetHead(X2SimpleBodyPartFilter BodyPartFilter, X2CharacterTemplate CharacterTemplate)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	if (IsSoldier(CharacterTemplate.DataName))
	{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHaircut, "Hair", BodyPartFilter.FilterByGenderAndNonSpecialized);
	}
	else
	{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHaircut, "Hair", BodyPartFilter.FilterByGenderAndNonSpecializedCivilian);
	}

	BodyPartFilter.AddCharacterFilter(CharacterTemplate.DataName, CharacterTemplate.bHasCharacterExclusiveAppearance); // Make sure heads get filtered properly
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHead, "Head", BodyPartFilter.FilterByGenderAndRaceAndCharacterAndClass);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmEye, "Eyes", BodyPartFilter.FilterAny);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmTeeth, "Teeth", BodyPartFilter.FilterAny);
}

function SetAccessories(X2SimpleBodyPartFilter BodyPartFilter, name CharacterTemplateName)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	//Custom settings depending on whether the unit is a soldier or not
	SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmPatterns, "Patterns", BodyPartFilter.FilterAny);
	SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmWeaponPattern, "Patterns", BodyPartFilter.FilterAny);
	SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmTattoo_LeftArm, "Tattoos", BodyPartFilter.FilterAny);
	SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmTattoo_RightArm, "Tattoos", BodyPartFilter.FilterAny);

	if (IsSoldier(CharacterTemplateName))
	{
		if (`SYNC_FRAND() < NewSoldier_BeardChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmBeard, "Beards", BodyPartFilter.FilterByGenderAndNonSpecialized);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmBeard, "Beards", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewSoldier_HatChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHelmet, "Helmets", BodyPartFilter.FilterByGenderAndNonSpecializedAndClassAndTechAndArmor);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmHelmet, "Helmets", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewSoldier_LowerFacePropChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmFacePropLower, "FacePropsLower", BodyPartFilter.FilterByGenderAndNonSpecialized);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmFacePropLower, "FacePropsLower", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewSoldier_UpperFacePropChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmFacePropUpper, "FacePropsUpper", BodyPartFilter.FilterByGenderAndNonSpecialized);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmFacePropUpper, "FacePropsUpper", BodyPartFilter.FilterAny);
		}
	}
	else
	{
		if (`SYNC_FRAND() < NewCivlian_BeardChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmBeard, "Beards", BodyPartFilter.FilterByGenderAndNonSpecialized);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmBeard, "Beards", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewCivlian_HatChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmHelmet, "Helmets", BodyPartFilter.FilterByGenderAndNonSpecializedAndTechAndArmor);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmHelmet, "Helmets", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewCivlian_LowerFacePropChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmFacePropLower, "FacePropsLower", BodyPartFilter.FilterByGenderAndNonSpecializedCivilian);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmFacePropLower, "FacePropsLower", BodyPartFilter.FilterAny);
		}

		if (`SYNC_FRAND() < NewCivlian_UpperFacePropChance )
		{
		RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmFacePropUpper, "FacePropsUpper", BodyPartFilter.FilterByGenderAndNonSpecializedCivilian);
		}
		else
		{
			SetBodyPartToFirstInArray(PartTemplateManager, kSoldier.kAppearance.nmFacePropUpper, "FacePropsUpper", BodyPartFilter.FilterAny);
		}
	}
}

function SetUnderlay(out X2SimpleBodyPartFilter BodyPartFilter)
{
	local X2BodyPartTemplateManager PartTemplateManager;

	PartTemplateManager = class'X2BodyPartTemplateManager'.static.GetBodyPartTemplateManager();

	//All character templates that use the character generator get an underlay definition, reset the filter for underlay part selection
	BodyPartFilter.Set(EGender(kSoldier.kAppearance.iGender), ECharacterRace(kSoldier.kAppearance.iRace), '');
	BodyPartFilter.SetTorsoSelection(MatchCharacterTemplateForTorso, 'Underlay');
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmTorso_Underlay, "Torso", BodyPartFilter.FilterTorso);
	BodyPartFilter.Set(EGender(kSoldier.kAppearance.iGender), ECharacterRace(kSoldier.kAppearance.iRace), kSoldier.kAppearance.nmTorso_Underlay);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmLegs_Underlay, "Legs", BodyPartFilter.FilterByTorsoAndArmorMatch);
	RandomizeSetBodyPart(PartTemplateManager, kSoldier.kAppearance.nmArms_Underlay, "Arms", BodyPartFilter.FilterByTorsoAndArmorMatch);
}

function SetArmorTints(X2CharacterTemplate CharacterTemplate)
{
	local TAppearance DefaultAppearance;
	local XComLinearColorPalette ArmorPalette;
	local int DefaultColors, SkipColors;

	//Randomly select colors for new soldiers
	ArmorPalette = `CONTENT.GetColorPalette(ePalette_ArmorTint);
	SkipColors = 10; //The last colors in the palette are rainbow colors, so skip those
	DefaultColors = 7; //Military default colors are 0-7

	//Use the default defined by the character template if set
	if (CharacterTemplate.DefaultAppearance.iArmorTint != DefaultAppearance.iArmorTint)
	{
		kSoldier.kAppearance.iArmorTint = CharacterTemplate.DefaultAppearance.iArmorTint;
	}
	else
	{
		if (NewSoldier_ForceColors)
		{
			kSoldier.kAppearance.iArmorTint = `SYNC_RAND(DefaultColors);
		}
		else
		{
			kSoldier.kAppearance.iArmorTint = `SYNC_RAND(ArmorPalette.Entries.length - SkipColors);
		}
	}

	//Use the default defined by the character template if set
	if (CharacterTemplate.DefaultAppearance.iArmorTintSecondary != DefaultAppearance.iArmorTintSecondary)
	{
		kSoldier.kAppearance.iArmorTintSecondary = CharacterTemplate.DefaultAppearance.iArmorTintSecondary;
	}
	else
	{
		if (NewSoldier_ForceColors)
		{
			kSoldier.kAppearance.iArmorTintSecondary = `SYNC_RAND(DefaultColors);
		}
		else
		{
			kSoldier.kAppearance.iArmorTintSecondary = `SYNC_RAND(ArmorPalette.Entries.length - SkipColors);
		}
	}

	// Begin issue #397
	kSoldier.kAppearance.iWeaponTint = iDefaultWeaponTint;
	// End issue #397
	kSoldier.kAppearance.iTattooTint = `SYNC_RAND(ArmorPalette.Entries.length - SkipColors);
}

private function bool SetCivilianVoice(const name CharTemplateName)
{
	local X2CharacterTemplate CharTemplate;

	if (CharTemplateName == 'Civilian')
	{
		CharTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate(CharTemplateName);
		if (CharTemplate != none)
		{
			kSoldier.kAppearance.nmVoice = CharTemplate.DefaultAppearance.nmVoice;
			return true;
		}
	}
	return false;
}

private function bool NeedsSoldierVoice(const name CharTemplateName)
{
	local X2CharacterTemplate CharTemplate;

	CharTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate(CharTemplateName);
	return CharTemplate != none && SoldierVoiceCharacterGroups.Find(CharTemplate.CharacterGroupName) != INDEX_NONE;
}

function SetVoice(name CharacterTemplateName, name CountryName)
{
	local X2CharacterTemplate CharTemplate;

	// Determine voice
	// If a civilian is being generated, set its voice according to its editor setting to prevent it from using soldier voices.
	// Otherwise, only set voices for soldiers and specific character groups that use soldier voices.
	if (!SetCivilianVoice(CharacterTemplateName) && (IsSoldier(CharacterTemplateName) || NeedsSoldierVoice(CharacterTemplateName)))
	{
		kSoldier.kAppearance.nmVoice = GetVoiceFromCountryAndGender(CountryName, kSoldier.kAppearance.iGender);

		if (kSoldier.kAppearance.nmVoice == '')
		{
			if (kSoldier.kAppearance.iGender == eGender_Male)
			{
				kSoldier.kAppearance.nmVoice = 'MaleVoice1_English_US';
			}
			else
			{
				kSoldier.kAppearance.nmVoice = 'FemaleVoice1_English_US';
			}
		}
	}
	else
	{
		CharTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate( CharacterTemplateName );
		if ((CharTemplate != none) && CharTemplate.DefaultAppearance.nmVoice != '')
		{
			kSoldier.kAppearance.nmVoice = CharTemplate.DefaultAppearance.nmVoice;
		}
	}
}

function SetAttitude()
{
	local array<X2StrategyElementTemplate> PersonalityTemplates;

	local XComOnlineProfileSettings ProfileSettings;
	local int BronzeScore, HighScore, Choice;

	// Give Random Personality
	PersonalityTemplates = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager().GetAllTemplatesOfClass(class'X2SoldierPersonalityTemplate');

	ProfileSettings = `XPROFILESETTINGS;
	BronzeScore = class'XComGameState_LadderProgress'.static.GetLadderMedalThreshold( 4, 0 );
	HighScore = ProfileSettings.Data.GetLadderHighScore( 4 );

	if (BronzeScore > HighScore)
	{
		do { // repick until we choose something not from TLE
			Choice = `SYNC_RAND(PersonalityTemplates.Length);
		} until (PersonalityTemplates[Choice].ClassThatCreatedUs.Name != 'X2StrategyElement_TLESoldierPersonalities');
	}
	else // anything will do
	{
		Choice = `SYNC_RAND(PersonalityTemplates.Length);
	}

	kSoldier.kAppearance.iAttitude = Choice;
}

function int GetRandomRaceByCountry(name CountryName)
{
	local X2StrategyElementTemplateManager StratMgr;
	local X2CountryTemplate CountryTemplate;
	local int iRoll;

	StratMgr = class'X2StrategyElementTemplateManager'.static.GetStrategyElementTemplateManager();
	CountryTemplate = X2CountryTemplate(StratMgr.FindStrategyElementTemplate(CountryName));

	if(CountryTemplate != none)
	{
		iRoll = `SYNC_RAND(CountryTemplate.Races.iCaucasian + CountryTemplate.Races.iAfrican +
			CountryTemplate.Races.iHispanic + CountryTemplate.Races.iAsian);

		if(CountryTemplate.Races.iCaucasian > 0 && iRoll < CountryTemplate.Races.iCaucasian)
			return eRace_Caucasian;
		else
			iRoll -= CountryTemplate.Races.iCaucasian;

		if(CountryTemplate.Races.iAfrican > 0 && iRoll < CountryTemplate.Races.iAfrican)
			return eRace_African;
		else
			iRoll -= CountryTemplate.Races.iAfrican;

		if(CountryTemplate.Races.iHispanic > 0 && iRoll < CountryTemplate.Races.iHispanic)
			return eRace_Hispanic;
		else
			iRoll -= CountryTemplate.Races.iHispanic;

		if(CountryTemplate.Races.iAsian > 0 && iRoll < CountryTemplate.Races.iAsian)
			return eRace_Asian;
		else
			iRoll -= CountryTemplate.Races.iAsian;
	}

	return eRace_Caucasian;
}

function string GenerateNickname(X2SoldierClassTemplate Template, int iGender)
{
	local int iNumChoices, iChoice;

	iNumChoices = Template.RandomNickNames.Length;

	if(iGender == eGender_Female)
	{
		iNumChoices += Template.RandomNickNames_Female.Length;
	}
	else if(iGender == eGender_Male)
	{
		iNumChoices += Template.RandomNickNames_Male.Length;
	}

	iChoice = `SYNC_RAND(iNumChoices);

	if(iChoice < Template.RandomNickNames.Length)
	{
		return Template.RandomNickNames[iChoice];
	}
	else
	{
		iChoice -= Template.RandomNickNames.Length;
	}

	if(iGender == eGender_Female)
	{
		return Template.RandomNickNames_Female[iChoice];
	}
	else if(iGender == eGender_Male)
	{
		return Template.RandomNickNames_Male[iChoice];
	}

	return "";
}