Custom digitalis, and optimization thereof

Started by planetfall, February 11, 2014, 09:24:12 PM

Previous topic - Next topic

planetfall

In my infinite naiveté, I seem to have created custom digitalis.

(this is my code test map, please excuse the mess)


# BurningDigitalis.crpl
# Created on: 2/11/2014 4:30:52 PM
# ------------------------------------------

$GrowDelay:90
$AtkSleep:15

#Don't need to be input vars, but makes it easier to change them.

once
HideTraceLog
ClearTraceLog
Self "main" "Custom11" SetImage
CurrentCoords 0 SetDigitalisGrowth
CurrentCoords 0 SetDigitalis
Self CONST_TAKEMAPSPACE 0 SetUnitAttribute
Self CONST_CELLWIDTH 1 SetUnitAttribute
Self CONST_CELLHEIGHT 1 SetUnitAttribute
Self "main" 1.0 3 div 1.0 3 div SetImageScale
90 SetTimer0
Self CONST_HEALTH 0.04 SetUnitAttribute
Self CONST_MAXHEALTH 0.04 SetUnitAttribute
Self CONST_DESTROYMODE 0 SetUnitAttribute
Self CONST_SHOWHEALTHBAR 0 SetUnitAttribute
endonce

<-killsleep if
Self "main" 255 255 255 100 SetImageColor
else
Self "main" 255 255 255 255 SetImageColor
endif

CurrentCoords GetCreeper 0 lt if
Self CONST_CREATEPZ 0 SetUnitAttribute
Self 0 destroy
else
<-killsleep if
return
endif
GetTimer0 0 eq <-growsleep not and if
#test four neighboring cells if there is digitalis growth. burning digitalis takes over and destroys normal digitalis and only grows in normal digitalis areas.
0 ->grew
CurrentCoords 1 add GetDigitalisGrowth if
"CRPLCORE" CurrentCoords 1 add CreateUnit ->newdigi
<-newdigi "interface.crpl" AddScriptToUnit
<-newdigi "interface.crpl" "Class" "missile" SetScriptVar
<-newdigi "interface.crpl" "Corename" "BurningDigitalis" SetScriptVar
<-newdigi "BurningDigitalis.crpl" AddScriptToUnit
1 ->grew
endif
CurrentCoords 1 sub GetDigitalisGrowth if
"CRPLCORE" CurrentCoords 1 sub CreateUnit ->newdigi
<-newdigi "interface.crpl" AddScriptToUnit
<-newdigi "interface.crpl" "Class" "missile" SetScriptVar
<-newdigi "interface.crpl" "Corename" "BurningDigitalis" SetScriptVar
<-newdigi "BurningDigitalis.crpl" AddScriptToUnit
1 ->grew
endif
CurrentX 1 add CurrentY GetDigitalisGrowth if
"CRPLCORE" CurrentX 1 add CurrentY CreateUnit ->newdigi
<-newdigi "interface.crpl" AddScriptToUnit
<-newdigi "interface.crpl" "Class" "missile" SetScriptVar
<-newdigi "interface.crpl" "Corename" "BurningDigitalis" SetScriptVar
<-newdigi "BurningDigitalis.crpl" AddScriptToUnit
1 ->grew
endif
CurrentX 1 sub CurrentY GetDigitalisGrowth if
"CRPLCORE" CurrentX 1 sub CurrentY CreateUnit ->newdigi
<-newdigi "interface.crpl" AddScriptToUnit
<-newdigi "interface.crpl" "Class" "missile" SetScriptVar
<-newdigi "interface.crpl" "Corename" "BurningDigitalis" SetScriptVar
<-newdigi "BurningDigitalis.crpl" AddScriptToUnit
1 ->grew
endif
<-grew if
1 ->growsleep
endif
0 ->found
CurrentCoords 1 GetEnemyUnitsInRange 0 do
"interface.crpl" "Corename" GetScriptVar "BurningDigitalis" eq if
<-found 1 add ->found
endif
loop
<-found 5 eq if
1 ->killsleep
endif
#do this only every three seconds
<-GrowDelay SetTimer0
endif
GetTimer1 eq0 <-killsleep not and if
CurrentCoords 5 GetUnitsInRange ->ct <-ct 0 do
->uid
<-uid GetUnitType ->type
<-type "POWERZONE" neq <-type "MESSAGEARTIFACT" neq <-type "TECHARTIFACT" neq <-type "" neq <-type "AOO" neq <-type "SHIELDKEY" neq and and and and and if
"owner" <-uid GetCoresWithVar ->count
0 ->didsomething
<-count if
<-count 0 do
->uid2
<-uid2 "interface.crpl" "Corename" GetScriptVar "Fire" eq if
<-uid2 "Fire.crpl" "duration" 150 SetScriptVar
1 ->didsomething
endif
#if rider core is a fire, reset the duration to 5s. Otherwise has no effect.
loop
endif
<-didsomething not if
#if the core riding the unit is not a fire, create one
"CRPLCORE" <-uid CONST_COORDX GetUnitAttribute <-uid CONST_COORDY GetUnitAttribute CreateUnit ->ignis
<-ignis "interface.crpl" AddScriptToUnit
<-ignis "interface.crpl" "Class" "bullet" SetScriptVar
<-ignis "interface.crpl" "Corename" "Fire" SetScriptVar
<-ignis "UnitRider.crpl" AddScriptToUnit
<-ignis "UnitRider.crpl" "owner" <-uid SetScriptVar
<-ignis "UnitRider.crpl" "A" "0" SetScriptVar
<-ignis "Fire.crpl" AddScriptToUnit
endif
endif
loop
<-ct eq0 if
<-AtkSleep 10 mul SetTimer1
else
<-AtkSleep SetTimer1
endif
endif
endif

