fixed bouncing and other problems

fixed bouncing and other problems

class CRZMutator_Piledriver extends Mutator;

const StompDepth = 25.0;
const DeathDepth = 35.0;
const ExcavateDelay = 1.0;
const ExcavateSpeed = 20.0; // = StompDepth / 1.75sec

struct StompInfo
{
  var CRZPawn pawn;
  var float time;
  var vector location;
  var float deltaZ;
};

var array<StompInfo> Stomped;

function InitMutator(string options, out string errorMessage)
{
  SetTickGroup(TG_PostAsyncWork);
  Enable('Tick');
}

function NetDamage(int originalDamage, out int damage, Pawn injured, Controller instigatedBy, Vector hitLocation, out Vector momentum, class<DamageType> damageType, Actor damageCauser)
{
  super.NetDamage(originalDamage, damage, injured, instigatedBy, hitLocation, momentum, damageType, damageCauser);

  if (damageType == class'DmgType_Crushed' && instigatedBy != None && instigatedBy.Pawn != None)
  {
    damage = 1;
    if (injured.Physics == PHYS_Walking)
    {
      // if you stomp someone deep enough into the ground, you deal 300 dmg
      if (Stomp(CRZPawn(injured)))
        damage = 300;
    }
    else if (injured.Physics == PHYS_Falling)
    {
      // if you can jump onto someone mid-air, you deal 300 damage
      damage = 300;
    }
  }
}

function bool Stomp(CRZPawn injured)
{
  local vector loc;
  local int i;

  if (injured == None)
    return false;

  loc = injured.Location;

  for (i=0; i<Stomped.Length; i++)
  {
    if (Stomped[i].pawn == injured)
    {
      if (Stomped[i].time + 0.2 >= WorldInfo.TimeSeconds)
        return false;
      break;
    }
  }
  if (i == Stomped.Length)
  {
    Stomped.Add(1);
    Stomped[i].pawn = injured;
  }
  Stomped[i].time = WorldInfo.TimeSeconds;
  if (Stomped[i].deltaZ == 0)
    Stomped[i].location = loc;
  Stomped[i].deltaZ += StompDepth;

  loc.Z -= StompDepth;
  injured.CylinderComponent.SetCylinderSize(injured.CylinderComponent.CollisionRadius, injured.CylinderComponent.CollisionHeight - StompDepth);
  injured.SetLocation(loc);
  injured.BaseTranslationOffset -= StompDepth;
  injured.MovementSpeedModifier = 0;
  injured.Acceleration = vect(0,0,0);
  injured.Velocity = vect(0,0,0);
  injured.bCanCrouch = false;
  injured.bJumpCapable = false;
  return Stomped[i].deltaZ >= DeathDepth;
}

function Tick(float deltaTime)
{
  local int i;
  local StompInfo info;
  local float adj, time;
  local vector loc;

  time = WorldInfo.TimeSeconds;
  for (i=0; i<Stomped.Length; i++)
  {
    info = Stomped[i];
    if (info.deltaZ == 0 || info.pawn == None || !info.pawn.IsAliveAndWell())
    {
      Stomped.Remove(i, 1);
      --i;
      continue;
    }

    adj = ((time < info.time + ExcavateDelay) ? 0.0 : FMin(time - info.time - ExcavateDelay, deltaTime)) * ExcavateSpeed;
    adj = FMin(adj, info.deltaZ);
    Stomped[i].deltaZ -= adj;
    loc = info.location;
    loc.Z -= Stomped[i].deltaZ;
    info.pawn.SetLocation(loc);
    info.pawn.BaseTranslationOffset += adj;
    info.pawn.CylinderComponent.SetCylinderSize(info.pawn.CylinderComponent.CollisionRadius, info.pawn.CylinderComponent.CollisionHeight + adj);
    if (Stomped[i].deltaZ == 0)
    {
      info.pawn.MovementSpeedModifier = 1.0;
      info.pawn.bCanCrouch = true;
      info.pawn.bJumpCapable = true;
    }
  }
}

DefaultProperties
{
}

class CRZMutator_Piledriver extends Mutator;

const StompDepth = 25.0;
const DeathDepth = 35.0;
const ExcavateDelay = 3.0;
const ExcavateTime = 1.5;

struct StompInfo
{
  var CRZPawn pawn;
  var float time;
  var float deltaZ;
};

var array<StompInfo> LastStomped;

function InitMutator(string options, out string errorMessage)
{
  SetTickGroup(TG_PreAsyncWork);
  Enable('Tick');
}

function Mutate(string cmd, PlayerController sender)
{
  if (cmd == "pile")
    Stomp(CRZPawn(sender.Pawn));
  else
    super.Mutate(cmd, sender);
}

function NetDamage(int originalDamage, out int damage, Pawn injured, Controller instigatedBy, Vector hitLocation, out Vector momentum, class<DamageType> damageType, Actor damageCauser)
{
  super.NetDamage(originalDamage, damage, injured, instigatedBy, hitLocation, momentum, damageType, damageCauser);

  if (damageType == class'DmgType_Crushed' && instigatedBy != None && instigatedBy.Pawn != None)
  {
    damage = 1;
    if (injured.Physics == PHYS_Walking)
    {
      if (Stomp(CRZPawn(injured)))
        damage = 300;
    }
  }
}

function bool Stomp(CRZPawn injured)
{
  local vector loc;
  local int i;

  if (injured == None)
    return false;

  for (i=0; i<LastStomped.Length; i++)
  {
    if (LastStomped[i].pawn == injured)
    {
      if (LastStomped[i].time + 0.2 >= WorldInfo.TimeSeconds)
        return false;
      break;
    }
  }
  if (i == LastStomped.Length)
  {
    LastStomped.Add(1);
    LastStomped[i].pawn = injured;
  }
  LastStomped[i].time = WorldInfo.TimeSeconds;
  LastStomped[i].deltaZ += StompDepth;

  loc = injured.Location;
  loc.Z += StompDepth;
  injured.SetLocation(loc);
  injured.BaseTranslationOffset -= StompDepth;
  injured.CylinderComponent.SetCylinderSize(injured.CylinderComponent.CollisionRadius, injured.CylinderComponent.CollisionHeight - StompDepth);
  injured.MovementSpeedModifier = 0;
  return LastStomped[i].deltaZ >= DeathDepth;
}

function Tick(float deltaTime)
{
  local int i;
  local StompInfo info;
  local float time, adj;

  time = WorldInfo.TimeSeconds;
  for (i=0; i<LastStomped.Length; i++)
  {
    info = LastStomped[i];
    if (info.deltaZ == 0 || time-info.time < ExcavateDelay)
      continue;
    adj = info.deltaZ - (1 - FMin(time - info.time - ExcavateDelay, ExcavateTime)/ExcavateTime) * StompDepth;
    info.pawn.SetLocation(info.pawn.Location - vect(0,0,1) * adj);
    info.pawn.BaseTranslationOffset += adj;
    info.pawn.CylinderComponent.SetCylinderSize(info.pawn.CylinderComponent.CollisionRadius, info.pawn.CylinderComponent.CollisionHeight + adj);
    LastStomped[i].deltaZ -= adj;
    if (LastStomped[i].deltaZ < 0.0001)
      info.pawn.MovementSpeedModifier = 1.0;
  }
}

DefaultProperties
{
}


Update pb-to-cpp.uc

Update pb-to-cpp.uc

Added <stdafx.h> and <vector>
Shortened type names that take two words to one word
Partially fixed vector syntax

