Custom Sleeper Scripts/Textures PT.2

Started by Nicant, October 27, 2015, 06:21:07 PM

Previous topic - Next topic

Nicant

I ran out of room on part 1 so i had to split it into two parts  :P
Make sure you did not miss part 1. Part 1 of the post!
Edit: changed the code boxes into teletype boxes. Hope it made reading easier!  :)
Edit2: got ride of smilies on the post cause it was interfering with the code  ;)
Get my Sleeper template if you want to use the custom units! (You can get it here!) I am no longer updating this or part 1 of the thread.

Any questions or need help? ask me!


You can use these in your maps!


Creep Bomber Script:
Spoiler
# CBomber.crpl
# Created on: 10/25/2015 1:52:52 PM
# ------------------------------------------

# VERSION 1.0

$health:5.0
$ammo:-1

$speed:1.5
$rotSpeed:0.1

$BombPayload:5.0
$BombDamage:5.0
$shotDistance:1
$cooldown:15
$reach:5

$sight:120

$C_BOMBER_BASE:-1
$SPAWN_FULL_AMMO:0
$IS_LANDED:1

# THIS SCRIPT DOES NOT SUPPORT RUNNING WHILE PAUSED.
# However, due to internal bugs occasionally CW3 will still give them frame time
# even though nowhere is it explicitly stated they are allowed to run while paused.
IsPaused if
   1 SuspendMove
   return
endif

once
   # internal variables
   @AI_NONE ->ai
   @AN_NONE ->anim
   -1 ->target
   #alternate target
   -1 ->targetX
   -1 ->targetY
   0 ->ai_stage
   
   
   self "main" "Custom29" SetImage
   self "main" -0.1 SetImagePositionZ
   
   self CONST_DESTROYMODE 3 SetUnitAttribute
   self CONST_CREATEPZ 0 SetUnitAttribute
   self CONST_TAKEMAPSPACE 0 SetUnitAttribute
   self CONST_SUPPORTSDIGITALIS 0 SetUnitAttribute
   self CONST_NULLIFIERDAMAGES 0 SetUnitAttribute
   self CONST_COUNTSFORVICTORY 0 SetUnitAttribute
   self CONST_SNIPERTARGET 0 SetUnitAttribute
   self CONST_SNIPERIGNORELOS 0 SetUnitAttribute
   self CONST_MAXHEALTH <-health SetUnitAttribute
   self CONST_HEALTH <-health SetUnitAttribute
   self CONST_HEALRATE 0 SetUnitAttribute
   
   <-ammo -1 neq if
      self CONST_MAXAMMO <-ammo SetUnitAttribute
      <-SPAWN_FULL_AMMO or(not(@HasBase)) if
         self CONST_AMMO <-ammo SetUnitAttribute
      else
         self CONST_AMMO 0 SetUnitAttribute
         @AI_REFILLING ->ai
      endif
      self CONST_SHOWAMMOBAR true SetUnitAttribute
   endif
   
   @BeginList
      "ALL:BUILT,LANDED"
      "COMMANDNODE"
      "COLLECTOR:RANK=1.5" "RELAY:RANK=1.5" "REACTOR:rank=1.5" "OREMINE:rank=1.65" "SIPHON:rank=1.5" "TERP" "GUPPY"
      "PULSECANNON" "MORTAR" "STRAFER" "BOMBER" "SPRAYER" "NULLIFIER:rank=5" "BEAM" "SNIPER:rank=3"
      "SHIELD:rank=1.5"
      "FORGE:rank=1.5"
      "BERTHA"
   @BuildList ->TARGETS
endonce

<-IS_LANDED not if
   GetTimer3 eq0 if
      #Create exhaust trail
      self "main" GetImageRotation ->rot
      CurrentPixelCoords ->qy ->qx
      <-rot add(PI) 14 @GetPixelVector ->vx ->vy
      <-qy add(<-vy) ->qy
      <-qx add(<-vx) ->qx
      
      <-rot add(PI div(2)) 8 @GetPixelVector ->vx ->vy
      <-qy add(<-vy) ->py
      <-qx add(<-vx) ->px
      11 <-px <-py -0.09 0.12 0.12 0.025 CreateEffect
      CurrentPixelCoords ->py ->px
      <-rot add(neg(PI div(2))) 8 @GetPixelVector ->vx ->vy
      <-qy add(<-vy) ->py
      <-qx add(<-vx) ->px
      11 <-px <-py -0.09 0.12 0.12 0.025 CreateEffect
      5 SetTimer3
   endif
endif

<-anim eq(@AN_NONE) if
   GetTimer0 eq0 if
      @AI
   endif
else
   @Animate
endif

#AI STATE
:AI_NONE 0 :AI_ENGAGING 1 :AI_ROAMING 2 :AI_TRAILING 3 :AI_REFILLING 4
#ANIMATION STATE
:AN_NONE 0 :AN_LANDING 1 :AN_LIFTOFF 2

:AI
   @HandleTarget
   <-ai eq(@AI_NONE) if
      @TargetExists if
         @DistanceToTarget lt(<-sightRadius) if
            @AI_ENGAGING ->ai
         endif
      else
         # Decide AI
         <-IS_LANDED if
            @AN_LIFTOFF ->anim
            "Misc20" PlaySound
         else
            CurrentCoords <-sightRadius <-TARGETS @GetClosestUnit ->target
            @TargetExists if
               @AI_ENGAGING ->ai
            else
               @Roam
            endif
         endif
      endif
   endif
      
   <-ai eq(@AI_TRAILING) if
      @IsAtAI if
         @AI_NONE ->ai
      else
         @RotateToObjective
         @Move(1)
      endif
   endif
   
   <-ai eq(@AI_ROAMING) if
      @IsAtAI if
         @DoneAI
      else
         @RotateToObjective
         @Move(1)
      endif
   endif
   
   <-ai eq(@AI_ENGAGING) if
      @TargetExists if
         @DistanceToTarget lte(<-reach) if
            @Move(0.65)
            @RotateToObjective
            @IsAtAI if
               12 @Trail
            else
               GetTimer1 eq0 if
                  @HasAmmo if
                     <-onTarget if
                        @UseAmmo
                        @Fire
                        <-cooldown SetTimer1
                     endif
                  else
                     @DoneAI
                     @Dock
                  endif
               endif
            endif
         else
            @RotateToObjective
            @Move(1)
         endif
      else
         @DoneAI
      endif
   endif
   
   <-ai eq(@AI_REFILLING) if
      @HasBase if
         <-IS_LANDED if
            <-C_BOMBER_BASE "CBomberBase.crpl" "AMMO_FULL" GetScriptVar ->full
            <-full if
               @Ammo Approximately(@MaxAmmo) if
                  <-C_BOMBER_BASE "CBomberBase.crpl" "WIPE_AMMO" true SetScriptVar
                  @MaxAmmo @SetAmmo
                  SetUnitAttribute(self CONST_HEALTH GetUnitAttribute(self CONST_MAXHEALTH))
                  @DoneAI
                  "Misc16" PlaySound
                  90 SetTimer0
               else
                  @Ammo add(1) @SetAmmo
                  30 SetTimer0
               endif
            else
               # Sleep
               30 SetTimer0
            endif
         else
            @IsOnAI if
               @AN_LANDING ->anim
            else
               @Move(1)
               @RotateToObjective
            endif
         endif
      else
         @DoneAI
         <-IS_LANDED if
            self 0 Destroy
         endif
      endif
   endif
   
   @TargetCoords CurrentPixelCoords Distance lt(2 mul(<-speed mul(8))) and(not(<-onTarget)) if
      3 @Trail
   endif
   
:Dock
   @HasBase if
      # y-offset
      @BaseCoords sub(21.0 div(8)) CellToPixel ->targetY ->targetX
      @AI_REFILLING ->ai
   endif
   
:Roam
   CurrentCoords 20 RandCoordsInRange CellToPixel ->targetY ->targetX
   @AI_ROAMING ->ai
   
:Trail
   ->dist
   self "main" GetImageRotation ->rot
   CurrentPixelCoords -5000 dup <-rot <-dist mul(8) mul(<-speed) @MovePixelPosition ->targetY ->targetX
   @AI_TRAILING ->ai
   
:VoidNext
   true ->void
   
:DoneAI
   -?void not if
      -1 ->target
      -1 ->targetX
      -1 ->targetY
      0 ->ai_stage
   else
      --void
   endif
   @AI_NONE ->ai

:Animate
   if(<-anim eq(@AN_LANDING))
      self "main" GetImageScale pop ->scale
      CurrentPixelCoords ->py ->px
      <-scale gt(1.0) if
         <-scale sub(0.02) ->scale
         self "main" <-scale <-scale SetImageScale
         <-px <-py sub(1.4) @SetPixelCoords
      else
         true ->IS_LANDED
         self "main" -0.01 SetImagePositionZ
         self CONST_BEAMTARGET false SetUnitAttribute
         @AN_NONE ->anim
      endif
   endif
   if(<-anim eq(@AN_LIFTOFF))
   # Misc20
      self "main" GetImageScale pop ->scale
      CurrentPixelCoords ->py ->px
      <-scale lt(1.3) if
         <-scale add(0.02) ->scale
         self "main" <-scale <-scale SetImageScale
         <-px <-py add(1.4) @SetPixelCoords
      else
         false ->IS_LANDED
         self "main" -0.1 SetImagePositionZ
         self CONST_BEAMTARGET true SetUnitAttribute
         @AN_NONE ->anim
      endif
   endif

:HasBase
   <-C_BOMBER_BASE neq(-1)

:BaseCoords
   @UnitCoords(<-C_BOMBER_BASE)
   
:RotateToObjective
   self "main" GetImageRotation ->rot
   CurrentPixelCoords @TargetCoords <-rot <-rotSpeed @RotateToPixelAngle ->rot ->onTarget
   self "main" <-rot SetImageRotation

:Move
   asfloat ->mod
   self "main" GetImageRotation ->rot
   CurrentPixelCoords @TargetCoords <-rot mul(<-speed <-mod) @MovePixelPosition @SetPixelCoords
   
:IsAtAI
   @TargetCoords CurrentPixelCoords Distance lt(<-speed mul(2))
   
:IsOnAI
   @TargetCoords CurrentPixelCoords Distance lt(<-speed)
   
:HasAmmo
   @Ammo gt(0)
   
:UseAmmo
   @Ammo sub(1) @SetAmmo

:SetAmmo
   ->_ammo
   self CONST_AMMO <-_ammo SetUnitAttribute

:MaxAmmo
   <-ammo

:Ammo
   self CONST_AMMO GetUnitAttribute
   
:TargetCoords
   <-targetX <-targetY
   
:TargetExists
   -?target if
      <-target -1 neq
   else
      FALSE
   endif
   
:DistanceToTarget
   @TargetCoords PixelToCell CurrentCoords Distance
   
:HandleTarget
   <-target -1 neq if
      <-target CONST_ISDESTROYED GetUnitAttribute if
         -1 ->target
      else
         <-ai eq(@AI_ENGAGING) or(<-ai eq(@AI_NONE)) if
            @UnitPixelCoords(<-target) ->targetY ->targetX
         endif
      endif
   endif
   @HasBase if
      <-C_BOMBER_BASE CONST_ISDESTROYED GetUnitAttribute if
         -1 ->C_BOMBER_BASE
      endif
   endif
   <-target CONST_ISLANDED GetUnitAttribute not if
      -1 ->target
   endif

:Fire
   "Weapons1" PlaySound

   self "main" GetImageRotation ->rot
   CurrentPixelCoords -5000 dup <-rot <-shotDistance mul(8) @MovePixelPosition PixelToCell ->sty ->stx
   
   "CRPLCore" CurrentCoords CreateUnit ->sb
   <-sb "CBomb.crpl" AddScriptToUnit
   <-sb "CBomb.crpl" "targetX" <-stx SetScriptVar
   <-sb "CBomb.crpl" "targetY" <-sty SetScriptVar
   <-sb "CBomb.crpl" "payload" <-BombPayload SetScriptVar
   <-sb "CBomb.crpl" "damage" <-BombDamage SetScriptVar
   <-sb "main" "Custom9" SetImage
   <-sb "main" 0.25 0.25 SetImageScale
   <-sb "main" <-rot SetImageRotation
   
:destroyed
   "Explosion9" PlaySound
   
