Author Topic: How do you make player units indestructible? (may be picking units wrong)  (Read 94 times)

StardustAlpha

  • Newbie
  • *
  • Posts: 7
What I want: health of pulse cannon, mortar, sheild, collector, ect. to never go down. EVER.
My current code:
Code: [Select]
while 1 0 gt
repeat
->unit
<-unit CONST_HEALTH 999999999 SetUnitAttribute
endwhile
I think my code might not be actually finding any units.

Grabz

  • Community Guild
  • *****
  • Posts: 83
  • Where will you be when the creeper waves hit?
Your code reads as follows:

While 1 is larger than 0 (i.e. always), pull a unit from the stack (which doesn't work, because nothing has been added to the stack so it's empty). As your while loop never ends, this only doesn't crash the game because it runs into the opcode limit and is halted.

I think you want something more like this:

Code: [Select]
0 0 9999 GetUnitsInRange 0 do
->unit

<-unit CONST_ISBUILDING GetUnitAttribute not if
<-unit CONST_HEALTH <-unit CONST_MAXHEALTH GetUnitAttribute SetUnitAttribute
endif
loop
This script will make regular units impervious, except for units that are destroyed in one hit - these cannot unfortunately be made impervious to creeper with CRPL.

I'll explain how it works below. However, if you don't know what a stack is, you should give this a read first: https://knucklecracker.com/wiki/doku.php?id=crpl:overview

The function GetUnitsInRange will find all player units in a specified range originating from specific coordinates. It accepts three arguments from the stack - X, Y and range. Then, it pushes all the unit ID's that it finds to the stack, followed by the amount of units that were found. We feed the function the 0,0 X,Y coordinates and 9999 as range, which is our way of saying we want to basically find everything.

Let's say the function has found four units. After calling GetUnitsInRange, the stack will look something like this
Code: [Select]
345 196 67 741 4

Now, the reason this function outputs the amount of units that were found as the last argument to the stack, is to make it more convenient to use with loops.

A do loop has the following syntax:
Code: [Select]
4 0 do
   
loop

the do function takes two arguments from the stack - the iteration index to stop at, and the iteration index to start at. Then it will start iterating the code between the do function call and the loop function call, in our case, four times.

Now, when we say this:
Code: [Select]
0 0 9999 GetUnitsInRange 0 do

loop
0 0 9999 GetUnitsInRange executes and pushes all the items into the stack, then we push 0 to the stack, and then we call the do loop. The do loop now sees the stack like this:
Code: [Select]
345 196 67 741 4 0It takes the 4 and the 0 for its own use (end index, start index) and then begins iteration. Now the stack only contains unit ID's.

To save an item from the stack to a variable, we use ->
Code: [Select]
ClearTraceLog
ShowTraceLog
0 0 9999 GetUnitsInRange 0 do
    ->unit

    <-unit Trace
loop
Now we save an item from the stack each iteration to a unit variable, and we can perform operations on it. In this case I'm just logging the ID's to the console.

Code: [Select]
<-unit CONST_ISBUILDING GetUnitAttribute not if
<-unit CONST_HEALTH <-unit CONST_MAXHEALTH GetUnitAttribute SetUnitAttribute
endif
What I'm doing here first is ignoring every unit if it's under construction. For some reason if I don't do that, units never build - presumably because a unit's CONST_MAXHEALTH increases as the unit builds, but at the beginning it's 0 so we keep setting health to 0 so it never builds. If it isn't building, I'm calling <-unit CONST_MAXHEALTH GetUnitAttribute to get the max health, and feeding it as an argument to <-unit CONST_HEALTH health_here SetUnitAttribute.

However, if a unit has so little health that it will get destroyed by Creeper in one frame, it will explode before we have a chance to increase its health back up, and there's nothing we can do about that.
« Last Edit: July 10, 2018, 02:13:29 pm by Grabz »

StardustAlpha

  • Newbie
  • *
  • Posts: 7
Well explained code
If I also added some code to increase all unit’s max health before the ‘set unitHP to maxHP’ bit, would that make them less likely to be one-shotted by the creeper? Also, is it possible to change how much damage creeper does?

GoodMorning

  • Community Guild
  • *****
  • Posts: 3227
  • (Pusillanimous)
You would have to replicate the Creeper damage behaviour, or deal damage yourself.

Unit max HP is not settable except for CRPL units.
A narrative is a lightly-marked path to another reality.

Grabz

  • Community Guild
  • *****
  • Posts: 83
  • Where will you be when the creeper waves hit?
If I also added some code to increase all unit’s max health before the ‘set unitHP to maxHP’ bit, would that make them less likely to be one-shotted by the creeper?
As GoodMorning says, you cannot change default units' max health. The game generally prevents you from changing many attributes of default units - presumably so that players are not surprised by their armament acting differently.

Also, is it possible to change how much damage creeper does?
Not easily - this functionality is hardcoded into the game against default player units, and you cannot use it for your own CRPL core units, so you have to write your own.

It would be possible to store all of the units found by GetUnitsInRange in a list as well as their current health, then on the next frame compare their current health to the health in the list. This way we know how much damage the unit took, and we can apply a modifier to that to effectively change how much damage Creeper would do. I can write this for you if you're interested, although it may or may not be very fast on bigger maps with many units.

Builder17

  • Community Guild
  • *****
  • Posts: 889
  • My account picture is from Nitrome Mutiny.
Click [Select] button and then copy that code, do you know basics?
https://knucklecracker.com/wiki/doku.php?id=crpl:crpltutorial:interactive:importing

