package Key import ItemObject import ClosureTimers import ChannelAbilityPreset import HashMap import Assets public constant KEY_DUMMY_ID = compiletime(UNIT_ID_GEN.next()) public let keyIds = ['I009','I00C','I004','I008','I00J','I005','I00O','I00B','I006'] constant DYN_ID = 'A00M' constant STATIC_ID = 'A01O' constant INFINITE_ID = compiletime(ABIL_ID_GEN.next()) constant KEY_COLOR = "c" public class Key extends ItemObject var col = EBRColor.WHITE var picked = false var followWalkable = true var infinite = false effect infiniteEff = null effect returnSfx = null let effectMap = new IterableMap() var ignoreNextDrop = false construct(vec2 ppos, player owner) super(ppos, createItem(keyIds[0], ppos), createUnit(owner, KEY_DUMMY_ID, ppos, 0 .fromDeg())) EventListener.add(setup.actor, EVENT_PLAYER_UNIT_SPELL_CAST, () -> onCast()) function recolor() setNewActor(createItem(keyIds[col castTo int], pos)) if returnSfx != null returnSfx.destr() returnSfx = addEffect("XTex" + (this.col castTo int).toString() + ".mdx", pos) override function onPickup(UnitEntity holder) if isEscaperPlayer(holder.owner) or this.getCurrentRegion().getPermission(holder.owner) != Permission.GUEST var count = 0 for itm in holder.actor.inventory() if itm.getEntity() == this count++ if count > 1 ignoreNextDrop = true holder.actor.removeItem(actor) actor.setPos(pos.toVec2()) else let eff = addEffect("war3mapImported\\Key_ITEM.mdx", holder.actor, "overhead") ..setColor(col.toColor()) effectMap.put(holder castTo Escaper, eff) if infinite actor = createItem(keyIds[col castTo int], pos) actor.setEntity(this) else picked = true this.holder = holder setup.actor.hide() activate() else picked = false holder.actor.removeItem(actor) actor.setPos(pos.toVec2()) setup.setXY(pos) setup.actor.show() this.holder = null override function onDrop(UnitEntity e) if ignoreNextDrop ignoreNextDrop = false else if effectMap.has(e castTo Escaper) effectMap.getAndRemove(e castTo Escaper).destr() if infinite GetManipulatedItem().setEntity(null) GetManipulatedItem().remove() else picked = false holder = null setup.setXY(pos) setup.actor.show() nullTimer() -> actor..setEntity(null) ..remove() actor = createItem(keyIds[col castTo int], pos) actor.setEntity(this) override function update() if followWalkable and picked and holder instanceof Escaper let escaper = holder castTo Escaper if not holder.flying and not escaper.sliding and escaper.alive pos = holder.getPos() else super.update() function setFollow(boolean flag) if flag and returnSfx != null returnSfx.destr() returnSfx = null else if not flag and returnSfx == null returnSfx = addEffect("XTex" + (this.col castTo int).toString() + ".mdx", pos) followWalkable = flag function setInfinite(boolean flag) if not flag and infinite infiniteEff.destr() infiniteEff = null clearKeys() effectMap.flush() else if flag and not infinite infiniteEff = addEffect(Abilities.generalAuraTarget, actor.getPos()) infinite = flag function onCast() let id = GetSpellAbilityId() switch id case DYN_ID setInfinite(false) setFollow(false) setup.actor..removeAbility(DYN_ID)..addAbility(STATIC_ID) case STATIC_ID setInfinite(false) setFollow(true) setup.actor..removeAbility(STATIC_ID)..addAbility(DYN_ID) case INFINITE_ID setInfinite(not infinite) setFollow(false) for i = 0 to 8 if colorSpells[i] == id col = i castTo EBRColor break recolor() function clearKeys() for escaper in effectMap for itm in escaper.actor.inventory() if itm.getEntity() == this itm..setEntity(null)..remove() effectMap.get(escaper).destr() override function serialize() returns Json let json = super.serialize() json.addProperty(new Property(KEY_TYPE, KEY_INDEX.toString())) json.addProperty(new Property(KEY_COLOR, (col castTo int).toString())) return json override function deserialize(Json json) col = json.getInt(KEY_COLOR) castTo EBRColor recolor() ondestroy clearKeys() destroy effectMap holder = null if returnSfx != null returnSfx.destr() if infiniteEff != null infiniteEff.destr() @objectgen public function genKey() new UnitDefinition(KEY_DUMMY_ID, 'hfoo') ..setAttacksEnabled(0) ..setTargetedAs("structure") ..setAnimationBlendTimeseconds(0) ..setAnimationCastBackswing(0) ..setAnimationCastPoint(0) ..setScalingValue(0.55) ..setShadowImageUnit("") ..setShadowImageCenterX(0) ..setShadowImageCenterY(0) ..setShadowImageWidth(0) ..setShadowImageHeight(0) ..setIconGameInterface("ReplaceableTextures\\CommandButtons\\BTNGhostKey.blp") ..setModelFile("models\\tcBox.mdl") ..setSelectionScale(0.55) ..setSpeedBase(0) ..setTurnRate(0) ..setMovementType(MovementType.None) ..setUnitSoundSet("") ..setCollisionSize(0) ..setNormalAbilities(commaList(DYN_ID, 'A05R', INFINITE_ID, REMOVE_OBJECT_ID, GHOST_INVIS_ID, INVULNERABILITY_ID)) ..setName("Key Dummy") ..setUpgradesUsed("") ..setBuildTime(1) ..setUnitClassification("mechanical") ..setHitPointsMaximumBase(100000000) ..setHideMinimapDisplay(true) ..setIsaBuilding(true) ..setShadowImageUnit("") new ChannelAbilityPreset(INFINITE_ID, 1, true) ..setName("Infinite Key Source") ..presetTooltipNormal(lvl -> "Mode: One Key Per Player") ..presetTooltipNormalExtended(lvl -> "This enables every escaper to pick up this key once.\nIf one of the keys is used all instances currently in inventory will be removed.\nForces rooted key mode.")