library ProjectileStack import TerrainData import Units import DamageType import Players native UnitAlive takes unit u returns boolean struct ProjectileDat unit projectile unit damageSource real speed real dx real dy real damage real rangeLeft end public struct ProjectileStack private static constant string DUST_FX = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl" private static constant string ERROR_NO_PROJECTILE = "Error: projectile was not found for attacking unit! Reverting to Hunter projectile." private static constant string ERROR_NO_SPEED = "Error: projectile speed was not found! Reverting to 20." private static constant string ERROR_NO_DAMAGE = "Error: projectile damage was not found! Reverting to 10." private static constant real PROJECTILE_OFFSET = 50. private static constant real DEFAULT_VELOCITY = 20. private static constant real BULLET_ENUM_RADIUS = 75. private static constant real PROJECTILE_VELOCITY = 25. private static constant real PROJECTILE_DAMAGE = 30. private static constant real DEFAULT_DAMAGE = 10. private static constant real CLOCK_PERIOD = 1. / 30. private static constant real FLY_HEIGHT = 60. private static constant real RANGE = 1200. private static timer time = CreateTimer() private static ProjectileDat array projectileDB private static integer dbIndex = -1 private static group g = CreateGroup() private static method getUnitProjectile takes unit u, real sX, real sY, real tX, real tY returns unit integer uType = GetUnitTypeId(u) real facing = TerrainData.radiansBetweenXY(sX, tX, sY, tY) real x = sX + PROJECTILE_OFFSET*Cos(facing) real y = sY + PROJECTILE_OFFSET*Sin(facing) unit returnedUnit if uType == Units.HUNTER_ID then returnedUnit = CreateUnit(GetOwningPlayer(u), Units.HUNTER_PROJECTILE_ID, 0., 0., facing*bj_RADTODEG) SetUnitX(returnedUnit, x) SetUnitY(returnedUnit, y) return returnedUnit end debug DisplayTextToPlayer(Players.locl, 0., 0., ERROR_NO_PROJECTILE) returnedUnit = CreateUnit(GetOwningPlayer(u), Units.HUNTER_PROJECTILE_ID, 0., 0., GetUnitFacing(u)) SetUnitX(returnedUnit, x) SetUnitY(returnedUnit, y) return returnedUnit end private static method getProjectileSpeed takes unit u returns real integer uType = GetUnitTypeId(u) if uType == Units.HUNTER_PROJECTILE_ID then return PROJECTILE_VELOCITY end debug DisplayTextToPlayer(Players.locl, 0., 0., ERROR_NO_SPEED) return DEFAULT_VELOCITY end private static method getUnitDamage takes unit u returns real integer uType = GetUnitTypeId(u) if uType == Units.HUNTER_ID then return PROJECTILE_DAMAGE end debug DisplayTextToPlayer(Players.locl, 0., 0., ERROR_NO_DAMAGE) return DEFAULT_DAMAGE end private static method bulletTick takes nothing returns nothing ProjectileDat tempDat integer index = 0 real x real y real cz real nz unit iter boolean killBullet = false loop exitwhen index > dbIndex tempDat = projectileDB[index] x = GetUnitX(tempDat.projectile) + tempDat.dx y = GetUnitY(tempDat.projectile) + tempDat.dy nz = TerrainData.getZ(x, y) cz = TerrainData.getZ(GetUnitX(tempDat.projectile), GetUnitY(tempDat.projectile)) nz = nz - cz SetUnitFlyHeight(tempDat.projectile, GetUnitFlyHeight(tempDat.projectile) - nz, 0.) SetUnitX(tempDat.projectile, x) SetUnitY(tempDat.projectile, y) tempDat.rangeLeft = tempDat.rangeLeft - tempDat.speed if tempDat.rangeLeft <= 0. then killBullet = true end GroupEnumUnitsInRange(g, x, y, BULLET_ENUM_RADIUS, null) loop iter = FirstOfGroup(g) exitwhen iter == null if IsUnitEnemy(tempDat.projectile, GetOwningPlayer(iter)) and UnitAlive(iter) then DamageType.dealCodeDamage(tempDat.damageSource, iter, tempDat.damage) killBullet = true end GroupRemoveUnit(g, iter) end if GetUnitX(tempDat.projectile) > TerrainData.MAX_X or GetUnitX(tempDat.projectile) < TerrainData.MIN_X or GetUnitY(tempDat.projectile) > TerrainData.MAX_Y or GetUnitY(tempDat.projectile) < TerrainData.MIN_Y then killBullet = true end if GetUnitFlyHeight(tempDat.projectile) < 1. then DestroyEffect(AddSpecialEffect(DUST_FX, x, y)) killBullet = true end if killBullet then KillUnit(tempDat.projectile) destroy tempDat projectileDB[index] = projectileDB[dbIndex] dbIndex-- if dbIndex == -1 then PauseTimer(time) end killBullet = false index-- end index++ end end static method add takes unit u, real sX, real sY, real tX, real tY, real offset returns nothing real rad ProjectileDat tempDat = new ProjectileDat() tempDat.projectile = getUnitProjectile(u, sX, sY, tX, tY) tempDat.damageSource = u tempDat.speed = getProjectileSpeed(tempDat.projectile) rad = TerrainData.radiansBetweenXY(sX, tX, sY, tY) + offset tempDat.dx = tempDat.speed * Cos(rad) tempDat.dy = tempDat.speed * Sin(rad) tempDat.damage = getUnitDamage(u) tempDat.rangeLeft = RANGE SetUnitFlyHeight(tempDat.projectile, FLY_HEIGHT, 0.) dbIndex++ projectileDB[dbIndex] = tempDat if dbIndex == 0 then TimerStart(time, CLOCK_PERIOD, true, function bulletTick) end end end endlibrary