Добавлен , опубликован
Способ реализации:
Версия Warcraft:

Описание

Способность цепной молнии с плавным сгасанием эффекта молнии.
Используется vJass (только ради удобного scope).
Если где-то обнаружена ошибка / лишний код / недостающий код / не правильный код - просьба доложить в комментарии об этом!
MUI

Видео

Код

Шапка карты (нестандартный код карты)
globals
    hashtable HT = InitHashtable( )
    key k_caster
    key k_target
    key k_group
    key k_lightning
    key k_alpha
endglobals

native UnitAlive takes unit id returns boolean
Библиотека для групп
library GroupLib
    globals
        private real Range = 0. 
        private real LocX = 0.
        private real LocY = 0.
        private unit Nearest = null
    endglobals

    private function NearestUnitInGroup_Group takes nothing returns nothing
        local unit u = GetEnumUnit( )
        local real x = GetUnitX( u )
        local real y = GetUnitY( u )
        local real d = SquareRoot( ( x - LocX ) * ( x - LocX ) + ( y - LocY ) * ( y - LocY ) )
        
        if d < Range then
            set Range = d
            set Nearest = u
        endif
        
        set u = null
    endfunction

    function NearestUnitInGroup takes group g, real x, real y returns unit
        set Range = 999999
        set Nearest = null
        set LocX = x
        set LocY = y
        
        call ForGroup( g, function NearestUnitInGroup_Group )
        return Nearest
    endfunction
endlibrary
Способность
scope ChainLightning initializer Init
    globals
        private constant integer ID = 'A001' // Айди способности
        private constant real PERIOD = 0.1 // Период поиска
        
        private constant real RADIUS = 600.0 // Радиус поиска
        private constant real DAMAGE = 30.0 // Урон
        
        private constant real LIFE_TIME = 0.3 // Время жизни молнии
        private constant real EXTINCTION_TIME = 0.3 // Время сгасания молнии
    endglobals
    
    private function Extinction takes nothing returns nothing
        local timer tmr = GetExpiredTimer( )
        local integer id = GetHandleId( tmr )
        local real alpha = LoadReal( HT, id, k_alpha ) - 1.0 / ( EXTINCTION_TIME / 0.02 )
        
        call SaveReal( HT, id, k_alpha, alpha )
        call SetLightningColor( LoadLightningHandle( HT, id, k_lightning ), 1.0, 1.0, 1.0, alpha )
        
        if alpha <= 0 then
            call DestroyLightning( LoadLightningHandle( HT, id, k_lightning ) )
            call FlushChildHashtable( HT, id )
            
            call PauseTimer( tmr )
            call DestroyTimer( tmr )
        endif
        
        set tmr = null
    endfunction
    
    private function Remove takes nothing returns nothing
        local timer tmr = GetExpiredTimer( )
        local integer id = GetHandleId( tmr )
        
        call SaveReal( HT, id, k_alpha, 1.0 )
        call TimerStart( tmr, 0.02, true, function Extinction )
        
        set tmr = null
    endfunction

    private function Timer takes nothing returns nothing
        local timer tmr = GetExpiredTimer( )
        local integer id = GetHandleId( tmr )
        
        local unit caster = LoadUnitHandle( HT, id, k_caster )
        local unit target = LoadUnitHandle( HT, id, k_target )
        local group hittedGroup = LoadGroupHandle( HT, id, k_group )
        
        local real targetX = GetUnitX( target )
        local real targetY = GetUnitY( target )
        
        local group g = CreateGroup( )
        local unit u = null
        local timer t = null
        
        call GroupEnumUnitsInRange( g, targetX, targetY, RADIUS + 200., null )
        
        loop
            set u = NearestUnitInGroup( g, targetX, targetY )
            exitwhen u == null
            
            if IsUnitInRangeXY( u, targetX, targetY, RADIUS ) and not IsUnitInGroup( u, hittedGroup ) then
                if UnitAlive( u ) and IsUnitEnemy( u, GetOwningPlayer( caster ) ) and not IsUnitType( u, UNIT_TYPE_STRUCTURE ) then
                    call UnitDamageTarget( caster, u, DAMAGE, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_LIGHTNING, WEAPON_TYPE_WHOKNOWS )
                    call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Weapons\\Bolt\\BoltImpact.mdl", u, "chest" ) )
                    
                    call GroupAddUnit( hittedGroup, u )
                    call SaveUnitHandle( HT, id, k_target, u )
                    
                    set t = CreateTimer( )
                    call SaveLightningHandle( HT, GetHandleId( t ), k_lightning, AddLightningEx( "CLSB", false, targetX, targetY, GetUnitFlyHeight( target ) + 70., GetUnitX( u ), GetUnitY( u ), GetUnitFlyHeight( u ) + 70. ) )
                    call TimerStart( t, LIFE_TIME, false, function Remove )
                    set t = null
                    
                    exitwhen true
                endif
            endif
            
            call GroupRemoveUnit( g, u )
        endloop
        
        if u == null then
            call GroupClear( hittedGroup )
            call DestroyGroup( hittedGroup )
            
            call PauseTimer( tmr )
            call DestroyTimer( tmr )
        else
            set u = null
        endif
        
        call GroupClear( g )
        call DestroyGroup( g )
        
        set tmr = null
        set caster = null
        set target = null
        set hittedGroup = null
        set g = null
    endfunction

    private function Actions takes nothing returns nothing
        local unit caster = GetSpellAbilityUnit( )
        local unit target = GetSpellTargetUnit( )
        
        local timer tmr = CreateTimer( )
        local integer id = GetHandleId( tmr )
        
        call SaveUnitHandle( HT, id, k_caster, caster )
        call SaveUnitHandle( HT, id, k_target, target )
        call SaveGroupHandle( HT, id, k_group, CreateGroup( ) )
        call TimerStart( tmr, PERIOD, true, function Timer )
        
        set tmr = CreateTimer( )
        call SaveLightningHandle( HT, GetHandleId( tmr ), k_lightning, AddLightningEx( "CLSB", false, GetUnitX( target ), GetUnitY( target ), GetUnitFlyHeight( target ) + 70., GetUnitX( caster ), GetUnitY( caster ), GetUnitFlyHeight( caster ) + 70. ) )
        call TimerStart( tmr, LIFE_TIME, false, function Remove )

        set caster = null
        set target = null
        set tmr = null
    endfunction

    private function Conditions takes nothing returns nothing
        if GetSpellAbilityId( ) == ID then
            call Actions( )
        endif
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger( )
        local integer i = 0

        loop
            call TriggerRegisterPlayerUnitEvent( t, Player( i ), EVENT_PLAYER_UNIT_SPELL_EFFECT, null )
            set i = i + 1
            exitwhen i >= 16
        endloop
        
        call TriggerAddAction( t, function Conditions )
        set t = null
        
        call FogEnable( false )
        call FogMaskEnable( false )
    endfunction