(Untested, please tell if any problems occur.)
Code: [Select]
# This script rebuilds any lost units and keeps existing ones alive

# Defaults and initial setup
$name:"Abraxis"

# These are the units that will get captured:
# 1 means it is a normal unit. 0 means it will not be captured (typically done for the large units that take a long time to die). Any units not listed here will not be captured.
$COLLECTOR:1
$RELAY:1
$REACTOR:1
$OREMINE:1
$SIPHON:1
$GUPPY:1
$SHIELD:1
$STRAFER:1
$BOMBER:1
$TERP:1
$PULSECANNON:1
$MORTAR:1
$SPRAYER:1
$BEAM:1
$SNIPER:1
$FORGE:1
$BERTHA:1
$COMMANDNODE:0

once
@Awake
ShowTraceLog
ClearTraceLog
#SetImage(Self "Main" "None")
SetUnitAttribute(Self CONST_COUNTSFORVICTORY FALSE)
SetUnitAttribute(Self CONST_CREATEPZ FALSE)
SetUnitAttribute(Self CONST_TAKEMAPSPACE FALSE)
SetUnitAttribute(Self CONST_NULLIFIERDAMAGES FALSE)
SetUnitAttribute(Self CONST_SUPPORTSDIGITALIS FALSE)
#SetUnitAttribute(Self CONST_COORDX 1) # Moves to coordinates (1,1)
#SetUnitAttribute(Self CONST_COORDY 1)
CreateList ->UnitX
CreateList ->UnitY
CreateList ->UnitType # Type of unit such as RELAY, PULSECANNON, BEAM, etc...
endonce

GetTimer3 eq0 if
0 ->Units # Keep track of how many units are on the map (used to add unit information into lists and when cycling through the units to rebuild

# Loop through each unit on the map and store its information
GetUnitsInRange(0 0 9999) 0 do # This finds all units on map and loops through them
->UnitID # Capture the unit's ID for use later
GetUnitType(<-UnitID) ->Type # Get the unit type
if(<-Type <-!) # Look up the unit type in the list of units that will be captured. If 1 proceed, otherwise skip
GetUnitAttribute(<-UnitID CONST_COORDX) ->X # Get the X coordinate of the unit
GetUnitAttribute(<-UnitID CONST_COORDY) ->Y # Get the Y coordinate of the unit
<-Type <-X <-Y @AddUnit
<-UnitID CONST_ISBUILDING GetUnitAttribute not if
<-UnitID CONST_HEALTH <-UnitID CONST_MAXHEALTH GetUnitAttribute SetUnitAttribute
endif
else
<-UnitID CONST_ISBUILDING GetUnitAttribute not if
<-UnitID CONST_HEALTH <-UnitID CONST_MAXHEALTH GetUnitAttribute SetUnitAttribute
endif
endif
loop
2 SetTimer3 endif

# Run checks to rebuild units
<-Units 0 do
# First, check the current unit index. If it is over the unit count, move it back to the start of the list (0)
if(<-Index <-Units gte)
0 ->Index
endif

# Get unit information
GetListElement(<-UnitX <-Index) ->X
GetListElement(<-UnitY <-Index) ->Y
GetListElement(<-UnitType <-Index) ->Type

if(GetCellOccupiedCount(<-X <-Y) eq0)
if(GetCreeper(<-X <-Y) 0 lte)
"NoCreeper" Trace
if(GetDigitalis(<-X <-Y) eq0)
"yes" ->Rebuild # Assume the unit will be rebuilt unless determined otherwise

# Rebuild the unit
if(<-Rebuild "yes" eq)
# Show this to make sure units are being rebuilt
"Rebuilding unit " <-Index concat " - " concat <-Type concat " at " concat <-X concat ", " concat <-Y concat trace
PauseGame
CreateUnit(<-Type <-X <-Y) ->UnitID
<-UnitID CONST_ISBUILDING FALSE SetUnitAttribute
<-UnitID CONST_HEALTH <-UnitID CONST_MAXHEALTH GetUnitAttribute SetUnitAttribute
UnpauseGame
endif
endif
endif
endif

# Increment the index number by 1
<-Index 1 add ->Index
loop

:AddUnit # Adds a unit to the rebuild list
# Unit Type, X coordinate, Y coordinate pulls from the stack and is assigned to the corresponding list index
->Y ->X ->Type
SetListElement(<-UnitX <-Units <-X)
SetListElement(<-UnitY <-Units <-Y)
SetListElement(<-UnitType <-Units <-Type)

# Increment the unit count
<-Units 1 add ->Units

# Uncomment the following line to show the results of the unit capture (This should only be done during testing. Make sure these are commented so it doesn't show in the final map)
#ShowTraceLog <-Type " (" concat <-X concat "," concat <-Y concat ")" concat trace

:Awake
TRUE OperateWhilePaused

« Last Edit: July 16, 2018, 01:24:23 pm by Builder17 »

Grabz

  • Community Guild
  • *****
  • Posts: 83
  • Where will you be when the creeper waves hit?
Well, while you can rebuild, rebuilding is not ideal with thousands of info messages popping up and explosion noises...

Builder17

  • Community Guild
  • *****
  • Posts: 889
  • My account picture is from Nitrome Mutiny.
Hmm, should OP tell more about if he wants it to rebuild or just keep existing ones alive?

Edit: Script in previous message updated.
« Last Edit: July 15, 2018, 03:28:06 pm by Builder17 »