- Joined
- Jul 15, 2004
- Messages
- 5
A simple Lifedrainspell, which allowes you, to choose two targets - one allied and one enemy - between which life will be transfered.
Since i never saw a spell in a map, which lets you select two targets - expect in some lame, poor made way with 2 abilitys, which you have to use after each other - I tryed to make such a selection as intuitive as possible.
The requierments are H2I, TimerUtils, UnitIndexingUtils and SimError. But just take a look at the demomap.
Since i never saw a spell in a map, which lets you select two targets - expect in some lame, poor made way with 2 abilitys, which you have to use after each other - I tryed to make such a selection as intuitive as possible.
The requierments are H2I, TimerUtils, UnitIndexingUtils and SimError. But just take a look at the demomap.
JASS:
scope lifedrain initializer Init
//*********************************************************************
//* A simple Lifedrainspell, which allowes you, to choose two targets - one allied and one enemy - between which life will be transfered
//* by Hans_Maulwurf, feel free to modify and use
//*********************************************************************
//*********************************************************************
//* Configure Spellvalues here
//*********************************************************************
globals
private constant integer Spell = 'A000' // The ID of your dummyspell
private constant string SpellOrder = "chainlightning" // The Orderstring of your dummyspell
private constant string hotkey = "R" // Tht hotkey for your spell
private constant real looptime = 0.03 // The frameinterval; lower = smoother, higher = better performace
private constant string transfer_effect = "Abilities\\Spells\\Other\\Drain\\DrainCaster.mdl" // The Effect on the units to which life is transfered to
private constant string drain_effect = "Abilities\\Spells\\Other\\Drain\\DrainTarget.mdl" // The Effect on the unit from which life is drained from
private constant string light_effect = "DRAL" // The Lightning effect
private constant string error_toolong = "Selection cancled, because no second target selected"
private constant string error_targetdead = "Selection cancled, because the first target died"
private constant string error_casterdead = "Selection cancled, because the caster died"
private constant string error_outofrange = "Target out of range"
private constant string error_2ally = "Choose an enemy unit, to drain life from"
private constant string error_2enemy = "Choose an allied unit, to transfer life to"
endglobals
private constant function damage takes integer level returns real
//The amount of damage dealted per second
return 10.00 + (level * 10.00)
endfunction
private constant function transfer takes integer level returns real
//The percentage amount from the damage dealted, which is healed
return 100.00 + (level * 0.00)
endfunction
private constant function distance takes integer level returns real
//The max allowed distance between the two targets
return 500.00 + (level * 100.00)
endfunction
private constant function duration takes integer level returns real
//The max duration of the spell
return 10.00 + (level * 0.00)
endfunction
//*********************************************************************
//* End of Configuration
//*********************************************************************
private struct unitdata
unit target1
unit target2
unit caster
integer level
real time
timer timy
integer progress
endstruct
private struct spelldata
unit caster
unit drain
unit transfer
integer level
real time
effect draineffect
effect transfereffect
lightning light
endstruct
globals
public trigger Order = CreateTrigger()
public trigger Cast = CreateTrigger()
private unitdata array arrayunitdata
private unitdata array arrayunitdata2
private spelldata array arrayspelldata
private integer casts = 0
private integer reorders = 0
private unit array reordered
endglobals
//*********************************************************************
//* The Loop for the Lifedrain spell
//*********************************************************************
private function DrainLifeLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
local spelldata lifedrain = arrayspelldata[GetTimerData(t)]
local real dmgdealt = GetUnitState( lifedrain.drain, UNIT_STATE_LIFE )
local real xdist = GetUnitX(lifedrain.transfer) - GetUnitX(lifedrain.drain)
local real ydist = GetUnitY(lifedrain.transfer) - GetUnitY(lifedrain.drain)
local real dist = SquareRoot(xdist * xdist + ydist * ydist)
call UnitDamageTarget(lifedrain.caster, lifedrain.drain, (damage(lifedrain.level) * looptime), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
set dmgdealt = dmgdealt - GetUnitState( lifedrain.drain, UNIT_STATE_LIFE )
call heal(lifedrain.transfer, transfer(lifedrain.level)/100*dmgdealt)
call MoveLightningEx(lifedrain.light, true, GetUnitX(lifedrain.transfer), GetUnitY(lifedrain.transfer), 50, GetUnitX(lifedrain.drain), GetUnitY(lifedrain.drain), 50)
set lifedrain.time = lifedrain.time + looptime
if ( lifedrain.time > duration(lifedrain.level) or GetUnitState(lifedrain.drain, UNIT_STATE_LIFE) <= 0 or GetUnitState(lifedrain.transfer, UNIT_STATE_LIFE) <= 0 or dist > distance(lifedrain.level) ) then
call DestroyEffect(lifedrain.draineffect)
call DestroyEffect(lifedrain.transfereffect)
call DestroyLightning(lifedrain.light)
call ReleaseTimer(t)
call spelldata.destroy(lifedrain)
endif
set t = null
endfunction
//*********************************************************************
//* Starting the Lifedrain spell
//*********************************************************************
private function DrainLife takes unit drained, unit transfered, unit caster, integer level returns nothing
local spelldata lifedrain = spelldata.create()
local timer t = NewTimer()
set casts = casts + 1
call SetTimerData(t, casts)
set arrayspelldata[casts] = lifedrain
set lifedrain.drain = drained
set lifedrain.transfer = transfered
set lifedrain.caster = caster
set lifedrain.level = level
set lifedrain.time = 0.00
set lifedrain.draineffect = AddSpecialEffectTarget(transfer_effect, drained, "chest")
set lifedrain.transfereffect = AddSpecialEffectTarget(drain_effect, transfered, "chest")
set lifedrain.light = AddLightningEx(light_effect, true, GetUnitX(transfered), GetUnitY(transfered), 50, GetUnitX(drained), GetUnitY(drained), 50)
call TimerStart(t, looptime, true, function DrainLifeLoop)
set t = null
endfunction
//*********************************************************************
//* actually casting the spell. we will only allow this, when the frist target was already selected and the second target is valid
//*********************************************************************
private function Cast_Conditions takes nothing returns boolean
return GetSpellAbilityId() == Spell
endfunction
private function Cast_Actions takes nothing returns nothing
local unitdata lifedrain = arrayunitdata[GetUnitId(GetTriggerUnit())]
set lifedrain.target2 = GetSpellTargetUnit()
if ( IsPlayerAlly( GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(lifedrain.target1)) ) then
call DrainLife(lifedrain.target2, lifedrain.target1, GetTriggerUnit(), lifedrain.level)
else
call DrainLife(lifedrain.target1, lifedrain.target2, GetTriggerUnit(), lifedrain.level)
endif
// The targeting is over, so we reset it
call ReleaseTimer(lifedrain.timy)
set lifedrain.progress = 0
call ReleaseUnitId(lifedrain.caster)
call unitdata.destroy(lifedrain)
endfunction
//*********************************************************************
//* Cancel the selection, when the first target dies, or the player needs more than 10 seconds to select the second
//*********************************************************************
private function Cancel takes nothing returns nothing
local timer t = GetExpiredTimer()
local unitdata lifedrain = arrayunitdata[GetTimerData(t)]
set lifedrain.time = lifedrain.time + looptime
if ( lifedrain.time > 10.00 or GetUnitState(lifedrain.target1, UNIT_STATE_LIFE) <= 0 or GetUnitState(lifedrain.caster, UNIT_STATE_LIFE) <= 0 ) then
if ( lifedrain.time > 10.00 ) then
call SimError( GetOwningPlayer(lifedrain.caster), error_toolong )
endif
if ( GetUnitState(lifedrain.target1, UNIT_STATE_LIFE) <= 0 ) then
call SimError( GetOwningPlayer(lifedrain.caster), error_targetdead )
endif
if ( GetUnitState(lifedrain.caster, UNIT_STATE_LIFE) <= 0 ) then
call SimError( GetOwningPlayer(lifedrain.caster), error_casterdead )
endif
call ReleaseTimer(t)
call ReleaseUnitId(lifedrain.caster)
set lifedrain.progress = 0
call unitdata.destroy(lifedrain)
endif
set t = null
endfunction
//*********************************************************************
//* Opening the targetselection again, by simulating the hotkey
//*********************************************************************
private function reorder takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u = reordered[GetTimerData(t)]
call IssueImmediateOrder(u, "stop")
if (GetLocalPlayer() == GetOwningPlayer(u)) then
call ForceUIKey(hotkey)
endif
call ReleaseTimer(t)
set u = null
set t = null
endfunction
//*********************************************************************
//* When the spell gets ordered for the first time, we cancel it, and save the first target
//* When its ordered, and we already got the frist target, we check if the second one is valied and cancel if not
//*********************************************************************
private function Order_Conditions takes nothing returns boolean
return GetIssuedOrderId() == OrderId(SpellOrder) and GetUnitAbilityLevel(GetTriggerUnit(), Spell) > 0
endfunction
private function Order_Actions takes nothing returns nothing
local timer t
local timer t2
local unitdata lifedrain = arrayunitdata[GetUnitId(GetTriggerUnit())] // checking if we already got data for this unit
local real xdist
local real ydist
local real dist
if ( lifedrain.progress == 0 ) then // When we cast the spell the first time
set lifedrain = unitdata.create() // Since we have no unitdata yet, we create it...
set arrayunitdata[GetUnitId(GetTriggerUnit())] = lifedrain // ...and link it with the caster
set lifedrain.target1 = GetOrderTargetUnit()
set lifedrain.level = GetUnitAbilityLevel( GetTriggerUnit(), Spell )
set lifedrain.caster = GetTriggerUnit()
set lifedrain.progress = 1
set t = NewTimer()
set lifedrain.timy = t
set lifedrain.time = 0.00
call SetTimerData(t, GetUnitId(GetTriggerUnit()))
call TimerStart(t, looptime, true, function Cancel)
set t = null
set t2 = NewTimer()
set reorders = reorders + 1
call SetTimerData(t2, reorders)
set reordered[reorders] = GetTriggerUnit()
call TimerStart(t2, 0.00, false, function reorder )
set t2 = null
endif
if ( lifedrain.progress == 1 ) then // When we already got the first unit
set lifedrain.target2 = GetOrderTargetUnit()
set xdist = GetUnitX(lifedrain.target1) - GetUnitX(lifedrain.target2)
set ydist = GetUnitY(lifedrain.target1) - GetUnitY(lifedrain.target2)
set dist = SquareRoot(xdist * xdist + ydist * ydist)
if ( ( IsPlayerAlly( GetOwningPlayer(lifedrain.target1), GetOwningPlayer(lifedrain.target2)) ) or dist > distance(lifedrain.level) ) then
if ( IsPlayerAlly( GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(lifedrain.target1)) ) then
call SimError( GetOwningPlayer(lifedrain.caster), error_2ally )
else
call SimError( GetOwningPlayer(lifedrain.caster), error_2enemy )
endif
if ( dist > distance(lifedrain.level) ) then
call SimError( GetOwningPlayer(lifedrain.caster), error_outofrange )
endif
set t2 = NewTimer()
set reorders = reorders + 1
call SetTimerData(t2, reorders)
set reordered[reorders] = GetTriggerUnit()
call TimerStart(t2, 0.00, false, function reorder )
set t2 = null
endif
endif
endfunction
public function Init takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ( Order, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
call TriggerAddCondition( Order, Condition( function Order_Conditions ) )
call TriggerAddAction( Order, function Order_Actions )
call TriggerRegisterAnyUnitEventBJ( Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( Cast, Condition( function Cast_Conditions ) )
call TriggerAddAction( Cast, function Cast_Actions )
call BJDebugMsg("Multiple targets system and lifedrain by Hans_Maulwurf. Have fun!")
endfunction
endscope
Attachments
Last edited: