package DummyCaster import DummyRecycler // @credit Anitarf constant boolean FORCE_INVISIBLE_CAST = false constant boolean AUTO_RESET_MANA_COOLDOWN = true Table delayTable = new Table() public class DummyCaster private int abilityId = 0 //ID (rawcode) of the ability to cast private int level = 1 //Level of the ability to cast private player owner = DUMMY_PLAYER private int orderId = 0 real recycleDelay = 0.5 function setOrderId( int id ) orderId = id function setOrderString( string s ) orderId = OrderId(s) function setLevel(int lvl) level = lvl private boolean customSource = false private vec3 customPos /** Set a custom source point from which the dummycaster will cast the spell. */ function setSourcePoint( vec3 cpos ) customPos = cpos customSource = true private boolean autodestroy = false function setOwner( player p ) this.owner = p construct( int abilityId, string orderString, player owner, boolean autodestroy ) this.orderId = OrderId( orderString ) this.abilityId = abilityId this.owner = owner this.autodestroy = autodestroy debugPrint("DC constructed", 3) function createDummyCaster( vec2 tpos ) returns unit unit dummy if customSource dummy = DummyRecycler.get(customPos.toVec2(), angle(0)) else debugPrint("no custom source", 3) dummy = DummyRecycler.get(tpos, angle(0)) debugPrint("new dummy " + dummy.getName(), 3) dummy.setOwner(owner, false) dummy.unpause() debugPrint("AbilId: " + abilityId.toString(), 3) if not dummy.hasAbility(abilityId) dummy.addAbility(abilityId) if AUTO_RESET_MANA_COOLDOWN UnitResetCooldown( dummy ) dummy.setMana( 10000.0 ) if level > 1 dummy.setAbilityLevel(abilityId, level) debugPrint("Dummycaster created", 3) return dummy function castOnTarget( unit target ) returns boolean debugPrint("cast on target", 3) unit dummy = createDummyCaster(target.getPos()) debugPrint("dummy created", 3) if FORCE_INVISIBLE_CAST UnitShareVision(target, owner, true) let res = dummy.issueTargetOrderById(orderId,target) if FORCE_INVISIBLE_CAST UnitShareVision(target, owner, false) delayTable.saveUnit(dummy.getHandleId(), dummy) getTimer()..setData(dummy.getHandleId())..start(recycleDelay, () -> begin let t = GetExpiredTimer() DummyRecycler.recycle(delayTable.loadUnit(t.getData())) t.release() end) debugPrint("recycled", 3) if autodestroy destroy this return res //accepts units, items and destructables, if you know it is // a unit it is better to use castOnTarget since that would // be able to use FORCE_INVISIBLE_CAST if necessary. // function castOnWidgetTarget( widget target ) returns boolean unit dummy = createDummyCaster(vec2(target.getX(), target.getY())) let res = dummy.issueTargetOrderById(orderId,target) getTimer()..setData(dummy.getHandleId())..start(recycleDelay, () -> begin let t = GetExpiredTimer() DummyRecycler.recycle(delayTable.loadUnit(t.getData())) t.release() end) if autodestroy destroy this return res function castOnPoint( vec2 target ) returns boolean debugPrint("cast on point", 3) unit dummy = createDummyCaster(target) debugPrint("dummy created " + dummy.getName() + " begin let t = GetExpiredTimer() DummyRecycler.recycle(delayTable.loadUnit(t.getData())) t.release() end) debugPrint("recycled", 3) if autodestroy destroy this return res function castOnPoint( vec3 target ) castOnPoint(vec2(target.x, target.y)) //ignores custom source x and y (for obvious reasons) function castInPoint( vec2 target ) returns boolean customSource = false unit dummy = createDummyCaster(target) let res = dummy.issueImmediateOrderById(orderId) getTimer()..setData(dummy.getHandleId())..start(recycleDelay, () -> begin let t = GetExpiredTimer() DummyRecycler.recycle(delayTable.loadUnit(t.getData())) t.release() end) if autodestroy destroy this return res //=================================================================================================== // For method castOnAOE: // private static group enumgroup private static real aoex private static real aoey private static real aoeradius private static DummyCaster cinstance private static boolexpr aoefunc = Condition(function filterAOE) // function castOnAOE( real x, real y, real radius ) boolean ad = autodestroy if ad autodestroy=false aoex=x aoey=y aoeradius=radius cinstance=this GroupEnumUnitsInRange(DummyCaster.enumgroup,x,y,radius + MAX_COLLISION_SIZE , DummyCaster.aoefunc) if ad destroy this //================================================================================================== // A quick and dirt castOnGroup method, perhaps it'll later have castOntarget inlined, but not now // function castOnGroup( group g ) boolean ad = autodestroy if ad this.autodestroy=false for unit u from g castOnTarget(u) if ad destroy this // Might look wrong, but this is the way to make it consider collision size, a spell that // got a target circle and uses this method will let the user know which units it will // hit with the mass cast. static function filterAOE() returns boolean unit u=GetFilterUnit() if IsUnitInRangeXY(u, DummyCaster.aoex, DummyCaster.aoey, DummyCaster.aoeradius) DummyCaster.cinstance.castOnTarget(u) return false