This is an old revision of the document!
#=============================================
# ===== :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.
# @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
#=============================================
#======================================
#=========== 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 :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 centarl 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