endscope

Требования

`
ОЖИДАНИЕ РЕКЛАМЫ...
30
Если ты используешь VJASS, то не нужно выносить код в шапку карты. Компилятор сам это сделает. И я не вижу в коде ресурса реализации NearestUnitInGroup.
17
nazarpunk, перенес в шапку карты, что-бы человек понимал что оно должно быть в шапке карты. В спелле не совсем правильно объявлять ХТ и ключи для неё.

NearestUnitInGroup - позже на сайт вынесу.
17
nazarpunk, по идее неплохо сделал оформление раздела с кодом.
10
Я чтото подобное через ГПТ делал на удивление работает...
26
А в старкрафте такое легко можно сделать только используя данные)))
И более настраиваемо
В данном примере она по мимо цепной молнии ещё и раздваивается от каждой цели на две
17
ShadowDragonSC2, и почему тогда мапмейкерство более популярно в WC3?
31
LastUchiha, про какую, черт возьми, популярность вообще идет речь..?
14
LastUchiha, полагаю, что из-за сюжетной составляющей. Весь варкрафт построен на сюжете "добрый герой пожертвовал собой во имя спасения кого-то и обретения некой силы". Это и Артес, и Гром, и Иллидан.
26
LastUchiha, вот и я не понимаю, почему менее популярен... Типа потому что "сай-фай"?
26
Хотя и то, это скорее не "менее популярно" А более закрыто
32
Z молнии не правильно определяется, нет Impaсt xyz для кастера как у дефолтных молний.
17
quq_CCCP, да ладно уж, не хочется сильно заморачиваться с этим.
Если будет желание разузнаю что это такое ваш "ImpactZ" и обновлю спелл.
Но насколько я понял ImpactZ - это высота с учётом высоты ландшафта, и прочих мелочей.
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;
	}

}
Чтобы оставить комментарий, пожалуйста, войдите на сайт.