Простая реализация способности "Цепная Молния" на JASS.
Компонент
19 397
32
LastUchiha, точка начала атаки юнита, в редакторе у юнита есть 3 координаты, это смещение от центра модели для места откуда вылетает дальний снаряд, или спелл, в том числе молния, ну а молния бьет юнитов не в ноги а в этот самый ImpactZ.
function GetUnitZ takes unit u returns real
call MoveLocation( TempLoc, GetUnitX( u ), GetUnitY( u ) )
return ( GetUnitImpactZ( u ) + GetLocationZ( TempLoc ) + GetUnitFlyHeight( u ) ) * 1.00
endfunction
Если мемхак не юзать, то импакт координаты просто базой данных делаются.
20
Не оптимальный вариант, но на zinc.
ChainLightning
library ChainLightning requires Filters, Lightning {

	//Цепная молния\\
	struct ChainLightning {
		unit caster;
		unit target;
		real value;
		real reduction;
		integer countTargets;
		real radius;

		group grTargetsUnits;
		timer tm;
		static integer CK_HID = 0;
		static real TIMER_PERIOD = 0.25;

		static method create(unit caster, unit target, real value, real reduction, integer countTargets, real radius) -> ChainLightning {
			ChainLightning this = ChainLightning.allocate();
			this.caster = caster;
			this.target = target;
			this.value = value;
			this.reduction = reduction;
			this.countTargets = countTargets;
			this.radius = radius;

			this.grTargetsUnits = CreateGroup();
			this.tm = CreateTimer();

			SaveInteger(HT, GetHandleId(this.tm), CK_HID, this);
			TimerStart(this.tm, TIMER_PERIOD, true, function ChainLightning.callback);
			this.action(caster, target);
			return this;
		}

		method destroy() {
			caster = null;
			target = null;

			DestroyGroup(grTargetsUnits); grTargetsUnits = null;
			FlushChildHashtable(HT, GetHandleId(tm)); PauseTimer(tm); DestroyTimer(tm); tm = null;

			this.deallocate();
		}

		method action(unit target1, unit target2) {
			CreateUnitLightningTarget(target1, target2, "CLPB", "", "", "Abilities\\Weapons\\Bolt\\BoltImpact.mdl", "origin", 1.4, 10000, 50., 0.75, true);
			UnitDamageMagicToTarget(caster, target2, value);
			value = value - (value * reduction);
			countTargets = countTargets - 1;
			GroupAddUnit(grTargetsUnits, target2);

			target = target2;

			if (countTargets <= 0) {
				this.destroy();
			}
		}

		static method callback() {
			ChainLightning this = LoadInteger(HT, GetHandleId(GetExpiredTimer()), CK_HID);
			group g = CreateGroup();
			real x = GetUnitX(target);
			real y = GetUnitY(target);
			unit newTarget;

			This = this;
			GroupEnumUnitsInRange(g, x, y, radius, Condition(function() -> boolean {
				ChainLightning this = This;
				FilterUnit = GetFilterUnit();
				return
					UnitAlive(FilterUnit) &&
					!IsUnitAlly(FilterUnit, GetOwningPlayer(caster)) &&
					!IsUnitInGroup(FilterUnit, grTargetsUnits) &&
					!IsUnitType(FilterUnit, UNIT_TYPE_MECHANICAL) &&
					!IsUnitType(FilterUnit, UNIT_TYPE_STRUCTURE) &&
					!IsUnitType(FilterUnit, UNIT_TYPE_TAUREN) &&
					!IsUnitType(FilterUnit, UNIT_TYPE_MAGIC_IMMUNE) &&
					!IsUnitFogged(FilterUnit, GetOwningPlayer(caster)) &&
					IsUnitVisible(FilterUnit, GetOwningPlayer(caster)) &&
					!IsUnitInvulnerable(FilterUnit);
			}));

			FilteredUnit = FirstOfGroup(g);
			ForGroup(g, function() {
				ChainLightning this = This;
				EnumUnit = GetEnumUnit();
				if (DistanceBetweenWidgets(FilteredUnit, target) > DistanceBetweenWidgets(EnumUnit, target)) {
					FilteredUnit = EnumUnit;
				}
			});

			newTarget = FilteredUnit;
			if (newTarget != null) {
				this.action(target, newTarget);
			} else {
				this.destroy();
			}

			newTarget = null;
			DestroyGroup(g);
			g = null;
		}

	}

	public function CastChainLightning(
		unit caster,            
		unit target,            
		real value,             
		real reduction,         
		integer countTargets,   
		real radius             
	) {
		ChainLightning.create(caster, target, value, reduction, countTargets, radius);
	}

}

/**
 * CREDITS:
 * NazarPunk - original author
 */
