#============================================= # ===== :EasyCRPL: Collection by Telanir ===== # --VERSION: 1.1 # CREDITS: # Big thanks to Knuckle Cracker (knucracker) for an amazing game and lots of well commented source! # Big thanks to Tyler21 for a contribution of Forcefield code using Field cells! # # 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 # -- # # @GetCreeperInRadius: x y f1 - f2 # @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. :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 neg ->SF_ACPUSH neg ->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 <-SF_X J add <-SF_RANGE sub ->xCell <-SF_Y I add <-SF_RANGE sub ->yCell <-xCell neq(<-SF_X) or(<-yCell neq(<-SF_Y)) if <-SF_X <-SF_Y <-xCell <-yCell Distance ->SF_DIST <-SF_DIST lte(<-SF_RANGE) if <-SF_DIST neg add(<-SF_RANGE) div(<-SF_RANGE) mul(0.7) add(0.3) ->SF_MOD <-SF_ACPUSH mul(<-SF_MOD) asint ->SF_ACPUSHMOD <-SF_CPUSH mul(<-SF_MOD) asint ->SF_CPUSHMOD @GetVector(@GetAngle(<-xCell <-yCell <-SF_X <-SF_Y) <-SF_CPUSHMOD) ->cRLCell ->cUDCell @GetVector(@GetAngle(<-xCell <-yCell <-SF_X <-SF_Y) <-SF_ACPUSHMOD) ->acUDCell ->acRLCell 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 endif endif 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 :GetCreeperInRadius # Returns the SUM of all creeper in a certain square # around an origin. If there is anti-creeper present this # value may go into the negatives. # Notation: x y f1 - f2 # eg. CurrentCoords 1 @GetCreeperInRadius ->sumcreepergrid3x3 :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 # 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