Landmine Script

Started by ParkourPenguin, May 23, 2014, 07:43:49 PM

Previous topic - Next topic

ParkourPenguin

Hello everyone! I was playing through a bunch of custom maps, and I decided to try to make my own CRPL map. That didn't go so well, unfortunately, but I did find out that I loved CRPL.

In that map, I decided to make a landmine. It started out as a really simple idea at first- something that destroys itself and emits creeper when a tower gets near it. But, I just kept on adding more and more, until finally I decided it's complete.

So, the general idea is this: when a certain number of player units get close enough to the CRPL tower, it blows up, emits creeper, and (depending on user settings) will destroy terrain around it in the same pattern as an actual explosion.

Here are the variables:
triggerRange (default 1.5)- the maximum distance away from the landmine at which player units must be in in order to trigger/blow up the CRPL tower. 1.5 is a good range, because if the center tile of the mine is contained in any part of the user-built tower, it will explode.
minUnitsInRange (default 1)- the minimum amount of units that must be in that range in order for it to blow up. Useful for countering the player sending in a single unit to see if it is safe before sending in their main force.
creeperTriggerType(default 0)- the type of creeper under the mine that will make the mine detonate. 0=no detonation due to type of creeper underneath it, 1=detonate when creeper hits it, -1=detonate when AC hits it.
creeperIntensity(default 100)- the intensity of the creeper added. Set to negative values if you want AC to spawn.
creeperSpawnRange(default 0)- the maximum distance (in tiles) away from the center of the mine that creeper with the above intensity will spawn. e.g., if this is set to 1, it will spawn creeper in a plus sign formation on the landmine. Useful for if you want tons of creeper to spawn.
groundDestructionPower(default 0)- the maximum amount of levels of terrain the landmine will destroy around itself. e.g., if this is set to 5 and the tower is on level 7 terrain, it will destroy the terrain at most down to level 2. Note that the amount of terrain destroyed will decrease as the distance away from the tower increases. If this is left as 0, it will not destroy any terrain.
rangeOfDestruction(default 4.0)- the radius of the circle of destruction created with the tower as the center. Note that if groundDestructionPower > 0, the mine will also destroy all player units in this range.
destructionGradeConstant(default 0.5)- This will take some explaining. In the formula I used to relate levels destroyed vs. distance from the center, I added a "constant" (it should be called a constant in mathematics but a variable in programming) into that equation that would allow the user to change the destruction pattern created. Recommended input values for this are between -1 and 1; however, all values are taken into account, even though most outside of that range aren't really too interesting. Attached is a .png image depicting different values of the destuctionGradeConstant (dGC values.png). The explosions were with rangeOfDestruction = 18, groundDestructionPower = 10, terrain height 10.

Please note: changing the terrain has a significant impact on game performance and can cause an insufferable amount of lag relative to normal gameplay speeds. I know of no way to avoid this other than to simply not change the terrain. To that end, I would recommend you to not use high values for rangeOfDestruction, or to simply keep groundDestructionPower at 0.

Below is the code (it is also attached as Landmine.crpl). I tried to comment everything that I could, just so someone else that's new could better understand it.
# Landmine.crpl
# Created by: ParkourPenguin
# Created on: 5/21/2014
# Last edited: 5/24/2014
# ------------------------------------------
$triggerRange:1.5
$minUnitsInRange:1
$creeperTriggerType:0
$creeperIntensity:100
$creeperSpawnRange:0.0
$groundDestructionPower:0
$rangeOfDestruction:4.0
$destructionGradeConstant:0.5
once
#initialization of attributes I think shouldn't change.
SetUnitAttribute(self CONST_NULLIFIERDAMAGES false)
SetUnitAttribute(self CONST_TAKEMAPSPACE false)
SetUnitAttribute(self CONST_COUNTSFORVICTORY false)
SetUnitAttribute(self CONST_CREATEPZ false)

<-destructionGradeConstant 4 div ->destructionGradeConstant #While I said the range of destructionGradeConstant should be from -1 to 1, it's better if it was from -0.25 to 0.25. Easier on the user.

<-triggerRange 0 lt <-triggerRange 250 gt or if #Makes sure triggerRange is a reasonable value
1.5 ->triggerRange
endif
<-minUnitsInRange 1 lt if #Makes sure minUnitsInRange is a reasonable value
1 ->minUnitsInRange
endif
<-creeperSpawnRange 0 lt <-creeperSpawnRange 250 gt or if #Makes sure creeperSpawnRange is a reasonable value
1 ->creeperSpawnRange
endif
<-groundDestructionPower 0 lt if #Makes sure groundDestructionPower is a reasonable value- if it's greater than the ground below it, that'll be handled later on as it's destroying terrain
0 ->groundDestructionPower
endif
<-rangeOfDestruction 0 lt <-rangeOfDestruction 250 gt or if #Makes sure rangeOfDestruction is a reasonable value
2.5 ->rangeOfDestruction
endif
<-destructionGradeConstant -3 lt <-groundDestructionPower eq0 not and if #I'm limiting destructionGradeConstant to [-3,3] due to the possibility of overflow later on.
1 ->groundDestructionPower
4 ->destructionGradeConstant # destructionGradeConstant>3 results in the least calculations later on
endif
"pow" <-groundDestructionPower "const" <-destructionGradeConstant Trace4
endonce