:destroyed
CurrentCoords 1 SetDigitalisGrowth
CurrentCoords 1 GetEnemyUnitsInRange 0 do
->unit
<-unit "interface.crpl" "Corename" GetScriptVar "BurningDigitalis" eq if
<-unit "BurningDigitalis.crpl" "growsleep" 0 SetScriptVar
<-unit "BurningDigitalis.crpl" "killsleep" 0 SetScriptVar
endif
loop


It behaves exactly like one would expect it to -- that is, incredibly laggily.

I have tried to make it as efficient as possible, but optimization isn't my strong suit -- getting it to work is. Is there anything at all I could do to improve performance? Or should I just scrap the idea? :'(
Pretty sure I'm supposed to be banned, someone might want to get on that.

Quote from: GoodMorning on December 01, 2016, 05:58:30 PM"Build a ladder to the moon" is simple as a sentence, but actually doing it is not.

MadMag


Flabort

So whenever it spreads, it creates a new core? There's your slowdown issue, I think.
Use ONCE to create cores all at the beginning wherever it can spread. Give it multiple states, and default to off. Hide it's image when off, and only destroy digi + growth when not off, and replace the growth when it turns off (AKA is damaged).
Set it so that it can't be destroyed by damage, but beams damage it; when it gets to 0 damage, switch to off state.

This would mean writing the script from scratch, possibly, but see if that makes it lag less. The amount of cores on the field would still cause it to be rather laggy, but because the cores are not being destroyed and rebuilt every few frames, I think that solution might work.
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

eduran

A big number of cores calling GetEnemyUnitsInRange/GetUnitsInRange with lots of unit on the field are a problem.

Quote from: virgilw on February 06, 2014, 01:29:04 PM
- Since each core has to get the unitcount that is in a range, this map is going to be intrinsically expensive to update.  The more units are on the map, the more computation will occur.  GetUnitCount is an O(n) operation where n is the number of units on the map.  So 'smaller' maps are better with this technique since that limits the number of cells.  If a map had large void areas, FOW cells could also be left absent from purely void areas.

That quote is about a map that had one core per ~11x11 square calling GetUnitCount every 10 or so frames.

planetfall

Right. I'm also going to try removing the fire part from the digitalis script and having a single off-map core handle it. Probably won't do much, but if it calls 0 0 999 GetUnitsInRange once and then GetEnemyUnitsInRange on each of those it should reduce the amount of total calls... right? And won't have each unit getting processed more than once per frame.
Pretty sure I'm supposed to be banned, someone might want to get on that.

Quote from: GoodMorning on December 01, 2016, 05:58:30 PM"Build a ladder to the moon" is simple as a sentence, but actually doing it is not.

Lost in Nowhere

You could possibly have 1 crpl core that follows each beam built, and have them just always be at the nearest custom digitalis to that beam, and then have a main core that handles all of the growth and such.
Don't die! :)

planetfall

Quote from: Lost in Nowhere on February 12, 2014, 10:22:57 AM
You could possibly have 1 crpl core that follows each beam built, and have them just always be at the nearest custom digitalis to that beam, and then have a main core that handles all of the growth and such.

I'm not sure I understand what you mean.
Pretty sure I'm supposed to be banned, someone might want to get on that.

Quote from: GoodMorning on December 01, 2016, 05:58:30 PM"Build a ladder to the moon" is simple as a sentence, but actually doing it is not.

knucracker

He's talking about using one core to calculate and draw the Digitalis (using many images presumably).   That allows for spreading digitalis and drawing it, but it doesn't support being able to be targeted by beams or snipers.  To do that, he is suggesting creating/moving a a crpltower on the digitalis at the nearest location to a beam.  It would probably be difficult and expensive to do this, though... but it is thinking outside the box.

A modification of the idea might be to just change the rules for this custom digitalis a little bit.  Maybe it isn't weapons that shoot at it, but AC instead that damages it.  Or maybe it creates a super node every now and then and those are the things that can be shot at.  Once destroyed, the area around the super node no longer supports this custom digitalis.

planetfall

I tried Flabort's suggestion, but couldn't get it to spread... I'm not sure what I'm doing wrong.

Hmmm... If I change the range effect to a contact effect like regular digitalis, I could do it with GetUnitOccupiedCount and save the constant looping. That's something to consider...

Because of the max 1024 images per core, and because this uses the same growth area as normal digitalis (and conceivably both could be present on the same map)... there would have to be one core for every 32x32 square, which could then query some "master core" that stores an array of cells -- yegh, this is getting very messy very fast.

I think maybe this idea will go on the back burner for now, while I make something more practical or until a miracle happens. I still really like the concept, but it's an absolute pain to implement.
Pretty sure I'm supposed to be banned, someone might want to get on that.

Quote from: GoodMorning on December 01, 2016, 05:58:30 PM"Build a ladder to the moon" is simple as a sentence, but actually doing it is not.

Lost in Nowhere

Not to mention almost guaranteed to be laggy to some degree...
Don't die! :)

Flabort

I actually like Virgil's description of Lost's idea better than my own.

Something like:
$DestroyX:0
$DestroyY:0
$Destroy:false
$SpreadRate:60
$CheckActivityRate:7

once
  0 ->loopIndex
  while MapHeight MapWidth mul <-loopIndex lte repeat
    #Turn loopIndex into coords.
    #Look for digi, if found, add to list "DigiLocation", and to "DigiActive".
    #Set up a image, add it to that location.
  endwhile
  <-SpreadRate SetTimer0
  <-CheckActivityRate SetTimer1
endonce

GetTimer1 eq0 if
  @CheckActivity
endif
GetTimer0 eq0 if
  @Spread
endif
<-Destroy if
  @Destruct
endif

:Spread
#Check "DigiActive", look for digitalis growth beside each list piece.
#Add a value to "DigiActive" whenever growth is found,
#Destroy that growth,
#and set the image alpha to full.
<-SpreadRate SetTimer0

:CheckActivity
#check each DigiActive location for units, creeper, etc, and do what you're going to do to them.
#Check whole map for beams. Store number of beams found in "BeamN".
<-BeamN <-BeamO lt if
  <-BeamN ->BeamO
  #Now, create a unit and attach the second script.
endif
<-CheckActivityRate SetTimer1

:Destruct
#Remove the DigiActive entry corresponding to the coords in $DestroyX and $DestroyY.
#Set the image alpha at that spot to 0,
#place growth there.


As for the second script, it's going to look for a beam without one of it's own kind there, and sit on it. It will use TargetOffset X and Y (or whichever command) to set a target at the nearest location from DigiActive. When it's own health reaches 0, it refills it's health to full, sets the $DestroyX and $DestroyY to the coords of the target, and sets $Destroy to "true".
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)