#=============================================
# ===== :EasyCRPL: Collection by Telanir =====
# --VERSION: 1.1
# CREDITS:
# Big thanks to VirgilW for an amazing game and lots of well commented source!
# Big thanks to Tyler21 for the Forcefield code!
#
# Usage: Simply paste this script at the bottom of your
# project to get the full benefits of the pre-written code.
# You can also just copy-paste parts of this you need.
#
# EasyCRPL is a collection of scripts from various community
# projects, members, and maps that have been created by myself
# or modified to meet a more generic purpose.
#
# -----
# A List of all available functions and their notation:
# @FunctionName: order of input - order of output with last object on top of the stack
# x - X coordinate
# y - Y coordinate
# n - numerical value
# i - integer value
# f - float value
# b - boolean value
# s - string value
# u - unit id, integer value
# L - list
# * - any value
#
# NOTE: Non-pixel coords are flipped vertically
# with (0,0) being top-left, whereas pixel (0,0)
# is the bottom left.
#
# -- Special Code --
# This code can be used at any time if it is in your
# script, and generally comes in blocks of functions.
# Forcefield Manager: A custom forcefield manager that helps safely create functional forcefields.
# @SetForcefieldProperties:
#
# List-Builder: Two simple functions that will safely create a list without touching the stack.
# @BeginList [* *...] @BuildList
# @DoesListContain: L1 * - b1
#
# Pre-Built Beam: A beam with a customizable color, alpha, and width.
# @SetBeamProperties: s1 i1 i2 i3 i4 i5 i6 f1 -
# @AnimateBeam: -
# @SetBeamTarget: u -
# @TargetBeam: x y b1 -
# @DestroyBeam: -
#
# -- Various Convenience Methods --
#
# SPECIAL METHODS, READ COMMENTS ABOVE CODE
# --
# @GetClosestUnit: x y f1 L1 - u
# @GetAllUnits: x y f1 L1 - [u1 u2...]i1
# @CRPLMatchesValue: u s1 * - b1
# --
#
# @SetImageAlpha: s1 i1 -
# @TerrainAccessible: x y i1 - b1
# @AreaOccupied: x y i1 - b1
# @AddOccupy: x y i1 b1 -
# -- Cell-Related Code --
# @UnitCoords: u - x y
# @MovePosition: x y x2 y2 f1 f2 - x y
# @GetVector: f1 f2 - x y
# @NormalizeVector: x y - x y
# @GetLength: x y - f1
# @GetAngle: x y x2 y2 - f1
# @RotateToAngle: x y x2 y2 f1 f2 - b1 f1
# @IsPointOnMap: x y - b1
# -- Pixel-Related Code --
# @UnitPixelCoords: u - x y
# @SetPixelCoords: x y -
# @MovePixelPosition: x y x2 y2 f1 f2 - x y
# @GetPixelVector: f1 f2 - x y
# @GetPixelAngle: x y x2 y2 - f1
# @RotateToPixelAngle: x y x2 y2 f1 f2 - b1 f1
#
# Wiki CRPL Reference: http://knucklecracker.com/wiki/doku.php?id=crpl:crplreference
#=============================================

#======================================
#======== FORCEFIELD MANAGER ==========
#======================================
   
# Sets the Forcefield properties.
# Order goes: RANGE CPUSH ACPUSH
# The higher the PUSH values the more the
# forcefield repels from the epicenter.
# Values are in millionths per unit of creeper moved.
# Notation: f1 i1 i2 -
# 12.5 100000 100000 @SetForcefieldProperties
:SetForcefieldProperties
   -?FF_INITIALIZED not if
      ->FF_ACPUSH
      ->FF_CPUSH
      ->FF_RANGE
      true ->FF_INITIALIZED
      -?FF_X or(-?FF_Y) not if
         CurrentCoords ->FF_Y ->FF_X
      endif
   endif
   
# Function :SetForcefieldCoords
# Sets the coordinates of future forcefields.
# If a previous forcefield was present it removes
# the old one and creates a new one.
# Notation: x y -
# e.g. CurrentCoords @SetForcefieldCoords
:SetForcefieldCoords
   -?FORCEFIELD_AVAILABLE if
      @WipeForcefield
      ->FF_Y
      ->FF_X
      @CreateForcefield
   else
      ->FF_Y
      ->FF_X
   endif

# Function :OverrideWipeForcefieldProperties
# By default initializing forcefield properties locks the
# method and helps prevent leftover forcefields. This
# function will also wipe any existing forcefields.
:OverrideWipeForcefieldProperties
   @WipeForcefield
   0 ->FF_ACPUSH
   0 ->FF_CPUSH
   0 ->FF_RANGE
   --FF_INITIALIZED

# Function :WipeForcefield
# Safe method to wipe forcefield, if there is none it will be ignored.
# Notation: -
# e.g. @WipeForcefield
:WipeForcefield
   -?FF_INITIALIZED if
      -?FORCEFIELD_AVAILABLE if
         --FORCEFIELD_AVAILABLE
         @ModifyForcefield(<-FF_X <-FF_Y <-FF_RANGE <-FF_CPUSH neg <-FF_ACPUSH neg)
      endif
   endif

# Function :CreateForcefield
# Creates a Forcefield with the initialized parameters.
# Notation: -
# e.g. @CreateForcefield
:CreateForcefield
   -?FF_INITIALIZED if
      -?FORCEFIELD_AVAILABLE not if
         true ->FORCEFIELD_AVAILABLE
         @ModifyForcefield(<-FF_X <-FF_Y <-FF_RANGE <-FF_CPUSH <-FF_ACPUSH)
      endif
   endif
   
# Major props to Tyler, thanks for helping make forcefields work as intended.
# Function :ModifyForcefield
# Adds input creeper and anti-creeper to surrounding field cells with
# strength decreasing with distance form the epicenter.
# Negative values will modify the forces in opposite directions and 0 will change nothing.
# Notation: x y f1 i1 i2 -
# e.g. CurrentCoords 10 100000 -100000 @ModifyForcefield
:ModifyForcefield
->SF_ACPUSH
->SF_CPUSH
asfloat ->SF_RANGE # float-value is required for the div operations
->SF_Y
->SF_X

<-SF_RANGE 2 mul 1 add 0
do
   <-SF_RANGE 2 mul 1 add 0
   do
      <-FF_X J add <-SF_RANGE sub ->xCell
      <-FF_Y I add <-SF_RANGE sub ->yCell
      <-FF_X <-xCell sub ->xDistCell
      <-FF_Y <-yCell sub ->yDistCell
      
      # setting a linearly decreasing field strength:
      <-xDistCell 0 gte
      if
         <-SF_RANGE <-xDistCell sub <-SF_RANGE div neg ->strengthX   # 1 at core, 0 at range
      else
         <-SF_RANGE <-xDistCell abs sub <-SF_RANGE div  ->strengthX   # 1 at core, 0 at range
      endif
      
      <-yDistCell 0 gte
      if
         <-SF_RANGE <-yDistCell sub <-SF_RANGE div neg ->strengthY   # 1 at core, 0 at range
      else
         <-SF_RANGE <-yDistCell abs sub <-SF_RANGE div  ->strengthY   # 1 at core, 0 at range
      endif
      
      <-SF_CPUSH <-strengthY mul ->cUDCell
      <-SF_CPUSH <-strengthX mul ->cRLCell
      <-SF_ACPUSH <-strengthY mul ->acUDCell
      <-SF_ACPUSH <-strengthX mul ->acRLCell
      # Get old values and modify them in case 2+ shields in one area.
      GetFieldCell(<-xCell <-yCell) ->oacRLCell ->oacUDCell ->ocRLCell ->ocUDCell
      SetFieldCell(<-xCell <-yCell <-cUDCell add(<-ocUDCell) <-cRLCell add(<-ocRLCell) <-acUDCell add(<-oacUDCell) <-acRLCell add(<-oacRLCell)) # n1, n2, n3 and n4 are for creeper U(+)/D(-), creeper R(+)/L(-), AC U/D and AC R/L
   loop
loop

#======================================
#=========== LIST-BUILDER =============
#======================================

# How to use:
# If you ever need to quickly build a list of X units then
# you can use these two functions to do it in one line.
# You can use this in conjuction with @DoesListContain listed in the index.
# e.g. @BeginList "COLLECTOR" "RELAY" "COMMANDNODE" "PULSECANNON" "MORTAR" @BuildList ->newList

:BeginList
   StackSize ->BL_I
   
:BuildList
   StackSize ->BL_M
   CreateList ->BL_L
   <-BL_M <-BL_I do
      ->BL_O
      <-BL_L <-BL_O PrependToList
   loop
   <-BL_L

# Function :DoesListContain
# This function an object, then a list and returns a boolean value.
# Notation: L1 n1 - b1
# eg. <-myList <-myValue @DoesListContain ->contains
:DoesListContain
   ->dl_value
   ->dl_list
   false ->dl_contained

   <-dl_list GetListCount 0 do
      <-dl_list I GetListElement eq(<-dl_value) if
         true ->dl_contained
         break
      endif
   loop

   <-dl_contained

#======================================
#=========== PRE-BUILT BEAM ===========
#======================================
# How to use:
# Copy the 4 beam methods into your code.
# @AnimateBeam needs to run /every/ script invocation.
# Use @SetBeamProperties at any time to change the
# appearence of the beam.
# Use @TargetBeam at any time to focus the beam on something,
# if it is moving, keep calling @TargetBeam.
# When finished, call @DestroyBeam and the beam will vanish!
#
# Warning: DO NOT USE @BeamInit!

# Function: :SetBeamProperties
# Sets the properties of the beam.
# Alpha, red, green, and blue range from 0 - 255
# With 0 being transparent and 255 being opaque
# The string is the "Beam" item, it can be found here:
# http://knucklecracker.com/wiki/doku.php?id=crpl:custom_image_repository
# The two integers after the image name are the indexes for
# the damage icons. If your "Damage 0" is at "Custom1" then write 1,
# then if your "Damage 3" is at "Custom4" then write 4 after.
# Notation: s1 i1 i2 i3 i4 i5 i6 f1 -
# eg. "Custom0" 1 4 255 0 0 255 1.5 @SetBeamProperties
:SetBeamProperties
   ->BM_SCALEY
   ->BM_ALPHA
   ->BM_BLUE
   ->BM_GREEN
   ->BM_RED
   ->BM_DAMAGEINDEXEND
   ->BM_DAMAGEINDEXSTART
   ->BM_IMAGE
   true ->BM_SET
   -?BM_ACTIVE if
      @BeamInit
   endif

# Function: :AnimateBeam
# Animates the beam and the explosion animation.
# The boolean indicates whether it is a pixel position.
# Notation: -
# eg. @AnimateBeam
:AnimateBeam
   -?BM_ACTIVE and(-?BM_SET) if
      if(<-BM_TOG)
         <-BM_X <-BM_Y ->BM_PY ->BM_PX
      else
         <-BM_X <-BM_Y CellToPixel ->BM_PY ->BM_PX
      endif
      
      -?BM_T if
         GetUnitType(<-BM_T) ->BM_TT
         if(<-BM_TT eq("BOMBERAIR") or(<-BM_TT eq("STRAFERAIR")))
            <-BM_PY add(27) ->BM_PY
         endif
         if(<-BM_TT eq("GUPPYAIR"))
            <-BM_PY add(10) ->BM_PY
         endif
      endif
      
      self CONST_PIXELCOORDX GetUnitAttribute ->BM_SX
      self CONST_PIXELCOORDY GetUnitAttribute ->BM_SY

      <-BM_PX <-BM_SX sub ->BM_DX
      <-BM_PY <-BM_SY sub ->BM_DY
      <-BM_DY <-BM_DX atan2 ->BM_A
      <-BM_SX <-BM_SY <-BM_PX <-BM_PY Distance ->BM_D
      <-BM_D 24 div ->BM_SCX
      
      <-BM_DX 2 div ->BM_PPX
      <-BM_DY 2 div ->BM_PPY
      
      SetImagePosition(self "beam" <-BM_PPX <-BM_PPY -0.1)
      SetImageScaleX(self "beam" <-BM_SCX)
      SetImageRotation(self "beam" <-BM_A)
      
      <-BM_DAMAGEINDEXSTART neq(-1) and(<-BM_DAMAGEINDEXEND neq(-1)) if
         <-B_IMG add(1) ->B_IMG
         <-B_IMG <-BM_DAMAGEINDEXSTART lt if <-BM_DAMAGEINDEXSTART ->B_IMG endif
         <-B_IMG <-BM_DAMAGEINDEXEND gt if <-BM_DAMAGEINDEXSTART ->B_IMG endif
         SetImage(self "damage" concat("Custom" <-B_IMG))
         SetImagePosition(self "damage" <-BM_DX <-BM_DY -0.1)
      endif
   endif
   
# Function: :SetBeamTarget
# The purpose of this function is for the main
# @AnimateBeam function to be aware if the target
# is a certain type of unit. It will automatically
# make corrections for flying units.
# Use -1 if the target is a non-unit.
:SetBeamTarget
   ->BT_T
   <-BT_T eq(-1) if
      --BM_T
   else
      <-BT_T ->BM_T
   endif

# Function: :TargetBeam
# Focuses the beam on a target location.
# If a beam does not exist, makes one.
# The boolean value indicates whether or not the
# new position is a pixel position.
# IF THE ENEMY IS AN 'AIR' SUFFIX ENEMY (aka STRAFERAIR)
# YOU MUST USE @SetBeamTarget TO CORRECT THE Y-OFFSET.
# Notation: x y b1 -
# eg. @UnitCoords(<-targetUnit) false @TargetBeam
# eg. 100 100 true @TargetBeam
:TargetBeam
   ->BM_TOG
   ->BM_Y
   ->BM_X
   -?BM_SET not if
      # Set some default values.
      "Custom0" 1 4 255 0 0 255 1.5 @SetBeamProperties
   endif
   <-BM_ACTIVE not if
      true ->BM_ACTIVE
      @BeamInit
   endif

# Function: :DestroyBeam
# Removes the active beam, if there is one.
# Notation: -
# eg. @DestroyBeam
:DestroyBeam
   -?BM_ACTIVE if
      --BM_ACTIVE
      self "beam" "NONE" SetImage
      self "damage" "NONE" SetImage
   endif
   
# Function: :BeamInit
# Used by the Beam script to update Beam appearance.
:BeamInit
   self "beam" <-BM_IMAGE SetImage
   self "beam" <-BM_RED <-BM_GREEN <-BM_BLUE <-BM_ALPHA SetImageColor
   self "beam" <-BM_SCALEY SetImageScaleY
   self "beam" -0.04 SetImagePositionZ