if(CurrentCoords <-triggerRange GetUnitCountInRange <-minUnitsInRange gte CurrentCoords GetCreeper 0 gt <-creeperTriggerType 0 gt and CurrentCoords GetCreeper 0 lt <-creeperTriggerType 0 lt and or or) #checks if trigger conditions are met
if(<-groundDestructionPower eq0 not) #if groundDestructionPower > 0, then try to destroy the ground.
<-rangeOfDestruction ceil 1 add <-rangeOfDestruction ceil neg do #These two do loops set up a square region of side length ceil(rangeOfDestruction)*2+1
<-rangeOfDestruction ceil 1 add <-rangeOfDestruction ceil neg do
sqrt(pow(CurrentX dup I add sub 2) pow(CurrentY dup J add sub 2) add) ->distanceFromCenter # Finds the distance from the mine to a grid point
if(<-distanceFromCenter <-rangeOfDestruction lte CurrentX I add CurrentY J add GetTerrain 1 gt and) #This checks to see if it should set the terrain at that coordinate
if(<-destructionGradeConstant eq0 not <-destructionGradeConstant 3 lte and) # These if statements set the terrain. This one is the standard one that will most often be used.
CurrentX I add CurrentY J add dup2 GetTerrain ceil(max(mul(<-groundDestructionPower div(sub(pow(E mul(<-destructionGradeConstant <-distanceFromCenter)) pow(E mul(<-destructionGradeConstant <-rangeOfDestruction))) sub(1 pow(E mul(<-destructionGradeConstant <-rangeOfDestruction))))) 1)) sub dup 0 gt if
SetTerrain
else #these "else" statements are for the case where destroying the terrain too much would end up setting it as void- I don't want that to happen.
pop 1 SetTerrain
endif
else if(<-destructionGradeConstant eq0) # There's an unintended side effect dealing with the above equation I used to modulate the terrain: if the destructionGradeConstant is 0, I'll have to do another function to prevent indeterminacy.
CurrentX I add CurrentY J add dup2 GetTerrain ceil(max(mul(<-groundDestructionPower sub(1 div(<-distanceFromCenter <-rangeOfDestruction))) 1)) sub dup 0 gt if
SetTerrain
else
pop 1 SetTerrain
endif
else #this is for if destructionGradeConstant isn't in [-3,3]
CurrentX I add CurrentY J add dup2 GetTerrain <-groundDestructionPower sub dup 0 gt if
SetTerrain
else
pop 1 SetTerrain
endif
endif # Virgil, y u no elseif? =(
endif
endif
loop
loop

CurrentCoords <-rangeOfDestruction 1.1 add GetUnitsInRange # this destroys all units in rangeOfDestruction. It adds 1.1 to account for the fact that it will only get the center of units.
0 do # Decrease 1.1 if it's destroying units it shouldn't, and increase it if it should be destroying units but it's not.
-1 Destroy
loop
endif

# The following block of code spawns the creeper.
<-creeperSpawnRange ceil 1 add <-creeperSpawnRange ceil neg do # These two do loops set up a similar square area as the terrain destroyer.
<-creeperSpawnRange ceil 1 add <-creeperSpawnRange ceil neg do
sqrt(pow(CurrentX dup I add sub 2) pow(CurrentY dup J add sub 2) add) ->distanceFromCenter # same method of finding the distance
if(<-distanceFromCenter <-creeperSpawnRange lte)
CurrentX I add CurrentY J add <-creeperIntensity AddCreeper # and this adds the creeper in every cell a distance <= creeperSpawnRange from the mine.
endif
loop
loop

Destroy(self 1)
endif
5 Delay #I don't think one if statement should hinder performance significantly, but there's no point in checking if a unit is within range every single frame.


I'm sure someone who is more experienced at CRPL could rewrite this in a much cleaner format, but it works.

Anyways, any comments or suggestions anyone has on my code are much appreciated. This is the first thing I've done in CRPL, so I'm looking to improve in any way I can. If anyone has any questions or concerns on this, please don't hesitate to ask.

I've tried to take into account as many scenarios as I could, but bugs always seem to find their way into everything. So if anything is not working to your expectations, please report them, and I will update it as soon as I am able to.

Thank you all and have a nice day!

---Edit---
5/24/2014: added variable "creeperTriggerType", updated code
"Only a life lived for others is a life worthwhile."
-Albert Einstein

knucracker

Looks interesting...
You should put up a simple map that uses this.  I know you said you didn't have much luck with that, but you might be surprised if you just take a fairly standard map design and put a few of these around an emitter (clearly labeled).  Might add an extra dimension to some speed run maps....

Relli

I was kind of shocked to see this thread, because I also made a unit that I call a Landmine. Only mine is built by the player and destroys Creeper instead. I can't put the code up here though, because for one, it's not completely done, and for two, I'd kinda like to keep it until I eventually finish the map that introduces it.

I really like your idea. And yes, CRPL is a wonderful tool, mightily powerful, and I imagine if you stick with it, you'll make many great things. Maybe even more things that share a name with my own ideas :P
I'll give you a hint, one of them is called a Scrubber.

chwooly

Nice Idea, However I have a few questions.

1) Are they visible?
2) If not visible can they be detected?
3) If not detectable, will they be an item that must be destroyed to win a map? ( this could be a problem on big maps)
4) If detected can they be defused without them going off?

Thanks

I am free, no matter what rules surround me. If I find them tolerable, I tolerate them; if I find them too obnoxious, I break them. I am free because I know that I alone am morally responsible for everything I do."
― Robert A. Heinlein

ParkourPenguin

Quote from: chwooly on May 24, 2014, 12:13:00 AM
Nice Idea, However I have a few questions.

1) Are they visible?
2) If not visible can they be detected?
3) If not detectable, will they be an item that must be destroyed to win a map? ( this could be a problem on big maps)
4) If detected can they be defused without them going off?

Thanks

1) If you want them to be. Simply set the image of the CRPL tower to be whatever you want by double-clicking on it and selecting what you want for "Image". If you want no image displayed (not visible), then select "none". If you want some sort of custom image displayed, then load up a custom image by selecting "Custom Images" right below "Scripts" in the main UI at the bottom of the screen. I decided to make my own image for it (attached as "landmine.png"- feel free to use it if you want).

2) If they're not visible, I can't think of any way of easily detecting them in-game other than simply building units on the terrain until one blows up. But I guess that's not really detecting them, more so triggering them.

3) No. I haven't even given users that much of a say in this regard. Line 17 prevents this: SetUnitAttribute(self CONST_COUNTSFORVICTORY false)

4) There really wouldn't be that much of a point to the mines themselves if they're visible and you can defuse them. If they're not visible, however, then the problem becomes how to detect them, which goes back to your 2nd question.

With further regards to questions 2 and 4, I suppose it would be possible to make a new tower that could be controlled by the player and would be able to detect mines in a certain radius from it even if they're not visible (and potentially disable/defuse/destroy them). I have some ideas for this, but I'm currently working on something else. I'll see what I can do about this, though: it looks like a fun project! ;D
"Only a life lived for others is a life worthwhile."
-Albert Einstein

ParkourPenguin

Quote from: Relli on May 23, 2014, 08:47:58 PM
I was kind of shocked to see this thread, because I also made a unit that I call a Landmine. Only mine is built by the player and destroys Creeper instead. I can't put the code up here though, because for one, it's not completely done, and for two, I'd kinda like to keep it until I eventually finish the map that introduces it.

I really like your idea. And yes, CRPL is a wonderful tool, mightily powerful, and I imagine if you stick with it, you'll make many great things. Maybe even more things that share a name with my own ideas :P
I'll give you a hint, one of them is called a Scrubber.
Thank you! I actually had an idea to add an option to make it a friendly emitter... perhaps it could be triggered if the creeper around it reaches a certain value, where 0 would just mean to ignore this scenario completely. I think I'll add this in later, thank you for the idea!

P.S.: I noticed your profile pic... I <3 RWBY! Can't wait for season 2!  ;D
"Only a life lived for others is a life worthwhile."
-Albert Einstein

ParkourPenguin

Quote from: virgilw on May 23, 2014, 08:31:59 PM
Looks interesting...
You should put up a simple map that uses this.  I know you said you didn't have much luck with that, but you might be surprised if you just take a fairly standard map design and put a few of these around an emitter (clearly labeled).  Might add an extra dimension to some speed run maps....
The reason why I kind of gave up on that map was because I'm a perfectionist. I don't notice minor flaws in other people's maps, but with my own, I notice every single little terrain cell that doesn't look natural, and it's impossible for me to make myself happy that way. So eventually of course I got fixated on CRPL and kind of forgot about the whole map idea itself. But, I will finish it! (Only once I think it's good enough to be released- pressing that "Create Finalized Map" button is going to be harder than the AP Physics C exam I took a while back).
"Only a life lived for others is a life worthwhile."
-Albert Einstein

Zoura3025

Would you mind if I used this in one of my maps?
(09:06:07) Zoura3025: But for us diehard fans: "Time is Sanity"

warren

I like the idea of this script. Landmines are not new, but this looks to be well designed. I could offer tips, but most of them would increase performance at the cost of readability. The one thing I will say is break it up into functions. I would suggest :detect and :explode

ViperZer0

I just have one question! From what I see, the terrain itself actually gets destroyed by the mine. How do you do that? The only way I can think of is SetTerrain, which is SUPER laggy.