library Lightning requires TechUtils {	

	struct Lightning {
		unit caster, target;
		real time, expire, maxDist, lightningZ, timeZ, expireZ;
		lightning lighting;
		timer tm;
		effect effectCaster, effectTarget;
	
		static method create(unit caster, unit target, string lightningType, string effectCasterModel, string effectCasterAttach, string effectTargetModel, string effectTargetAttach, real duration, real maxDistance, real lightningZ, real decayTime, boolean destroyEffects) -> Lightning {
			Lightning this = Lightning.allocate();
			this.caster = caster;
			this.target = target;
			this.time = 0;
			this.maxDist = maxDistance;
			this.expire = duration;
			this.expireZ = decayTime;
			this.timeZ = 0;
			this.lightningZ = lightningZ;
			
			this.lighting = AddLightningEx(
				lightningType,
				true,
				GetUnitX(caster),
				GetUnitY(caster),
				GetUnitZ(caster) + lightningZ,
				GetUnitX(target),
				GetUnitY(target),
				GetUnitZ(target) + lightningZ
			);
			
			this.effectCaster = AddSpecialEffectTarget(effectCasterModel, caster, effectCasterAttach);
			this.effectTarget = AddSpecialEffectTarget(effectTargetModel, target, effectTargetAttach);
			if (destroyEffects) {
				DestroyEffect(this.effectCaster); this.effectCaster = null;
				DestroyEffect(this.effectTarget); this.effectTarget = null;
			}

			this.tm = CreateTimer();
			SaveHandleDataInt(tm, this);
			TimerStart(this.tm, TIMER_PERIOD, true, function Lightning.callback);
			return this;
		}
		
		method destroy(){
			FlushTimer(this.tm); this.tm = null;
			DestroyLightning(this.lighting); this.lighting = null;
			DestroyEffect(this.effectCaster); this.effectCaster = null;
			DestroyEffect(this.effectTarget); this.effectTarget = null;
			this.caster = null;
			this.target = null;
			this.deallocate();
		}
		
		static method callback(){
			Lightning this = LoadHandleDataInt(GetExpiredTimer());
			real xc, yc, zc, xt, yt, zt;
			
			xc = GetUnitX(this.caster);
			yc = GetUnitY(this.caster);
			zc = GetUnitZ(this.caster) + this.lightningZ;
			xt = GetUnitX(this.target);
			yt = GetUnitY(this.target);
			zt = GetUnitZ(this.target) + this.lightningZ;
			
			MoveLightningEx(this.lighting, true, xc, yc, zc, xt, yt, zt);
			if (this.expire - this.time <= this.expireZ) {
				this.timeZ = this.timeZ + TIMER_PERIOD;
				SetLightningColor(this.lighting, 100, 100, 100, (this.expireZ - this.timeZ)/this.expireZ);
			}
			
			this.time = this.time + TIMER_PERIOD;
			if (
				/*
				!UnitAlive(this.caster)
				||
				!UnitAlive(this.target)
				||
				*/
				this.time >= this.expire
				||
				DistanceBetweenCoords3D(xc, yc, zc, xt, yt, zt) >= this.maxDist
			) {
				this.destroy();
			}
		}
	}
	
	// Создаёт графическую молнию между двумя юнитами
	// @arg unit caster юнит-источник молнии
	// @arg unit target юнит-цель молнии
	// @arg string lightningType тип молнии
	// @arg string effectCasterModel модель эффекта источника молнии
	// @arg string effectCasterAttach точка прикрепления эффекта источника молнии
	// @arg string effectTargetModel модель эффекта цели молнии
	// @arg string effectTargetAttach точка прикрепления эффекта цели молнии
	// @arg real duration время жизни молнии
	// @arg real maxDistance максимальная дистанция молнии
	// @arg real lightningZ высота молнии над землёй
	// @arg real decayTime время затухания молнии
	// @arg boolean destroyEffects уничтожать ли эффекты молнии
	// @return integer идентификатор молнии
	//! @gui [Unit], TriggerActions, TriggerCalls
	public function CreateUnitLightningTarget(unit caster, unit target, string lightningType, string effectCasterModel, string effectCasterAttach, string effectTargetModel, string effectTargetAttach, real duration, real maxDistance, real lightningZ, real decayTime, boolean destroyEffects) -> integer {
		Lightning current;
		current = Lightning.create(caster, target, lightningType, effectCasterModel, effectCasterAttach, effectTargetModel, effectTargetAttach, duration, maxDistance, lightningZ, decayTime, destroyEffects);
		return current;
	}

}
17
quq_CCCP, да ладно уж, не хочется сильно заморачиваться с этим.
Если будет желание разузнаю что это такое ваш "ImpactZ" и обновлю спелл.
Но насколько я понял ImpactZ - это высота с учётом высоты ландшафта, и прочих мелочей.
26
Хотя и то, это скорее не "менее популярно" А более закрыто
15 человек на сундук мертвеца! Йо-хо-хо! И бутылка рому!
Модель
10 708
19
darkowlom, Понял) Значит я просто не особо увлекался пиратами и не знал, что это его фишка.
24
CaZaNoVa, фитили в бороде исторически его фишка, за полтора года дядька накуролесил нормально, до сих пор вспоминаем
35
CaZaNoVa, ну даже на старых портретах его есть фитили.
19
PUVer,
Просто фитили у него в бороде я увидел именно в AC BlackFlag. Не знал, что его где-то еще так представляют.
35
CaZaNoVa, нет. Вдохновлялся артами про чёрную бороду.