#======================================
#==== VARIOUS CONVENIENCE METHODS =====
#======================================

# Function :GetClosestUnit
# WARNING: use this convenience function sparingly, tens of CRPL Cores
# running this all at once every frame will slow the game down significantly.
#
# This function uses a flexible algorithm to filter
# units within a certain distance and retrieve the one
# closest to the position given to the function.
# It requires a position, a maximum distance, and an
# instruction list.
#
# The instruction list can contain multiple objects (but at least one is needed).
# An instruction follows this format: "unittype:parameter1,parameter2,parameter3=value,parameter4=value..."
# All applicable unit types can be found on:
# https://knucklecracker.com/wiki/doku.php?id=crpl:docs:getunittype
#
# The following parameters are acceptable:
# LANDED BUILT RANK=value
# LANDED and BUILT can accept modifiers. * means __either__, - means NOT, no modifier means IS
# If the parameter is none of the three, and the unit type is defined as "CRPLCORE" the assumed
# parameter type is a CRPLCore variable which is defined like so: myVar=myVal
#
# The unit type can be substituted by ALL which will affect all instructions, ALL must be in the
# beginning of the list or it will be assumed to be a unit type.
# ALL does not accept RANK or script variables as parameters.
#
# == EXAMPLE ==
# @BeginList "ALL:BUILT" "COLLECTOR" "relay:*built,rank=0.5" "pulsecannon:-landed,rank=2" "crplcore:emit=3.5" @BuildList ->instructions
# CurrentCoords 50 <-instructions @GetClosestUnit ->unit
#
# Explanation: All of the instructions within the list are acceptable. The only time the parameters are
# case sensitive is when defining script parameters. CONST_ISLANDED and CONST_ISBUILDING do not affect CRPLCores.
# According to these instructions, to get the closest unit within a range of 50 we will accept any built collector,
# any relay that is built or not but we give the a half the normal rank so the algorithm will treat them as if
# they are twice the distance away from anything else in the way. A collector and relay at the same distance will
# always return the collector. Next we prioritize any flying pulse cannon and override the ALL parameter, we
# then also look for any CRPLCores we made that had a script variable of "emit" that is equal to 3.5.
#
# Notation: x y f1 L1 - u
# e.g. CurrentCoords 50 <-instructionList @GetClosestUnit ->unit
:GetClosestUnit
   ->un_list
   asfloat ->un_dist
   asfloat ->un_y
   asfloat ->un_x
   
   -1 ->un_unit
   -1 ->un_udist
   
   <-un_x <-un_y <-un_dist <-un_list @GetAllUnits ->un_count
   <-un_count neq0 if
      <-un_count 0 do
         ->un_temp
         <-un_temp GetUnitType ->un_type
         # GET INSTRUCTION FOR THIS UNIT TYPE
         <-un_instructions GetListCount 0 do
            <-un_instructions GetListElement(I) ->un_inst
            # found correct instruction
            <-un_inst StartsWith(<-un_type) if
               <-un_inst Split(" ") ->un_ilist
               <-un_ilist GetListElement(3) asfloat ->une_rank
               
               # now we apply the priority and get the closest unit
               <-un_x <-un_y @UnitCoords(<-un_temp) Distance div(<-une_rank) ->un_tdist
               <-un_tdist lt(<-un_udist) or(<-un_udist eq(-1)) if
                  <-un_tdist ->un_udist
                  <-un_temp ->un_unit
               endif
               break
            endif
         loop
      loop
   endif
   <-un_unit

# Function :GetAllUnits
# Grabs all units of specified instruction within a radius of
# a given point. Read :GetClosestUnit for more info.
# Notation: x y f1 L1 - [u1 u2...]i1
# e.g.
# CurrentCoords 50 <-instructionList @GetAllUnits ->unitCount
# <-unitCount 0 do
# # code here
# loop
:GetAllUnits
   ->un_list
   asfloat ->un_dist
   asfloat ->un_y
   asfloat ->un_x
   
   CreateList ->un_units
   --un_all_landed
   --un_all_built
   
   <-un_list GetListCount gt(0) if
      <-un_list GetListElement(0) ToUpper ->un_elm
      <-un_elm StartsWith("ALL") if
         <-un_elm StringReplace("ALL:" "") ->un_elm
         <-un_elm Split(",") ->un_values
         <-un_values GetListCount 0 do
            <-un_values GetListElement(I) ->un_elm
            <-un_elm ToUpper EndsWith("LANDED") if
               <-un_elm StartsWith("-") if
                  false ->un_all_landed
               else
                  true ->un_all_landed
               endif
            endif
            <-un_elm ToUpper EndsWith("BUILT") if
               <-un_elm StartsWith("-") if
                  false ->un_all_built
               else
                  true ->un_all_built
               endif
            endif
         loop
      endif
   endif
   
   CreateList ->un_instructions
   <-un_list GetListCount 0 do
      "" ->compile_task
      "" ->un_type
      1.0 ->rank
      
      --un_landed_req
      --un_built_req
      
      -?un_all_landed if <-un_all_landed ->un_landed_req endif
      -?un_all_built if <-un_all_built ->un_built_req endif
      
      <-un_list GetListElement(I) ->un_elm
      <-un_elm Split(":") ->un_lelm
      
      <-un_lelm GetListElement(0) ToUpper ->un_type
      
      <-un_type ->compile_task
      "" ->append_task
      
      <-un_lelm GetListCount gt(1) if
         <-un_lelm GetListElement(1) ->un_elm
         <-un_elm StringReplace(<-un_type concat(":") "") ->un_elm
         <-un_elm Split(",") ->un_values
         <-un_values GetListCount 0 do
            <-un_values GetListElement(I) ->un_elm
            true ->definingVariable
            <-un_elm ToUpper EndsWith("LANDED") if
               <-un_elm StartsWith("-") if
                  false ->un_landed_req
               else
                  true ->un_landed_req
               endif
               <-un_elm StartsWith("*") if
                  --un_landed_req
               endif
               false ->definingVariable
            endif
            <-un_elm ToUpper EndsWith("BUILT") if
               <-un_elm StartsWith("-") if
                  false ->un_built_req
               else
                  true ->un_built_req
               endif
               <-un_elm StartsWith("*") if
                  --un_built_req
               endif
               false ->definingVariable
            endif
            <-un_elm ToUpper StartsWith("RANK") if
               <-un_elm Split("=") ->un_rankl
               <-un_rankl GetListElement(1) asfloat ->rank
               false ->definingVariable
            endif
            
            <-definingVariable and(<-un_type eq("CRPLCORE")) if
               <-un_elm Split("=") ->un_varl
               <-append_task concat(" ") concat(<-un_varl GetListElement(0)) concat(" ") concat(<-un_varl GetListElement(1) StringReplace("true" "1") StringReplace("false" "0")) ->append_task
            endif
         loop
      endif
      # 2 means not applicable
      <-compile_task concat(" ") ->compile_task
      -?un_built_req if <-compile_task concat(<-un_built_req) else <-compile_task concat("2") endif ->compile_task
      <-compile_task concat(" ") ->compile_task
      -?un_landed_req if <-compile_task concat(<-un_landed_req) else <-compile_task concat("2") endif ->compile_task
      <-compile_task concat(" ") ->compile_task
      <-compile_task concat(<-rank) ->compile_task
      <-compile_task concat(<-append_task) ->un_instruction
      <-un_instructions AppendToList(<-un_instruction)
   loop
   
   <-un_x <-un_y <-un_dist false GetAllUnitsInRange ->un_count
   <-un_count neq0 if
      <-un_count 0 do
         ->un_temp
         <-un_temp neq(self) if
            <-un_temp GetUnitType ->un_type
            # GET INSTRUCTION FOR THIS UNIT TYPE
            <-un_instructions GetListCount 0 do
               <-un_instructions GetListElement(I) ->un_inst
               <-un_inst StartsWith(<-un_type) if
                  <-un_inst Split(" ") ->un_ilist
                  --une_built
                  --une_landed
                  # 0 is the type
                  <-un_ilist GetListElement(1) ->une_t <-une_t neq(2) if <-une_t ->une_built endif
                  <-un_ilist GetListElement(2) ->une_t <-une_t neq(2) if <-une_t ->une_landed endif
                  <-un_ilist GetListElement(3) asfloat ->une_rank
                  
                  false ->skip
                  <-un_type eq("CRPLCORE") if
                     4 ->unc_index
                     while <-unc_index lt(<-un_ilist GetListCount) repeat
                        <-un_ilist GetListElement(<-unc_index) ->unc_var
                        <-unc_index add(1) ->unc_index
                        <-un_ilist GetListElement(<-unc_index) ->unc_val
                        <-unc_index add(1) ->unc_index
                        <-un_temp <-unc_var <-unc_val @CRPLMatchesValue ->unc_match
                        <-unc_match not if
                           true ->skip
                        endif
                     endwhile
                  endif
                  not(<-skip) if
                     # MATCH VALUES
                     <-un_type @BuildApplies if
                        -?une_built if GetUnitAttribute(<-un_temp CONST_ISBUILDING) eq(<-une_built) ->skip endif
                     endif
                     <-un_type @LandedApplies if
                        -?une_landed and(not(<-skip)) if GetUnitAttribute(<-un_temp CONST_ISLANDED) neq(<-une_landed) ->skip endif
                     endif
                     not(<-skip) if
                        <-un_units AppendToList(<-un_temp)
                     endif
                  endif
                  break
               endif
            loop
         endif
      loop
   endif
   <-un_units GetListCount 0 do
      <-un_units GetListElement(I)
   loop
   <-un_units GetListCount
   
:BuildApplies
   ->ba_type
   # list of what DOESN'T
   -?ba_applies not if
      @BeginList
         "GUPPYAIR" "BOMBERAIR" "STRAFERAIR" "CRPLCORE" "EMITTER" "SPORETOWER" "RUNNER" "RUNNERNEST"
         "AETOWER" "INHIBITOR" "POWERZONE" "OREDEPOSIT" "RESOURCEPACK" "TOTEM" "AOO" "SHIELDKEY"
         "MESSAGEARTIFACT"
      @BuildList ->ba_applies
   endif
   not(<-applies @DoesListContain(<-ba_type))

:LandedApplies
   ->la_type
   # list of what DOES
   -?la_applies not if
      @BeginList
         "TERP" "SHIELD" "PULSECANNON" "MORTAR" "SPRAYER" "BEAM" "SNIPER"
      @BuildList ->la_applies
   endif
   <-la_applies @DoesListContain(<-la_type)
   
# Function :CRPLMatchesValue
# Takes a unit, a value and a variable and determines
# without a given script whether or not the CRPLCore
# has a variable with that value.
# Notation: u s1 * - b1
# e.g. self "myVar" 5 @CRPLMatchesValue ->matchesValue
:CRPLMatchesValue
   ->uv_val
   ->uv_var
   ->uv_unit
   false ->uv_found
   
   <-uv_var <-uv_val GetCoresWithVar ->uv_count
   <-uv_count neq(0) if
      <-uv_count 0 do
         ->uv_temp
         <-uv_temp eq(<-uv_unit) if
            true ->uv_found
         endif
      loop
   endif
   <-uv_found

# Function: :SetImageAlpha
# Sets the alpha of the specified image for self.
# Alpha ranges from 0 - 255 with 0 transparent and 255 opaque
# Notation: s1 i1 -
# eg. "main" 100 @SetImageAlpha
:SetImageAlpha
asint ->si_alpha_new
->si_image
self <-si_image GetImageColor ->si_alpha ->si_blue ->si_green ->si_red
self <-si_image <-si_red <-si_green <-si_blue <-si_alpha_new SetImageColor
   
# Function :TerrainAccessible
# Returns whether or not there is smooth terrain available
# of equivalent height in a certain area of specified radius.
# Radius is measured in cells.
# Area of radius 0 is 1 cell, area of radius 1 is 9 cells, etc.
# Notation: x y i1 - b1
# eg. 10 10 5 @TerrainAccessible ->accessible
:TerrainAccessible
   ->ta_rad
   ->ta_y
   ->ta_x
   
   -1 ->ta_fr
   <-ta_rad mul(2) add(1) ->ta_length
   <-ta_rad ->ta_mid

   <-ta_length 0 do
      <-ta_length 0 do
         <-ta_y I add <-ta_mid sub ->ta_aoy
         <-ta_x J add <-ta_mid sub ->ta_aox
         <-ta_aox <-ta_aoy GetTerrain ->ta_terr
         <-ta_terr 0 lt if
            false
            return
         else
            <-ta_fr -1 eq if
               <-ta_terr ->ta_fr
            else
               <-ta_fr <-ta_terr neq if
                  false
                  return
               else
               endif
            endif
         endif
      loop
   loop
   true

# Function :AreaOccupied
# Returns whether or not a certain area is occupied by one or
# more other units. Radius is measured in cells.
# Notation: x y i1 - b1
# eg. 10 10 5 @AreaOccupied ->occupied
:AreaOccupied
   ->ao_rad
   ->ao_y
   ->ao_x

   <-ao_rad mul(2) add(1) ->ao_length
   <-ao_rad ->ao_mid

   <-ao_length 0 do
      <-ao_length 0 do
         <-ao_y I add <-ao_mid sub ->ao_aoy
         <-ao_x J add <-ao_mid sub ->ao_aox
         <-ao_aox <-ao_aoy GetCellOccupiedCount 0 gt if
            true
            return
         endif
      loop
   loop
   false

