Skip to content

Commit

Permalink
Slightly optimize shroom spreading code (#53652)
Browse files Browse the repository at this point in the history
There were some recent lag events on the servers caused by the timer  
subsystem jamming up with glowshroom timers. I whined about this but  
nobody did anything so

(Thanos "Fine... I'll do it myself" GIF)

There are a few changes here, first we collect the potential turfs for 
the view call in a single pass, not once per yield run.

Second we don't run a second view(1) on every single potential turf, 
instead we try to randomly pick a turf 3 times during yield phase and 
check the view then.

If we fail to find a potential location then we bail out of that yield 
phase. This is a tradeoff between processing time spent finding 
locations and the chance that the glowshroom fails to spread.

Finally, we have maximum limit on how many times a glowshroom fails to 
spread, if it fails to spread at least 5 times over any iteration, it 
stops processing for spread completely.

As a bonus, the timers have been made unique, so we don't accidentally 
generate multiple timers for a single shroom, other than the two it 
already needs.

This code would benefit from being a separate subsystem and grouping, 
generations of the plants together as a single ticking entity and just 
spreading from selected edge plants. However I don't particularly feel 
like plumbing that together, so this will suit for now.
  • Loading branch information
optimumtact committed Sep 18, 2020
1 parent b94daf2 commit 538aaf8
Showing 1 changed file with 52 additions and 25 deletions.
77 changes: 52 additions & 25 deletions code/game/objects/effects/glowshroom.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
var/spreadIntoAdjacentChance = 75
/// Internal seed of the glowshroom, stats are stored here
var/obj/item/seeds/myseed = /obj/item/seeds/glowshroom

/// If we fail to spread this many times we stop trying to spread
var/max_failed_spreads = 5
/// Turfs where the glowshroom cannot spread to
var/static/list/blacklisted_glowshroom_turfs = typecacheof(list(
/turf/open/lava,
Expand Down Expand Up @@ -92,39 +95,59 @@
else //if on the floor, glowshroom on-floor sprite
icon_state = base_icon_state

addtimer(CALLBACK(src, .proc/Spread), delay_spread)
addtimer(CALLBACK(src, .proc/Decay), delay_decay, FALSE) // Start decaying the plant

addtimer(CALLBACK(src, .proc/Spread), delay_spread, TIMER_UNIQUE|TIMER_NO_HASH_WAIT)
addtimer(CALLBACK(src, .proc/Decay), delay_decay, TIMER_UNIQUE|TIMER_NO_HASH_WAIT)

/**
* Causes glowshroom spreading across the floor/walls.
*/

/obj/structure/glowshroom/proc/Spread()

//We could be deleted at any point and the timers might not be cleaned up
if(QDELETED(src))
return

var/turf/ownturf = get_turf(src)
var/shrooms_planted = 0
var/list/possibleLocs = list()
//Lets collect a list of possible viewable turfs BEFORE we iterate for yield so we don't call view multiple
//times when there's no real chance of the viewable range changing, really you could do this once on item
//spawn and most people probably would not notice.
for(var/turf/open/floor/earth in view(3,src))
if(is_type_in_typecache(earth, blacklisted_glowshroom_turfs))
continue
if(!ownturf.CanAtmosPass(earth))
continue
possibleLocs += earth

//Lets not even try to spawn again if somehow we have ZERO possible locations
if(!possibleLocs.len)
return

for(var/i in 1 to myseed.yield)
var/chance_stats = ((myseed.potency + myseed.endurance * 2) * 0.2) // Chance of generating a new mushroom based on stats
var/chance_generation = (100 / (generation * generation)) // This formula gives you diminishing returns based on generation. 100% with 1st gen, decreasing to 25%, 11%, 6, 4, 2...
if(prob(max(chance_stats, chance_generation))) // Whatever is the higher chance we use it
var/list/possibleLocs = list()
var/spreadsIntoAdjacent = FALSE

if(prob(spreadIntoAdjacentChance))
spreadsIntoAdjacent = TRUE

for(var/turf/open/floor/earth in view(3,src))
if(is_type_in_typecache(earth, blacklisted_glowshroom_turfs))
continue
if(!ownturf.CanAtmosPass(earth))
continue
if(spreadsIntoAdjacent || !locate(/obj/structure/glowshroom) in view(1,earth))
possibleLocs += earth
CHECK_TICK

if(!possibleLocs.len)
break

var/turf/newLoc = pick(possibleLocs)
// Whatever is the higher chance we use it (this is really stupid as the diminishing returns are effectively pointless???)
if(prob(max(chance_stats, chance_generation)))
var/spreadsIntoAdjacent = prob(spreadIntoAdjacentChance)
var/turf/newLoc = null

//Try three random locations to spawn before giving up tradeoff
//between running view(1, earth) on every single collected possibleLoc
//and failing to spread if we get 3 bad picks, which should only be a problem
//if there's a lot of glow shroom clustered about
for(var/Potato in 1 to 3)
var/turf/possibleLoc = pick(possibleLocs)
if(spreadsIntoAdjacent || !locate(/obj/structure/glowshroom) in view(1,possibleLoc))
newLoc = possibleLoc
break

//We failed to find any location, skip trying to yield
if(newLoc == null)
break

var/shroomCount = 0 //hacky
var/placeCount = 1
Expand All @@ -144,10 +167,14 @@
child.generation = generation + 1
shrooms_planted++

CHECK_TICK
if(shrooms_planted <= myseed.yield) //if we didn't get all possible shrooms planted, try again later
if(!shrooms_planted)
max_failed_spreads--

//if we didn't get all possible shrooms planted or we haven't failed to spread at least 5 times then try to spread again later
if( (shrooms_planted <= myseed.yield) && (max_failed_spreads >= 0) )
myseed.adjust_yield(-shrooms_planted)
addtimer(CALLBACK(src, .proc/Spread), delay_spread)
//Lets make this a unique hash
addtimer(CALLBACK(src, .proc/Spread), delay_spread, TIMER_UNIQUE|TIMER_NO_HASH_WAIT)

/obj/structure/glowshroom/proc/CalcDir(turf/location = loc)
var/direction = 16
Expand Down Expand Up @@ -194,7 +221,7 @@
else // Timed decay
myseed.endurance -= 1
if (myseed.endurance > 0)
addtimer(CALLBACK(src, .proc/Decay), delay_decay, FALSE) // Recall decay timer
addtimer(CALLBACK(src, .proc/Decay), delay_decay, TIMER_UNIQUE|TIMER_NO_HASH_WAIT) // Recall decay timer
return
if (myseed.endurance < 1) // Plant is gone
qdel(src)
Expand Down

0 comments on commit 538aaf8

Please sign in to comment.