Skip to content

Commit

Permalink
Shroom spreading code was slightly optimized
Browse files Browse the repository at this point in the history
We did a number of things here, first we collect the potential turfs for
the view call in a single pass, not once per yield run.

Second we dont' 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 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 seperate 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 12, 2020
1 parent 824be91 commit 78e309a
Showing 1 changed file with 50 additions and 23 deletions.
73 changes: 50 additions & 23 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,40 +95,60 @@
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

// 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,earth))
possibleLocs += earth
CHECK_TICK
newLoc = possibleLoc
break

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

var/turf/newLoc = pick(possibleLocs)

var/shroomCount = 0 //hacky
var/placeCount = 1
for(var/obj/structure/glowshroom/shroom in newLoc)
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 78e309a

Please sign in to comment.