# Function :OccupyArea
# Safety net for @AddOccupy to prevent bad areas.
# Notation: x y f1 -
# eg. <-targetX <-targetY 1 @OccupyArea # this would occupy an area of land
# #in a 3x3 grid around the central point, useful for flying units looking to land
:OccupyArea
   -?OA_OCCUPIED not if
      # First occupation is legal.
      ->OA_RAD
      ->OA_Y
      ->OA_X
      
      <-OA_X <-OA_Y <-OA_RAD true @AddOccupy
      # Does occupy, is initialized.
      true ->OA_OCCUPIED
   else
      <-OA_OCCUPIED if
         # If occupying land already, disable occupation for re-assigning.
         <-OA_X <-OA_Y <-OA_RAD false @AddOccupy
      endif
      ->OA_RAD
      ->OA_Y
      ->OA_X
      <-OA_OCCUPY if
         <-OA_X <-OA_Y <-OA_RAD true @AddOccupy
      endif
      <-OA_OCCUPY ->OA_OCCUPIED
   endif

:ReleaseOccupiedArea
   <-OA_OCCUPIED if
      # If occupying land already, disable occupation.
      <-OA_X <-OA_Y <-OA_RAD false @AddOccupy
      false ->OA_OCCUPIED
   endif
   
# Function :AddOccupy
# Returns whether or not a certain area is occupied by one or
# more other units. Radius is measured in cells.
# Notation: x y f1 b1 -
# eg. CurrentCoords 1 false @AddOccupy #this would allow another unit to land
# #in a 3x3 grid around the central point.
:AddOccupy
   ->OC_ADD
   ->OC_RAD
   ->OC_Y
   ->OC_X
   
   <-OC_RAD mul(2) add(1) ->OC_LENGTH
   <-OC_RAD ->OC_MID
   
   <-OC_LENGTH 0 do
      <-OC_LENGTH 0 do
         <-OC_Y I add <-OC_MID sub ->OC_AOY
         <-OC_X J add <-OC_MID sub ->OC_AOX
         <-OC_AOX <-OC_AOY GetCellOccupiedCount ->OC_COUNT
         <-OC_ADD if
            <-OC_AOX <-OC_AOY <-OC_COUNT add(1) SetCellOccupiedCount
         else
            <-OC_AOX <-OC_AOY <-OC_COUNT sub(1) SetCellOccupiedCount
         endif
      loop
   loop

#======================================
#========= CELL RELATED CODE ==========
#======================================

# Function :UnitCoords
# Simple convenience method. Takes a unit and returns the coordinates.
# 'CurrentCoords' and 'self @UnitCoords' are equivalent.
# Notation: n1 - x y
# eg. <-myUnitID @UnitCoords ->unitX ->unitY
:UnitCoords
   asint ->uc_u
   <-uc_u CONST_COORDX GetUnitAttribute
   <-uc_u CONST_COORDY GetUnitAttribute
   
# Function :SetUnitCoords
# Simple convenience method. Sets current units pixel coordinates.
# Notation: x y -
# eg. x y u @SetPixelCoords
:SetUnitCoords
   ->sp_unit
   asfloat ->sp_y
   asfloat ->sp_x
   SetUnitAttribute(<-sp_unit CONST_COORDX <-sp_x)
   SetUnitAttribute(<-sp_unit CONST_COORDY <-sp_y)

# Function :MovePosition
# Uses the given position, angle, and speed and returns a new position.
# The target position is used for correctional purposes so that it doesn't overstep.
# If you want to ignore the correctional functionality set the target coordinates to -5000 -5000.
# Notation: x y x2 y2 f1 f2 - x y
# eg. CurrentCoords <-targetX <-targetY <-angle 1.5 @MovePosition ->y ->x
:MovePosition
   asfloat ->mp_ms
   asfloat ->mp_an
   asfloat ->mp_tY
   asfloat ->mp_tX
   asfloat ->mp_pY
   asfloat ->mp_pX

   <-mp_an <-mp_ms @GetVector ->mp_vY ->mp_vX

   <-mp_pX <-mp_tX sub <-mp_pY <-mp_tY sub @GetLength <-mp_ms lt if
      <-mp_tX
      <-mp_tY
   else
      <-mp_pX <-mp_vX add
      <-mp_pY <-mp_vY add
   endif

# Function :GetIntermediatePosition
# Uses two points and a distance to find a position that is
# between the two points, but X distance away from the target.
# Negative distances will go beyond the target.
# Notation: x y x2 y2 f1 - x y
# eg. CurrentPosition @GetUnitCoords(<-targetUnit) 10 @GetIntermediatePosition QueueMove
:GetIntermediatePosition
   asfloat ->gip_dist
   asfloat ->gip_ty
   asfloat ->gip_tx
   asfloat ->gip_y
   asfloat ->gip_x

   <-gip_tx <-gip_ty <-gip_x <-gip_y @GetAngle ->gip_a
   <-gip_a <-gip_dist @GetVector ->gip_vy ->gip_vx
   <-gip_tx <-gip_vx add
   <-gip_ty <-gip_vy add
   
# Function :GetVector
# Uses an angle, and speed and returns a vector
# You can add the vector to a position to get a moving effect
# Notation: f1 f2 - x y
# eg. <-angle 1.5 @GetVector ->y ->x
:GetVector
   asfloat ->gv_vrsm
   asfloat ->gv_vrAngle

   <-gv_vrAngle sin ->gv_vrY
   <-gv_vrAngle cos ->gv_vrX
   <-gv_vrX <-gv_vrY @NormalizeVector ->gv_vrY ->gv_vrX
   <-gv_vrY <-gv_vrsm mul ->gv_vrY
   <-gv_vrX <-gv_vrsm mul ->gv_vrX
   <-gv_vrX
   <-gv_vrY neg

# Function :NormalizeVector
# Takes an (x,y) coordinate and returns it in terms of 0 to 1,
# with 1 being the maximum length (traveling directly up/down or left/right)
# and 0 being the minimum.
# Notation: x y - x y
# eg. <-vectorX <-vectorY @NormalizeVector ->vectorY ->vectorX
:NormalizeVector
   asfloat ->nv_theY
   asfloat ->nv_theX
   <-nv_theX <-nv_theY @GetLength ->nv_length
   <-nv_theY <-nv_length div ->nv_ny
   <-nv_theX <-nv_length div ->nv_nx
   <-nv_nx
   <-nv_ny

# Function :GetLength
# Takes a width and height and calculates a length.
# Notation: x y - f1
# eg. 4 3 @GetLength ->length #(will return 5)
:GetLength
   asfloat ->gl_theY
   asfloat ->gl_theX
   <-gl_theX 2.0 pow <-gl_theY 2.0 pow add sqrt
   
# Function :GetAngle
# Takes an initial position and a target position,
# then calculates an angle to face the target.
# Value returned in radians.
# Notation: x y x2 y2 - f1
# eg. CurrentCoords <-targetUnit @UnitCoords @GetAngle ->angle
:GetAngle
   asfloat ->ga_targetY
   asfloat ->ga_targetX
   asfloat ->ga_curY
   asfloat ->ga_curX

   <-ga_targetX <-ga_curX sub ->ga_deltaX
   <-ga_curY <-ga_targetY sub ->ga_deltaY
   <-ga_deltaY <-ga_deltaX atan2 ->ga_desiredAngle
   <-ga_desiredAngle

# Function :RotateToAngle
# Takes an initial position and a target position,
# calculates an angle, and then returns an angle
# restricted by the specified speed and angle.
# This function can allow for slow rotations.
# It will also return a boolean value which is
# true if the angle directly faces the target.
# Notation: x y x2 y2 f1 f2 - b1 f1
# eg. CurrentCoords <-targetUnit @UnitCoords <-angle 0.03 @RotateToAngle ->newAngle ->isOnTarget
:RotateToAngle
   asfloat ->rta_maxRot
   asfloat ->rta_curAngle
   asfloat ->rta_targetY
   asfloat ->rta_targetX
   asfloat ->rta_curY
   asfloat ->rta_curX

   <-rta_targetX <-rta_curX sub ->rta_deltaX
   #(vertical 0 is TOP-left)
   #cell-y is flipped so we instead subtract target-y from current-y
   <-rta_curY <-rta_targetY sub ->rta_deltaY
   <-rta_deltaY <-rta_deltaX atan2 ->rta_desiredAngle

   <-rta_curAngle <-rta_desiredAngle ShortestAngle ->rta_rot

   <-rta_rot <-rta_maxRot gt if
      <-rta_maxRot ->rta_rot
   else
      <-rta_rot <-rta_maxRot neg lt if
         <-rta_maxRot neg ->rta_rot
      endif
   endif

   <-rta_rot abs 0.0001 lt if
      1 ->rta_onTarget
   else
      0 ->rta_onTarget
   endif

   <-rta_curAngle <-rta_rot add ->rta_rot

   <-rta_onTarget
   <-rta_rot

# Function :IsPointOnMap
# Takes a set of cell-coordinates and returns true
# if the coordinates are on the map.
# Notation: x y - b1
# eg. -1 -1 @IsPointOnMap ->onMap #(will return false)
:IsPointOnMap
   ->pom_pointY
   ->pom_pointX
   1 ->pom_onMap
   <-pom_pointY MapHeight gt if
      0 ->pom_onMap
   endif
   <-pom_pointY 0 lt if
      0 ->pom_onMap
   endif
   <-pom_pointX MapWidth gt if
      0 ->pom_onMap
   endif
   <-pom_pointX 0 lt if
      0 ->pom_onMap
   endif
   <-pom_onMap

#======================================
#========= PIXEL RELATED CODE =========
#======================================

# Function :UnitPixelCoords
# Simple convenience method. Takes a unit and returns the pixel coordinates.
# 'CurrentPixelCoords' and 'self @UnitPixelCoords' are equivalent.
# Notation: u - x y
# eg. <-myUnitID @UnitCoords ->unitY ->unitX
:UnitPixelCoords
   asint ->uc_u
   <-uc_u CONST_PIXELCOORDX GetUnitAttribute
   <-uc_u CONST_PIXELCOORDY GetUnitAttribute
   
# Function :SetUnitPixelCoords
# Simple convenience method. Sets current units pixel coordinates.
# Notation: u x y -
# eg. <-unit 10 10 @SetPixelCoords
:SetUnitPixelCoords
   asfloat ->sp_y
   asfloat ->sp_x
   ->sp_unit
   SetUnitAttribute(<-sp_unit CONST_PIXELCOORDX <-sp_x)
   SetUnitAttribute(<-sp_unit CONST_PIXELCOORDY <-sp_y)

# Function :SetPixelCoords
# Simple convenience method. Sets current units pixel coordinates.
# Notation: x y -
# eg. x y @SetPixelCoords
:SetPixelCoords
   asfloat ->sp_y
   asfloat ->sp_x
   SetUnitAttribute(self CONST_PIXELCOORDX <-sp_x)
   SetUnitAttribute(self CONST_PIXELCOORDY <-sp_y)

# Function :MovePixelPosition
# Uses the given position, angle, and speed and returns a new position.
# The target position is used for correctional purposes so that it doesn't overstep.
# Notation: x y x2 y2 f1 f2 - x y
# eg. CurrentPixelCoords <-targetX <-targetY <-angle 1.5 @MovePixelPosition ->y ->x
:MovePixelPosition
   asfloat ->mp_ms
   asfloat ->mp_an
   asfloat ->mp_tY
   asfloat ->mp_tX
   asfloat ->mp_pY
   asfloat ->mp_pX

   <-mp_an <-mp_ms @GetPixelVector ->mp_vY ->mp_vX

   <-mp_pX <-mp_tX sub <-mp_pY <-mp_tY sub @GetLength <-mp_ms lt if
      <-mp_tX
      <-mp_tY
   else
      <-mp_pX <-mp_vX add
      <-mp_pY <-mp_vY add
   endif

# Function :GetIntermediatePixelPosition
# Uses two points and a distance to find a position that is
# between the two points, but X distance away from the target.
# Notation: x y x2 y2 f1 - x y
# eg. CurrentPixelPosition @GetUnitPixelCoords(<-targetUnit) 10 @GetIntermediatePosition QueueMove
:GetIntermediatePixelPosition
   asfloat ->gip_dist
   asfloat ->gip_ty
   asfloat ->gip_tx
   asfloat ->gip_y
   asfloat ->gip_x

   <-gip_tx <-gip_ty <-gip_x <-gip_y @GetPixelAngle ->gip_a
   <-gip_a <-gip_dist @GetPixelVector ->gip_vy ->gip_vx
   <-gip_ty <-gip_vy add
   <-gip_tx <-gip_vx add

# Function :GetPixelVector
# Uses an angle, and speed and returns a vector
# You can add the vector to a position to get a moving effect
# Notation: f1 f2 - x y
# eg. <-angle 1.5 @GetPixelVector ->y ->x
:GetPixelVector
   asfloat ->gv_vrsm
   asfloat ->gv_vrAngle

   <-gv_vrAngle sin ->gv_vrY
   <-gv_vrAngle cos ->gv_vrX
   <-gv_vrX <-gv_vrY @NormalizeVector ->gv_vrY ->gv_vrX
   <-gv_vrY <-gv_vrsm mul ->gv_vrY
   <-gv_vrX <-gv_vrsm mul ->gv_vrX
   <-gv_vrX
   <-gv_vrY
   
