Moderator
M
Moderator

  WorldBounds  (Nestharus) 
  Table  (Bribe) 
  List  (Nestharus) 
  TileDefinition  (IcemanBo) 
library TerrainInfection/* v2.1 By IcemanBo
TerrainInfection will start at given coordinates and infect nearby tiles. (change terrain type)
TerrainInfection also allows to undo the infection.
Furthermore it provides events for(un)infection.
*/ requires /*
*/ TileDefinition /* hiveworkshop.com/forums/submissions414/snippettiledefinition259347/
*/ WorldBounds /* github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
*/ List /* as Nestharus removed it check out the demo
*/ Table /* hiveworkshop.com/forums/jassresources412/snippetnewtable188084/
API
¯¯¯¯¯¯
function CreateTerrainInfection takes integer t, real x, real y, real chance, real interval returns TerrainInfection
t = infected terrain type
x = x of start
y = y of start
chance = chance to infect nearby tiles between 0 and 1
interval = interval to potentialy infect nearby tiles
you also can set/read public members:
real maxDistance = max distance to RootInfection
real chance = chance on infection between 0 and 1
real maxX = maxX of Infection
real minX = minX of Infection
real maxY = maxY of Infection
real minY = minY of Infection
region bound = bound region for infection
boolean diagonal = should Infection act diagonal
boolean enabled = is Infection currently enabled
you can read readonly members:
real x = x of infectionStart
real y = y of infectionStart
integer size = amount of infected tiles
integer terrainTypeInfected = terrain type of infection
methods (TerrainInfection)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
destroy() returns nothing
undoInfection() returns nothing
Will uninfect all tiles.
undoInfection_last() returns boolean
Will uninfect the last tile.
Returns false if there is no tile anymore.
static methods (TerrainInfection)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
enableResistor(integer Infector, integer Resistor, boolean b returns nothing
Takes terrain types as paramater.
Resistor will be immune against Infector. (true/false)
enableInfections(boolean b) returns nothing
Enables/disables all current running Infections.
isTileInfected takes real x, real y returns boolean
Events
¯¯¯¯¯¯¯¯
You can use TriggerRegisterVariableEvent to catch following events:
s__TerrainInfection_infection_state == 1 > onInfection
s__TerrainInfection_infection_state == 2 > Infection has completly finished.
s__TerrainInfection_infection_state == 3 > onUnInfection
s__TerrainInfection_infection_state == 4 > Infection has been completly removed.
onEvents you can use TerrainInfection_currrent as current instance.
onEvents (1 and 3) you can use TerrainInfection_currentTileId to refer to operated tile.
Look into "Event Example" for more info.
Credits:
¯¯¯¯¯¯¯¯¯
List (Nestharus)
Table (Bribe)
WorldBounds (Nestharus)
*/
globals
private hashtable hash = InitHashtable()
private timer tim = CreateTimer()
private constant real TIMEOUT = .031250000
private constant real TILE_SIDE_LENGTH = 128.
private constant real TILE_DIAGONAL_LENGTH = TILE_SIDE_LENGTH*SquareRoot(2.)
endglobals
private struct InfectionData extends array
implement List
integer tileId
integer originTerrainType
endstruct
struct TerrainInfection
private InfectionData list
private real timeCountMax // Interval of Infecion.
private real timeCountCurrent // Time left until next Infection.
readonly real x // x of RootInfection
readonly real y // y of RootInfection
readonly integer size // size == amount of infected tiles
readonly integer terrainTypeInfected // new (infected) terrain type.
// public members
real maxDistance = 1
real chance = 1
real maxX = WorldBounds.maxX
real minX = WorldBounds.minX
real maxY = WorldBounds.maxY
real minY = WorldBounds.minY
region bound = null
boolean diagonal = true
boolean enabled = true
// for list
private thistype prev
private thistype next
private static thistype first = 0
// for events
static real infection_state = 0
static thistype current = 0
static integer currentTileId = 0
// to save if a tile is infected
private static key k
private static Table table = k
/*
* These methods check if a point matches all conditions,
* so it can be infected successfully.
*/
private method coordinateCheck takes real x, real y returns boolean
return (bound == null or IsPointInRegion(bound, x, y)) and(x <= maxX and x >= minX and y <= maxY and y >= minY)
endmethod
private method distanceCheck takes real x, real y returns boolean
return (maxDistance < 0 or SquareRoot((xthis.x)*(xthis.x) + (ythis.y)*(ythis.y)) <= maxDistance)
endmethod
private method terrainCheck takes real x, real y returns boolean
return (GetTerrainType(x, y) != terrainTypeInfected) and not(LoadBoolean(hash, terrainTypeInfected, GetTerrainType(x, y)))
endmethod
private method isPointValid takes real x, real y returns boolean
return coordinateCheck(x,y) and distanceCheck(x,y) and terrainCheck(x,y)
endmethod
/*
* This method will make a circular check around given
* coordinates to find a terrain tile which is not
* infected yet. It will return boolean, if there exists
* a (new/old) tile around for potential infection.
*/
private method checkNearbyTiles takes real x, real y returns boolean
local real angle = 0
local real angleChange
local real xNew
local real yNew
local real offset
local boolean diagonalCheck = true
local InfectionData element
if (this.diagonal) then
set angleChange = bj_PI/4
else
set angleChange = bj_PI/2
endif
loop
if (this.diagonal) then
// For 45/135/225/315 degrees the centre
// of the tile has an other distance, so
// we check if it's a digonal.
set diagonalCheck = not(diagonalCheck)
if (diagonalCheck) then
set offset = TILE_DIAGONAL_LENGTH
else
set offset = TILE_SIDE_LENGTH
endif
else
set offset = TILE_SIDE_LENGTH
endif
set xNew = x + offset * Cos(angle)
set yNew = y + offset * Sin(angle)
if (this.isPointValid(xNew, yNew)) then
if (GetRandomReal(0, 1) <= this.chance) then
set element = this.list.enqueue()
set element.tileId = GetTileId(xNew,yNew)
set element.originTerrainType = GetTerrainType(xNew, yNew)
call SetTerrainType(xNew, yNew, this.terrainTypeInfected, 1, 1, 1)
set thistype.table.boolean[element.tileId] = true
set this.size = (this.size + 1)
// Fire onInfection event.
set thistype.current = this
set thistype.currentTileId = element.tileId
set thistype.infection_state = 1
set thistype.infection_state = 0
set thistype.current = 0
set thistype.currentTileId = 0
endif
return true
endif
set angle = angle + angleChange
exitwhen (angle >= 2*bj_PI) // Full circular check was done.
endloop
return false
endmethod
// callback is called periodicly
private static method callback takes nothing returns nothing
local thistype this = thistype.first
local InfectionData element
local integer i
local boolean b // Will check if infection is completly finished.
// loop through all instances (root infections)
loop
exitwhen (this == 0)
if (this.enabled) then
set this.timeCountCurrent = this.timeCountCurrent  TIMEOUT
if (this.timeCountCurrent <= 0) then
set this.timeCountCurrent = this.timeCountMax
set element = this.list.first
set i = this.size
set b = false
// We use preloop defined integer to define
// the cancel condition, because the list itself
// is dynamic, not static. (size can be changed during loop)
// loop through all infected tiles of an instance
loop
exitwhen (i < 1)
if this.checkNearbyTiles(GetTileCenterXById(element.tileId), GetTileCenterYById(element.tileId)) then
set b = true
endif
set element = element.next
set i = (i  1)
endloop
// if there is no potential tile left for infection
if not(b) then
// Fire onFinish event.
set thistype.current = this
set thistype.infection_state = 2
set thistype.infection_state = 0
set thistype.current = 0
endif
endif
endif
set this = this.next
endloop
endmethod
static method create takes integer t, real x, real y, real chance, real interval returns thistype
local thistype this = thistype.allocate()
local InfectionData element
set this.list = InfectionData.create()
set element = this.list.enqueue()
set element.tileId = GetTileId(x,y)
set element.originTerrainType = GetTerrainType(x, y)
set this.x = x
set this.y = y
set this.terrainTypeInfected = t
set this.chance = chance
set this. size = 1
if (interval < TIMEOUT) then
set this.timeCountMax = TIMEOUT
else
set this.timeCountMax = interval
endif
set this.timeCountCurrent = this.timeCountMax
call SetTerrainType(x, y, t, 1, 1, 1)
set thistype.table.boolean[element.tileId] = true
// Fire onInfection event.
set thistype.current = this
set thistype.currentTileId = element.tileId
set thistype.infection_state = 1
set thistype.infection_state = 0
set thistype.current = 0
set thistype.currentTileId = 0
if (thistype.first == 0) then
call TimerStart(tim, TIMEOUT, true, function thistype.callback)
endif
set this.next = thistype.first
set thistype.first.prev = this
set thistype.first = this
set this.prev = 0
return this
endmethod
method destroy takes nothing returns nothing
set this.bound = null
call this.deallocate()
call this.list.destroy()
if (this == thistype.first) then
set thistype.first = this.next
endif
set this.next.prev = this.prev
set this.prev.next = this.next
if (thistype.first == 0) then
call PauseTimer(tim)
endif
endmethod
method undoInfection_last takes nothing returns boolean
local InfectionData element = this.list.last
local integer tileId = element.tileId
if (element == 0) then
// Fire onRemoved event.
set thistype.current = this
set thistype.infection_state = 4
set thistype.infection_state = 0
set thistype.current = 0
return false
endif
call SetTerrainType(GetTileCenterXById(tileId), GetTileCenterYById(tileId), element.originTerrainType, 1, 1, 1)
set this.size = this.size  1
call thistype.table.remove(tileId)
call element.remove()
// Fire onUnInfection event.
set thistype.current = this
set thistype.currentTileId = tileId
set thistype.infection_state = 3
set thistype.infection_state = 0
set thistype.current = 0
set thistype.currentTileId = 0
return true
endmethod
// loop until all infected tiles are removed
method undoInfection takes nothing returns nothing
loop
exitwhen not (this.undoInfection_last())
endloop
endmethod
static method isTileInfected takes real x, real y returns boolean
return thistype.table.has(GetTileId(x,y))
endmethod
static method enableResistor takes integer infector, integer resistor, boolean b returns nothing
call SaveBoolean(hash, infector, resistor, b)
endmethod
static method enableInfections takes boolean b returns nothing
if (b) then
call TimerStart(tim, TIMEOUT, true, function thistype.callback)
else
call PauseTimer(tim)
endif
endmethod
endstruct
function CreateTerrainInfection takes integer t, real x, real y, real chance, real interval returns TerrainInfection
return TerrainInfection.create(t, x, y, chance, interval)
endfunction
endlibrary
v2.1  now uses List instead of Vector v2.0a minor changes  removed not needed method v2.0 changed documentation a bit  undoInfection is now possible v1.0 count infected tiles is possible  check if a tile is infected is possible  release 