package Spinner import EscaperSensor import SpinnerObjects class SpinNode SpinNode next = null SpinNode prev = null SetupObject obj real angl real dist construct(SetupObject ent, real ang, real dist) obj = ent angl = ang this.dist = dist ondestroy if next != null next.prev = prev if prev != null prev.next = next if not obj.done and obj.actor != null obj.setup.addAbility(REMOVE_OBJECT_ID) function superformulaRadius(real t, real a, real b, real m, real n1, real n2, real n3) returns real let part1 = (1 / a) * Cos(m * t / 4) let part2 = (1 / b) * Sin(m * t / 4) let absPart1PowN2 = Pow((part1).abs(), n2) let absPart2PowN3 = Pow((part2).abs(), n3) let r = Pow(absPart1PowN2 + absPart2PowN3, -1 / n1) return r function superformula(real t, real a, real b, real m, real n1, real n2, real n3, real n4) returns real let part1 = Pow((Cos(m * t / 4) / a).abs(), n2) let part2 = Pow((Sin(m * t / 4) / b).abs(), n3) let part3 = Pow(part1 + part2, -1 / n1) let part4 = Pow((Cos(m * t / 4) / a).abs(), n4) let part5 = Pow((Sin(m * t / 4) / b).abs(), n4) let part6 = Pow(part4 + part5, -1 / n1) return Pow(part3 + part6, -1 / n1) public class Spinner extends SetupObject SpinNode firstNode = null let spinSpeed = new ConfigValue(2 * DEGTORAD, 0.25 * DEGTORAD, "Spinspeed") let cosFactor = new ConfigValue(1, 0.1, "Cos-Factor") let sinFactor = new ConfigValue(1, 0.1, "Sin-Factor") var useSuperFormula = false construct(unit existing) super(createUnit(existing.getOwner(), SPINNER_ID, existing.getPos(), angle(0)), existing, 0) sleeps = false setup..addAbility(TURN_OFF_ID)..addAbility(GHOST_INVIS_ID) EventListener.add(setup, EVENT_PLAYER_UNIT_SPELL_EFFECT, () -> onCast()) construct(vec2 pos, player owner) super(createUnit(owner, SPINNER_ID, pos, angle(0)), createUnit(owner, SPINNER_ID, pos, angle(0)), 0) sleeps = false setup.addAbility(TURN_OFF_ID) EventListener.add(setup, EVENT_PLAYER_UNIT_SPELL_EFFECT, () -> onCast()) override function update() super.update() let speed = spinSpeed.get() var node = firstNode while node != null if not node.obj.done and node.obj.actor != null if useSuperFormula // Use superformula curve let a = 1.0 let b = 1.0 let m = 4.0 let n1 = 30.0 let n2 = 15.0 let n3 = 15.0 let r = superformulaRadius(node.angl, a, b, m, n1, n2, n3) node.obj.setXY(vec3(pos.x + r * node.dist * Cos(node.angl) * cosFactor.get(), pos.y + r * node.dist * Sin(node.angl) * sinFactor.get(), 0.)) else // Use normal circle node.obj.setXY(vec3(pos.x + node.dist * Cos(node.angl) * cosFactor.get(), pos.y + node.dist * Sin(node.angl) * sinFactor.get(), 0.)) if enabled node.angl += speed else let tmp = node if node == firstNode firstNode = node.next destroy tmp node = node.next function checkObj(Entity e) if e instanceof SetupObject and not e instanceof StaticEscaperSensorSetup let dobj = e castTo SetupObject boolean isNew = true var node = firstNode while node != null if node.obj == dobj isNew = false if firstNode == node firstNode = node.next destroy node break node = node.next if isNew and (dobj.setup.hasAbility(REMOVE_OBJECT_ID) or dobj.owner == STAFF_PLAYER) dobj.setup.removeAbility(REMOVE_OBJECT_ID) let newnode = new SpinNode(dobj, getPos().angleTo2d(dobj.getPos()).radians(), getPos().distanceTo(dobj.getPos())) if firstNode != null firstNode.prev = newnode newnode.next = firstNode firstNode = newnode ondestroy var node = firstNode firstNode = null while node != null let tmp = node node = node.next destroy tmp destroy cosFactor destroy sinFactor destroy spinSpeed function onCast() let id = GetSpellAbilityId() switch id case TURN_ON_ID setEnabled(true) case TURN_OFF_ID setEnabled(false) case SPIN_ADD_SINGLE_ID let u = GetSpellTargetUnit() let data = u.getEntity() if owner == owner checkObj(data) case SPIN_ADD_GROUP_ID forUnitsInRange(EventData.getSpellTargetPos(), 300.) u -> if u != actor and u != setup if u.getUserData() > 0 let ed = u.getEntity() if ed.owner == owner checkObj(ed) case SPIN_INC_SPEED_ID spinSpeed.increment(this, 10 * DEGTORAD) case SPIN_DEC_SPEED_ID spinSpeed.decrement(this, -10 * DEGTORAD) case SPIN_COS_INC_ID cosFactor.increment(this, 1) case SPIN_COS_DEC_ID if cosFactor.get() == 0. and sinFactor.get() == 0. useSuperFormula = not useSuperFormula else cosFactor.decrement(this, 0) case SPIN_SIN_INC_ID sinFactor.increment(this, 1) case SPIN_SIN_DEC_ID sinFactor.decrement(this, 0) override function serialize() returns Json let json = super.serialize() json.addProperty(new Property(KEY_TYPE, SPINNER_INDEX.toString())) return json