# Function :GetPixelAngle
# Takes an initial position and a target position,
# then calculates an angle to face the target.
# Value returned in radians.
# Notation: x y x2 y2 - f1
# eg. CurrentPixelCoords <-targetUnit @UnitPixelCoords @GetPixelAngle ->angle
:GetPixelAngle
   asfloat ->ga_targetY
   asfloat ->ga_targetX
   asfloat ->ga_curY
   asfloat ->ga_curX

   <-ga_targetX <-ga_curX sub ->ga_deltaX
   <-ga_targetY <-ga_curY sub ->ga_deltaY
   <-ga_deltaY <-ga_deltaX atan2 ->ga_desiredAngle
   <-ga_desiredAngle

# Function :RotateToPixelAngle
# Takes an initial position and a target position,
# calculates an angle, and then returns an angle
# restricted by the specified speed and angle.
# This function can allow for slow rotations.
# It will also return a boolean value which is
# true if the angle directly faces the target.
# Notation: x y x2 y2 f1 f2 - b1 f1
# eg. CurrentPixelCoords <-targetUnit @UnitPixelCoords <-angle 0.03 @RotateToPixelAngle ->newAngle ->isOnTarget
:RotateToPixelAngle
   asfloat ->rta_maxRot
   asfloat ->rta_curAngle
   asfloat ->rta_targetY
   asfloat ->rta_targetX
   asfloat ->rta_curY
   asfloat ->rta_curX

   <-rta_targetX <-rta_curX sub ->rta_deltaX
   <-rta_targetY <-rta_curY sub ->rta_deltaY
   <-rta_deltaY <-rta_deltaX atan2 ->rta_desiredAngle

   <-rta_curAngle <-rta_desiredAngle ShortestAngle ->rta_rot

   <-rta_rot <-rta_maxRot gt if
      <-rta_maxRot ->rta_rot
   else
      <-rta_rot <-rta_maxRot neg lt if
         <-rta_maxRot neg ->rta_rot
      endif
   endif

   <-rta_rot abs 0.0001 lt if
      1 ->rta_onTarget
   else
      0 ->rta_onTarget
   endif

   <-rta_curAngle <-rta_rot add ->rta_rot

   <-rta_onTarget
   <-rta_rot
[close]

Creep Bomber Bombs Script:
Spoiler
# CBomb.crpl
# Created on: 10/25/2015 2:27:12 PM
# ------------------------------------------
Self "main" "Custom9" SetImage
Self CONST_CREATEPZ 0 SetUnitAttribute               
Self "main" 0.9 0.9 SetImageScale
3.75 Delay
Self "main" 0.8 0.8 SetImageScale
3.75 Delay
Self "main" 0.7 0.7 SetImageScale
3.75 Delay
Self "main" 0.6 0.6 SetImageScale
3.75 Delay
Self "main" 0.5 0.5 SetImageScale
3.75 Delay
Self "main" 0.4 0.4 SetImageScale
3.75 Delay
Self "main" 0.3 0.3 SetImageScale
3.75 Delay
Self "main" 0.2 0.2 SetImageScale
3.75 Delay
Self "main" 0.1 0.1 SetImageScale
3.75 Delay
CurrentCoords 10 SetCreeper
Self 3 Destroy
[close]

Also replace the old SleeperBase.crpl Script with this one. If not then the sleeper node cannot build any of the custom units.  :P Also do note that you must have all of the other scripts in order for the units to build properly.
Spoiler
# CSleeperBase.crpl
# Created on: 4/11/2015 2:06:13 PM
# ------------------------------------------
# VERSION 1.2

# == CUSTOM VARS ==

$waitTime:900
$dontFlyTimeSeconds:90
$emit:20
$interval:15
$turboModeMultiplier:10.0
$turboTime:300
$moveSpeed:2

$dangerRadius:8
$buildRadius:20
$distToPlayer:22
$maxTravelRadius:70

$DENSITY_RADIUS:16
$UNITS_PER_RADIUS:8
$AETHER:0
# IF FALSE, EVERYTHING CAN BE BUILT INSTANTLY
$USE_AETHER:1
$SET_SNIPER_LIMIT:10

# == DEFAULT VARS ==
# Health is simulated differently because there is no direct
# way to know cannon damage and Digitalis is ONLY damaged by
# player actions.
# A side effect is that every unit will have to support Digitalis.

# Health reduces sharply when unit digitalis is damaged.
$health:10000.0
# If digitalis under unit is less than min, damage health.
$minDigitalis:1.0
# Digitalis will restore to this value.
$maxDigitalis:1.2

# CONSTANTS (only modify here, or during initialization)
$DEFAULT_IMAGE:"Custom0_256"
$BUILD_SOUND:"Misc7"
$CREEPER_FOR_BUILD:10000.0
$CREEP_TYPE:"SLEEPERBASE"
# Every unit has a forcefield unless range is 0 or below.
# Negative PUSH values will induce a PULL on creeper to "suck it in".
$CREEP_FORCEFIELD_CPUSH:10000
$CREEP_FORCEFIELD_ACPUSH:1000
$CREEP_FORCEFIELD_RANGE:10

# Enabling hard sleep will allow the AI to activate
# SleepAI when the unit is far away from the action
# or can't do anything for a bit. Otherwise, it is up
# to you to make the unit optimized with @IsSleeping
$SUPPORTS_HARDSLEEP:0
# When a unit is in travel mode it will run an alternate
# AI that is called TravelAI where you can perform tasks
# specific to flying mode. As soon as a unit lands on their
# target position they resume standard AI. If ignoring flight
# is enabled, normal AI will continue to run even during flight.
$IGNORES_FLIGHT:0
$FLIGHT_SPEED:1.0

# RUNTIME (do not modify)
$BUILT_ON_SPAWN:1
$IS_BUILT:0

# THIS SCRIPT DOES NOT SUPPORT RUNNING WHILE PAUSED.
# However, due to internal bugs occasionally CW3 will still give them frame time
# even though nowhere is it explicitly stated they are allowed to run while paused.
IsPaused if
   1 SuspendMove
   return
endif
# == END DEFAULT ==

once
   self "main" "NONE" SetImage
   # Flash without sound
   self CONST_DESTROYMODE 3 SetUnitAttribute
   
   self CONST_CREATEPZ false SetUnitAttribute
   
   # Set size here
   self CONST_CELLHEIGHT 8 SetUnitAttribute
   self CONST_CELLWIDTH 8 SetUnitAttribute
   self CONST_NULLIFIERDAMAGES false SetUnitAttribute
   
   "cinematic" "Sleeper" GetCoresWithVar ->count
   ->cinematic

   <-waitTime Delay
   
   self "main" <-DEFAULT_IMAGE SetImage
   @RegisterImage("main")
   self "main" 3 3 SetImageScale
   self "main" SetImagePositionZ(0.01)
   
   CurrentCoords DropFromOrbit
   90 Delay
   
   GetCoresWithVar("SLEEPER_LANDED" 0) ->count
   <-count 0 do
      ->core
      <-core "CSleeperEmitter.crpl" "SLEEPER_LANDED" 1 SetScriptVar
      <-core "Sleeping flipper.crpl" "SLEEPER_LANDED" 1 SetScriptVar
   loop
   
   "Misc35" PlaySound
   11 CurrentPixelCoords 0.01 3 3 0.03 CreateEffect
   "Explosion2" PlaySound
   "Explosion7" PlaySound
   
   90 Delay

   self "CSleeperAA.crpl" AddScriptToUnit
   self "CSleeperPD.crpl" AddScriptToUnit
   
   @SetBeamProperties("Custom0" 1 4 255 255 0 255 1.0)
   
   SetBuildLimit("SNIPER" <-SET_SNIPER_LIMIT)
   
   @AI_NONE ->ai
   false ->turboMode
   0 ->turboCooldown
   300 mul(30) ->turboCooldownMax
   
   CreateList ->CREEP_TYPES
   CreateList ->CREEP_BUILDCHANCE
   CreateList ->CREEP_SCRIPTS
   CreateList ->CREEP_AETHER
   CreateList ->CREEP_PURPOSE
   
   @RegisterCreep("CTotemSiphon.crpl" "TOTEMSIPHON" 0 0 @UNIT_PRODUCER)
   @RegisterCreep("COreMine.crpl" "OREMINE" 0 0 @UNIT_PRODUCER)
   @RegisterCreep("CCollector.crpl" "COLLECTOR" 40 0 @UNIT_PRODUCER)
   @RegisterCreep("CCannon.crpl" "CANNON" 15 0 @UNIT_CONSUMER)
   @RegisterCreep("CMortar.crpl" "MORTAR" 10 0 @UNIT_CONSUMER)
   @RegisterCreep("CBeam.crpl" "BEAM" 10 0 @UNIT_CONSUMER)
   @RegisterCreep("CSporeTower.crpl" "SPORETOWER" 10 80 @UNIT_CONSUMER)
   @RegisterCreep("CTerp.crpl" "TERP" 8 35 @UNIT_CONSUMER)
   @RegisterCreep("CShield.crpl" "SHIELD" 8 125 @UNIT_CONSUMER_HEAVY)
   @RegisterCreep("CStraferBase.crpl" "STRAFERBASE" 10 250 @UNIT_CONSUMER_HEAVY)
   @RegisterCreep("CSniper.crpl" "SNIPER" 15 25 @UNIT_CONSUMER)
   @RegisterCreep("CReactor.crpl" "REACTOR" 5 100 @UNIT_PRODUCER)
   @RegisterCreep("CBomberBase.crpl" "BOMBERBASE" 10 300 @UNIT_CONSUMER_HEAVY)
        @RegisterCreep("CSprayer.crpl" "SPRAYER" 15 50 @UNIT_CONSUMER)
        @RegisterCreep("CGuppy.crpl" "GUPPYBASE" 15 275 @UNIT_CONSUMER_HEAVY)
   @FinalizeCreepRegistry
   
   # Set who can target this unit
   self CONST_NULLIFIERDAMAGES true SetUnitAttribute
   self CONST_BEAMTARGET false SetUnitAttribute
   self CONST_SNIPERTARGET true SetUnitAttribute
   
   # REQUIRED TO FUNCTION, DO NOT TOUCH
   self CONST_SUPPORTSDIGITALIS true SetUnitAttribute
   self CONST_COUNTSFORVICTORY true SetUnitAttribute
   self CONST_MAXHEALTH <-health SetUnitAttribute
   self CONST_HEALTH <-health SetUnitAttribute
   self CONST_HEALRATE 0.02 SetUnitAttribute
   @CreeperInitialize
   # ==================================
   
   @BeginList
      #"COMMANDNODE"
      #"COLLECTOR" "RELAY" "REACTOR" "OREMINE" "SIPHON" "TERP" "GUPPY"
      "ALL:BUILT,LANDED" "PULSECANNON" "MORTAR" "STRAFER" "BOMBER" "SPRAYER" "NULLIFIER:rank=5" "BEAM" "SNIPER:rank=3"
      "SHIELD:rank=0.8"
      #"FORGE"
      "BERTHA:rank=5" "THOR:rank=10"
      #"GUPPYAIR" "STRAFERAIR" "BOMBERAIR"
      #"POWERZONE" "OREDEPOSIT" "RESOURCEPACK" "TOTEM"
      #"AOO" "SHIELDKEY" "TECHARTIFACT" "MESSAGEARTIFACT"
      #"EMITTER" "SPORETOWER" "RUNNERNEST" "AETOWER" "INHIBITOR"
      #"RUNNER"
   @BuildList ->THREATS
   
   @BeginList
      "ALL:BUILT,LANDED"
      "COMMANDNODE"
      "COLLECTOR" "RELAY" "REACTOR:rank=2" "OREMINE:rank=1.5" "SIPHON:rank=1.5" "TERP" "GUPPY"
      "PULSECANNON" "MORTAR" "STRAFER" "BOMBER" "SPRAYER" "NULLIFIER:rank=5" "BEAM" "SNIPER:rank=3"
      "SHIELD:rank=0.8"
      "FORGE:rank=1.5"
      "BERTHA:rank=5" "THOR:rank=10"
   @BuildList ->PLAYER_UNITS
   
   @BeginList
      "ALL:BUILT" "PULSECANNON" "MORTAR" "STRAFER" "BOMBER" "SPRAYER" "NULLIFIER:rank=5" "BEAM" "SNIPER:rank=3"
      "SHIELD:rank=0.8" "STRAFERAIR" "BOMBERAIR" "GUPPYAIR"
   @BuildList ->TURBO_EFFECT
   
   @BeginList
      "SHIELDKEY" "TECHARTIFACT" "MESSAGEARTIFACT"
   @BuildList ->COMPLETE_VICTORY
   
   @BeginList
      "CRPLCORE:S_BASE=" concat(self)
   @BuildList ->FRIENDLY
   
   @BeginList
      "CRPLCORE:S_BASE=" concat(self) concat(",IS_BUILT=1")
   @BuildList ->BUILT
   
   @BeginList
      "CRPLCORE:S_BASE=" concat(self) concat(",IS_BUILT=0")
   @BuildList ->BUILDING
   
   @BeginList
      "TOTEM"
   @BuildList ->TOTEMS
   
   @BeginList
      "CRPLCORE:CREEP_TYPE=TOTEMSIPHON"
   @BuildList ->TOTEMSIPHONS
   
   @BeginList
      "CRPLCORE:CREEP_TYPE=COLLECTOR"
      "CRPLCORE:CREEP_TYPE=OREMINE"
      "CRPLCORE:CREEP_TYPE=TOTEMSIPHON"
   @BuildList ->PRODUCERS
   
   @BeginList
      "OREDEPOSIT"
   @BuildList ->ORE
   
   @BeginList
      "CRPLCORE:CREEP_TYPE=OREMINE" "OREMINE"
   @BuildList ->OREMINES
   
   <-dontFlyTimeSeconds mul(30) ->cold_feet
   self CONST_HEALTH GetUnitAttribute ->HEALTH_LAST
   false ->UNDER_ATTACK
