1. library UnitStatus initializer Init requires TimerUtils, AutoIndex, optional xebasic
  2. //******************************************************************************
  3. //* BY: Rising_Dusk
  4. //*
  5. //* This library exists because oftentimes, a mapmaker needs to apply a specific
  6. //* status effect to a unit. If he were to do it on his own, he'd need to find a
  7. //* way to get the status effects to stack properly with one another and with
  8. //* multiple instances of themselves. This script does just that with the five
  9. //* most useful status options in WC3 that cannot be reproduced perfectly with
  10. //* just code and not actual in-game buffs.
  11. //*
  12. //* WARNING: This library uses the following buffs. If you use any of the listed
  13. //* buffs in your map, they will not stack with this script's buffs and
  14. //* it may not work. If you need these effects, then only use this
  15. //* script to achieve them.
  16. //* - Drunken Haze
  17. //* - Soul Burn
  18. //* - Ensnare
  19. //* - Storm Bolt
  20. //* - Hurl Boulder
  21. //*
  22. //******************************************************************************
  23. //*
  24. //* The following status effects are supported by this library.
  25. //*
  26. //* - Silence
  27. //* This status disables a given unit's ability to cast spells. This works
  28. //* properly on spell immune and normal units.
  29. //*
  30. //* Example Usage:
  31. //* call SilenceUnit(u, true) //Silences a unit
  32. //* call SilenceUnit(u, false) //Removes silence from a unit
  33. //* call SilenceUnitTimed(u, 4.0) //Adds a timed silence to a unit
  34. //*
  35. //* - Disarm
  36. //* This status disables a given unit's ability to attack and attack ground.
  37. //* This will not work properly on spell immune units because of a Blizzard
  38. //* bug. The function calls will return false when used on units that are
  39. //* spell immune. This status disables both ranged and melee attacks.
  40. //*
  41. //* Example Usage:
  42. //* call DisarmUnit(u, true) //Disarms a unit
  43. //* call DisarmUnit(u, false) //Removes disarm from a unit
  44. //* call DisarmUnitTimed(u, 4.0) //Adds a timed disarm to a unit
  45. //*
  46. //* - Ensnare
  47. //* This status disables a given unit's ability to move. This works properly
  48. //* on spell immune and normal units. Because of the nature of the ability
  49. //* this is based upon, it will exhibit strange behavior on flying units. If
  50. //* this is used on flying units, they will retain their flying height, but
  51. //* be treated as ground units by the game. There is unfortunately no
  52. //* workaround for this behavior. It is recommended to not use it on flying
  53. //* units because for that reason.
  54. //*
  55. //* Example Usage:
  56. //* call EnsnareUnit(u, true) //Ensnares a unit
  57. //* call EnsnareUnit(u, false) //Removes ensnare from a unit
  58. //* call EnsnareUnitTimed(u, 4.0) //Adds a timed ensnare to a unit
  59. //*
  60. //* - Stun
  61. //* This status is identical in nature to the standard melee stun. A stunned
  62. //* unit cannot move, attack, or cast spells. Both spell immune and normal
  63. //* units can be stunned.
  64. //*
  65. //* Example Usage:
  66. //* call StunUnit(u, true) //Ensnares a unit
  67. //* call StunUnit(u, false) //Removes stun from a unit
  68. //* call StunUnitTimed(u, 4.0) //Adds a timed stun to a unit
  69. //*
  70. //* - Disable
  71. //* Disable is a unique status meant to be used as a replacement for pausing a
  72. //* unit using the PauseUnit native. Pausing a unit has the negative side
  73. //* effects of removing the unit's command card and not preserving queued
  74. //* orders. Disabling a unit retains both of those sought features. Disable is
  75. //* a non-graphical stun at its core that is always 'underneath' stun when
  76. //* both are applied on a unit at once as listed below.
  77. //*
  78. //* Disable also interacts with stun in a unique way.
  79. //* - If stun is used on a disabled unit, the unit becomes stunned instead.
  80. //* - If stun ends on a disabled unit, the unit is disabled until the disable
  81. //* ends.
  82. //* - If disable is used on a stunned unit, the unit remains visibly stunned.
  83. //*
  84. //* Example Usage:
  85. //* call DisableUnit(u, true) //Disables a unit
  86. //* call DisableUnit(u, false) //Removes disable from a unit
  87. //* call DisableUnitTimed(u, 4.0) //Adds a timed disable to a unit
  88. //*
  89. //* WARNING: These status effects, when used on invulnerable units, will have
  90. //* absolutely no effect.
  91. //*
  92. //******************************************************************************
  93. //*
  94. //* There is a textmacro call below that runs a series of ObjectMerger calls
  95. //* inside of an embedded .lua script. This sub-script generates all of the
  96. //* abilities and buffs required by this library automatically for you. Enable
  97. //* it by uncommenting the textmacro, saving your map, closing your map,
  98. //* reopening your map, and commenting the macro again.
  99. //*
  100. //* Note that you, as the user, may edit any of the buff icons or tooltips to
  101. //* your liking. It is not recommended to edit the data fields for the spells,
  102. //* though. They are the way they are so that they will work.
  103. //*
  104. //* WARNING: The ObjectMerger call for Disarm's ability seems to be unable to
  105. //* properly configure the "Data - Attacks Prevented" field. If you
  106. //* find that the DisarmUnit call isn't functioning as intended, then
  107. //* set that field to "None", save your map, set the field back to
  108. //* "Melee, Ranged", and then save your map again. It should now work
  109. //* properly.
  110. //*
  111. //* You may change the raw ids of any of the generated abilities as needed for
  112. //* your map. (If there are conflicts) If you want to do so, then make sure that
  113. //* you change the raw ids inside all affected ObjectMerger calls and the
  114. //* constants in the globals block below.
  115. //*
  116. //* WARNING: If you choose to change the raw id for the Silence ability, do NOT
  117. //* let the buff raw id match the ability raw id. If you do, the buff's
  118. //* special effect fields will not show in-game. (This may or may not
  119. //* even affect you, but it is worth noting regardless)
  120. //*
  121. //* xebasic is an optional requirement. If you have xebasic in your map, this
  122. //* script will use xebasic's dummy unit id instead of the constant below. If
  123. //* you do not have xebasic in your map, for this to work you will need to make
  124. //* (if you have not already) a dummy unit caster for your map. Put its raw id
  125. //* below in the DUMMY_UNITID constant field.
  126. //*
  127. //* Enjoy!
  128. //*
  129. //* Uncomment the following textmacro to create the abilities.
  130. ////! runtextmacro GenerateAbilities()
  131. //! textmacro GenerateAbilities
  132. //! externalblock extension=lua ObjectMerger $FILENAME$
  133. //! i
  134. //! i function set(field, value)
  135. //! i makechange(current, field, value)
  136. //! i end
  137. //! i function setl(field, level, value)
  138. //! i makechange(current, field, level, value)
  139. //! i end
  140. //! i
  141. //! i setobjecttype("abilities")
  142. //! i
  143. //! i createobject("AHtb", "stun"); set("anam", "Stun Ability")
  144. //! i set ("aani", "") ; set ("aart", "") ; set ("amat", "") ; set ("amsp", 0) ; setl("Htb1", 1, 0.0);
  145. //! i setl("aran", 1, 99999.0); setl("acdn", 1, 0.0); setl("ahdu", 1, 0.0) ; setl("adur", 1, 0.0) ; set ("arlv", 6) ;
  146. //! i set ("alev", 1) ; setl("amcs", 1, 0) ; set ("arac", "other"); setl("atar", 1, "notself");
  147. //! i
  148. //! i createobject("ANso", "&sil"); set("anam", "Silence Ability")
  149. //! i set ("aart", "") ; setl("Nso1", 1, 0.0); setl("Nso3", 1, 0.0) ; setl("Nso2", 1, 99999.0) ; setl("aran", 1, 99999.0);
  150. //! i setl("abuf", 1, "&SIL"); setl("acdn", 1, 0.0); setl("ahdu", 1, 0.0) ; setl("adur", 1, 0.0) ; set ("arlv", 1);
  151. //! i set ("alev", 1) ; setl("amcs", 1, 0) ; set ("arac", "other"); setl("atar", 1, "notself");
  152. //! i
  153. //! i createobject("ANdh", "&arm"); set("anam", "Disarm Ability")
  154. //! i set ("aart", "") ; set ("amat", "") ; set ("amac", 0.0) ; set ("amsp", 0) ; setl("Nsi1", 1, 3) ;
  155. //! i setl("Nsi2", 1, 0.0) ; setl("Nsi3", 1, 0.0) ; setl("aare", 1, 0.0); setl("aran", 1, 99999.0); setl("abuf", 1, "&ARM");
  156. //! i setl("acdn", 1, 0.0) ; setl("ahdu", 1, 0.0) ; setl("adur", 1, 0.0); set ("arlv", 6) ; set ("alev", 1) ;
  157. //! i setl("amcs", 1, 0) ; set ("arac", "other") ; setl("atar", 1, "notself");
  158. //! i
  159. //! i createobject("ACen", "&ens"); set("anam", "Ensnare Ability")
  160. //! i set ("aart", "") ; set ("amat", "") ; set ("amsp", 0) ; setl("Ens1", 1, -1.0); setl("Ens2", 1, -1.0);
  161. //! i setl("aran", 1, 99999.0) ; setl("abuf", 1, "&EN1,&EN2"); setl("acdn", 1, 0.0); setl("ahdu", 1, 0.0) ; set ("aher", 1) ;
  162. //! i setl("adur", 1, 0.0) ; set ("arlv", 6) ; set ("alev", 1) ; setl("amcs", 1, 0) ; set ("arac", "other");
  163. //! i setl("atar", 1, "notself"); set ("areq", "") ; set("ansf", "") ;
  164. //! i
  165. //! i createobject("ACtb", "&dis"); set("anam", "Disable Ability")
  166. //! i set ("aani", "") ; set ("aart", "") ; set ("amat", "") ; set ("amsp", 0) ; setl("Ctb1", 1, 0.0) ;
  167. //! i setl("aran", 1, 99999.0) ; setl("abuf", 1, "&DIS"); setl("acdn", 1, 0.0); setl("ahdu", 1, 0.0); setl("adur", 1, 0.0) ;
  168. //! i set ("aher", 1) ; set ("arlv", 6) ; set ("alev", 1) ; setl("amcs", 1, 0) ; set ("arac", "other");
  169. //! i setl("atar", 1, "notself");
  170. //! i
  171. //! i setobjecttype("buffs")
  172. //! i
  173. //! i createobject("BNso", "&SIL"); set("fnam", "Disabled (Spells)")
  174. //! i set("ftip", "Disabled (Spells)") ; set("fube", "This unit cannot cast spells.");
  175. //! i set("fart", "ReplaceableTextures\\CommandButtons\\BTNCancel.blp"); set("ftat", "") ; set("fta0", "");
  176. //! i
  177. //! i createobject("BNdh", "&ARM"); set("fnam", "Disabled (Attacks)")
  178. //! i set("ftip", "Disabled (Attacks)") ; set("fube", "This unit cannot attack.");
  179. //! i set("fart", "ReplaceableTextures\\CommandButtons\\BTNCancel.blp"); set("ftat", "") ; set("fta0", "");
  180. //! i
  181. //! i createobject("Beng", "&EN1"); set("fnam", "Disabled (Movement)")
  182. //! i set("ftip", "Disabled (Movement)") ; set("fube", "This unit cannot move." );
  183. //! i set("fart", "ReplaceableTextures\\CommandButtons\\BTNCancel.blp"); set("ftat", "") ; set("frac", "other");
  184. //! i
  185. //! i createobject("Bena", "&EN2"); set("fnam", "Disabled (Movement)")
  186. //! i set("ftip", "Disabled (Movement)") ; set("fube", "This unit cannot move.");
  187. //! i set("fart", "ReplaceableTextures\\CommandButtons\\BTNCancel.blp"); set("ftat", "") ; set("fta0", "");
  188. //! i set("frac", "other");
  189. //! i
  190. //! i createobject("BPSE", "&DIS"); set("fnam", "Disabled")
  191. //! i set("ftip", "Disabled") ; set("fube", "This unit cannot do anything.");
  192. //! i set("fart", "ReplaceableTextures\\CommandButtons\\BTNCancel.blp"); set("ftat", "") ; set("fta0", "")
  193. //! i
  194. //! endexternalblock
  195. //! endtextmacro GenerateAbilities
  196. globals
  197. //General constants
  198. private constant integer DUMMY_UNITID = 'e000' //Replace this with your dummy's id
  199. //Stun constants
  200. private constant integer STUN_ID = 'stun' //This needs to match the ObjectMerger call above
  201. private constant integer STUN_ORDER_ID = 852095 //Order id to stun a unit
  202. private constant integer STUN_BUFF_ID = 'BPSE' //Normal storm bolt stun buff
  203. //Silence constants
  204. private constant integer SILENCE_ID = '&sil' //This needs to match the ObjectMerger call above
  205. private constant integer SILENCE_ORDER_ID = 852668 //Order id to soul burn a unit
  206. private constant integer SILENCE_BUFF_ID = '&SIL' //Generated soul burn based buff
  207. //Disarm constants
  208. private constant integer DISARM_ID = '&arm' //This needs to match the ObjectMerger call above
  209. private constant integer DISARM_ORDER_ID = 852585 //Order id to drunken haze a unit
  210. private constant integer DISARM_BUFF_ID = '&ARM' //Generated drunken haze based buff
  211. //Ensnare constants
  212. private constant integer ENSNARE_ID = '&ens' //This needs to match the ObjectMerger call above
  213. private constant integer ENSNARE_ORDER_ID = 852106 //Order id to ensnare a unit
  214. private constant integer ENSNARE_BUFF_ID = '&EN1' //Generated ensnare based buff (ground)
  215. private constant integer ENSNARE_BUFF_ID2 = '&EN2' //Generated ensnare based buff (air)
  216. //Disable constants
  217. private constant integer DISABLE_ID = '&dis' //This needs to match the ObjectMerger call above
  218. private constant integer DISABLE_ORDER_ID = 852252 //Order id to hurl boulder at a unit
  219. private constant integer DISABLE_BUFF_ID = '&DIS' //Generated hurl boulder based buff
  220. endglobals
  221. globals
  222. private unit Caster = null //Dummy caster
  223. private timer Temp = null //For callback referencing
  224. endglobals
  225. //******************************************************************************
  226. //! textmacro UnitStatus_GenerateBase takes TYPE, CONSTANT, STRUCTNAME, INJECTPOSTBUFF, INJECTCHECK, INJECTPREBUFF
  227. private struct clear$TYPE$
  228. unit u = null
  229. //Exists only for clearing on Add/Remove situations
  230. static method create takes unit t returns thistype
  231. local thistype c = thistype.allocate()
  232. set c.u = t
  233. return c
  234. endmethod
  235. endstruct
  236. private function TimerRemove$TYPE$ takes nothing returns nothing
  237. local clear$TYPE$ c = clear$TYPE$(GetTimerData(GetExpiredTimer()))
  238. local unit whichUnit = c.u
  239. if $TYPE$Counter[GetUnitId(c.u)] == 0 then
  240. //Make sure we should still remove it
  241. call UnitRemoveAbility(c.u, $CONSTANT$_BUFF_ID)
  242. $INJECTPOSTBUFF$
  243. endif
  244. call ReleaseTimer(GetExpiredTimer())
  245. call c.destroy()
  246. set whichUnit = null
  247. endfunction
  248. function $TYPE$Unit takes unit whichUnit, boolean flag returns boolean
  249. local integer id = GetUnitId(whichUnit)
  250. local boolean b = true
  251. if whichUnit == null then
  252. //Target can't be null
  253. debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null unit given to $TYPE$Unit")
  254. return false
  255. endif
  256. if flag then
  257. if $TYPE$Counter[id] == 0 $INJECTCHECK$ then
  258. //Buff the unit
  259. $INJECTPREBUFF$
  260. call UnitShareVision(whichUnit, GetOwningPlayer(Caster), true)
  261. set b = IssueTargetOrderById(Caster, $CONSTANT$_ORDER_ID, whichUnit)
  262. call UnitShareVision(whichUnit, GetOwningPlayer(Caster), false)
  263. endif
  264. if not b then
  265. //Cast failed somehow
  266. debug call BJDebugMsg(SCOPE_PREFIX+"Error: Unit could not be buffed ($TYPE$Unit)")
  267. return false
  268. endif
  269. set $TYPE$Counter[id] = $TYPE$Counter[id] + 1
  270. elseif $TYPE$Counter[id] > 0 then //Only run this if unit is buffed at all
  271. //Decrement Counter
  272. set $TYPE$Counter[id] = $TYPE$Counter[id] - 1
  273. if $TYPE$Counter[id] == 0 then
  274. //Clear the buff
  275. if GetUnitAbilityLevel(whichUnit, $CONSTANT$_BUFF_ID) == 0 then
  276. //Remove it in a 0.01s callback because it hasn't been applied yet
  277. set Temp = NewTimer()
  278. call SetTimerData(Temp, integer(clear$TYPE$.create(whichUnit)))
  279. call TimerStart(Temp, 0.01, false, function TimerRemove$TYPE$)
  280. else
  281. call UnitRemoveAbility(whichUnit, $CONSTANT$_BUFF_ID)
  282. $INJECTPOSTBUFF$
  283. endif
  284. endif
  285. else
  286. //Unit doesn't have the buff we're trying to remove
  287. return false
  288. endif
  289. return true
  290. endfunction
  291. private struct $STRUCTNAME$
  292. timer t
  293. unit tar
  294. real dur
  295. private static method end takes nothing returns nothing
  296. call thistype(GetTimerData(GetExpiredTimer())).destroy()
  297. endmethod
  298. static method start takes unit target, real duration returns boolean
  299. local thistype s
  300. local boolean b = $TYPE$Unit(target, true)
  301. if not b then
  302. //Failed, return false
  303. return false
  304. endif
  305. set s = thistype.allocate()
  306. set s.tar = target
  307. set s.dur = duration
  308. set s.t = NewTimer()
  309. call SetTimerData(s.t, integer(s))
  310. call TimerStart(s.t, duration, false, function $STRUCTNAME$.end)
  311. return true
  312. endmethod
  313. private method onDestroy takes nothing returns nothing
  314. call $TYPE$Unit(.tar, false)
  315. call ReleaseTimer(.t)
  316. endmethod
  317. endstruct
  318. function $TYPE$UnitTimed takes unit whichUnit, real duration returns boolean
  319. if duration <= 0.01 then
  320. debug call BJDebugMsg(SCOPE_PREFIX+"Error: Less than 0.01 duration given to $TYPE$UnitTimed")
  321. return false
  322. endif
  323. return $STRUCTNAME$.start(whichUnit, duration)
  324. endfunction
  325. //! endtextmacro
  326. //******************************************************************************
  327. //Declare all necessary globals first
  328. globals
  329. private integer array StunCounter
  330. private integer array SilenceCounter
  331. private integer array DisarmCounter
  332. private integer array EnsnareCounter
  333. private integer array DisableCounter
  334. endglobals
  335. //Special functions for Stun
  336. private function StunLingeringDisable takes unit u returns nothing
  337. local integer id = GetUnitId(u)
  338. if DisableCounter[id] > 0 then
  339. //Add the disabled buff because it lingers on
  340. call UnitShareVision(u, GetOwningPlayer(Caster), true)
  341. call IssueTargetOrderById(Caster, DISABLE_ORDER_ID, u)
  342. call UnitShareVision(u, GetOwningPlayer(Caster), false)
  343. endif
  344. endfunction
  345. private function StunFinishDisable takes unit u returns nothing
  346. local integer id = GetUnitId(u)
  347. if DisableCounter[id] > 0 then
  348. //Remove the disable buff for first refcount stun
  349. call UnitRemoveAbility(u, DISABLE_BUFF_ID)
  350. endif
  351. endfunction
  352. //Generates all of the base code
  353. //! runtextmacro UnitStatus_GenerateBase("Stun" , "STUN" , "stun" , "call StunLingeringDisable(whichUnit)", "", "call StunFinishDisable(whichUnit)")
  354. //! runtextmacro UnitStatus_GenerateBase("Silence", "SILENCE", "silence", "", "", "")
  355. //! runtextmacro UnitStatus_GenerateBase("Disarm" , "DISARM" , "disarm" , "", "", "")
  356. //! runtextmacro UnitStatus_GenerateBase("Ensnare", "ENSNARE", "ensnare", "call UnitRemoveAbility(whichUnit, ENSNARE_BUFF_ID2)", "", "")
  357. //! runtextmacro UnitStatus_GenerateBase("Disable", "DISABLE", "disable", "", "and StunCounter[id] == 0", "")
  358. private function Init takes nothing returns nothing
  359. static if LIBRARY_xebasic then
  360. set Caster = CreateUnit(Player(15), XE_DUMMY_UNITID, 0., 0., 0.)
  361. else
  362. set Caster = CreateUnit(Player(15), DUMMY_UNITID , 0., 0., 0.)
  363. endif
  364. call UnitRemoveAbility(Caster, 'Amov')
  365. if GetUnitAbilityLevel(Caster, 'Aloc') == 0 then
  366. call UnitAddAbility(Caster, 'Aloc') //xe dummies don't have this automatically
  367. endif
  368. //Add the abilities
  369. call UnitAddAbility(Caster, STUN_ID)
  370. call UnitAddAbility(Caster, SILENCE_ID)
  371. call UnitAddAbility(Caster, DISARM_ID)
  372. call UnitAddAbility(Caster, ENSNARE_ID)
  373. call UnitAddAbility(Caster, DISABLE_ID)
  374. endfunction
  375. endlibrary

Diesen Code in Standard-Formatierung anzeigen
goto line:
Compare with:
text copy window edit this code post new code