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! (https://knucklecracker.com/forums/index.php?topic=19486.msg136567#msg136567)
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! (https://knucklecracker.com/forums/index.php?topic=19643.0)) 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
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
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.
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
Or put the code outside the spoilers, it will automatically add a scrollbar on long scripts
Thanks! I changed the code box to a teletype! never knew the code boxes would do that :P
Just realized i could've post this other half to the first half by replying the rest. :P Oh well.
I can't find the sprayer code. Did you post it?
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.