endonce

@StandardCreep

@IsBuilt if
   @ConsistentAI
   GetTimer3 eq0 if
      if(or(not(@IsFlying) <-IGNORES_FLIGHT))
         if (<-SUPPORTS_HARDSLEEP)
            if(@IsSleeping)
               @SleepAI
            else
               @AI
            endif
         else
            @AI
         endif
      else
         @FlightAI
      endif
   endif
else
   @Build
endif

# Unit Purpose
:UNIT_PRODUCER 0 :UNIT_CONSUMER 1 :UNIT_CONSUMER_HEAVY 2

# AI State
:AI_NONE 0 :AI_WAIT 1 :AI_WAIT_LONG 2 :AI_BUILD 3 :AI_SEEK_TERRAIN 4

:AI
   GetTimer0 eq0 if
      CurrentCoords <-emit if(<-turboMode) mul(<-turboModeMultiplier) endif SetCreeper
      <-interval SetTimer0
      if(<-turboMode and(@TurboModeAvailable))
         @DisengageTurboMode
      endif
   endif
   
   if(<-turboMode) return endif
   
   <-ai eq(@AI_NONE) if
      # Reset old terrain count.
      0 ->terrain_count
      # Randomize a new AI.
      @InDanger if
         @TurboModeAvailable if
            CurrentCoords 15 <-THREATS @GetAllUnits ->count
            <-count 0 do ->garbage loop --garbage
            <-count gt(7) if
               @EngageTurboMode
            endif
         endif
         0 13 RandInt ->rand
         <-rand lte(4) and(@CanMove) if
            #@AI_NONE ->ai
            #@FindNewLocation
            @AI_SEEK_TERRAIN ->ai
         else
            @AIBuild
         endif
      else
         0 20 RandInt ->rand
         <-rand lt(6) if
            @AI_WAIT ->ai
         else
            <-rand lt(19) if
               @AIBuild
            else
               <-rand eq(19) if
                  @AI_SEEK_TERRAIN ->ai
                  #@AI_NONE ->ai
                  #@FindNewLocation
               endif
            endif
         endif
      endif
   endif
   
   <-ai eq(@AI_WAIT) if
      30 mul(1 3 RandInt) SetTimer3
      @AI_NONE ->ai
   endif
   
   <-ai eq(@AI_WAIT_LONG) if
      150 SetTimer3
      @AI_NONE ->ai
   endif
   
   <-ai eq(@AI_BUILD) if
      GetTimer2 eq0 if
         @DestroyBeam
         @AI_NONE ->ai
      else
         <-BM_X <-BM_Y 0.3 AddCreeper
      endif
   endif
   
   <-ai eq(@AI_SEEK_TERRAIN) if
      <-cold_feet lte(0) if      
         # If the terrain count is too large it means there's no area to settle.
         # Here we are spreading numerous checks over time, limiting each time to 5
         # checks for empty space per tick with a maximum of 125 checks over 25 ticks
         # indicating a failure.
         <-terrain_count add(1) ->terrain_count
         <-terrain_count gt(25) if
            @AI_WAIT_LONG ->ai
         else
            900 ->cold_feet
            CurrentCoords 4 <-maxTravelRadius <-distToPlayer 5 @FindOpenSpace ->y ->x
            @IsPointOnMap(<-x <-y) if
               @SetFlightLandingTarget(<-x <-y)
               @ExecuteLiftoff
            else
               20 mul(RandFloat) add(10) SetTimer3
            endif
         endif
      else
         @AI_NONE ->ai
      endif
   endif

:ConsistentAI
   @AnimateBeam
   <-turboCooldown sub(1) ->turboCooldown
   <-turboCooldown lt(0) if 0 ->turboCooldown endif
   if(<-turboMode)
      @TurboEffect
      GetTimer1 eq0 if
         "Misc30" PlaySound
         #SetTimer1(30 add(20 0 RandInt))
         SetTimer1(30)
      endif
   endif
   <-cold_feet sub(1) ->cold_feet
   <-cold_feet lt(0) if 0 ->cold_feet endif
   <-danger_cl sub(1) ->danger_cl
   <-danger_cl lt(0) if 0 ->danger_cl endif

:SleepAI
   # SLEEP DISABLED FOR UNIT

:FlightAI
   # SLEEPER AI INACTIVE FOR THE DURATION OF FLIGHT
   
:built
   # TYPICALLY BUILT ON INCEPTION

:death
   "CRPLCORE" CurrentCoords CreateUnit ->death
   <-death "CSleeperDeath.crpl" AddScriptToUnit
   
   # Upon death remove anything that could slow victory.
   CurrentCoords 1000 <-COMPLETE_VICTORY @GetAllUnits ->units
   <-units 0 do
      ->unit
      <-unit 3 Destroy
   loop
   <-cinematic "Cinematic.crpl" "destroyed" true SetScriptVar
   
:FindNewLocation
   CurrentCoords 4 70 <-distToPlayer 20 @FindOpenSpace ->y ->x
   @IsPointOnMap(<-x <-y) if
      @SetFlightLandingTarget(<-x <-y)
      @ExecuteLiftoff
   else
      @AIBuild
   endif
   
:AIBuild
   90 SetTimer2
   @AI_BUILD ->ai
   
   # It is hard to predict the players capacity to destroy.
   # Therefore sometimes just don't auto build an ore-mine or aether base
   # because occasionally they are insta-destroyed.
   
   0 3 RandInt eq0 ->do_auto
   <-do_auto if
      CurrentCoords <-buildRadius <-ORE @GetAllUnits ->count
      <-count gt(0) if
         <-count 0 do
            ->ore
            @UnitCoords(<-ore) 2 <-OREMINES @GetClosestUnit ->unit
            <-unit eq(-1) and(@SafeForBuilding(@UnitCoords(<-ore) 0 0 12)) if
               @UnitCoords(<-ore) false @TargetBeam
               @UnitCoords(<-ore) "OREMINE" @BuildUnit
               return
            endif
         loop
      endif
      CurrentCoords <-buildRadius <-TOTEMS @GetAllUnits ->count
      <-count gt(0) if
         <-count 0 do
            ->totem
            @UnitCoords(<-totem) 2 <-TOTEMSIPHONS @GetClosestUnit ->unit
            <-unit eq(-1) and(@SafeForBuilding(@UnitCoords(<-totem) 0 0 15)) if
               @UnitCoords(<-totem) false @TargetBeam
               @UnitCoords(<-totem) "TOTEMSIPHON" @BuildUnit
               return
            endif
         loop
      endif
   endif
   
   CurrentCoords <-buildRadius <-PRODUCERS @GetAllUnits ->count
   <-count 0 do ->garbage loop --garbage
   
   # Here we want to determine that Creeps built will have the required
   # creeper to finish their built state.
   
   true ->heavy
   true ->consumer
   true ->producer
   <-count lt(2) if
      false ->heavy
      false ->consumer
   else
      <-count lt(4) if
         false ->heavy
      endif
   endif
   
   # Find something to build, and a location.
   
   <-producer <-consumer <-heavy @RandomValidCreepIndex ->index
   CurrentCoords 1 <-buildRadius 1 40 @FindOpenSpace ->y ->x
   false ->built
   false ->too_many
   
   # Check that it is safe for building, don't build something beside a
   # cannon.
   <-x neq(-1) and(<-y neq(-1)) and(@SafeForBuilding(<-x <-y 0 0 5)) if
      <-x <-y <-DENSITY_RADIUS <-FRIENDLY @GetAllUnits ->count
      <-count 0 do ->garbage loop --garbage
      
      <-count lt(<-UNITS_PER_RADIUS) if
         <-x <-y false @TargetBeam
         <-x <-y @TypeForIndex(<-index) @BuildUnit
         true ->built
      else
         true ->too_many
      endif
   endif
   <-too_many if
      @AI_SEEK_TERRAIN ->ai
   endif
   <-built not if
      @AI_WAIT_LONG ->ai
   endif
   
:BuildUnit
   "Weapons18" PlaySound
   ->unit_type
   ->unity
   ->unitx
   @IndexForType(<-unit_type) ->index
   "CRPLCORE" <-unitx <-unity CreateUnit ->unit
   <-unit @ScriptForIndex(<-index) AddScriptToUnit
   <-unit @ScriptForIndex(<-index) "S_BASE" self SetScriptVar
   <-unit @ScriptForIndex(<-index) "BUILT_ON_SPAWN" false SetScriptVar

:SafeForBuilding
   ->DANGER_MIN_RAD
   ->MIN_CREEPER_RAD
   ->MIN_CREEPER
   ->SFB_Y
   ->SFB_X
   
   <-SFB_X <-SFB_Y <-MIN_CREEPER_RAD @GetCreeperInRadius ->creep
   <-creep lt(<-MIN_CREEPER) if
      return(false)
   endif
   <-SFB_X <-SFB_Y <-DANGER_MIN_RAD <-THREATS @GetAllUnits ->count
   <-count 0 do ->garbage loop --garbage
   <-count neq0 if
      return(false)
   endif
   true
   
:GetCreeperInRadius
   ->CR_RAD
   ->CR_Y
   ->CR_X
   0 ->CR_C
   <-CR_RAD mul(2) add(1) ->CR_LENGTH
   
   <-CR_LENGTH 0 do
      <-CR_LENGTH 0 do
         <-CR_Y I add <-CR_RAD sub ->CR_FY
         <-CR_X J add <-CR_RAD sub ->CR_FX
         <-CR_FX <-CR_FY GetCreeper add(<-CR_C) ->CR_C
      loop
   loop
   
   <-CR_C
   
:RandomUnlockedCreepIndex
   @RandomCreepIndex ->crpi
   while not(<-crpi @IsIndexUnlocked) repeat
      @RandomCreepIndex ->crpi
   endwhile
   <-crpi

:RandomValidCreepIndex
   ->heavy
   ->consumer
   ->producer
   @RandomUnlockedCreepIndex ->crin
   while
      false ->good
      <-crin @PurposeForIndex ->purpose
      <-heavy not if
         <-crin eq(@UNIT_CONSUMER_HEAVY) not ->good
      endif
      <-consumer not and(<-good) if
         <-crin eq(@UNIT_CONSUMER) not ->good
      endif
      <-producer not and(<-good) if
         <-crin eq(@UNIT_PRODUCER) not ->good
      endif
   <-good repeat
      @RandomUnlockedCreepIndex ->crin
   endwhile
   <-crin
   
:PurposeForIndex
   ->index
   <-CREEP_PURPOSE GetListElement(<-index)
   
:RandomCreepIndex
   0 <-CREEP_LIMIT RandInt @IndexForChance

:IsIndexUnlocked
   ->index
   <-USE_AETHER if
      <-AETHER gte(@AetherForIndex(<-index))
   else
      TRUE
   endif
:AetherForIndex
   ->index
   <-CREEP_AETHER GetListElement(<-index)

:TypeForIndex
   ->index
   <-CREEP_TYPES GetListElement(<-index)
   
:ChanceForIndex
   ->index
   <-CREEP_BUILDCHANCE GetListElement(<-index)
   
:ScriptForIndex
   ->index
   <-CREEP_SCRIPTS GetListElement(<-index)
   
:IndexForType
   ->type
   <-CREEP_TYPES GetListCount 0 do
      <-CREEP_TYPES GetListElement(I) eq(<-type) if
         return(I)
      endif
   loop
   -1
   
:IndexForChance
   ->chance
   0 ->lim
   <-CREEP_BUILDCHANCE GetListCount 0 do
      <-lim add(<-CREEP_BUILDCHANCE GetListElement(I)) ->lim
      <-chance lt(<-lim) if
         return(I)
      endif
   loop
   -1
   
:TurboModeAvailable
   <-turboCooldown lte(0)

:TurboEffect
   CurrentCoords 20 <-TURBO_EFFECT @GetAllUnits
   0 do
      ->unit
      2.0 ->speed
      0.02 ->damage
      CurrentCoords @UnitCoords(<-unit) Distance div(20.0) mul(0.7) ->modifier
      <-speed mul(<-modifier) ->speed
      <-damage mul(<-modifier) ->damage
      
      <-unit CONST_ISLANDED GetUnitAttribute not if <-speed mul(4.0) ->speed endif
      
      CurrentPixelCoords @UnitPixelCoords(<-unit) @GetPixelAngle ->angle
      <-angle sub(PI div(10) mul(5.25)) <-speed @GetPixelVector ->vx ->vy
      @UnitPixelCoords(<-unit) add(<-vy) swap add(<-vx) ->ppx ->ppy

      @IsPointOnMap(<-ppx <-ppy PixelToCell) if
         <-ppx <-ppy @SetUnitPixelCoords(<-unit)
      endif
      <-unit CONST_AMMO dup2 GetUnitAttribute sub(0.5) SetUnitAttribute
      <-unit Damage(<-damage)
   loop
   