# pb-to-cpp.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/5/2014 6:05:13 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
            VAR: UDT
         }
         {@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 "stdafx.h"
         #include "pbOS.h"
         #include "pbMisc.h"
         #include <vector>
         #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-z0-9\@\.\_]+"}[({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: %}|{wstr: $$}|{str: $} }{equate} = {value}
Replace: {nl}const{num: int}{str: string}{wstr: wstring} {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 } | 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: Case [IS] {test%} [:] [' {comment:".*"}]
Replace: {@Evaluate:
            IIf(Case1==1, Case1=0; "", "} else")
         } if (CASE_VAR == {test}) {{comment: // {comment}}

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

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

Criteria: 43
Find: End Select
Replace: }}

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

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

Criteria: 46
Find: End { Type | Union }
Replace: };

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

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

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

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

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

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

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

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

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

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

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

Criteria: 58
Find: Iterate
Replace: continue

Criteria: 59
Find: Exit
Replace: break

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

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

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

Criteria: 63
Find: Double
Replace: double

Criteria: 64
Highlight: True
Find: Byte
Replace: UCHAR

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

Criteria: 66
Highlight: True
Find: Word
Replace: USHORT

Criteria: 67
Highlight: True
Find: Dword
Replace: unsigned

Criteria: 68
Find: AsciiZ
Replace: LPCSTR

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

Criteria: 70
SkipOver: True
Find: EXTENDED
Replace: [Skip over]

Criteria: 71
Selected: True
Highlight: True
Find: { Extended | Ext }
Replace: EXTENDED

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

Criteria: 73
PassOnce: False
Find: ByRef {array}() As {type:1}
Replace: std::vector<{type}>& {array}{@Define::
            Syntax: {array}([{index}]) ::= {array}[{index}]
         }

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

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

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

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

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

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

Criteria: 80
PassOnce: False
Find: Choose$({index}, {choice})
Replace: IIf({index}, {choice}, "")

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

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

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

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

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

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

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

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

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

Criteria: 90
Highlight: True
Find: :
Replace: ;{nl}

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

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

Criteria: 93
StopAfter: 1
Find: //{".*"}
Replace: {Self}{@Define:: Token: [\x7b\x7d\;]}

Criteria: 94
Find: {nl} {code} [{comment:" *//.*"}] {delim-: {nl}}
Replace: {nl}{code};{comment}

Criteria: 95
SkipOver: True
Find: {nl} [{code}] { ; | "{" | "}" } [// {".*"}] {delim-: {nl}}
Replace: [Skip over]

Criteria: 96
SkipOver: True
Find: {nl} { # | // } [{".*"}]
Replace: [Skip over]

Criteria: 97
Comment: Nested UDTs - 1st pass (gather members)
Pass: 6

Criteria: 98
Find: {nl} struct {name} "{" {members+} "}";
Replace: {Self}{@Eval: Dim {name} As String = {Q}{members}{Q}}

Criteria: 99
Find: {nl} union {name} "{" {members+} "}";
Replace: {Self}{@Eval: Dim {name} As String = {Q}union{{members}};{Q}}

Criteria: 100
Comment: Nested UDTs - 2nd pass (insert members)
Pass: 7

Criteria: 101
Find: {nl} { struct | union }
Replace: {Self}{@Eval: UDT = True}

Criteria: 102
PassOnce: False
Find: {nl}{nested:1}; {@If: UDT}
Replace: {@Eval: {nested}}

Criteria: 103
SkipOver: True
Find: {nl}{type:1} {member};
Replace: [Skip over]

Criteria: 104
Find: {nl} "}";
Replace: {Self}{@Eval: UDT = False}

Criteria: 105
Comment: Misc
Pass: 8

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

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

Criteria: 108
Find: @
Replace: *

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

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

Criteria: 111
Find: std::vector<{type}>({size})
Replace: std::vector<{type}>[{size}]

# End Search

# pb-to-cpp.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 5/1/2014 7:27:38 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
            VAR: UDT
         }
         {@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-z0-9\@\.\_]+"}[({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
Selected: True
Highlight: True
BackColor: Green
PassOnce: False
Find: {nl}{ {num: %}|{wstr: $$}|{str: $} }{equate} = {value}
Replace: {nl}const{num: int}{str: string}{wstr: wstring} {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 } | 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: Case [IS] {test%} [:] [' {comment:".*"}]
Replace: {@Evaluate:
            IIf(Case1==1, Case1=0; "", "} else")
         } if (CASE_VAR == {test}) {{comment: // {comment}}

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

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

Criteria: 43
Find: End Select
Replace: }}

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

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

Criteria: 46
Find: End { Type | Union }
Replace: };

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

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

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

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

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

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

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

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

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

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

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

Criteria: 58
Find: Iterate
Replace: continue

Criteria: 59
Find: Exit
Replace: break

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

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

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

Criteria: 63
Find: Double
Replace: double

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

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

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

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

Criteria: 68
Find: AsciiZ
Replace: LPCSTR

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

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

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

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

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

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

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

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

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

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

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

Criteria: 80
PassOnce: False
Find: Choose$({index}, {choice})
Replace: IIf({index}, {choice}, "")

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

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

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

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

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

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

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

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

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

Criteria: 90
Highlight: True
Find: :
Replace: ;{nl}

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

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

Criteria: 93
StopAfter: 1
Find: //{".*"}
Replace: {Self}{@Define:: Token: [\x7b\x7d\;]}

Criteria: 94
Find: {nl} {code} [{comment:" *//.*"}] {delim-: {nl}}
Replace: {nl}{code};{comment}

Criteria: 95
SkipOver: True
Find: {nl} [{code}] { ; | "{" | "}" } [// {".*"}] {delim-: {nl}}
Replace: [Skip over]

Criteria: 96
SkipOver: True
Find: {nl} { # | // } [{".*"}]
Replace: [Skip over]

Criteria: 97
Comment: Nested UDTs - 1st pass (gather members)
Pass: 6

Criteria: 98
Find: {nl} struct {name} "{" {members+} "}";
Replace: {Self}{@Eval: Dim {name} As String = {Q}{members}{Q}}

Criteria: 99
Find: {nl} union {name} "{" {members+} "}";
Replace: {Self}{@Eval: Dim {name} As String = {Q}union{{members}};{Q}}

Criteria: 100
Comment: Nested UDTs - 2nd pass (insert members)
Pass: 7

Criteria: 101
Find: {nl} { struct | union }
Replace: {Self}{@Eval: UDT = True}

Criteria: 102
PassOnce: False
Find: {nl}{nested:1}; {@If: UDT}
Replace: {@Eval: {nested}}

Criteria: 103
SkipOver: True
Find: {nl}{type:1} {member};
Replace: [Skip over]

Criteria: 104
Find: {nl} "}";
Replace: {Self}{@Eval: UDT = False}

Criteria: 105
Comment: Misc
Pass: 8

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

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

Criteria: 108
Find: @
Replace: *

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

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

# End Search


fixed MOTD for local server

fixed MOTD for local server

class CRZMutator_Motd extends Mutator config (MutatH0r);

var config Array<string> WelcomeHeader;
var config Array<string> WelcomeMessage;
var repnotify string WelcomeHeaderString;
var repnotify string WelcomeMessageStrings[40];
var string WelcomeMessageString;

replication
{
  if (Role == ROLE_Authority && (bNetDirty || bNetInitial))
    WelcomeHeaderString, WelcomeMessageStrings;
}

simulated function PostBeginPlay()
{
  local string s;
  local int i;
  super.PostBeginPlay();

  if (Role == ROLE_Authority)
  {
    JoinArray(WelcomeHeader, WelcomeHeaderString, "\n", false);
    JoinArray(WelcomeMessage, WelcomeMessageString, "\n", false);
    s = WelcomeMessageString;
    while (len(s) > 250)
    {
      WelcomeMessageStrings[i++] = left(s, 250);
      s = mid(s, 250);
    }
    WelcomeMessageStrings[i++] = s;
  }
}

function NotifyLogin(Controller newPlayer)
{
  super.NotifyLogin(newPlayer);
  ShowWelcomeMessage(PlayerController(newPlayer));
}

simulated function ShowWelcomeMessage(PlayerController pc)
{
  if (pc == none || !pc.IsLocalPlayerController())
    return;
  class'MotdInteraction'.static.Create(self, pc, true);
}

simulated function ReplicatedEvent(name varName)
{
  local int i;

  // override the client's local .ini values with the server's strings
  if (varName == 'WelcomeHeaderString')
    ParseStringIntoArray(WelcomeHeaderString, WelcomeHeader, "\n", false);
  else if (varName == 'WelcomeMessageStrings')
  {
    WelcomeMessageString = "";
    for (i=0; i<ArrayCount(WelcomeMessageStrings); i++)
      WelcomeMessageString = WelcomeMessageString $ WelcomeMessageStrings[i];
    ParseStringIntoArray(WelcomeMessageString, WelcomeMessage, "\n", false);
  }

  ShowWelcomeMessage(GetALocalPlayerController());
}

DefaultProperties
{
  bAlwaysRelevant = true;
  RemoteRole = ROLE_SimulatedProxy;
}

class CRZMutator_Motd extends Mutator config (MutatH0r);

var config Array<string> WelcomeHeader;
var config Array<string> WelcomeMessage;
var string WelcomeHeaderString;
var string WelcomeMessageString;

//replication
//{
//  if (Role == ROLE_Authority && (bNetDirty || bNetInitial))
//    WelcomeMessage;
//}

simulated function PostBeginPlay()
{
  local PlayerController pc;

  super.PostBeginPlay();

  pc = GetALocalPlayerController();
  if (pc != None)
    ShowWelcomeMessage(pc);
}

function NotifyLogin(Controller newPlayer)
{
  local PlayerController pc;

  super.NotifyLogin(newPlayer);

  pc = PlayerController(newPlayer);
  if (pc != None)
    ShowWelcomeMessage(pc); // bootcamp
}

simulated function ShowWelcomeMessage(PlayerController pc)
{
  WelcomeHeaderString = ArrayToString(WelcomeHeader);
  WelcomeMessageString = ArrayToString(WelcomeMessage);
  class'MotdInteraction'.static.Create(self, pc, true);
}

simulated function string ArrayToString(Array<string> arr)
{
  local int i;
  local string str;

  str = "";
  for (i=0; i<arr.Length; i++)
  {
    if (i > 0)
      str = str $ "\n";
    str = str $ arr[i];
  }
  return str;
}

DefaultProperties
{
  bAlwaysRelevant = true;
  RemoteRole = ROLE_SimulatedProxy;
}


Fixed indentations to use tabs instead of spaces

//---------------------------------------------------------------------------------------
//  FILE:    X2Effect_ApplyWeaponDamage.uc
//  AUTHOR:  Joshua Bouscher
//
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------
class X2Effect_ApplyWeaponDamage extends X2Effect config(GameCore);

var bool    bExplosiveDamage;
var bool    bIgnoreBaseDamage;
var name    DamageTag;
var bool    bAlwaysKillsCivilians;
var bool    bApplyWorldEffectsForEachTargetLocation;
var bool	bAllowFreeKill;
var bool    bAllowWeaponUpgrade;
var bool    bBypassShields;
var bool    bIgnoreArmor;
var bool	bBypassSustainEffects;
var array<name> HideVisualizationOfResultsAdditional;

// These values are extra amount an ability may add or apply directly
var WeaponDamageValue EffectDamageValue;
var int EnvironmentalDamageAmount;

var config float GRAZE_DMG_MULT;
var config array<name> HideVisualizationOfResults;

struct ApplyDamageInfo
{
	var WeaponDamageValue BaseDamageValue;
	var WeaponDamageValue ExtraDamageValue;
	var WeaponDamageValue BonusEffectDamageValue;
	var WeaponDamageValue AmmoDamageValue;
	var WeaponDamageValue UpgradeDamageValue;
	var bool bDoesDamageIgnoreShields;
};

function WeaponDamageValue GetBonusEffectDamageValue(XComGameState_Ability AbilityState, XComGameState_Unit SourceUnit, XComGameState_Item SourceWeapon, StateObjectReference TargetRef) { return EffectDamageValue; }

simulated protected function OnEffectAdded(const out EffectAppliedData ApplyEffectParameters, XComGameState_BaseObject kNewTargetState, XComGameState NewGameState, XComGameState_Effect NewEffectState)
{
	local Damageable kNewTargetDamageableState;
	local int iDamage, iMitigated, NewRupture, NewShred, TotalToKill;
	local XComGameState_Unit TargetUnit, SourceUnit;
	local XComGameState_Item SourceWeapon;
	local array<X2WeaponUpgradeTemplate> WeaponUpgradeTemplates;
	local X2WeaponUpgradeTemplate WeaponUpgradeTemplate;
	local array<Name> AppliedDamageTypes;
	local int bAmmoBypassesShields, bFullyImmune;
	local bool bDoesDamageIgnoreShields;
	local XComGameStateHistory History;
	local StateObjectReference EffectRef;
	local XComGameState_Effect EffectState;
	local array<DamageModifierInfo> SpecialDamageMessages;
	local DamageResult ZeroDamageResult;

	kNewTargetDamageableState = Damageable(kNewTargetState);
	if( kNewTargetDamageableState != none )
	{
		AppliedDamageTypes = DamageTypes;
		iDamage = CalculateDamageAmount(ApplyEffectParameters, iMitigated, NewRupture, NewShred, AppliedDamageTypes, bAmmoBypassesShields, bFullyImmune, SpecialDamageMessages, NewGameState);
		bDoesDamageIgnoreShields = (bAmmoBypassesShields > 0) || bBypassShields;

		if ((iDamage == 0) && (iMitigated == 0) && (NewRupture == 0) && (NewShred == 0))
		{
			// No damage is being dealt
			if (SpecialDamageMessages.Length > 0 || bFullyImmune != 0)
			{
				TargetUnit = XComGameState_Unit(kNewTargetState);
				if (TargetUnit != none)
				{
					ZeroDamageResult.bImmuneToAllDamage = bFullyImmune != 0;
					ZeroDamageResult.Context = NewGameState.GetContext();
					ZeroDamageResult.SourceEffect = ApplyEffectParameters;
					ZeroDamageResult.SpecialDamageFactors = SpecialDamageMessages;
					TargetUnit.DamageResults.AddItem(ZeroDamageResult);
				}
			}
			return;
		}

		if (bAllowFreeKill)
		{
			//  check to see if the damage ought to kill them and if not, roll for a free kill
			TargetUnit = XComGameState_Unit(kNewTargetState);
			if (TargetUnit != none)
			{
				TotalToKill = TargetUnit.GetCurrentStat(eStat_HP) + TargetUnit.GetCurrentStat(eStat_ShieldHP);
				if (TotalToKill > iDamage)
				{
					History = `XCOMHISTORY;
					//  check weapon upgrades for a free kill
					SourceWeapon = XComGameState_Item(History.GetGameStateForObjectID(ApplyEffectParameters.ItemStateObjectRef.ObjectID));
					if (SourceWeapon != none)
					{
						WeaponUpgradeTemplates = SourceWeapon.GetMyWeaponUpgradeTemplates();
						foreach WeaponUpgradeTemplates(WeaponUpgradeTemplate)
						{
							if (WeaponUpgradeTemplate.FreeKillFn != none && WeaponUpgradeTemplate.FreeKillFn(WeaponUpgradeTemplate, TargetUnit))
							{
								TargetUnit.TakeEffectDamage(self, TotalToKill, 0, NewShred, ApplyEffectParameters, NewGameState, false, false, true, AppliedDamageTypes, SpecialDamageMessages);
								if (TargetUnit.IsAlive())
								{
									`RedScreen("Somehow free kill upgrade failed to kill the target! -jbouscher @gameplay");
								}
								else
								{
									TargetUnit.DamageResults[TargetUnit.DamageResults.Length - 1].bFreeKill = true;
								}
								return;
							}
						}
					}
					//  check source unit effects for a free kill
					SourceUnit = XComGameState_Unit(NewGameState.GetGameStateForObjectID(ApplyEffectParameters.SourceStateObjectRef.ObjectID));
					if (SourceUnit == none)
						SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(ApplyEffectParameters.SourceStateObjectRef.ObjectID));

					foreach SourceUnit.AffectedByEffects(EffectRef)
					{
						EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
						if (EffectState != none)
						{
							if (EffectState.GetX2Effect().FreeKillOnDamage(SourceUnit, TargetUnit, NewGameState, TotalToKill, ApplyEffectParameters))
							{
								TargetUnit.TakeEffectDamage(self, TotalToKill, 0, NewShred, ApplyEffectParameters, NewGameState, false, false, true, AppliedDamageTypes, SpecialDamageMessages);
								if (TargetUnit.IsAlive())
								{
									`RedScreen("Somehow free kill effect failed to kill the target! -jbouscher @gameplay");
								}
								else
								{
									TargetUnit.DamageResults[TargetUnit.DamageResults.Length - 1].bFreeKill = true;
									TargetUnit.DamageResults[TargetUnit.DamageResults.Length - 1].FreeKillAbilityName = EffectState.ApplyEffectParameters.AbilityInputContext.AbilityTemplateName;
								}
								return;
							}
						}
					}
				}
			}
		}

		if (NewRupture > 0)
		{
			kNewTargetDamageableState.AddRupturedValue(NewRupture);
		}

		kNewTargetDamageableState.TakeEffectDamage(self, iDamage, iMitigated, NewShred, ApplyEffectParameters, NewGameState, false, true, bDoesDamageIgnoreShields, AppliedDamageTypes, SpecialDamageMessages);
	}
}

simulated function ApplyEffectToWorld(const out EffectAppliedData ApplyEffectParameters, XComGameState NewGameState)
{
	local XComGameStateHistory History;
	local XComGameState_EnvironmentDamage DamageEvent;
	local XComGameState_Ability AbilityStateObject;
	local XComGameState_Unit SourceStateObject;
	local XComGameState_Item SourceItemStateObject;
	local X2WeaponTemplate WeaponTemplate;
	local float AbilityRadius;
	local vector DamageDirection;
	local vector SourceUnitPosition;
	local XComGameStateContext_Ability AbilityContext;
	local int DamageAmount;
	local int PhysicalImpulseAmount;
	local name DamageTypeTemplateName;
	local XGUnit SourceUnit;
	local int OutCoverIndex;
	local UnitPeekSide OutPeekSide;
	local int OutRequiresLean;
	local int bOutCanSeeFromDefault;
	local X2AbilityTemplate AbilityTemplate;
	local XComGameState_Item LoadedAmmo, SourceAmmo;
	local Vector HitLocation;
	local int i, HitLocationCount, HitLocationIndex;
	local array<vector> HitLocationsArray;
	local bool bLinearDamage;
	local X2AbilityMultiTargetStyle TargetStyle;
	local GameRulesCache_VisibilityInfo OutVisibilityInfo;

	// Variable for Issue #200
	local XComLWTuple ModifyEnvironmentDamageTuple;

	//If this damage effect has an associated position, it does world damage
	if( ApplyEffectParameters.AbilityInputContext.TargetLocations.Length > 0 || ApplyEffectParameters.AbilityResultContext.ProjectileHitLocations.Length > 0 )
	{
		History = `XCOMHISTORY;
		SourceStateObject = XComGameState_Unit(History.GetGameStateForObjectID(ApplyEffectParameters.SourceStateObjectRef.ObjectID));
		SourceItemStateObject = XComGameState_Item(History.GetGameStateForObjectID(ApplyEffectParameters.ItemStateObjectRef.ObjectID));
		if (SourceItemStateObject != None)
			WeaponTemplate = X2WeaponTemplate(SourceItemStateObject.GetMyTemplate());
		AbilityStateObject = XComGameState_Ability(History.GetGameStateForObjectID(ApplyEffectParameters.AbilityStateObjectRef.ObjectID));
		AbilityContext = XComGameStateContext_Ability(NewGameState.GetContext());

		if( (SourceStateObject != none && AbilityStateObject != none) && (SourceItemStateObject != none || EnvironmentalDamageAmount > 0) )
		{
			AbilityTemplate = AbilityStateObject.GetMyTemplate();
			if( AbilityTemplate != None )
			{
				TargetStyle = AbilityTemplate.AbilityMultiTargetStyle;

				if( TargetStyle != none && TargetStyle.IsA('X2AbilityMultiTarget_Line') )
				{
					bLinearDamage = true;
				}
			}
			AbilityRadius = AbilityStateObject.GetAbilityRadius();

			//Here, we want to use the target location as the input for the direction info since a miss location might possibly want a different step out
			SourceUnit = XGUnit(History.GetVisualizer(SourceStateObject.ObjectID));
			SourceUnit.GetDirectionInfoForPosition(ApplyEffectParameters.AbilityInputContext.TargetLocations[0], OutVisibilityInfo, OutCoverIndex, OutPeekSide, bOutCanSeeFromDefault, OutRequiresLean);
			SourceUnitPosition = SourceUnit.GetExitCoverPosition(OutCoverIndex, OutPeekSide);
			SourceUnitPosition.Z += 8.0f;

			DamageAmount = EnvironmentalDamageAmount;
			if ((SourceItemStateObject != none) && !bIgnoreBaseDamage)
			{
				SourceAmmo = AbilityStateObject.GetSourceAmmo();
				if (SourceAmmo != none)
				{
					DamageAmount += SourceAmmo.GetItemEnvironmentDamage();
				}
				else if(SourceItemStateObject.HasLoadedAmmo())
				{
					LoadedAmmo = XComGameState_Item(History.GetGameStateForObjectID(SourceItemStateObject.LoadedAmmo.ObjectID));
					if(LoadedAmmo != None)
					{
						DamageAmount += LoadedAmmo.GetItemEnvironmentDamage();
					}
				}

				DamageAmount += SourceItemStateObject.GetItemEnvironmentDamage();
			}

			if (WeaponTemplate != none)
			{
				PhysicalImpulseAmount = WeaponTemplate.iPhysicsImpulse;
				DamageTypeTemplateName = WeaponTemplate.DamageTypeTemplateName;
			}
			else
			{
				PhysicalImpulseAmount = 0;

				if( EffectDamageValue.DamageType != '' )
				{
					// If the damage effect's damage type is filled out, use that
					DamageTypeTemplateName = EffectDamageValue.DamageType;
				}
				else if( DamageTypes.Length > 0 )
				{
					// If there is at least one DamageType, use the first one (may want to change
					// in the future to make a more intelligent decision)
					DamageTypeTemplateName = DamageTypes[0];
				}
				else
				{
					// Default to explosive
					DamageTypeTemplateName = 'Explosion';
				}
			}

			// Issue #200 Start, allow listeners to modify environment damage
			ModifyEnvironmentDamageTuple = new class'XComLWTuple';
			ModifyEnvironmentDamageTuple.Id = 'ModifyEnvironmentDamage';
			ModifyEnvironmentDamageTuple.Data.Add(3);
			ModifyEnvironmentDamageTuple.Data[0].kind = XComLWTVBool;
			ModifyEnvironmentDamageTuple.Data[0].b = false;  // override? (true) or add? (false)
			ModifyEnvironmentDamageTuple.Data[1].kind = XComLWTVInt;
			ModifyEnvironmentDamageTuple.Data[1].i = 0;  // override/bonus environment damage
			ModifyEnvironmentDamageTuple.Data[2].kind = XComLWTVObject;
			ModifyEnvironmentDamageTuple.Data[2].o = AbilityStateObject;  // ability being used

			`XEVENTMGR.TriggerEvent('ModifyEnvironmentDamage', ModifyEnvironmentDamageTuple, self, NewGameState);

			if(ModifyEnvironmentDamageTuple.Data[0].b)
			{
				DamageAmount = ModifyEnvironmentDamageTuple.Data[1].i;
			}
			else
			{
				DamageAmount += ModifyEnvironmentDamageTuple.Data[1].i;
			}

			// Issue #200 End

			if( ( bLinearDamage || AbilityRadius > 0.0f || AbilityContext.ResultContext.HitResult == eHit_Miss) && DamageAmount > 0 )
			{
				// Loop here over projectiles if needed. If not single hit and use the first index.
				if(ApplyEffectParameters.AbilityResultContext.ProjectileHitLocations.Length > 0)
				{
					HitLocationsArray = ApplyEffectParameters.AbilityResultContext.ProjectileHitLocations;
				}
				else
				{
					HitLocationsArray = ApplyEffectParameters.AbilityInputContext.TargetLocations;
				}

				HitLocationCount = 1;
				if( bApplyWorldEffectsForEachTargetLocation )
				{
					HitLocationCount = HitLocationsArray.Length;
				}

				for( HitLocationIndex = 0; HitLocationIndex < HitLocationCount; ++HitLocationIndex )
				{
					HitLocation = HitLocationsArray[HitLocationIndex];

					DamageEvent = XComGameState_EnvironmentDamage(NewGameState.CreateNewStateObject(class'XComGameState_EnvironmentDamage'));
					DamageEvent.DEBUG_SourceCodeLocation = "UC: X2Effect_ApplyWeaponDamage:ApplyEffectToWorld";
					DamageEvent.DamageAmount = DamageAmount;
					DamageEvent.DamageTypeTemplateName = DamageTypeTemplateName;
					DamageEvent.HitLocation = HitLocation;
					DamageEvent.Momentum = (AbilityRadius == 0.0f) ? DamageDirection : vect(0,0,0);
					DamageEvent.PhysImpulse = PhysicalImpulseAmount;

					if( bLinearDamage )
					{
						TargetStyle.GetValidTilesForLocation(AbilityStateObject, DamageEvent.HitLocation, DamageEvent.DamageTiles);
					}
					else if (AbilityRadius > 0.0f)
					{
						DamageEvent.DamageRadius = AbilityRadius;
					}
					else
					{
						DamageEvent.DamageTiles.AddItem(`XWORLD.GetTileCoordinatesFromPosition(DamageEvent.HitLocation));
					}

					if( X2AbilityMultiTarget_Cone(TargetStyle) != none )
					{
						DamageEvent.bAffectFragileOnly = AbilityTemplate.bFragileDamageOnly;
						DamageEvent.CosmeticConeEndDiameter = X2AbilityMultiTarget_Cone(TargetStyle).GetConeEndDiameter(AbilityStateObject);
						DamageEvent.CosmeticConeLength = X2AbilityMultiTarget_Cone(TargetStyle).GetConeLength(AbilityStateObject);
						DamageEvent.CosmeticConeLocation = SourceUnitPosition;
						DamageEvent.CosmeticDamageShape = SHAPE_CONE;

						if (AbilityTemplate.bCheckCollision)
						{
							for (i = 0; i < AbilityContext.InputContext.VisibleTargetedTiles.Length; i++)
							{
								DamageEvent.DamageTiles.AddItem( AbilityContext.InputContext.VisibleTargetedTiles[i] );
							}

							for (i = 0; i < AbilityContext.InputContext.VisibleNeighborTiles.Length; i++)
							{
								DamageEvent.DamageTiles.AddItem( AbilityContext.InputContext.VisibleNeighborTiles[i] );
							}
						}
					}

					DamageEvent.DamageCause = SourceStateObject.GetReference();
					DamageEvent.DamageSource = DamageEvent.DamageCause;
					DamageEvent.bRadialDamage = AbilityRadius > 0;
				}
			}
		}
	}
}

simulated function GetDamagePreview(StateObjectReference TargetRef, XComGameState_Ability AbilityState, bool bAsPrimaryTarget, out WeaponDamageValue MinDamagePreview, out WeaponDamageValue MaxDamagePreview, out int AllowsShield)
{
	local XComGameStateHistory History;
	local XComGameState_Unit TargetUnit, SourceUnit;
	local XComGameState_Item SourceWeapon, LoadedAmmo;
	local WeaponDamageValue UpgradeTemplateBonusDamage, BaseDamageValue, ExtraDamageValue, AmmoDamageValue, BonusEffectDamageValue, UpgradeDamageValue;
	local X2Condition ConditionIter;
	local name AvailableCode;
	local X2AmmoTemplate AmmoTemplate;
	local StateObjectReference EffectRef;
	local XComGameState_Effect EffectState;
	local X2Effect_Persistent EffectTemplate;
	local int EffectDmg, UnconditionalShred, BaseEffectDmgModMin, BaseEffectDmgModMax;
	local EffectAppliedData TestEffectParams;
	local name DamageType;
	local array<X2WeaponUpgradeTemplate> WeaponUpgradeTemplates;
	local X2WeaponUpgradeTemplate WeaponUpgradeTemplate;
	local array<Name> AppliedDamageTypes;
	local bool bDoesDamageIgnoreShields;
	local DamageModifierInfo DamageModInfo;

	MinDamagePreview = UpgradeTemplateBonusDamage;
	MaxDamagePreview = UpgradeTemplateBonusDamage;
	bDoesDamageIgnoreShields = bBypassShields;

	if (!bApplyOnHit)
		return;

	History = `XCOMHISTORY;

	if (AbilityState.SourceAmmo.ObjectID > 0)
		SourceWeapon = AbilityState.GetSourceAmmo();
	else
		SourceWeapon = AbilityState.GetSourceWeapon();

	TargetUnit = XComGameState_Unit(History.GetGameStateForObjectID(TargetRef.ObjectID));
	SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(AbilityState.OwnerStateObject.ObjectID));

	if (TargetUnit != None)
	{
		foreach TargetConditions(ConditionIter)
		{
			AvailableCode = ConditionIter.AbilityMeetsCondition(AbilityState, TargetUnit);
			if (AvailableCode != 'AA_Success')
				return;
			AvailableCode = ConditionIter.MeetsCondition(TargetUnit);
			if (AvailableCode != 'AA_Success')
				return;
			AvailableCode = ConditionIter.MeetsConditionWithSource(TargetUnit, SourceUnit);
			if (AvailableCode != 'AA_Success')
				return;
		}
		foreach DamageTypes(DamageType)
		{
			if (TargetUnit.IsImmuneToDamage(DamageType))
				return;
		}
	}

	if (bAlwaysKillsCivilians && TargetUnit != None && TargetUnit.GetTeam() == eTeam_Neutral)
	{
		MinDamagePreview.Damage = TargetUnit.GetCurrentStat(eStat_HP) + TargetUnit.GetCurrentStat(eStat_ShieldHP);
		MaxDamagePreview = MinDamagePreview;
		return;
	}
	if (SourceWeapon != None)
	{
		if (!bIgnoreBaseDamage)
		{
			SourceWeapon.GetBaseWeaponDamageValue(TargetUnit, BaseDamageValue);
			ModifyDamageValue(BaseDamageValue, TargetUnit, AppliedDamageTypes);
		}
		if (DamageTag != '')
		{
			SourceWeapon.GetWeaponDamageValue(TargetUnit, DamageTag, ExtraDamageValue);
			ModifyDamageValue(ExtraDamageValue, TargetUnit, AppliedDamageTypes);
		}
		if (SourceWeapon.HasLoadedAmmo() && !bIgnoreBaseDamage)
		{
			LoadedAmmo = XComGameState_Item(History.GetGameStateForObjectID(SourceWeapon.LoadedAmmo.ObjectID));
			AmmoTemplate = X2AmmoTemplate(LoadedAmmo.GetMyTemplate());
			if (AmmoTemplate != None)
			{
				AmmoTemplate.GetTotalDamageModifier(LoadedAmmo, SourceUnit, TargetUnit, AmmoDamageValue);
				bDoesDamageIgnoreShields = AmmoTemplate.bBypassShields || bDoesDamageIgnoreShields;
			}
			else
			{
				LoadedAmmo.GetBaseWeaponDamageValue(TargetUnit, AmmoDamageValue);
			}
			ModifyDamageValue(AmmoDamageValue, TargetUnit, AppliedDamageTypes);
		}
		if (bAllowWeaponUpgrade)
		{
			WeaponUpgradeTemplates = SourceWeapon.GetMyWeaponUpgradeTemplates();
			foreach WeaponUpgradeTemplates(WeaponUpgradeTemplate)
			{
				if (WeaponUpgradeTemplate.BonusDamage.Tag == DamageTag)
				{
					UpgradeTemplateBonusDamage = WeaponUpgradeTemplate.BonusDamage;

					ModifyDamageValue(UpgradeTemplateBonusDamage, TargetUnit, AppliedDamageTypes);

					UpgradeDamageValue.Damage += UpgradeTemplateBonusDamage.Damage;
					UpgradeDamageValue.Spread += UpgradeTemplateBonusDamage.Spread;
					UpgradeDamageValue.Crit += UpgradeTemplateBonusDamage.Crit;
					UpgradeDamageValue.Pierce += UpgradeTemplateBonusDamage.Pierce;
					UpgradeDamageValue.Rupture += UpgradeTemplateBonusDamage.Rupture;
					UpgradeDamageValue.Shred += UpgradeTemplateBonusDamage.Shred;
					//  ignores PlusOne as there is no good way to add them up
				}
			}
		}
	}
	BonusEffectDamageValue = GetBonusEffectDamageValue(AbilityState, SourceUnit, SourceWeapon, TargetRef);
	ModifyDamageValue(BonusEffectDamageValue, TargetUnit, AppliedDamageTypes);

	MinDamagePreview.Damage = BaseDamageValue.Damage + ExtraDamageValue.Damage + AmmoDamageValue.Damage + BonusEffectDamageValue.Damage + UpgradeDamageValue.Damage -
							  BaseDamageValue.Spread - ExtraDamageValue.Spread - AmmoDamageValue.Spread - BonusEffectDamageValue.Spread - UpgradeDamageValue.Spread;

	MaxDamagePreview.Damage = BaseDamageValue.Damage + ExtraDamageValue.Damage + AmmoDamageValue.Damage + BonusEffectDamageValue.Damage + UpgradeDamageValue.Damage +
							  BaseDamageValue.Spread + ExtraDamageValue.Spread + AmmoDamageValue.Spread + BonusEffectDamageValue.Spread + UpgradeDamageValue.Spread;

	MinDamagePreview.Pierce = BaseDamageValue.Pierce + ExtraDamageValue.Pierce + AmmoDamageValue.Pierce + BonusEffectDamageValue.Pierce + UpgradeDamageValue.Pierce;
	MaxDamagePreview.Pierce = MinDamagePreview.Pierce;

	MinDamagePreview.Shred = BaseDamageValue.Shred + ExtraDamageValue.Shred + AmmoDamageValue.Shred + BonusEffectDamageValue.Shred + UpgradeDamageValue.Shred;
	MaxDamagePreview.Shred = MinDamagePreview.Shred;

	MinDamagePreview.Rupture = BaseDamageValue.Rupture + ExtraDamageValue.Rupture + AmmoDamageValue.Rupture + BonusEffectDamageValue.Rupture + UpgradeDamageValue.Rupture;
	MaxDamagePreview.Rupture = MinDamagePreview.Rupture;

	if (BaseDamageValue.PlusOne > 0)
		MaxDamagePreview.Damage++;
	if (ExtraDamageValue.PlusOne > 0)
		MaxDamagePreview.Damage++;
	if (AmmoDamageValue.PlusOne > 0)
		MaxDamagePreview.Damage++;
	if (BonusEffectDamageValue.PlusOne > 0)
		MaxDamagePreview.Damage++;

	TestEffectParams.AbilityInputContext.AbilityRef = AbilityState.GetReference();
	TestEffectParams.AbilityInputContext.AbilityTemplateName = AbilityState.GetMyTemplateName();
	TestEffectParams.ItemStateObjectRef = AbilityState.SourceWeapon;
	TestEffectParams.AbilityStateObjectRef = AbilityState.GetReference();
	TestEffectParams.SourceStateObjectRef = SourceUnit.GetReference();
	TestEffectParams.PlayerStateObjectRef = SourceUnit.ControllingPlayer;
	TestEffectParams.TargetStateObjectRef = TargetRef;
	if (bAsPrimaryTarget)
		TestEffectParams.AbilityInputContext.PrimaryTarget = TargetRef;

	if (TargetUnit != none)
	{
		foreach TargetUnit.AffectedByEffects(EffectRef)
		{
			EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
			EffectTemplate = EffectState.GetX2Effect();
			EffectDmg = EffectTemplate.GetBaseDefendingDamageModifier(EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams, MinDamagePreview.Damage, self);
			BaseEffectDmgModMin += EffectDmg;
			if (EffectDmg != 0)
			{
				DamageModInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
				DamageModInfo.Value = EffectDmg;
				MinDamagePreview.BonusDamageInfo.AddItem(DamageModInfo);
			}
			EffectDmg = EffectTemplate.GetBaseDefendingDamageModifier(EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams, MaxDamagePreview.Damage, self);
			BaseEffectDmgModMax += EffectDmg;
			if (EffectDmg != 0)
			{
				DamageModInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
				DamageModInfo.Value = EffectDmg;
				MaxDamagePreview.BonusDamageInfo.AddItem(DamageModInfo);
			}
		}
		MinDamagePreview.Damage += BaseEffectDmgModMin;
		MaxDamagePreview.Damage += BaseEffectDmgModMax;
	}

	foreach SourceUnit.AffectedByEffects(EffectRef)
	{
		EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
		EffectTemplate = EffectState.GetX2Effect();

		EffectDmg = EffectTemplate.GetAttackingDamageModifier(EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams, MinDamagePreview.Damage);
		MinDamagePreview.Damage += EffectDmg;
		if( EffectDmg != 0 )
		{
			DamageModInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
			DamageModInfo.Value = EffectDmg;
			MinDamagePreview.BonusDamageInfo.AddItem(DamageModInfo);
		}
		EffectDmg = EffectTemplate.GetAttackingDamageModifier(EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams, MaxDamagePreview.Damage);
		MaxDamagePreview.Damage += EffectDmg;
		if( EffectDmg != 0 )
		{
			DamageModInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
			DamageModInfo.Value = EffectDmg;
			MaxDamagePreview.BonusDamageInfo.AddItem(DamageModInfo);
		}

		EffectDmg = EffectTemplate.GetExtraArmorPiercing(EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams);
		MinDamagePreview.Pierce += EffectDmg;
		MaxDamagePreview.Pierce += EffectDmg;

		EffectDmg = EffectTemplate.GetExtraShredValue(EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams);
		MinDamagePreview.Shred += EffectDmg;
		MaxDamagePreview.Shred += EffectDmg;
	}

	// run through the effects again for any conditional shred.  A second loop as it is possibly dependent on shred outcome of the unconditional first loop.
	UnconditionalShred = MinDamagePreview.Shred;
	foreach SourceUnit.AffectedByEffects(EffectRef)
	{
		EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
		EffectTemplate = EffectState.GetX2Effect();

		EffectDmg = EffectTemplate.GetConditionalExtraShredValue(UnconditionalShred, EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams);
		MinDamagePreview.Shred += EffectDmg;
		MaxDamagePreview.Shred += EffectDmg;
	}

	if (TargetUnit != none)
	{
		foreach TargetUnit.AffectedByEffects(EffectRef)
		{
			EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
			EffectTemplate = EffectState.GetX2Effect();
			EffectDmg = EffectTemplate.GetDefendingDamageModifier(EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams, MinDamagePreview.Damage, self);
			MinDamagePreview.Damage += EffectDmg;
			if( EffectDmg != 0 )
			{
				DamageModInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
				DamageModInfo.Value = EffectDmg;
				MinDamagePreview.BonusDamageInfo.AddItem(DamageModInfo);
			}
			EffectDmg = EffectTemplate.GetDefendingDamageModifier(EffectState, SourceUnit, Damageable(TargetUnit), AbilityState, TestEffectParams, MaxDamagePreview.Damage, self);
			MaxDamagePreview.Damage += EffectDmg;
			if (EffectDmg != 0)
			{
				DamageModInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
				DamageModInfo.Value = EffectDmg;
				MaxDamagePreview.BonusDamageInfo.AddItem(DamageModInfo);
			}
		}
	}
	if (!bDoesDamageIgnoreShields)
		AllowsShield += MaxDamagePreview.Damage;
}

simulated function ApplyFalloff( out int WeaponDamage, Damageable Target, XComGameState_Item kSourceItem, XComGameState_Ability kAbility, XComGameState NewGameState )
{
	local XComGameStateContext_Ability AbilityContext;
	local float Radius, Interp, FloatDamage, HalfDamage;
	local int RadiusTiles, idx, ClosestRadius, TestRadius;
	local X2GameRulesetVisibilityInterface VisibilityTarget;
	local TTile TargetTile, VisibilityKeystone, TestTile;
	local array<TTile> AllTiles;
	local vector TargetPos;

	if (!IsExplosiveDamage()) // not explosive
		return;

	if (X2GrenadeTemplate( kSourceItem.GetMyTemplate() ) == none) // not a grenade
		return;

	AbilityContext = XComGameStateContext_Ability( NewGameState.GetContext() ); // not an ability
	if (AbilityContext == none)
		return;

	if (AbilityContext.InputContext.TargetLocations.Length == 0) // not location based targetting
		return;

	VisibilityTarget = X2GameRulesetVisibilityInterface( Target ); // don't know how to get location information
	if (VisibilityTarget == none)
		return;

	TargetPos = AbilityContext.InputContext.TargetLocations[ 0 ];
	TargetTile = `XWORLD.GetTileCoordinatesFromPosition( TargetPos );

	VisibilityTarget.GetKeystoneVisibilityLocation( VisibilityKeystone );
	VisibilityTarget.GetVisibilityLocation( AllTiles );

	// reduce all the tiles to the ones in the slice of the keystone Z
	for (idx = AllTiles.Length - 1; idx >= 0; --idx)
	{
		if (AllTiles[idx].Z > VisibilityKeystone.Z)
			AllTiles.Remove( idx, 1 );
	}

	Radius = kAbility.GetAbilityRadius( );
	RadiusTiles = Radius / class'XComWorldData'.const.WORLD_StepSize;

	ClosestRadius = RadiusTiles;
	foreach AllTiles( TestTile )
	{
		TestRadius = max( abs( TargetTile.X - TestTile.X ), abs( TargetTile.Y - TestTile.Y ) );
		ClosestRadius = min( ClosestRadius, TestRadius );
	}

	Interp = ClosestRadius / float(RadiusTiles);
	`assert( (Interp >= 0.0f) && (Interp <= 1.0f) );

	HalfDamage = WeaponDamage / 2.0f;
	FloatDamage = HalfDamage + (HalfDamage * (1.0 - Interp));
	WeaponDamage = round( FloatDamage );
}

// Cannot pass bools as out params, so use an int. Zero is false, non-zero is true.
simulated function int CalculateDamageAmount(const out EffectAppliedData ApplyEffectParameters, out int ArmorMitigation, out int NewRupture, out int NewShred, out array<Name> AppliedDamageTypes, out int bAmmoIgnoresShields, out int bFullyImmune, out array<DamageModifierInfo> SpecialDamageMessages, optional XComGameState NewGameState)
{
	local int TotalDamage, WeaponDamage, DamageSpread, ArmorPiercing, EffectDmg, CritDamage;

	local XComGameStateHistory History;
	local XComGameState_Unit kSourceUnit;
	local Damageable kTarget;
	local XComGameState_Unit TargetUnit;
	local XComGameState_Item kSourceItem, LoadedAmmo;
	local XComGameState_Ability kAbility;
	local XComGameState_Effect EffectState;
	local StateObjectReference EffectRef;
	local X2Effect_Persistent EffectTemplate;
	local WeaponDamageValue BaseDamageValue, ExtraDamageValue, BonusEffectDamageValue, AmmoDamageValue, UpgradeTemplateBonusDamage, UpgradeDamageValue;
	local X2AmmoTemplate AmmoTemplate;
	local int RuptureCap, RuptureAmount, OriginalMitigation, UnconditionalShred;
	local int EnvironmentDamage, TargetBaseDmgMod;
	local XComDestructibleActor kDestructibleActorTarget;
	local array<X2WeaponUpgradeTemplate> WeaponUpgradeTemplates;
	local X2WeaponUpgradeTemplate WeaponUpgradeTemplate;
	local DamageModifierInfo ModifierInfo;
	local bool bWasImmune, bHadAnyDamage;

	ArmorMitigation = 0;
	NewRupture = 0;
	NewShred = 0;
	EnvironmentDamage = 0;
	bAmmoIgnoresShields = 0;    // FALSE
	bWasImmune = true;			//	as soon as we check any damage we aren't immune to, this will be set false
	bHadAnyDamage = false;

	//Cheats can force the damage to a specific value
	if (`CHEATMGR != none && `CHEATMGR.NextShotDamageRigged )
	{
		`CHEATMGR.NextShotDamageRigged = false;
		return `CHEATMGR.NextShotDamage;
	}

	History = `XCOMHISTORY;
	kSourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(ApplyEffectParameters.SourceStateObjectRef.ObjectID));
	kTarget = Damageable(History.GetGameStateForObjectID(ApplyEffectParameters.TargetStateObjectRef.ObjectID));
	kDestructibleActorTarget = XComDestructibleActor(History.GetVisualizer(ApplyEffectParameters.TargetStateObjectRef.ObjectID));
	kAbility = XComGameState_Ability(History.GetGameStateForObjectID(ApplyEffectParameters.AbilityStateObjectRef.ObjectID));
	if (kAbility != none && kAbility.SourceAmmo.ObjectID > 0)
		kSourceItem = XComGameState_Item(History.GetGameStateForObjectID(kAbility.SourceAmmo.ObjectID));
	else
		kSourceItem = XComGameState_Item(History.GetGameStateForObjectID(ApplyEffectParameters.ItemStateObjectRef.ObjectID));

	if( bAlwaysKillsCivilians )
	{
		TargetUnit = XComGameState_Unit(kTarget);

		if( (TargetUnit != none) && (TargetUnit.GetTeam() == eTeam_Neutral) )
		{
			// Return the amount of health the civlian has so that it will be euthanized
			return TargetUnit.GetCurrentStat(eStat_HP) + TargetUnit.GetCurrentStat(eStat_ShieldHP);
		}
	}

	`log("===" $ GetFuncName() $ "===", true, 'XCom_HitRolls');

	if (kSourceItem != none)
	{
		`log("Attacker ID:" @ kSourceUnit.ObjectID @ "With Item ID:" @ kSourceItem.ObjectID @ "Target ID:" @ ApplyEffectParameters.TargetStateObjectRef.ObjectID, true, 'XCom_HitRolls');
		if (!bIgnoreBaseDamage)
		{
			kSourceItem.GetBaseWeaponDamageValue(XComGameState_BaseObject(kTarget), BaseDamageValue);
			EnvironmentDamage += kSourceItem.GetItemEnvironmentDamage();
			if (BaseDamageValue.Damage > 0) bHadAnyDamage = true;

			bWasImmune = bWasImmune && ModifyDamageValue(BaseDamageValue, kTarget, AppliedDamageTypes);
		}
		if (DamageTag != '')
		{
			kSourceItem.GetWeaponDamageValue(XComGameState_BaseObject(kTarget), DamageTag, ExtraDamageValue);
			if (ExtraDamageValue.Damage > 0) bHadAnyDamage = true;

			bWasImmune = bWasImmune && ModifyDamageValue(ExtraDamageValue, kTarget, AppliedDamageTypes);
		}
		if (kSourceItem.HasLoadedAmmo() && !bIgnoreBaseDamage)
		{
			LoadedAmmo = XComGameState_Item(History.GetGameStateForObjectID(kSourceItem.LoadedAmmo.ObjectID));
			AmmoTemplate = X2AmmoTemplate(LoadedAmmo.GetMyTemplate());
			if (AmmoTemplate != none)
			{
				AmmoTemplate.GetTotalDamageModifier(LoadedAmmo, kSourceUnit, XComGameState_BaseObject(kTarget), AmmoDamageValue);
				if (AmmoTemplate.bBypassShields)
				{
					bAmmoIgnoresShields = 1;  // TRUE
				}
			}
			else
			{
				LoadedAmmo.GetBaseWeaponDamageValue(XComGameState_BaseObject(kTarget), AmmoDamageValue);
				EnvironmentDamage += LoadedAmmo.GetItemEnvironmentDamage();
			}
			if (AmmoDamageValue.Damage > 0) bHadAnyDamage = true;
			bWasImmune = bWasImmune && ModifyDamageValue(AmmoDamageValue, kTarget, AppliedDamageTypes);
		}
		if (bAllowWeaponUpgrade)
		{
			WeaponUpgradeTemplates = kSourceItem.GetMyWeaponUpgradeTemplates();
			foreach WeaponUpgradeTemplates(WeaponUpgradeTemplate)
			{
				if (WeaponUpgradeTemplate.BonusDamage.Tag == DamageTag)
				{
					UpgradeTemplateBonusDamage = WeaponUpgradeTemplate.BonusDamage;

					if (UpgradeTemplateBonusDamage.Damage > 0) bHadAnyDamage = true;
					bWasImmune = bWasImmune && ModifyDamageValue(UpgradeTemplateBonusDamage, kTarget, AppliedDamageTypes);

					UpgradeDamageValue.Damage += UpgradeTemplateBonusDamage.Damage;
					UpgradeDamageValue.Spread += UpgradeTemplateBonusDamage.Spread;
					UpgradeDamageValue.Crit += UpgradeTemplateBonusDamage.Crit;
					UpgradeDamageValue.Pierce += UpgradeTemplateBonusDamage.Pierce;
					UpgradeDamageValue.Rupture += UpgradeTemplateBonusDamage.Rupture;
					UpgradeDamageValue.Shred += UpgradeTemplateBonusDamage.Shred;
					//  ignores PlusOne as there is no good way to add them up
				}
			}
		}
	}

	// non targeted objects take the environmental damage amount
	if( kDestructibleActorTarget != None && !kDestructibleActorTarget.IsTargetable() )
	{
		return EnvironmentDamage;
	}

	BonusEffectDamageValue = GetBonusEffectDamageValue(kAbility, kSourceUnit, kSourceItem, ApplyEffectParameters.TargetStateObjectRef);
	if (BonusEffectDamageValue.Damage > 0 || BonusEffectDamageValue.Crit > 0 || BonusEffectDamageValue.Pierce > 0 || BonusEffectDamageValue.PlusOne > 0 ||
		BonusEffectDamageValue.Rupture > 0 || BonusEffectDamageValue.Shred > 0 || BonusEffectDamageValue.Spread > 0)
	{
		bWasImmune = bWasImmune && ModifyDamageValue(BonusEffectDamageValue, kTarget, AppliedDamageTypes);
		bHadAnyDamage = true;
	}

	WeaponDamage = BaseDamageValue.Damage + ExtraDamageValue.Damage + BonusEffectDamageValue.Damage + AmmoDamageValue.Damage + UpgradeDamageValue.Damage;
	DamageSpread = BaseDamageValue.Spread + ExtraDamageValue.Spread + BonusEffectDamageValue.Spread + AmmoDamageValue.Spread + UpgradeDamageValue.Spread;
	CritDamage = BaseDamageValue.Crit + ExtraDamageValue.Crit + BonusEffectDamageValue.Crit + AmmoDamageValue.Crit + UpgradeDamageValue.Crit;
	ArmorPiercing = BaseDamageValue.Pierce + ExtraDamageValue.Pierce + BonusEffectDamageValue.Pierce + AmmoDamageValue.Pierce + UpgradeDamageValue.Pierce;
	NewRupture = BaseDamageValue.Rupture + ExtraDamageValue.Rupture + BonusEffectDamageValue.Rupture + AmmoDamageValue.Rupture + UpgradeDamageValue.Rupture;
	NewShred = BaseDamageValue.Shred + ExtraDamageValue.Shred + BonusEffectDamageValue.Shred + AmmoDamageValue.Shred + UpgradeDamageValue.Shred;
	RuptureCap = WeaponDamage;

	`log(`ShowVar(bIgnoreBaseDamage) @ `ShowVar(DamageTag), true, 'XCom_HitRolls');
	`log("Weapon damage:" @ WeaponDamage @ "Potential spread:" @ DamageSpread, true, 'XCom_HitRolls');

	if (DamageSpread > 0)
	{
		WeaponDamage += DamageSpread;                          //  set to max damage based on spread
		WeaponDamage -= `SYNC_RAND(DamageSpread * 2 + 1);      //  multiply spread by 2 to get full range off base damage, add 1 as rand returns 0:x-1, not 0:x
	}
	`log("Damage with spread:" @ WeaponDamage, true, 'XCom_HitRolls');
	if (PlusOneDamage(BaseDamageValue.PlusOne))
	{
		WeaponDamage++;
		`log("Rolled for PlusOne off BaseDamage, succeeded. Damage:" @ WeaponDamage, true, 'XCom_HitRolls');
	}
	if (PlusOneDamage(ExtraDamageValue.PlusOne))
	{
		WeaponDamage++;
		`log("Rolled for PlusOne off ExtraDamage, succeeded. Damage:" @ WeaponDamage, true, 'XCom_HitRolls');
	}
	if (PlusOneDamage(BonusEffectDamageValue.PlusOne))
	{
		WeaponDamage++;
		`log("Rolled for PlusOne off BonusEffectDamage, succeeded. Damage:" @ WeaponDamage, true, 'XCom_HitRolls');
	}
	if (PlusOneDamage(AmmoDamageValue.PlusOne))
	{
		WeaponDamage++;
		`log("Rolled for PlusOne off AmmoDamage, succeeded. Damage:" @ WeaponDamage, true, 'XCom_HitRolls');
	}

	if (ApplyEffectParameters.AbilityResultContext.HitResult == eHit_Crit)
	{
		WeaponDamage += CritDamage;
		`log("CRIT! Adjusted damage:" @ WeaponDamage, true, 'XCom_HitRolls');
	}
	else if (ApplyEffectParameters.AbilityResultContext.HitResult == eHit_Graze)
	{
		WeaponDamage *= GRAZE_DMG_MULT;
		`log("GRAZE! Adjusted damage:" @ WeaponDamage, true, 'XCom_HitRolls');
	}

	RuptureAmount = min(kTarget.GetRupturedValue() + NewRupture, RuptureCap);
	if (RuptureAmount != 0)
	{
		WeaponDamage += RuptureAmount;
		`log("Target is ruptured, increases damage by" @ RuptureAmount $", new damage:" @ WeaponDamage, true, 'XCom_HitRolls');
	}

	if( kSourceUnit != none)
	{
		//  allow target effects that modify only the base damage
		TargetUnit = XComGameState_Unit(kTarget);
		if (TargetUnit != none)
		{
			foreach TargetUnit.AffectedByEffects(EffectRef)
			{
				EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
				EffectTemplate = EffectState.GetX2Effect();
				EffectDmg = EffectTemplate.GetBaseDefendingDamageModifier(EffectState, kSourceUnit, kTarget, kAbility, ApplyEffectParameters, WeaponDamage, self, NewGameState);
				if (EffectDmg != 0)
				{
					TargetBaseDmgMod += EffectDmg;
					`log("Defender effect" @ EffectTemplate.EffectName @ "adjusting base damage by" @ EffectDmg, true, 'XCom_HitRolls');

					if (EffectTemplate.bDisplayInSpecialDamageMessageUI)
					{
						ModifierInfo.Value = EffectDmg;
						ModifierInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
						ModifierInfo.SourceID = EffectRef.ObjectID;
						SpecialDamageMessages.AddItem(ModifierInfo);
					}
				}
			}
			if (TargetBaseDmgMod != 0)
			{
				WeaponDamage += TargetBaseDmgMod;
				`log("Total base damage after defender effect mods:" @ WeaponDamage, true, 'XCom_HitRolls');
			}
		}

		//  Allow attacker effects to modify damage output before applying final bonuses and reductions
		foreach kSourceUnit.AffectedByEffects(EffectRef)
		{
			ModifierInfo.Value = 0;

			EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
			EffectTemplate = EffectState.GetX2Effect();
			EffectDmg = EffectTemplate.GetAttackingDamageModifier(EffectState, kSourceUnit, kTarget, kAbility, ApplyEffectParameters, WeaponDamage, NewGameState);
			if (EffectDmg != 0)
			{
				WeaponDamage += EffectDmg;
				`log("Attacker effect" @ EffectTemplate.EffectName @ "adjusting damage by" @ EffectDmg $ ", new damage:" @ WeaponDamage, true, 'XCom_HitRolls');

				ModifierInfo.Value += EffectDmg;
			}
			EffectDmg = EffectTemplate.GetExtraArmorPiercing(EffectState, kSourceUnit, kTarget, kAbility, ApplyEffectParameters);
			if (EffectDmg != 0)
			{
				ArmorPiercing += EffectDmg;
				`log("Attacker effect" @ EffectTemplate.EffectName @ "adjusting armor piercing by" @ EffectDmg $ ", new pierce:" @ ArmorPiercing, true, 'XCom_HitRolls');

				ModifierInfo.Value += EffectDmg;
			}
			EffectDmg = EffectTemplate.GetExtraShredValue(EffectState, kSourceUnit, kTarget, kAbility, ApplyEffectParameters);
			if (EffectDmg != 0)
			{
				NewShred += EffectDmg;
				`log("Attacker effect" @ EffectTemplate.EffectName @ "adjust new shred value by" @ EffectDmg $ ", new shred:" @ NewShred, true, 'XCom_HitRolls');

				ModifierInfo.Value += EffectDmg;
			}

			if( ModifierInfo.Value != 0 && EffectTemplate.bDisplayInSpecialDamageMessageUI )
			{
				ModifierInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
				ModifierInfo.SourceID = EffectRef.ObjectID;
				SpecialDamageMessages.AddItem(ModifierInfo);
			}
		}

		// run through the effects again for any conditional shred.  A second loop as it is possibly dependent on shred outcome of the unconditional first loop.
		UnconditionalShred = NewShred;
		foreach kSourceUnit.AffectedByEffects(EffectRef)
		{
			ModifierInfo.Value = 0;
			EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
			EffectTemplate = EffectState.GetX2Effect();

			EffectDmg = EffectTemplate.GetConditionalExtraShredValue(UnconditionalShred, EffectState, kSourceUnit, kTarget, kAbility, ApplyEffectParameters);
			if (EffectDmg != 0)
			{
				NewShred += EffectDmg;
				`log("Attacker effect" @ EffectTemplate.EffectName @ "adjust new shred value by" @ EffectDmg $ ", new shred:" @ NewShred, true, 'XCom_HitRolls');

				ModifierInfo.Value += EffectDmg;
			}

			if( ModifierInfo.Value != 0 && EffectTemplate.bDisplayInSpecialDamageMessageUI )
			{
				ModifierInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
				ModifierInfo.SourceID = EffectRef.ObjectID;
				SpecialDamageMessages.AddItem(ModifierInfo);
			}
		}

		//  If this is a unit, apply any effects that modify damage
		TargetUnit = XComGameState_Unit(kTarget);
		if (TargetUnit != none)
		{
			foreach TargetUnit.AffectedByEffects(EffectRef)
			{
				EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
				EffectTemplate = EffectState.GetX2Effect();
				EffectDmg = EffectTemplate.GetDefendingDamageModifier(EffectState, kSourceUnit, kTarget, kAbility, ApplyEffectParameters, WeaponDamage, self, NewGameState);
				if (EffectDmg != 0)
				{
					WeaponDamage += EffectDmg;
					`log("Defender effect" @ EffectTemplate.EffectName @ "adjusting damage by" @ EffectDmg $ ", new damage:" @ WeaponDamage, true, 'XCom_HitRolls');

					if( EffectTemplate.bDisplayInSpecialDamageMessageUI )
					{
						ModifierInfo.Value = EffectDmg;
						ModifierInfo.SourceEffectRef = EffectState.ApplyEffectParameters.EffectRef;
						ModifierInfo.SourceID = EffectRef.ObjectID;
						SpecialDamageMessages.AddItem(ModifierInfo);
					}
				}
			}
		}

		if (kTarget != none && !bIgnoreArmor)
		{
			ArmorMitigation = kTarget.GetArmorMitigation(ApplyEffectParameters.AbilityResultContext.ArmorMitigation);
			if (ArmorMitigation != 0)
			{
				OriginalMitigation = ArmorMitigation;
				ArmorPiercing += kSourceUnit.GetCurrentStat(eStat_ArmorPiercing);
				`log("Armor mitigation! Target armor mitigation value:" @ ArmorMitigation @ "Attacker armor piercing value:" @ ArmorPiercing, true, 'XCom_HitRolls');
				ArmorMitigation -= ArmorPiercing;
				if (ArmorMitigation < 0)
					ArmorMitigation = 0;
				if (ArmorMitigation >= WeaponDamage)
					ArmorMitigation = WeaponDamage - 1;
				if (ArmorMitigation < 0)    //  WeaponDamage could have been 0
					ArmorMitigation = 0;
				`log("  Final mitigation value:" @ ArmorMitigation, true, 'XCom_HitRolls');
			}
		}
	}
	//Shred can only shred as much as the maximum armor mitigation
	NewShred = Min(NewShred, OriginalMitigation);

	if (`SecondWaveEnabled('ExplosiveFalloff'))
	{
		ApplyFalloff( WeaponDamage, kTarget, kSourceItem, kAbility, NewGameState );
	}

	TotalDamage = WeaponDamage - ArmorMitigation;

	if ((WeaponDamage > 0 && TotalDamage < 0) || (WeaponDamage < 0 && TotalDamage > 0))
	{
		// Do not allow the damage reduction to invert the damage amount (i.e. heal instead of hurt, or vice-versa).
		TotalDamage = 0;
	}
	`log("Total Damage:" @ TotalDamage, true, 'XCom_HitRolls');
	`log("Shred from this attack:" @ NewShred, NewShred > 0, 'XCom_HitRolls');

	// Set the effect's damage
	bFullyImmune = (bWasImmune && bHadAnyDamage) ? 1 : 0;
	`log("FULLY IMMUNE", bFullyImmune == 1, 'XCom_HitRolls');

	return TotalDamage;
}

