package Escaper import public Json import public Entity import public BuilderConstants import public GameConstants import public SerializeIds import public TerrainData import Assets import PhysicsEntity import RevivePointModifier import TerrainBlocker import initlater Weapon import initlater ReviveManager import public initlater RegionData import Projectile import JukeBox import initlater PlayerData import initlater Key import ClosureTimers import EscaperObjects constant COLL_RADIUS = 32. constant SLIDE_ADD = 3.3 constant ANK_ITEM_ID = 'A091' constant TELEPORT_ITEM_ID = 'A08S' public constant JUMP_SPELL = 'A062' public constant ORB_SPELL = 'A08A' public constant JAUNT_SPELL = 'A08D' constant POISON_EFFECT = Abilities.poisonStingTarget constant LAVA_EFFECT = Abilities.flameStrikeEmbers public let escapers = new LinkedList() public var currentRevivepoint = gg_unit_revp_0002.getPos() public enum KillMode FORCE UNWALKABLE KILLER public abstract class Escaper extends UnitEntity use PhysicsModule RevivePointModifier revmod = null var sliding = false var poisoned = false var onLava = false var camlock = false int deaths = 0 effect lavaEffect effect poisonEffect var walkVel = vec2(0,0) var slideVelocity = vec2(0,0) var otherVel = vec2(0,0) var threshold = 0 var alive = true var selectatrevive = true var camOnRevive = true var left = false unit revivetomb = null construct(vec3 pos, player owner, int typ) super(createUnit(owner, typ, pos + vec3(0,0,32), angle(0))) actor..addAbility(LOCUST_ID)..hide()..removeAbility(LOCUST_ID)..show() ..setPropWindow(60. .fromDeg()) EventListener.add(actor, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, () -> changeAngle()) EventListener.add(actor, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, () -> changeAngle()) EventListener.add(actor, EVENT_PLAYER_UNIT_DEATH, () -> kill(null)) EventListener.add(actor, EVENT_PLAYER_UNIT_PICKUP_ITEM, () -> onPick()) EventListener.add(actor, EVENT_PLAYER_UNIT_SPELL_EFFECT, () -> onCast(GetSpellAbilityId())) escapers.add(this) this.setCurrentRegion() if this.getCurrentRegion().specialAbil > 0 actor.addAbility(this.getCurrentRegion().specialAbil) function onCPReach() if revivetomb != null revivetomb.remove() revivetomb = null actor.setHP(9999) function onPick() let id = GetManipulatedItem().getTypeId() if id == 'I010' flashEffect(UI.goldCredit, pos) owner.addGold(15) for pd in escaperPlayers pd.p.addGold(5) override function inAir(Entity e) if not e.flying e.flying = true onEscapeGround() e.addVel(vec3(0,0,gravity)) function setLava(boolean add) if add and not onLava lavaEffect = AddSpecialEffectTarget(LAVA_EFFECT, actor, "origin") onLava = true else if not add and onLava lavaEffect.destr() onLava = false function setPoison(boolean add) if add and not poisoned poisonEffect = AddSpecialEffectTarget(POISON_EFFECT, actor, "chest") poisoned = true else if not add and poisoned poisonEffect.destr() poisoned = false function kill(UnitEntity killer) this.kill(killer, KillMode.KILLER) function kill(UnitEntity killer, KillMode kmode) if alive if this.getCurrentRegion().easyMode setPos(pos-getRealVel()*1.05) let vel = getRealVel() if killer != null let ang = killer.getPos().angleTo2d(pos) addVel(vec3(ang.cos()*(10 + vel.length()),ang.sin()*(10+vel.length()),0)) setPos(pos + vel) else setVel(getRealVel() * -1.75) flashEffect(Abilities.boltImpact, pos) actor.damageTarget(actor, 85) else if actor.isAliveTrick() actor.damageTarget(actor, killer == null and not kmode == KillMode.FORCE ? 195 : 1000) if alive and not actor.isAliveTrick() alive = false deaths++ manageRevive(this) setPoison(false) setLava(false) flying = false sliding = false function stop() sliding = false slideVelocity = vec2(0,0) vel = vec3(0,0,0) otherVel = vec2(0,0) override function setNewActor(unit u) error("Don't do this..") override function onGroundHit() if pos.getEBRTile().modifiers.get(TileModifier.BLUGOO castTo int) and vel.z.abs() > 2 let ttype = GetTerrainType(pos.x, pos.y) // Handle Goo it let angl = actor.getFacingAngle() if ttype == CONTROLLABLEICE let length = vel.length() vel = angl.toVec(length).withZ(vel.z) vel.z = -vel.z * 1.25 else pos.z = 0 vel *= 0.175 vel.z = 0. if GetTerrainType(pos.x, pos.y) == this.getCurrentRegion().getTypeSet().unwalkableId actor.damageTarget(actor, 1000) kill(null, KillMode.UNWALKABLE) actor.setPropWindow(60 .fromDeg()) override function onEscapeGround() actor.setPropWindow(0 .fromDeg()) sliding = false int lastVal = -1 override function onGround(Entity e) if e.flying e.flying = false onGroundHit() // If the Entity is moving towards e.scaleVel(surfaceFriction) let rv = getRealVel() let lpos = pos.toVec2().polarOffset(rv.toVec2().getAngle()-(90.).asAngleDegrees(), 16.50) let rpos = pos.toVec2().polarOffset(rv.toVec2().getAngle()+(90.).asAngleDegrees(), 16.50) let ttype1 = GetTerrainType(lpos.x, lpos.y) let ttype2 = GetTerrainType(rpos.x, rpos.y) let rdata = this.getCurrentRegion() int ttype int newval = -1 if ttype1 == rdata.getTypeSet().unwalkableId if ttype2 == rdata.getTypeSet().unwalkableId ttype = rdata.getTypeSet().unwalkableId else ttype = GetTerrainType(pos.x, pos.y) newval = 2 else if ttype2 == rdata.getTypeSet().unwalkableId ttype = ttype1 newval = 1 else ttype = GetTerrainType(pos.x, pos.y) newval = 3 if (lastVal == 1 and newval == 2) or (lastVal == 2 and newval == 1) threshold++ if threshold > 2 ttype = rdata.getTypeSet().unwalkableId else ttype = GetTerrainType(pos.x, pos.y) else if threshold > 0 threshold-- lastVal = newval slideVelocity *= 0.99 if ttype == rdata.getTypeSet().unwalkableId or ttype == 'Ibkb' or ttype == RUNES if alive if rdata.allowCut let fpos = pos.toVec2().polarOffset(rv.toVec2().getAngle(), 18) let bpos = pos.toVec2().polarOffset(rv.toVec2().getAngle(), -18) let t1type = GetTerrainType(fpos.x, fpos.y) let t2type = GetTerrainType(bpos.x, bpos.y) if (t2type == rdata.getTypeSet().unwalkableId or t2type == 'Ibkb' or t2type == RUNES) and (t1type == rdata.getTypeSet().unwalkableId or t1type == 'Ibkb' or t1type == RUNES) kill(null) else kill(null, KillMode.FORCE) else if alive and (ttype == CONTROLLABLEICE or ttype == UNCONTROLLABLEICE or ttype == REVICE) let angl = actor.getFacingAngle() slideVelocity = vec2(angl.cos() * rdata.slidespeed * SLIDE_ADD, angl.sin() * rdata.slidespeed * SLIDE_ADD) if not sliding speedFactor = 1 sliding = true setLava(false) else if pos.getEBRTile().hasModifier(TileModifier.ORANGEGOO) if vel.lengthSquared() < 1 vel = walkVel.toVec3() vel *= 1.0075 vel *= 0.96 sliding = false setLava(false) if ttype == 1147958883 and not onLava setLava(true) else if ttype == POISON if not poisoned setPoison(true) if sliding speedFactor *= 0.9 else slideVelocity *= 0.8 if onLava doLavaDamage() if poisoned let hp2 = actor.getHP() if hp2 > .405 actor.setHP(hp2-this.getCurrentRegion().poisonDmg) else if alive kill(null) function doLavaDamage() let hp = actor.getHP() if hp > .405 actor.setHP(hp-this.getCurrentRegion().lavaDmg) else if alive kill(null) override function update() let newPos = actor.getPos() walkVel = newPos-pos.toVec2() pos = newPos.withZ(pos.z) physicsUpdate(this) pos += vel pos += slideVelocity * (1-speedFactor) pos += otherVel otherVel = vec2(0,0) if sliding setPos(pos) else setXY(pos) override function setXY(vec3 tpos) if tpos.inPlayable() pos = tpos actor.setXYZ(pos) else setPos(currentRevivepoint.toVec3()) kill(null) function getRealVel() returns vec3 return vel + slideVelocity + otherVel + walkVel function revive() if not alive stop() let rev = currentRevivepoint if revmod == null if actor == null printLog(Loglevel.ERROR, "why this happening :(") super.setNewActor(createUnit(owner, 1, rev, angle(0))) else ReviveHero(actor, rev.x, rev.y, false) setXY(vec3(rev.x, rev.y, 32.)) else if actor == null super.setNewActor(createUnit(owner, 1, revmod.pos, angle(0))) else ReviveHero(actor, revmod.pos.x, revmod.pos.y, false) setXY(vec3(revmod.pos.x, revmod.pos.y, 32.)) alive = true actor.setPropWindow(60 .fromDeg()) updateRegionData() setPoison(false) setLava(false) if left terminate() return CameraClearNoiseForPlayer(owner) if (localPlayer == owner) if selectatrevive and revivetomb == null and not camlock PanCameraToTimed(rev.x, rev.y, 0) if camOnRevive ClearSelection() SelectUnit(actor, true) if camlock SetCameraTargetControllerNoZForPlayer(owner, actor, 0,0,true) if revivetomb != null setPos(revivetomb.getPos3Real()) if selectatrevive and not camlock owner.panCamToTimed(revivetomb, 0.) revivetomb.remove() revivetomb = null if this.getCurrentRegion().specialAbil > 0 actor.addAbility(this.getCurrentRegion().specialAbil) else actor.removeAbility(JUMP_SPELL) actor.removeAbility(ORB_SPELL) override function setTarget(vec3 tpos, real speed) stop() super.setTarget(tpos, speed) function updateRegionData() let currentReg = this.getCurrentRegion() let newReg = this.setCurrentRegion() if newReg != null and newReg != currentReg currentReg.removeEscaper(this) newReg.addEscaper(this) gravity = newReg.gravity actor.removeAbility(currentReg.specialAbil) if newReg.specialAbil > 0 actor.addAbility(newReg.specialAbil) if isEscaperPlayer(actor.getOwner()) printTimedToPlayer("|cffFFCC00>> |rYou were granted a |cff0884BDspecial ability |rin this region |r(use with hotkey |cffFFCC00T|r)", 10, actor.getOwner()) else actor.removeAbility(JUMP_SPELL) actor.removeAbility(ORB_SPELL) actor.setTurnSpeed(newReg.turnSpeed) function changeAngle() let id = GetIssuedOrderId() let w = GetOrderTarget() let orderPos = EventData.getOrderPos() let widgetPos = w.getPos() let rv = getRealVel() let lpos = pos.toVec2().polarOffset(rv.toVec2().getAngle()-(90.).asAngleDegrees(), 17) let rpos = pos.toVec2().polarOffset(rv.toVec2().getAngle()+(90.).asAngleDegrees(), 17) let ttype1 = GetTerrainType(lpos.x, lpos.y) let ttype2 = GetTerrainType(rpos.x, rpos.y) let rdata = this.getCurrentRegion() int ttype if ttype1 == rdata.getTypeSet().unwalkableId if ttype2 == rdata.getTypeSet().unwalkableId ttype = rdata.getTypeSet().unwalkableId else ttype = ttype2 else if ttype2 == rdata.getTypeSet().unwalkableId ttype = ttype1 else ttype = GetTerrainType(pos.x, pos.y) if ttype == CONTROLLABLEICE if w == null actor.setFacing(pos.angleTo2d(orderPos)) else actor.setFacing(pos.angleTo2d(widgetPos)) else if ttype == REVICE if OrderId2String(id) == "lightningshield" actor.setFacing(pos.angleTo2d(orderPos)) else if w == null actor.setFacing(pos.angleTo2d(orderPos)-(180).asAngleDegrees()) else actor.setFacing(pos.angleTo2d(widgetPos)-(180).asAngleDegrees()) static function neutralDeath() let escaper = GetTriggerUnit().getEntity() castTo Escaper if GetKillingUnit() != null escaper.kill(null) static function onCastEvent() let data = GetTriggerUnit().getEntity() castTo Escaper data.onCast(GetSpellAbilityId()) OrbProjectile orb = null function onCast(int id) actor.setAnimation("spell") if id == JUMP_SPELL if not flying addPos(vec3(0,0,1)) let angl = pos.angleTo2d(EventData.getSpellTargetPos()) addEffect(Abilities.aneuCaster, pos).destr() if sliding addVel(vec3(3 * angl.cos(), 3 * angl.sin(), 11.75)) else addVel(vec3(7.75 * angl.cos(), 7.75 * angl.sin(), 11.75)) if this.getCurrentRegion().specialAbil != JUMP_SPELL actor.removeAbility(JUMP_SPELL) else if id == ORB_SPELL orb = new OrbProjectile(this, EventData.getSpellTargetPos()) orb.addVel((getRealVel()-walkVel)*0.65) orb.setXYAngle(orb.vel.toVec2().getAngle()) actor..removeAbility(ORB_SPELL)..addAbility(JAUNT_SPELL) if this.getCurrentRegion().specialAbil != ORB_SPELL actor.removeAbility(ORB_SPELL) else if id == JAUNT_SPELL if orb != null setXY(orb.getPos()) slideVelocity = ZERO2 setVel(vel * 0.15) flashEffect(Abilities.polyMorphTarget, pos) orb.terminate() else if id == CREATE_REV_MOD_ID revmod = new RevivePointModifier(pos.toVec2(), owner) actor..removeAbility(CREATE_REV_MOD_ID) ..addAbility(REMOVE_REV_MOD_ID) else if id == REMOVE_REV_MOD_ID if revmod != null destroy revmod revmod = null UnitRemoveAbility(actor, REMOVE_REV_MOD_ID) UnitAddAbility(actor, CREATE_REV_MOD_ID) else if id == ATTACK_MELEE or id == ATTACK_RANGED // weapon.onUse(this) actor.setAnimation("attack") else if id == ANK_ITEM_ID if poisoned or not canCast() actor.addItemById('I00L') createFText(pos, vec2(0,.08), "canceled!", 8.5, 2, colorA(255,0,0,255), owner) else if revivetomb != null revivetomb.remove() revivetomb = createUnit(owner, TOMB_DUMMY_ID, pos, angle(0)) else if id == TELEPORT_ITEM_ID var teleport = true let itm = GetManipulatedItem() if GetItemCharges(itm) <= 1 itm.remove() for i = 0 to 5 let ie = UnitItemInSlot(actor, i).getEntity() if ie != null and ie instanceof Key teleport = false break if not teleport or poisoned or onLava or sliding or this.getCurrentRegion() == regions[21] or this.getCurrentRegion() == regions[22] or this.getCurrentRegion() == regions[23] or this.getCurrentRegion() == regions[24] createFText(pos, vec2(0,.08), "canceled!", 8.5, 2, colorA(255,0,0,255), owner) else let tpos = EventData.getSpellTargetPos() doAfter(0.15) -> var smallestDist = 9999999. Escaper target = null for e in escapers if e != this let dist = tpos.distanceToSq(e.getPos().toVec2()) if dist < smallestDist smallestDist = dist target = e if target == null target = this flashEffect(Abilities.blinkCaster, this.getPos()) setPos(target.getPos()) flashEffect(Abilities.blinkCaster, target.getPos()) else jukeBox.checkSpell(id) function canCast() returns boolean return not (poisoned or onLava or sliding or this.getCurrentRegion() == regions[21] or this.getCurrentRegion() == regions[22] or this.getCurrentRegion() == regions[23] or this.getCurrentRegion() == regions[24]) ondestroy lavaEffect.destr() poisonEffect.destr() escapers.remove(this) class OrbProjectile extends Projectile Escaper e = null RegionData rdata construct(Escaper e, vec2 target) super(e.getPos().moveTowards(target.toVec3(), 16) + vec3(0,0,16), 16, e.owner, e.pos.angleTo2d(target), "TC_EnergyBolt.mdx") this.e = e this.rdata = this.setCurrentRegion() setAcc(0.985) setRanged(360) setTimed(3) setSpeed(9.85) this.fx.setTintFromPlayer(e.owner) ondestroy e.orb = null e.actor..removeAbility(JAUNT_SPELL)..addAbility(e.getCurrentRegion().specialAbil) override function update() super.update() if not done and rdata != this.setCurrentRegion() e.kill(null) terminate() function correctRegion() for e from escapers.staticItr() e.updateRegionData() init getTimer().startPeriodic(1., function correctRegion)