:EngageTurboMode
   -?cin_turbo not if
      true ->cin_turbo
      <-cinematic "Cinematic.crpl" "turbo" true SetScriptVar
   endif

   "TotemExplosion" PlaySound
   "Weapons4" PlaySound
   5 CurrentPixelCoords -0.1 1 1 0.03 CreateEffect
   true ->turboMode
   #<-turboCooldownMax ->turboCooldown
   <-turboTime ->turboCooldown
   16 CurrentPixelCoords -0.06 2 2 0.05 <-turboTime CreateEffectClipped
   DisableTowerField
   30 -300000 0 0 true EnableTowerField
   
:DisengageTurboMode
   6 CurrentPixelCoords -0.08 5 5 0.01 CreateEffect
   "Misc29" PlaySound
   "BlackHoleDone" PlaySound
   @ResetField
   false ->turboMode
   <-turboCooldownMax ->turboCooldown
   
   CurrentCoords 20 <-TURBO_EFFECT @GetAllUnits
   0 do
      # Fix collision problems.
      ->unit
      @UnitPixelCoords(<-unit) PixelToCell @SetUnitCoords
   loop

:InDanger
   # Danger check limit.
   <-danger_cl lte(0) if
      300 ->danger_cl
      CurrentCoords <-dangerRadius <-THREATS @GetClosestUnit ->unit
      <-unit neq(-1)
   else
      return(false)
   endif
   
:CanMove
   CurrentCoords GetDigitalis <-maxDigitalis gte and(not(<-turboMode))
   
:RegisterCreep
   ->purpose
   ->aether
   ->chance
   ->creep_type
   ->script
   <-CREEP_PURPOSE AppendToList(<-purpose)
   <-CREEP_TYPES AppendToList(<-creep_type)
   <-CREEP_BUILDCHANCE AppendToList(<-chance)
   <-CREEP_AETHER AppendToList(<-aether)
   <-CREEP_SCRIPTS AppendToList(<-script)
   
:FindOpenSpace
->FO_ATT
->FO_MINDIST
->FO_RANGE
->FO_RAD
->FO_Y
->FO_X
while
   <-FO_X <-FO_Y <-FO_RANGE RandCoordsInRange ->FO_TY ->FO_TX
   <-FO_TX <-FO_TY <-FO_RAD @TerrainAccessible not ->FO_TERRAIN
   <-FO_TX <-FO_TY <-FO_RAD @AreaOccupied ->FO_AREA
   <-FO_TX <-FO_TY <-FO_MINDIST <-PLAYER_UNITS @GetClosestUnit neq(-1) ->FO_THREAT
   <-FO_TERRAIN or(<-FO_AREA or(<-FO_THREAT))->LAND_BAD
   <-LAND_BAD ->FO_TRY
   <-FO_TRY and(<-FO_ATT gt(0))
repeat
   <-FO_ATT 1 sub ->FO_ATT
endwhile

<-FO_ATT eq0 if
   -1 -1
else
   <-FO_TX <-FO_TY
endif
   
:FinalizeCreepRegistry
   0 ->CREEP_LIMIT
   <-CREEP_BUILDCHANCE GetListCount 0 do
      <-CREEP_LIMIT add(<-CREEP_BUILDCHANCE GetListElement(I)) ->CREEP_LIMIT
   loop
   
# ===============================
# ===== STANDARD CREEP CODE =====
# ===============================

# Bare minimum required code to run each tick.
# Ensures that player can damage sleeping Creeps.
:StandardCreep
   if(not(@IsFlying))
      #@CreepSleep cannot have a sleep mode
      CurrentCoords GetCreeper 0 lt if
         self 8 Damage
      endif
      CurrentCoords GetDigitalis <-minDigitalis lt if
         self 6 Damage
      endif
      @IsBuilt if
         @Repair
      endif
   endif
   @AnimateFlight


:RandomFixCoord
   while not(@IsPointOnMap(<-RF_X <-RF_Y)) repeat
      CurrentCoords 10 RandCoordsInRange ->RF_Y ->RF_X
   endwhile
   <-RF_Y
   <-RF_X
   
:AnimateFlight
   -?C_FLIGHT if
      self "main" 1 GetImageScaleX ->C_FX
      <-C_FLIGHT if
         # Liftoff
         <-C_FX 3.9 lt if
            <-C_FX 0.06 add ->C_FSN
            GetListCount(<-C_IMAGES) 0 do
               self GetListElement(<-C_IMAGES I) <-C_FSN <-C_FSN SetImageScale
            loop
         else
            GetQueuedMoveCount eq0 if
               <-C_FLIGHT_MOVE if
                  false ->C_FLIGHT_MOVE
                  @ExecuteLanding
               else
                  self CONST_BEAMTARGET true SetUnitAttribute
                  self CONST_SNIPERTARGET false SetUnitAttribute
                  true ->C_FLIGHT_MOVE
                  if(not(@IsPointOnMap(<-C_FLIGHT_X <-C_FLIGHT_Y)))
                     @RandomFixCoord ->C_FLIGHT_X ->C_FLIGHT_Y
                  else
                     <-C_FLIGHT_X <-C_FLIGHT_Y <-FLIGHT_SPEED QueueMove
                  endif
                  true ->C_FLYING
               endif
            endif
         endif
      else
         # Landing
         <-C_FX 3 gt if
            <-C_FX 0.06 sub ->C_FSN
            GetListCount(<-C_IMAGES) 0 do
               self GetListElement(<-C_IMAGES I) <-C_FSN <-C_FSN SetImageScale
            loop
         else
            "UnitLand" PlaySound
            11 CurrentPixelCoords 0.01 3 3 0.03 CreateEffect
            --C_FLIGHT
            self CONST_BEAMTARGET false SetUnitAttribute
            self CONST_SNIPERTARGET true SetUnitAttribute
            <-CREEP_FORCEFIELD_RANGE <-CREEP_FORCEFIELD_CPUSH <-CREEP_FORCEFIELD_ACPUSH 0 true EnableTowerField
            CurrentCoords GetDigitalisGrowth neq0 ->C_DIGITALIS_PRESENT
            CurrentCoords true SetDigitalisGrowth
            CurrentCoords 1.0 SetDigitalis
            -?C_FLIGHT_X and(-?C_FLIGHT_Y) if
               <-C_FLIGHT_X <-C_FLIGHT_Y GetUnitAttribute(self CONST_CELLWIDTH) sub(1) div(2) FALSE @AddOccupy
            endif
         endif
      endif
   endif
   
:ResetField
   DisableTowerField
   <-CREEP_FORCEFIELD_RANGE <-CREEP_FORCEFIELD_CPUSH <-CREEP_FORCEFIELD_ACPUSH 0 true EnableTowerField

:SetFlightLandingTarget
   -?C_FLIGHT_X and(-?C_FLIGHT_Y) if
      <-C_FLIGHT_X <-C_FLIGHT_Y GetUnitAttribute(self CONST_CELLWIDTH) sub(1) div(2) FALSE @AddOccupy
   endif
   ->C_FLIGHT_Y
   ->C_FLIGHT_X
   
   <-C_FLIGHT_X <-C_FLIGHT_Y GetUnitAttribute(self CONST_CELLWIDTH) sub(1) div(2) TRUE @AddOccupy
   -?C_FLIGHT and(<-C_FLYING) if
      AbortMove
      <-C_FLIGHT_X <-C_FLIGHT_Y <-FLIGHT_SPEED QueueMove
   endif
   
:ExecuteLiftoff
   <-C_DIGITALIS_PRESENT not if
      CurrentCoords 0 SetDigitalis
      CurrentCoords false SetDigitalisGrowth
      true ->C_DIGITALIS_PRESENT
   endif
   self CONST_TAKEMAPSPACE false SetUnitAttribute
   "Weapons3" PlaySound
   true ->C_FLIGHT
   false ->C_FLYING
   false ->C_FLIGHT_MOVE
   DisableTowerField
   
   # Beams deal significantly less damage, therefore, lower health to
   # within acceptable parameters.
   self CONST_HEALTH dup2 GetUnitAttribute div(10.0) SetUnitAttribute
   self CONST_MAXHEALTH dup2 GetUnitAttribute div(10.0) SetUnitAttribute
   
   GetListCount(<-C_IMAGES) 0 do
      GetListElement(<-C_IMAGES I) ->EX_IM
      self <-EX_IM GetImagePositionZ ->EX_ZPOS
      self <-EX_IM <-EX_ZPOS sub(0.1) SetImagePositionZ
   loop

:ExecuteLanding
   self CONST_TAKEMAPSPACE true SetUnitAttribute
   false ->IS_SLEEPING
   false ->C_FLIGHT
   false ->C_FLYING
   
   # Restore health back to normal values.
   self CONST_MAXHEALTH dup2 GetUnitAttribute mul(10.0) SetUnitAttribute
   self CONST_HEALTH dup2 GetUnitAttribute mul(10.0) SetUnitAttribute
   
   GetListCount(<-C_IMAGES) 0 do
      GetListElement(<-C_IMAGES I) ->EX_IM
      self <-EX_IM GetImagePositionZ ->EX_ZPOS
      self <-EX_IM <-EX_ZPOS add(0.1) SetImagePositionZ
   loop

:IsFlying
   -?C_FLIGHT

:Build
   CurrentCoords GetCreeper ->C_CRP
   <-CREEPER_FOR_BUILD 0 lte if
      @FinishBuild
   else
      <-C_CRP 0 gt if
         self CONST_MAXAMMOAC GetUnitAttribute self CONST_AMMOAC GetUnitAttribute sub ->C_CRPN
         <-C_CRP <-C_CRPN gt if
            <-C_CRP <-C_CRPN sub ->C_LEFT
            CurrentCoords <-C_LEFT SetCreeper
            @FinishBuild
         else
            self CONST_AMMOAC dup2 GetUnitAttribute <-C_CRP add SetUnitAttribute
            CurrentCoords 0 SetCreeper
         endif
      endif
   endif

:FinishBuild
   GetListCount(<-C_IMAGES) 0 do
      GetListElement(<-C_IMAGES I) 255 @SetImageAlpha
   loop
   
   self CONST_AMMO 0 SetUnitAttribute
   self CONST_MAXAMMOAC 0 SetUnitAttribute
   self CONST_SHOWAMMOACBAR false SetUnitAttribute
   true ->IS_BUILT
   <-CREEP_FORCEFIELD_RANGE <-CREEP_FORCEFIELD_CPUSH <-CREEP_FORCEFIELD_ACPUSH 0 true EnableTowerField
   <-BUILD_SOUND PlaySound
   @built
      
:Repair
   CurrentCoords GetDigitalis <-minDigitalis lt if
      CurrentCoords CurrentCoords GetDigitalis 0.16 add SetDigitalis
   else
      CurrentCoords GetDigitalis <-maxDigitalis lt if
         CurrentCoords CurrentCoords GetDigitalis 0.003 add SetDigitalis
      endif
   endif

:Sleep
   ->C_SLEEP
   true ->IS_SLEEPING
   
:IsSleeping
   <-IS_SLEEPING
   
:IsBuilt
   <-IS_BUILT

:destroyed
   -?C_FLIGHT_X and(-?C_FLIGHT_Y) if
      <-C_FLIGHT_X <-C_FLIGHT_Y GetUnitAttribute(self CONST_CELLWIDTH) sub(1) div(2) FALSE @AddOccupy
   endif
   <-C_DIGITALIS_PRESENT not if
      CurrentCoords 0 SetDigitalis
      CurrentCoords false SetDigitalisGrowth
   endif
   DisableTowerField
   @death

:CreeperInitialize
   <-BUILT_ON_SPAWN not if
      GetListCount(<-C_IMAGES) 0 do
         GetListElement(<-C_IMAGES I) 125 @SetImageAlpha
      loop
      
      self CONST_MAXAMMOAC <-CREEPER_FOR_BUILD SetUnitAttribute
      self CONST_AMMOAC 0 SetUnitAttribute
      self CONST_SHOWAMMOACBAR true SetUnitAttribute
      
      0.2   <-CREEPER_FOR_BUILD mul sqrt ->C_RANGE
      <-C_RANGE 5 lt if
         5 ->C_RANGE
      endif
      8000 <-C_RANGE mul neg ->C_PULL
      
      <-C_RANGE <-C_PULL 0 0 true EnableTowerField
   else
      0 ->CREEPER_FOR_BUILD
   endif
   false ->IS_SLEEPING
   0 ->C_SLEEP
   300 ->C_SLEEPCHECK
   #150 ->C_SLEEPTIME
   
   CurrentCoords GetDigitalisGrowth neq0 ->C_DIGITALIS_PRESENT
   CurrentCoords true SetDigitalisGrowth
   CurrentCoords 1.0 SetDigitalis

:RegisterImage
   ->ri_name
   if(not(-?C_IMAGES))
      CreateList ->C_IMAGES
   endif
   if(not(<-C_IMAGES <-ri_name @DoesListContain))
      <-C_IMAGES <-ri_name AppendToList
   endif

