|
前好几天看见疑问区有人问施法加速的问题。
实际上基本就是实现任意可变长度的吟唱施法时间(好像有点绕还是个病句……)。
那贴里我和一些人讨论了一下。我觉得用:
首先单位是瞬发技能单位(也就是说单位——魔法释放点为0.0)
单位准备释放技能——》
释放引导技能(channel)——》
等待吟唱时间秒——》
重新对单位发布技能Order。
(PS:最好把真正的技能的动画设为stand)
这样基本就可以了。
我的演示中,是牛头人酋长的踩地板。
有两个核心触发,一个是记录技能的Order的,另一个是灵魂触发。
Order触发:
[trigger]
Order
事件
单位 - 任意单位 发布指定物体目标指令
单位 - 任意单位 发布指定点目标指令
单位 - 任意单位 发布无目标指令
条件
(发布的命令ID) 不等于 (转换 channel 为命令ID)
动作
设置 Order = (发布的命令ID)
[/trigger]
灵魂触发:
[trigger]
Spell
事件
单位 - 任意单位 准备施放技能
条件
(施放技能) 不等于 spelling
动作
触发器 - 关闭 Order <预设>
单位 - 对 (触发单位) 发布 停止 命令
触发器 - 开启 Order <预设>
等待 0.00 秒
-------- 必须等待,否则无用 --------
-------- 使用其他触发貌似也一样 --------
单位 - 对 (触发单位) 发布 特殊 - 通魔 命令
等待 Speed 秒
设置 Mana = (魔法值 对于 (触发单位))
单位 - 设置 (触发单位) 的魔法值为 100.00%
触发器 - 关闭 Order <预设>
触发器 - 关闭 (当前触发)
自定义代码: call IssueImmediateOrderById( GetTriggerUnit(), udg_Order )
触发器 - 开启 (当前触发)
触发器 - 开启 Order <预设>
单位 - 设置 (触发单位) 的魔法值为 Mana
[/trigger]
其中mana什么的都是为了能够成功释放技能弄的。
至于为什么是ImmediateOrder,是因为演示里的踩地板是无目标技能。
(话说我第一次弄这么短小精悍的演示…………)
至于技能扣魔的问题。因为单位触发事件之后,虽然此时打印单位的魔法值是释放技能之前的魔法,但是如果等待0.0秒,就会发现单位的魔法减少了。
貌似不管是单位准备/开始释放技能,都得到的是单位没有扣魔的魔法值……
而施法延迟的问题嘛…………聪明的人都知道该怎么改。
于是我做了个系统~
顺便练练用HashTable(数组)替代全部存储结构…………
于是悲剧的发现系统长了300+行……………………
去掉HashTable的系统,400行。
[jass]
library SpellBefore initializer Init
globals
trigger UnitsOrder = null
trigger UnitsSpell = null
constant integer Unit_LoopingSpellAbilityId = 'A000'
constant integer Unit_LoopingSpellAbilityOrder = 852600
constant integer UnitStopOrder = 851972
constant integer UnitOrderKind_Target = 0
constant integer UnitOrderKind_Point = 1
constant integer UnitOrderKind_Immediate = 2
//constant integer AbilityMinId = 'A000'
endglobals
constant function H2I takes handle h returns integer
return h
return 0
endfunction
//TypeData------------------
//........................................................省略诸多内容
struct ForSpell
static integer TimerNumber = 2000
static integer MinTimerHandle = 0
timer WaitTimer
Unit U
boolean iswait
integer order
widget target
real targetx
real targety
integer orderkind
unit u
real x
real y
integer current
real angle
static method Get takes timer t returns ForSpell
return H2I(t) - ForSpell.MinTimerHandle
endmethod
static method create takes Unit u returns ForSpell
local ForSpell this = ForSpell.allocate()
set this.U = u
set this.u = u.u
set this.order = u.order
set this.target = u.target
set this.targetx = u.targetx
set this.targety = u.targety
set this.orderkind = u.orderkind
set this.angle = GetUnitFacing( this.u )
return this
endmethod
method Delete takes nothing returns nothing
set this.U = 0
set this.u = null
set this.iswait = false
set this.order = 0
set this.target = null
set this.targetx = 0.0
set this.targety = 0.0
set this.orderkind = 0
set this.current = 0
set this.angle = 0.0
call PauseTimer( this.WaitTimer )
call this.destroy()
endmethod
method ReorderUnit takes nothing returns nothing
call DisableTrigger( UnitsOrder )
call DisableTrigger( UnitsSpell )
if this.orderkind == UnitOrderKind_Target then
call IssueTargetOrderById( this.u, this.order, this.target )
elseif this.orderkind == UnitOrderKind_Point then
call IssuePointOrderById( this.u, this.order, this.targetx, this.targety )
elseif this.orderkind == UnitOrderKind_Immediate then
call IssueImmediateOrderById( this.u, this.order )
endif
call EnableTrigger( UnitsSpell )
call EnableTrigger( UnitsOrder )
endmethod
method ReorderUnit_Unit takes Unit U, integer currentorder returns nothing
call DisableTrigger( UnitsOrder )
call DisableTrigger( UnitsSpell )
if currentorder != UnitStopOrder and currentorder != Unit_LoopingSpellAbilityOrder and currentorder != 0 then
if U.orderkind == UnitOrderKind_Target then
call IssueTargetOrderById( this.u, U.order, U.target )
elseif U.orderkind == UnitOrderKind_Point then
call IssuePointOrderById( this.u, U.order, U.targetx, U.targety )
elseif U.orderkind == UnitOrderKind_Immediate then
call IssueImmediateOrderById( this.u, U.order )
endif
else
if currentorder == Unit_LoopingSpellAbilityOrder then
call IssueImmediateOrderById( this.u, Unit_LoopingSpellAbilityOrder )
endif
endif
call EnableTrigger( UnitsSpell )
call EnableTrigger( UnitsOrder )
endmethod
static method SpellWait_End takes nothing returns nothing
local ForSpell this = ForSpell.Get( GetExpiredTimer() )
call SetUnitX( this.u, this.x )
call SetUnitY( this.u, this.y )
call SetUnitFacingTimed( this.u, this.angle, 0.0 )
call this.ReorderUnit_Unit( this.U, this.current )
call this.Delete()
endmethod
static method SpellWait_Action takes nothing returns nothing
local ForSpell this = ForSpell.Get( GetExpiredTimer() )
local real x = GetUnitX( this.u )
local real y = GetUnitY( this.u )
local integer currentorder = GetUnitCurrentOrder( this.u )
local real oldangle = GetUnitFacing( this.u )
call SetUnitX( this.u, this.x )
call SetUnitY( this.u, this.y )
call SetUnitFacingTimed( this.u, this.angle, 0.0 )
call this.ReorderUnit()
set this.x = x
set this.y = y
set this.angle = oldangle
set this.current = currentorder
call TimerStart( this.WaitTimer, 0.001, false, function ForSpell.SpellWait_End )
endmethod
method SpellWait takes real abwait returns nothing
local real time = 0.0
if this != 0 then
set this.iswait = true
set time = abwait
set this.x = GetUnitX( this.u )
set this.y = GetUnitY( this.u )
if this.U.speed != 0.0 then
call DisableTrigger( UnitsOrder )
call IssueImmediateOrderById( this.u, UnitStopOrder )
call EnableTrigger( UnitsOrder )
set time = time / this.U.speed
call TimerStart( this.WaitTimer, time, false, function ForSpell.SpellWait_Action )
else
call this.Delete()
endif
endif
endmethod
endstruct
struct Unit
real speed
real spellbefore
real abbefore
AbilTypeHash ATH
integer order
widget target
real targetx
real targety
integer orderkind
boolean spelling
unit u
ForSpell Spell
static method Get takes timer t returns Unit
return ForSpell.Get( t ).U
endmethod
static method create takes unit u, real spellbefore, real speed returns Unit
local Unit this = Unit.allocate()
set this.u = u
set this.speed = speed
set this.spellbefore = spellbefore
set this.abbefore = 0.0
set this.order = 0
set this.target = null
set this.targetx = 0.0
set this.targety = 0.0
set this.spelling = false
if this.Spell != 0 then
call this.Spell.Delete()
set this.Spell = 0
endif
call SetUnitData( u, this )
return this
endmethod
static method New takes unit u returns Unit
local UnitTypeHash UT = GetUnitTypeData( GetUnitTypeId( u ) )
if UT != -1 then
return Unit.create( u, UT.spellbefore, UT.initspeed )
endif
return 0
endmethod
method Delete takes nothing returns nothing
call FlushUnitData( this.u )
set this.u = null
set this.speed = 0.0
set this.spellbefore = 0.0
set this.abbefore = 0.0
set this.order = 0
set this.target = null
set this.targetx = 0.0
set this.targety = 0.0
set this.spelling = false
call this.Spell.Delete()
set this.Spell = 0
set this.ATH = 0
call this.destroy()
endmethod
method ReorderUnit takes nothing returns nothing
call DisableTrigger( UnitsOrder )
call DisableTrigger( UnitsSpell )
if this.orderkind == UnitOrderKind_Target then
call IssueTargetOrderById( this.u, this.order, this.target )
elseif this.orderkind == UnitOrderKind_Point then
call IssuePointOrderById( this.u, this.order, this.targetx, this.targety )
elseif this.orderkind == UnitOrderKind_Immediate then
call IssueImmediateOrderById( this.u, this.order )
endif
call EnableTrigger( UnitsSpell )
call EnableTrigger( UnitsOrder )
endmethod
static method UnitSpellChannel_End takes nothing returns nothing
local Unit this = Unit.Get( GetExpiredTimer() )
local real mana = GetUnitState( this.u, UNIT_STATE_MANA )
local ForSpell temp = 0
if this.ATH.spellwait <= 0.0 then
call SetUnitState( this.u, UNIT_STATE_MANA, GetUnitState( this.u, UNIT_STATE_MAX_MANA ) )
call this.ReorderUnit()
call SetUnitState( this.u, UNIT_STATE_MANA, mana )
else
set temp = ForSpell.create( this )
call temp.SpellWait( this.ATH.spellwait )
endif
set this.spelling = false
call this.Spell.Delete()
endmethod
static method UnitSpellChannel_Stop takes nothing returns nothing
local Unit this = Unit.Get( GetExpiredTimer() )
local real time = this.spellbefore + this.abbefore
if this.speed != 0.0 then
set time = time / this.speed
else
set time = 0.0
endif
if time != 0.0 then
call DisableTrigger( UnitsOrder )
call DisableTrigger( UnitsSpell )
call IssueImmediateOrderById( this.u, Unit_LoopingSpellAbilityOrder )
call EnableTrigger( UnitsSpell )
call EnableTrigger( UnitsOrder )
call TimerStart( this.Spell.WaitTimer, time, false, function Unit.UnitSpellChannel_End )
else
call Unit.UnitSpellChannel_End()
endif
endmethod
method UnitSpellChannel takes AbilTypeHash ATH returns nothing
local real abbefore = ATH.spellbefore
local real time = this.spellbefore + abbefore
local ForSpell temp = 0
set this.abbefore = abbefore
if this.speed != 0.0 then
set time = time / this.speed
else
set time = 0.0
endif
if this != 0 and this.spelling == false then
if this.abbefore >= 0.0 then
if time > 0.0 then
call DisableTrigger( UnitsOrder )
call IssueImmediateOrderById( this.u, UnitStopOrder )
call EnableTrigger( UnitsOrder )
set this.spelling = true
set this.abbefore = abbefore
set this.Spell = ForSpell.create( this )
set this.ATH = ATH
call TimerStart( this.Spell.WaitTimer, 0.0, false, function Unit.UnitSpellChannel_Stop )
endif
else
if ATH.spellwait > 0.0 then
set temp = ForSpell.create( this )
call temp.SpellWait( ATH.spellwait )
endif
endif
endif
endmethod
method Stop takes nothing returns nothing
if this.spelling == true then
set this.spelling = false
call this.Spell.Delete()
endif
endmethod
//SpellBefore
endstruct
function UnitsSpell_Condition takes nothing returns boolean
local unit u = GetTriggerUnit()
local Unit U = GetUnitData( u )
local real abbefore = 0.0
local AbilTypeHash A = GetAbilTypeData( GetSpellAbilityId() )
if U != -1 and A != -1 then
call U.UnitSpellChannel( A )
endif
set u = null
return false
endfunction
function UnitsOrder_Condition takes nothing returns boolean
local unit u = GetTriggerUnit()
local Unit U = GetUnitData( u )
local eventid e = GetTriggerEventId()
if U != -1 then
if GetIssuedOrderId() == Unit_LoopingSpellAbilityOrder then
call DisableTrigger( UnitsOrder )
call IssueImmediateOrderById( u, UnitStopOrder )
call EnableTrigger( UnitsOrder )
else
if U.spelling then
call U.Stop()
endif
if e == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
set U.orderkind = UnitOrderKind_Target
set U.target = GetOrderTarget()
elseif e == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
set U.orderkind = UnitOrderKind_Point
set U.targetx = GetOrderPointX()
set U.targety = GetOrderPointY()
elseif e == EVENT_PLAYER_UNIT_ISSUED_ORDER then
set U.orderkind = UnitOrderKind_Immediate
endif
set U.order = GetIssuedOrderId()
endif
endif
set u = null
set e = null
return false
endfunction
//Init-------------------
function InitTypeData takes nothing returns nothing
call SetUnitTypeData( 'Otch', 0.80, 1.0 )
call SetAbilTypeData( 'AOws', 0.95, -1.0 )
call SetAbilTypeData( 'AOsh', 0.0, 2.0 )
endfunction
function UnitAllTimerInit takes nothing returns nothing
local integer i = 0
local ForSpell s = 0
loop
exitwhen i > ForSpell.TimerNumber
set s = i
set s.WaitTimer = CreateTimer()
set i = i + 1
endloop
set s = 0
set ForSpell.MinTimerHandle = H2I( s.WaitTimer )
call TimerStart( GetExpiredTimer(), 0.1, false, function InitTypeData )
endfunction
function Init takes nothing returns nothing
set UnitsOrder = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( UnitsOrder, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
call TriggerRegisterAnyUnitEventBJ( UnitsOrder, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
call TriggerRegisterAnyUnitEventBJ( UnitsOrder, EVENT_PLAYER_UNIT_ISSUED_ORDER )
call TriggerAddCondition( UnitsOrder, Condition( function UnitsOrder_Condition ) )
set UnitsSpell = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( UnitsSpell, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
call TriggerAddCondition( UnitsSpell, Condition( function UnitsSpell_Condition ) )
call TimerStart( CreateTimer(), 0.0, false, function UnitAllTimerInit )
endfunction
function InitUnit takes unit u returns nothing
call Unit.New( u )
endfunction
function DeleteUnit takes unit u returns nothing
local Unit U = GetUnitData( u )
if U != -1 then
call U.Delete()
endif
endfunction
function SetUnitSpellSpeed takes unit u, real speed returns nothing
local Unit U = GetUnitData( u )
if U != -1 then
if speed < 0.0 then
set U.speed = 0.0
else
set U.speed = speed
endif
endif
endfunction
function GetUnitSpellSpeed takes unit u returns real
local Unit U = GetUnitData( u )
if U != -1 then
return U.speed
endif
return -1.0
endfunction
function SetUnitSpellBefore takes unit u, real before returns nothing
local Unit U = GetUnitData( u )
if U != -1 then
if before < 0.0 then
set U.spellbefore = 0.0
else
set U.spellbefore = before
endif
endif
endfunction
function GetUnitSpellBefore takes unit u returns real
local Unit U = GetUnitData( u )
if U != -1 then
return U.spellbefore
endif
return -1.0
endfunction
endlibrary
[/jass]
实际上一开始写的时候,是按只有前摇的想法写的。但是有了延迟之后就复杂多了。
因为延迟是不能把数据绑定在单位身上的………………
事实上延迟其实就是不让单位使用引导技能,等到时间结束之后,单位直接重新发布技能命令(坐标、角度什么的全部和刚发布技能时的一样),再返回之前的状态,Order也要恢复。
但是事实上这就造成了一个打断单位当前动作的小小的瑕疵。
比如我的系统地图中。你先使用冲击波(两秒延迟),等一会(1s左右),释放踩地板。
你会发现牛头很囧地不连贯地跺地板…………实际上就是一次打断的效果,只不过没有让牛头从头再来了。
最后把自己用来绑定数据的HashTable和大家分享一下:
[jass]
struct $HashType$Hash
static integer $HashType$Hash_I = 8190
$HashType$Hash head
$HashType$Hash before
$HashType$Hash next
integer i
static method $HashType$Hash takes integer i returns integer
return i - i / $HashType$Hash.$HashType$Hash_I * $HashType$Hash.$HashType$Hash_I
endmethod
static method create takes integer i, ...... returns $HashType$Hash
local $HashType$Hash H = $HashType$Hash.allocate()
set H.i = i
......//init values
return H
endmethod
static method New takes integer i, ...... returns $HashType$Hash
local $HashType$Hash index = $HashType$Hash.$HashType$Hash( i )
local $HashType$Hash head = index.head
local $HashType$Hash new = $HashType$Hash.create( i, ...... )
set new.next = head
set index.head = new
if head != 0 then
set head.before = new
endif
return new
endmethod
static method Get takes integer i returns $HashType$Hash
local $HashType$Hash index = $HashType$Hash.$HashType$Hash( i )
local $HashType$Hash head = index.head
local $HashType$Hash H = head
loop
exitwhen H == 0
if H.i == i then
return H
endif
set H = H.next
endloop
return 0
endmethod
method Delete takes nothing returns nothing
local $HashType$Hash index = $HashType$Hash.$HashType$Hash( this.i )
local $HashType$Hash before = this.before
local $HashType$Hash next = this.next
if before == 0 then
set index.head = next
else
set before.next = next
endif
if next != 0 then
set next.before = before
endif
set this.before = 0
set this.next = 0
set this.i = 0
......//flush values
call this.destroy()
endmethod
endstruct
function Set$HashType$Data takes integer id, ...... returns nothing
local integer UH = id
local $HashType$Hash H = $HashType$Hash.Get( UH )
if H == 0 then
set H = H.New( UH, ...... )
else
......//set values
endif
endfunction
function Get$HashType$Data takes integer id returns $HashType$Hash
local integer UH = id
local $HashType$Hash H = $HashType$Hash.Get( UH )
if H == 0 then
return -1
else
return H
endif
endfunction
function Flush$HashType$Data takes integer id returns nothing
local integer UH = id
local $HashType$Hash H = $HashType$Hash.Get( UH )
if H != 0 then
call H.Delete()
endif
endfunction
[/jass]
如何使用:
copy到记事本里。用替换,把“$HashType$”替换为你的Hash的名称。
然后在“......”处填上你自己的数据类型 或者 init数据or flush数据的代码。
这样就行了。
施法延迟/前摇系统使用手册:
在一开始的globals里填写:
[jass]
constant integer Unit_LoopingSpellAbilityId = //引导技能的ID
constant integer Unit_LoopingSpellAbilityOrder = //释放引导技能的Order(无目标)
[/jass]
顺便说下,我的演示里用的是Channel(通魔)做引导技能,原因是它的图标不可见但是仍然可以释放Order。
我也推荐大家用这个。
当然Order需要改成没有用过的冷门Order。而不是我这样直接"channel"的OrderId了。
然后在这里填写:
[jass]
function InitTypeData takes nothing returns nothing
call SetUnitTypeData( 'Otch', 0.80, 1.0 )
call SetAbilTypeData( 'AOws', 0.95, -1.0 )
call SetAbilTypeData( 'AOsh', 0.0, 2.0 )
endfunction
[/jass]
SetUnitTypeData( UnitId, SpellBefore, InitSpeed )
SetAbilTypeData( AbilId, SpellBefore, SpellWait, TrueKind )
吟唱时间=( UnitSpellBefore + AbilSpellBefore ) / UnitSpeed
延迟时间=AbilSpellWait / UnitSpeed
当时间<=0.0时,系统会直接略过对应的阶段。
当速度等于0时,单位会瞬发技能(也就是说我的系统会什么也不做。)
AbilTrueKind这个玩意是为了把一些即可以PointOrder又可以TargetOrder(包括Immediate……)的技能在发布命令时不受某些因素影响而出Bug弄的东西。
Warning!这个东西最好直接设成-1就行了。
如果要让一个单位受到此系统的影响,初始化他(比如单位进入区域:完整地图区域)。
call InitUnit( unit )
如果一个单位死亡(不太严谨),要删除他的数据,
call DeleteUnit( unit )
这些都是使用者要自己编写的~
[jass]
function SetUnitSpellSpeed takes unit u, real speed returns nothing
function GetUnitSpellSpeed takes unit u returns real
function SetUnitSpellBefore takes unit u, real before returns nothing
function GetUnitSpellBefore takes unit u returns real
[/jass]
这四个函数看字面意思就知道是什么意思了吧。
不过这些是设置之后,再释放的技能才会起作用的数据。
也就是说,如果一个单位正在吟唱技能,那么你不管把数据设成什么都不会影响吟唱的时间。
但是会影响吟唱之后延迟的时间。
用的是VJ,WEHelper。 |
|