//	returns true if the unit is completely immune to the damage
simulated function bool ModifyDamageValue(out WeaponDamageValue DamageValue, Damageable Target, out array<Name> AppliedDamageTypes)
{
	local WeaponDamageValue EmptyDamageValue;

	if( Target != None )
	{
		if( Target.IsImmuneToDamage(DamageValue.DamageType) )
		{
			`log("Target is immune to damage type" @ DamageValue.DamageType $ "!", true, 'XCom_HitRolls');
			DamageValue = EmptyDamageValue;
			return true;
		}
		else if( AppliedDamageTypes.Find(DamageValue.DamageType) == INDEX_NONE )
		{
			AppliedDamageTypes.AddItem(DamageValue.DamageType);
		}
	}
	return false;
}

simulated function bool PlusOneDamage(int Chance)
{
	return `SYNC_RAND(100) < Chance;
}

simulated function bool IsExplosiveDamage()
{
	return bExplosiveDamage;
}

simulated function AddX2ActionsForVisualization(XComGameState VisualizeGameState, out VisualizationActionMetadata ActionMetadata, name EffectApplyResult)
{
	local X2Action_ApplyWeaponDamageToUnit UnitAction;
	local X2Action_PlaySoundAndFlyOver FlyOverAction;
	local XComGameState_Unit UnitState;
	local XComGameStateContext_Ability AbilityContext;
	local XComGameStateVisualizationMgr VisMgr;
	local X2Action_Fire FireAction;
	local Array<X2Action> ParentArray;
	local Actor SourceVisualizer;
	local X2Action_MoveTurn TurnAction;
	local XComGameState_Unit SourceState;
	local XComGameStateHistory History;
	local XComWorldData XWorld;
	local Vector SourceLocation;
	local X2Action_ExitCover SourceExitCover;

	local name EffectName;
	local int x, OverwatchExclusion;
	local bool bRemovedEffect;

	History = `XCOMHISTORY;
	VisMgr = `XCOMVISUALIZATIONMGR;
	XWorld = `XWORLD;

	if( ActionMetadata.StateObject_NewState.IsA('XComGameState_Unit') )
	{
		if ((HideVisualizationOfResults.Find(EffectApplyResult) != INDEX_NONE) ||
			(HideVisualizationOfResultsAdditional.Find(EffectApplyResult) != INDEX_NONE))
		{
			return;
		}

		AbilityContext = XComGameStateContext_Ability(VisualizeGameState.GetContext());
		if( AbilityContext.ResultContext.HitResult == eHit_Deflect || AbilityContext.ResultContext.HitResult == eHit_Parry || AbilityContext.ResultContext.HitResult == eHit_Reflect )
		{
			SourceState = XComGameState_Unit(History.GetGameStateForObjectID(AbilityContext.InputContext.SourceObject.ObjectID, eReturnType_Reference, VisualizeGameState.HistoryIndex));
			SourceVisualizer = History.GetVisualizer(SourceState.ObjectID);
			FireAction = X2Action_Fire(VisMgr.GetNodeOfType(VisMgr.BuildVisTree, class'X2Action_Fire', SourceVisualizer));
			SourceExitCover = X2Action_ExitCover(VisMgr.GetNodeOfType(VisMgr.BuildVisTree, class'X2Action_ExitCover', SourceVisualizer));

			// Jwats: Have the target face the attacker during the exit cover. (unless we are doing a reflect shot since we'll step out and aim)
			if( AbilityContext.ResultContext.HitResult != eHit_Reflect )
			{
				SourceLocation = XWorld.GetPositionFromTileCoordinates(SourceState.TileLocation);
				TurnAction = X2Action_MoveTurn(VisMgr.GetNodeOfType(VisMgr.BuildVisTree, class'X2Action_MoveTurn', ActionMetadata.VisualizeActor));
				if( TurnAction == None || TurnAction.m_vFacePoint != SourceLocation )
				{
					TurnAction = X2Action_MoveTurn(class'X2Action_MoveTurn'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(), false, , SourceExitCover.ParentActions));
					TurnAction.ParsePathSetParameters(SourceLocation);

					// Jwats: Make sure fire doesn't start until the target is facing us.
					VisMgr.ConnectAction(FireAction, VisMgr.BuildVisTree, false, TurnAction);
				}
			}

			// Jwats: For Parry and Deflect we want to start the fire animation and the hit reaction at the same time
			if( FireAction != None )
			{
				ParentArray = FireAction.ParentActions;
			}
		}

		UnitAction = X2Action_ApplyWeaponDamageToUnit(class'X2Action_ApplyWeaponDamageToUnit'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(),,, ParentArray));//auto-parent to damage initiating action
		UnitAction.OriginatingEffect = self;

		if (EffectApplyResult == 'AA_Success')
		{
			UnitState = XComGameState_Unit(ActionMetadata.StateObject_NewState);
			if (XComGameState_Unit(ActionMetadata.StateObject_OldState).NumAllReserveActionPoints() > 0 && UnitState.NumAllReserveActionPoints() == 0)
			{
				FlyOverAction = X2Action_PlaySoundAndFlyOver(class'X2Action_PlaySoundAndFlyOver'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(), false, ActionMetadata.LastActionAdded));
				bRemovedEffect = false;
				for (x = 0; x < UnitState.AppliedEffects.Length; ++x)
				{
					EffectName = UnitState.AppliedEffectNames[x];

					if (EffectName == class'X2Effect_Suppression'.default.EffectName)
					{
						FlyOverAction.SetSoundAndFlyOverParameters(none, class'XLocalizedData'.default.SuppressionRemovedMsg, '', eColor_Bad);
						bRemovedEffect = true;
					}
				}

				if (!bRemovedEffect)
				{
					FlyOverAction.SetSoundAndFlyOverParameters(none, class'XLocalizedData'.default.OverwatchRemovedMsg, '', eColor_Bad);
				}
			}
		}
		else
		{
			OverwatchExclusion = class'X2Ability_DefaultAbilitySet'.default.OverwatchExcludeReasons.Find(EffectApplyResult);
			if (OverwatchExclusion != INDEX_NONE)
			{
				FlyOverAction = X2Action_PlaySoundAndFlyOver(class'X2Action_PlaySoundAndFlyOver'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(), false, ActionMetadata.LastActionAdded));
				FlyOverAction.SetSoundAndFlyOverParameters(none, class'X2AbilityTemplateManager'.static.GetDisplayStringForAvailabilityCode(EffectApplyResult), '', eColor_Bad);
			}
			else
			{
				super.AddX2ActionsForVisualization(VisualizeGameState, ActionMetadata, EffectApplyResult);
			}
		}
	}
	else if( ActionMetadata.StateObject_NewState.IsA('XComGameState_EnvironmentDamage')
			|| ActionMetadata.StateObject_NewState.IsA('XComGameState_Destructible') )
	{
		if(EffectApplyResult == 'AA_Success')
		{
			//All non-unit damage is routed through XComGameState_EnvironmentDamage state objects, which represent an environmental damage event
			//It is expected that LastActionAdded will be none (causing the action to be autoparented) in most cases.
			//However we pass it in so that when building visualizations, callers can get the action parented to the right thing if they need it to be.
			class'X2Action_ApplyWeaponDamageToTerrain'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(), , ActionMetadata.LastActionAdded );
		}
	}
}

simulated function AddX2ActionsForVisualization_Tick(XComGameState VisualizeGameState, out VisualizationActionMetadata ActionMetadata, const int TickIndex, XComGameState_Effect EffectState)
{
	local X2Action_CameraLookAt LookAtAction;
	local X2Action_Delay DelayAction;
	local Actor UnitVisualizer;
	local X2Action_ApplyWeaponDamageToUnit UnitAction;
	local XComGameStateContext_TickEffect TickContext;
	local XComGameState_Effect TickedEffect;

	if( ActionMetadata.StateObject_NewState.IsA('XComGameState_Unit') )
	{
		//  cosmetic units should not take damage
		if (XComGameState_Unit(ActionMetadata.StateObject_NewState).GetMyTemplate().bIsCosmetic)
			return;

		UnitVisualizer = XComGameState_Unit(ActionMetadata.StateObject_NewState).GetVisualizer();
		LookAtAction = X2Action_CameraLookAt( class'X2Action_CameraLookAt'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(), false, ActionMetadata.LastActionAdded));
		LookAtAction.LookAtActor = UnitVisualizer;
		LookAtAction.BlockUntilActorOnScreen = true;
		LookAtAction.UseTether = false;
		LookAtAction.LookAtDuration = 2.0f;
		LookAtAction.DesiredCameraPriority = eCameraPriority_GameActions;

		UnitAction = X2Action_ApplyWeaponDamageToUnit(class'X2Action_ApplyWeaponDamageToUnit'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext()));//auto-parent to damage initiating action
		UnitAction.TickIndex = TickIndex;

		UnitAction.OriginatingEffect = self;

		//The "ancestor effect" for the weapon damage action should be the ticking effect that caused us.
		//This is needed to correctly identify the damage event to use.
		TickContext = XComGameStateContext_TickEffect(VisualizeGameState.GetContext());
		if (TickContext != None && TickContext.TickedEffect.ObjectID != 0)
		{
			TickedEffect = XComGameState_Effect(VisualizeGameState.GetGameStateForObjectID(TickContext.TickedEffect.ObjectID));
			if (TickedEffect != None)
				UnitAction.AncestorEffect = TickedEffect.GetX2Effect();
		}

		// the camera action is not longer blocking (but will hang out for a short duration), so add a delay function to
		// prevent further visualization from occuring
		DelayAction = X2Action_Delay(class'X2Action_Delay'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(), false, ActionMetadata.LastActionAdded));
		DelayAction.Duration = LookAtAction.LookAtDuration;
	}
	else if( ActionMetadata.StateObject_NewState.IsA('XComGameState_EnvironmentDamage')
			|| ActionMetadata.StateObject_NewState.IsA('XComGameState_Destructible') )
	{
			//All non-unit damage is routed through XComGameState_EnvironmentDamage state objects, which represent an environmental damage event
			class'X2Action_ApplyWeaponDamageToTerrain'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext());//auto-parent to damage initiating action
	}
}

function GetEffectDamageTypes(XComGameState GameState, EffectAppliedData EffectData, out array<name> EffectDamageTypes)
{
	local XComGameState_Item SourceWeapon;
	local XComGameState_Ability AbilityState;
	local XComGameStateHistory History;
	local XComGameState_Unit SourceUnit, TargetUnit;
	local ApplyDamageInfo DamageInfo;

	super.GetEffectDamageTypes(GameState, EffectData, EffectDamageTypes);

	History = `XCOMHISTORY;

	AbilityState = XComGameState_Ability(History.GetGameStateForObjectID(EffectData.AbilityStateObjectRef.ObjectID));

	if (AbilityState != none)
	{
		if (AbilityState.SourceAmmo.ObjectID > 0)
		{
			SourceWeapon = AbilityState.GetSourceAmmo();
		}
		else
		{
			SourceWeapon = AbilityState.GetSourceWeapon();
		}
	}

	if (GameState != none)
		SourceUnit = XComGameState_Unit(GameState.GetGameStateForObjectID(EffectData.SourceStateObjectRef.ObjectID));
	if (SourceUnit == none)
	{
		SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(EffectData.SourceStateObjectRef.ObjectID));
	}

	if (GameState != none)
		TargetUnit = XComGameState_Unit(GameState.GetGameStateForObjectID(EffectData.TargetStateObjectRef.ObjectID));
	if (TargetUnit == none)
	{
		TargetUnit = XComGameState_Unit(History.GetGameStateForObjectID(EffectData.TargetStateObjectRef.ObjectID));
	}

	CalculateDamageValues(SourceWeapon, SourceUnit, TargetUnit, AbilityState, DamageInfo, EffectDamageTypes);
}

function CalculateDamageValues(XComGameState_Item SourceWeapon, XComGameState_Unit SourceUnit, XComGameState_Unit TargetUnit, XComGameState_Ability AbilityState, out ApplyDamageInfo DamageInfo, out array<Name> AppliedDamageTypes)
{
	local XComGameState_Item LoadedAmmo;
	local XComGameStateHistory History;
	local WeaponDamageValue UpgradeTemplateBonusDamage;
	local array<X2WeaponUpgradeTemplate> WeaponUpgradeTemplates;
	local X2WeaponUpgradeTemplate WeaponUpgradeTemplate;
	local X2AmmoTemplate AmmoTemplate;

	if (SourceWeapon != None)
	{
		if (!bIgnoreBaseDamage)
		{
			SourceWeapon.GetBaseWeaponDamageValue(TargetUnit, DamageInfo.BaseDamageValue);
			ModifyDamageValue(DamageInfo.BaseDamageValue, TargetUnit, AppliedDamageTypes);
		}

		if (DamageTag != '')
		{
			SourceWeapon.GetWeaponDamageValue(TargetUnit, DamageTag, DamageInfo.ExtraDamageValue);
			ModifyDamageValue(DamageInfo.ExtraDamageValue, TargetUnit, AppliedDamageTypes);
		}

		if (SourceWeapon.HasLoadedAmmo() && !bIgnoreBaseDamage)
		{
			History = `XCOMHISTORY;

			LoadedAmmo = XComGameState_Item(History.GetGameStateForObjectID(SourceWeapon.LoadedAmmo.ObjectID));
			AmmoTemplate = X2AmmoTemplate(LoadedAmmo.GetMyTemplate());
			if (AmmoTemplate != None)
			{
				AmmoTemplate.GetTotalDamageModifier(LoadedAmmo, SourceUnit, TargetUnit, DamageInfo.AmmoDamageValue);
				DamageInfo.bDoesDamageIgnoreShields = AmmoTemplate.bBypassShields || DamageInfo.bDoesDamageIgnoreShields;
			}
			else
			{
				LoadedAmmo.GetBaseWeaponDamageValue(TargetUnit, DamageInfo.AmmoDamageValue);
			}

			ModifyDamageValue(DamageInfo.AmmoDamageValue, TargetUnit, AppliedDamageTypes);
		}

		if (bAllowWeaponUpgrade)
		{
			WeaponUpgradeTemplates = SourceWeapon.GetMyWeaponUpgradeTemplates();
			foreach WeaponUpgradeTemplates(WeaponUpgradeTemplate)
			{
				if (WeaponUpgradeTemplate.BonusDamage.Tag == DamageTag)
				{
					UpgradeTemplateBonusDamage = WeaponUpgradeTemplate.BonusDamage;

					ModifyDamageValue(UpgradeTemplateBonusDamage, TargetUnit, AppliedDamageTypes);

					DamageInfo.UpgradeDamageValue.Spread += UpgradeTemplateBonusDamage.Spread;
					DamageInfo.UpgradeDamageValue.Damage += UpgradeTemplateBonusDamage.Damage;
					DamageInfo.UpgradeDamageValue.Crit += UpgradeTemplateBonusDamage.Crit;
					DamageInfo.UpgradeDamageValue.Pierce += UpgradeTemplateBonusDamage.Pierce;
					DamageInfo.UpgradeDamageValue.Rupture += UpgradeTemplateBonusDamage.Rupture;
					DamageInfo.UpgradeDamageValue.Shred += UpgradeTemplateBonusDamage.Shred;
					//  ignores PlusOne as there is no good way to add them up
				}
			}
		}
	}

	DamageInfo.BonusEffectDamageValue = GetBonusEffectDamageValue(AbilityState, SourceUnit, SourceWeapon, TargetUnit.GetReference());
	ModifyDamageValue(DamageInfo.BonusEffectDamageValue, TargetUnit, AppliedDamageTypes);
}

defaultproperties
{
	bApplyWorldEffectsForEachTargetLocation=false
	bAllowFreeKill=true
	bAppliesDamage=true
}