#=============================================
# ===== :EasyCRPL: Collection by Telanir =====
#
# Usage: Simply paste this script at the bottom of your
# project to get the full benefits of the pre-written code.
# You can also just copy-paste parts of this you need.
#
# EasyCRPL is a collection of scripts from various community
# projects, members, and maps that have been created by myself
# or modified to meet a more generic purpose.
#
# -----
# A List of all available functions and their notation:
# @FunctionName: order of input - order of output with last object on top of the stack
# x - X coordinate
# y - Y coordinate
# n - numerical value
# i - integer value
# f - float value
# b - boolean value
# s - string value
# u - unit id, integer value
# L - list
# * - any value
#
# NOTE: Non-pixel coords are flipped vertically
# with (0,0) being top-left, whereas pixel (0,0)
# is the bottom left.
#
# -- Special Code --
# This code can be used at any time if it is in your
# script, and generally comes in blocks of functions.
# List-Builder: Two simple functions that will safely create a list without touching the stack.
# Pre-Built Beam: A beam with a customizable color, alpha, and width.
#
# -- Various Convenience Methods --
# @SetImageAlpha: s1 i1 -
# GetClosestUnit: x y f1 L1 - u
# @CRPLMatchesValue: u s1 * - b1
# @TerrainAccessible: x y i1 - b1
# @AreaOccupied: x y i1 - b1
# @AddOccupy: x y i1 b1 -
# @DoesListContain: L1 * - b1
# -- Cell-Related Code --
# @UnitCoords: u - x y
# @MovePosition: x y x2 y2 f1 f2 - y x
# @GetVector: f1 f2 - y x
# @NormalizeVector: x y - y x
# @GetLength: x y - f1
# @GetAngle: x y x2 y2 - f1
# @RotateToAngle: x y x2 y2 f1 f2 - b1 f1
# @IsPointOnMap: x y - b1
# -- Pixel-Related Code --
# @UnitPixelCoords: u - x y
# @SetPixelCoords: x y -
# @MovePixelPosition: x y x2 y2 f1 f2 - y x
# @GetPixelVector: f1 f2 - y x
# @GetPixelAngle: x y x2 y2 - f1
# @RotateToPixelAngle: x y x2 y2 f1 f2 - b1 f1
#
# Wiki CRPL Reference: http://knucklecracker.com/wiki/doku.php?id=crpl:crplreference
#=============================================

#======================================
#=========== LIST-BUILDER =============
#======================================

# How to use:
# If you ever need to quickly build a list of X units then
# you can use these two functions to do it in one line.
# You can use this in conjuction with @DoesListContain listed in the index.
# e.g. @BeginList "COLLECTOR" "RELAY" "COMMANDNODE" "PULSECANNON" "MORTAR" @BuildList ->newList

:BeginList
   StackSize ->BL_I
   
:BuildList
   StackSize ->BL_M
   CreateList ->BL_L
   <-BL_M <-BL_I do
      ->BL_O
      <-BL_L <-BL_O PrependToList
   loop
   <-BL_L

#======================================
#=========== PRE-BUILT BEAM ===========
#======================================
# How to use:
# Copy the 4 beam methods into your code.
# @AnimateBeam needs to run /every/ script invocation.
# Use @SetBeamProperties at any time to change the
# appearence of the beam.
# Use @TargetBeam at any time to focus the beam on something,
# if it is moving, keep calling @TargetBeam.
# When finished, call @DestroyBeam and the beam will vanish!
#
# Warning: DO NOT USE @BeamInit!

# Function: :SetBeamProperties
# Sets the properties of the beam.
# Alpha, red, green, and blue range from 0 - 255
# With 0 being transparent and 255 being opaque
# The string is the "Beam" item, it can be found here:
# http://knucklecracker.com/wiki/doku.php?id=crpl:custom_image_repository
# The two integers after the image name are the indexes for
# the damage icons. If your "Damage 0" is at "Custom1" then write 1,
# then if your "Damage 3" is at "Custom4" then write 4 after.
# Notation: s1 i1 i2 i3 i4 i5 i6 f1 -
# eg. "Custom0" 1 4 255 0 0 255 1.5 @SetBeamProperties
:SetBeamProperties
   ->BM_SCALEY
   ->BM_ALPHA
   ->BM_BLUE
   ->BM_GREEN
   ->BM_RED
   ->BM_DAMAGEINDEXEND
   ->BM_DAMAGEINDEXSTART
   ->BM_IMAGE
   true ->BM_SET
   -?BM_ACTIVE if
      @BeamInit
   endif

# Function: :AnimateBeam
# Animates the beam and the explosion animation.
# The boolean indicates whether it is a pixel position.
# Notation: -
# eg. @AnimateBeam
:AnimateBeam
   -?BM_ACTIVE and(-?BM_SET) if
      if(<-BM_TOG)
         <-BM_X <-BM_Y ->BM_PY ->BM_PX
      else
         <-BM_X <-BM_Y CellToPixel ->BM_PY ->BM_PX
      endif
      
      -?BM_T if
         GetUnitType(<-BM_T) ->BM_TT
         if(<-BM_TT eq("BOMBERAIR") or(<-BM_TT eq("STRAFERAIR")))
            <-BM_PY add(27) ->BM_PY
         endif
         if(<-BM_TT eq("GUPPYAIR"))
            <-BM_PY add(10) ->BM_PY
         endif
      endif
      
      self CONST_PIXELCOORDX GetUnitAttribute ->BM_SX
      self CONST_PIXELCOORDY GetUnitAttribute ->BM_SY

      <-BM_PX <-BM_SX sub ->BM_DX
      <-BM_PY <-BM_SY sub ->BM_DY
      <-BM_DY <-BM_DX atan2 ->BM_A
      <-BM_SX <-BM_SY <-BM_PX <-BM_PY Distance ->BM_D
      <-BM_D 24 div ->BM_SCX
      
      <-BM_DX 2 div ->BM_PPX
      <-BM_DY 2 div ->BM_PPY
      
      SetImagePosition(self "beam" <-BM_PPX <-BM_PPY -0.1)
      SetImageScaleX(self "beam" <-BM_SCX)
      SetImageRotation(self "beam" <-BM_A)
      
      <-BM_DAMAGEINDEXSTART neq(-1) and(<-BM_DAMAGEINDEXEND neq(-1)) if
         <-B_IMG add(1) ->B_IMG
         <-B_IMG <-BM_DAMAGEINDEXSTART lt if <-BM_DAMAGEINDEXSTART ->B_IMG endif
         <-B_IMG <-BM_DAMAGEINDEXEND gt if <-BM_DAMAGEINDEXSTART ->B_IMG endif
         SetImage(self "damage" concat("Custom" <-B_IMG))
         SetImagePosition(self "damage" <-BM_DX <-BM_DY -0.1)
      endif
   endif
   
# Function: :SetBeamTarget
# The purpose of this function is for the main
# @AnimateBeam function to be aware if the target
# is a certain type of unit. It will automatically
# make corrections for flying units.
# Use -1 if the target is a non-unit.
:SetBeamTarget
   ->BT_T
   <-BT_T eq(-1) if
      --BM_T
   else
      <-BT_T ->BM_T
   endif

# Function: :TargetBeam
# Focuses the beam on a target location.
# If a beam does not exist, makes one.
# The boolean value indicates whether or not the
# new position is a pixel position.
# IF THE ENEMY IS AN 'AIR' SUFFIX ENEMY (aka STRAFERAIR)
# YOU MUST USE @SetBeamTarget TO CORRECT THE Y-OFFSET.
# Notation: x y b1 -
# eg. @UnitCoords(<-targetUnit) false @TargetBeam
# eg. 100 100 true @TargetBeam
:TargetBeam
   ->BM_TOG
   ->BM_Y
   ->BM_X
   -?BM_SET not if
      # Set some default values.
      "Custom0" 1 4 255 0 0 255 1.5 @SetBeamProperties
   endif
   <-BM_ACTIVE not if
      true ->BM_ACTIVE
      @BeamInit
   endif

# Function: :DestroyBeam
# Removes the active beam, if there is one.
# Notation: -
# eg. @DestroyBeam
:DestroyBeam
   -?BM_ACTIVE if
      --BM_ACTIVE
      self "beam" "NONE" SetImage
      self "damage" "NONE" SetImage
   endif
   
# Function: :BeamInit
# Used by the Beam script to update Beam appearance.
:BeamInit
   self "beam" <-BM_IMAGE SetImage
   self "beam" <-BM_RED <-BM_GREEN <-BM_BLUE <-BM_ALPHA SetImageColor
   self "beam" <-BM_SCALEY SetImageScaleY
   self "beam" -0.11 SetImagePositionZ

#======================================
#==== VARIOUS CONVENIENCE METHODS =====
#======================================

# Function: :SetImageAlpha
# Sets the alpha of the specified image for self.
# Alpha ranges from 0 - 255 with 0 transparent and 255 opaque
# Notation: s1 i1 -
# eg. "main" 100 @SetImageAlpha
:SetImageAlpha
asint ->si_alpha_new
->si_image
self <-si_image GetImageColor ->si_alpha ->si_blue ->si_green ->si_red
self <-si_image <-si_red <-si_green <-si_blue <-si_alpha_new SetImageColor

# Function :GetClosestUnit
# WARNING: use this convenience function sparingly, tens of CRPL Cores
# running this all at once every frame will slow the game down significantly.
#
# This function uses a flexible algorithm to filter
# units within a certain distance and retrieve the one
# closest to the position given to the function.
# It requires a position, a maximum distance, and an
# instruction list.
#
# The instruction list can contain multiple objects (but at least one is needed).
# An instruction follows this format: "unittype:parameter1,parameter2,parameter3=value,parameter4=value..."
# All applicable unit types can be found on:
# https://knucklecracker.com/wiki/doku.php?id=crpl:docs:getunittype
#
# The following parameters are acceptable:
# LANDED BUILT RANK=value
# LANDED and BUILT can accept modifiers. * means __either__, - means NOT, no modifier means IS
# If the parameter is none of the three, and the unit type is defined as "CRPLCORE" the assumed
# parameter type is a CRPLCore variable which is defined like so: myVar=myVal
#
# The unit type can be substituted by ALL which will affect all instructions, ALL must be in the
# beginning of the list or it will be assumed to be a unit type.
# ALL does not accept RANK or script variables as parameters.
#
# == EXAMPLE ==
# @BeginList "ALL:BUILT" "COLLECTOR" "relay:*built,rank=0.5" "pulsecannon:-landed,rank=2" "crplcore:emit=3.5" @BuildList ->instructions
# CurrentCoords 50 <-instructions @GetClosestUnit ->unit
#
# Explanation: All of the instructions within the list are acceptable. The only time the parameters are
# case sensitive is when defining script parameters. CONST_ISLANDED and CONST_ISBUILDING do not affect CRPLCores.
# According to these instructions, to get the closest unit within a range of 50 we will accept any built collector,
# any relay that is built or not but we give the a half the normal rank so the algorithm will treat them as if
# they are twice the distance away from anything else in the way. A collector and relay at the same distance will
# always return the collector. Next we prioritize any flying pulse cannon and override the ALL parameter, we
# then also look for any CRPLCores we made that had a script variable of "emit" that is equal to 3.5.
#
# Notation: x y f1 L1 - u
# e.g. CurrentCoords 50 <-instructionList @GetClosestUnit ->unit
:GetClosestUnit
   ->un_list
   asfloat ->un_dist
   asfloat ->un_y
   asfloat ->un_x
   
   -1 ->un_unit
   -1 ->un_udist
   
   <-un_x <-un_y <-un_dist <-un_list @GetAllUnits ->un_count
   <-un_count neq0 if
      <-un_count 0 do
         ->un_temp
         <-un_temp GetUnitType ->un_type
         # GET INSTRUCTION FOR THIS UNIT TYPE
         <-un_instructions GetListCount 0 do
            <-un_instructions GetListElement(I) ->un_inst
            # found correct instruction
            <-un_inst StartsWith(<-un_type) if
               <-un_inst Split(" ") ->un_ilist
               <-un_ilist GetListElement(3) asfloat ->une_rank
               
               # now we apply the priority and get the closest unit
               <-un_x <-un_y @UnitCoords(<-un_temp) Distance div(<-une_rank) ->un_tdist
               <-un_tdist lt(<-un_udist) or(<-un_udist eq(-1)) if
                  <-un_tdist ->un_udist
                  <-un_temp ->un_unit
               endif
               break
            endif
         loop
      loop
   endif
   <-un_unit

# Function :GetAllUnits
# Grabs all units of specified instruction within a radius of
# a given point. Read :GetClosestUnit for more info.
[close]
CW4 hype!!

J

Don't put code in spoilers like that. It doesn't work on all browsers / operating systems. I can only read one line of code at a time, and even less of the bomber script.
Spoiler
Either use teletype within the spoiler
[close]
Or put the code outside the spoilers, it will automatically add a scrollbar on long scripts

Nicant

CW4 hype!!

Nicant

Thanks! I changed the code box to a teletype! never knew the code boxes would do that  :P
CW4 hype!!

Nicant

Just realized i could've post this other half to the first half by replying the rest.  :P Oh well.
CW4 hype!!

rer24

I can't find the sprayer code. Did you post it?
We shall end the infinite cycle of madness!

rer24

Also, any idea how to get the original unit textures?
We shall end the infinite cycle of madness!

Nicant

Quote from: rer24 on December 13, 2015, 10:18:56 AM
Also, any idea how to get the original unit textures?
Do you mean the creep cannon and stuff?
If you did then i have no idea how to get those textures and besides it is already in the sleeper template so you don't need to put it in manually.
CW4 hype!!