Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Here are some constants which I will not use in the abilities section.
library constants
globals
public unit spawnedTF
public constant integer FOOTMAN_ID='hfoo'
public constant integer PALADIN_ID='Hpal'
public constant player RED=Player(0)
public constant player OPPONENT=Player(1)
endglobals
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope init initializer i
private function after takes nothing returns nothing
set constants_spawnedTF=CreateUnit(constants_RED,tfConsts_TF_ID,0.,0.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,0.,500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,0.,1500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,0.,1500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,0.,1500.,0.)
call CreateUnit(constants_OPPONENT,constants_PALADIN_ID,1000.,1500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,1000.,1500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,1000.,1500.,0.)
call CreateUnit(constants_OPPONENT,constants_PALADIN_ID,1000.,2500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,1000.,2500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,1000.,2500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,1000.,2500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,1000.,2500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,1000.,2500.,0.)
call CreateUnit(constants_OPPONENT,constants_FOOTMAN_ID,1000.,2500.,0.)
call DestroyTimer(GetExpiredTimer())
endfunction
private function i takes nothing returns nothing
call TimerStart(CreateTimer(),0.,false,function after)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope detectEscape initializer i
private function c takes nothing returns boolean
call SetHeroLevel(constants_spawnedTF,GetHeroLevel(constants_spawnedTF)+1,true)
return false
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call DisplayTextToPlayer(GetLocalPlayer(),0.,0.,"Press escape to instantly level up twisted fate")
call TriggerRegisterPlayerEvent(t,constants_RED,EVENT_PLAYER_END_CINEMATIC)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
//==============================================================================
// TEXT TAG - Floating text system by Cohadar - v5.0
//==============================================================================
//
// PURPOUSE:
// * Displaying floating text - the easy way
// * Has a set of useful and commonly needed texttag functions
//
// CREDITS:
// * DioD - for extracting proper color, fadepoint and lifespan parameters
// for default warcraft texttags (from miscdata.txt)
//
// HOW TO IMPORT:
// * Just create a trigger named TextTag
// convert it to text and replace the whole trigger text with this one
//==============================================================================
library TextTag
globals
// for custom centered texttags
private constant real MEAN_CHAR_WIDTH = 5.5
private constant real MAX_TEXT_SHIFT = 200.0
private constant real DEFAULT_HEIGHT = 16.0
// for default texttags
private constant real SIGN_SHIFT = 16.0
private constant real FONT_SIZE = 0.024
private constant string MISS = "miss"
endglobals
//===========================================================================
// Custom centered texttag on (x,y) position
// color is in default wc3 format, for example "|cFFFFCC00"
//===========================================================================
public function XY takes real x, real y, string text, string color returns nothing
local texttag tt = CreateTextTag()
local real shift = RMinBJ(StringLength(text)*MEAN_CHAR_WIDTH, MAX_TEXT_SHIFT)
call SetTextTagText(tt, color+text, FONT_SIZE)
call SetTextTagPos(tt, x-shift, y, DEFAULT_HEIGHT)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.5)
call SetTextTagLifespan(tt, 4.0)
call SetTextTagPermanent(tt, false)
set tt = null
endfunction
//===========================================================================
// Custom centered texttag above unit
//===========================================================================
public function Unit takes unit whichUnit, string text, string color returns nothing
local texttag tt = CreateTextTag()
local real shift = RMinBJ(StringLength(text)*MEAN_CHAR_WIDTH, MAX_TEXT_SHIFT)
call SetTextTagText(tt, color+text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit)-shift, GetUnitY(whichUnit), DEFAULT_HEIGHT)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.5)
call SetTextTagLifespan(tt, 4.0)
call SetTextTagPermanent(tt, false)
set tt = null
endfunction
//===========================================================================
// Standard wc3 gold bounty texttag, displayed only to killing player
//===========================================================================
public function GoldBounty takes unit whichUnit, integer bounty, player killer returns nothing
local texttag tt = CreateTextTag()
local string text = "+" + I2S(bounty)
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit)-SIGN_SHIFT, GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 255, 220, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.03)
call SetTextTagVisibility(tt, GetLocalPlayer()==killer)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 3.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
//==============================================================================
public function LumberBounty takes unit whichUnit, integer bounty, player killer returns nothing
local texttag tt = CreateTextTag()
local string text = "+" + I2S(bounty)
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit)-SIGN_SHIFT, GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 0, 200, 80, 255)
call SetTextTagVelocity(tt, 0.0, 0.03)
call SetTextTagVisibility(tt, GetLocalPlayer()==killer)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 3.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
//===========================================================================
public function ManaBurn takes unit whichUnit, integer dmg returns nothing
local texttag tt = CreateTextTag()
local string text = "-" + I2S(dmg)
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit)-SIGN_SHIFT, GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 82, 82 ,255 ,255)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 5.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
//===========================================================================
public function Miss takes unit whichUnit returns nothing
local texttag tt = CreateTextTag()
call SetTextTagText(tt, MISS, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit), GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 255, 0, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.03)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 1.0)
call SetTextTagLifespan(tt, 3.0)
call SetTextTagPermanent(tt, false)
set tt = null
endfunction
//===========================================================================
public function CriticalStrike takes unit whichUnit, integer dmg returns nothing
local texttag tt = CreateTextTag()
local string text = I2S(dmg) + "!"
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit), GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 255, 0, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 5.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
//===========================================================================
public function ShadowStrike takes unit whichUnit, integer dmg, boolean initialDamage returns nothing
local texttag tt = CreateTextTag()
local string text = I2S(dmg)
if initialDamage then
set text = text + "!"
endif
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit), GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 160, 255, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 5.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.1
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
private hashtable ht = InitHashtable()
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
//TESH.scrollpos=30
//TESH.alwaysfold=0
//* Introduction:
//* StructuredDD is a straight forward damage detection system which utilizes buckets of units, each tied to a single trigger. Traditional damage detection
//* systems used a single trigger for each unit, which was completely unusable if one required any large amount of units to have DD capabilities. Later, another
//* system became the norm in which one trigger registered all units, and periodically rebuilt itself. This is extraordinarily inefficient and thus we have this
//* bucket method. StructuredDD aims to balance code eloquence with an easy to use and functional API, while simultaneously balancing code optimization with
//* readability and length.
//*
//* API:
//* boolean ADD_ALL_UNITS
//* integer BUCKET_SIZE
//* real PER_CLEANUP_TIMEOUT
//* static method addHandler
//* static method add
//*
//* Limitations:
//* -StructuredDD requires vJass
//* -StructuredDD is not the most optimized script (many linear searches)
//* -There is no support for deallocating handlers for dynamic use (though this is possible)
//* -There is no support for automatic periodic cleanup timeouts (though this is possible)
//* -There is no support for grouping heroes in the same buckets (for maps where heroes are never removed from the game) (though this is possible)
//* -There is no included "safe" method for damaging a unit - though it is possible, it is trivial for the client to do this in their own handler(s).
//* -There is no included method to check for physical/spell/code damage - that will be another library which requires StructuredDD. That library is already
//* written and must simply be finalized.
//* -Handlers are added in order to an array and are executed in order. If you have handlers that depend on each other, it is your job to avoid race conditions.
//*
//* Please see http://www.hiveworkshop.com/forums/jass-resources-412/system-structureddd-structured-damage-detection-216968/ for preface, design explanation,
//* detailed API, example usage, change log, and special thanks.
library StructuredDD
//We must declare the UnitAlive native because it is (as I understand) an AI native that is not by default available in JASS. Once declared, however, it is
//fully functional. UnitAlive can also be declared multiple times (in different libraries, for example) without any issues.
native UnitAlive takes unit u returns boolean
globals
///BEGIN CUSTOMIZE SECTION
//* Set this to true if you want all units in your map to be automatically added to StructuredDD. Otherwise you will have to manually add them with
//* StructuredDD_ddBucket.add(u) where u is some instantiated unit.
private constant boolean ADD_ALL_UNITS=true
//* This is the amount of units that exist in each trigger bucket. This number should be something between 5 and 30. A good starting value will be your
//* estimate of the map's average count of units, divided by 10. When in doubt, just use 20.
private constant integer BUCKET_SIZE=20
//* This is how often StructuredDD will search for empty buckets. If your map has units being created and dying often, a lower value is better. Anything
//* between 10. and 180. is good. When in doubt, just use 60.
private constant real PER_CLEANUP_TIMEOUT=60.
///END CUSTOMIZE SECTION
endglobals
public struct ddBucket
integer bucketIndex=0
trigger trig=CreateTrigger()
unit array members[BUCKET_SIZE]
private static boolexpr array conditions
private static ddBucket array bucketDB
private static integer conditionsIndex=-1
private static integer dbIndex=-1
private static integer maxDBIndex=-1
//public static method getMaxDBIndex takes nothing returns integer
// return .maxDBIndex
//endmethod
//* This method gets a readily available bucket for a unit to be added. If the "current" bucket is full, it returns a new one, otherwise it just returns
//* the current bucket.
private static method getBucket takes nothing returns integer
local integer index=0
local integer returner=-1
local ddBucket tempDat
if .dbIndex!=-1 and bucketDB[.dbIndex].bucketIndex<BUCKET_SIZE then
return .dbIndex
else
set .maxDBIndex=.maxDBIndex+1
set .dbIndex=.maxDBIndex
set tempDat=ddBucket.create()
set .bucketDB[.maxDBIndex]=tempDat
loop
exitwhen index>.conditionsIndex
call TriggerAddCondition(tempDat.trig,.conditions[index])
set index=index+1
endloop
return .dbIndex
endif
return -1
endmethod
//* This method is for adding a handler to the system. Whenever a handler is added, damage detection will immediately trigger that handler. There is no
//* way to deallocate a handler, so don't try to do this dynamically (!) Support for handler deallocation is feasible (please contact me)
public static method addHandler takes code func returns nothing
local ddBucket tempDat
local integer index=0
set .conditionsIndex=.conditionsIndex+1
set .conditions[.conditionsIndex]=Condition(func)
loop
exitwhen index>.maxDBIndex
set tempDat=.bucketDB[index]
call TriggerAddCondition(tempDat.trig,.conditions[.conditionsIndex])
set index=index+1
endloop
endmethod
//* This method adds a unit to the damage detection system. If ADD_ALL_UNITS is enabled, you don't have to (and should not) ever use this method. If you
//* add a unit that's already in the system, it will be added that second time and hence damage to it will fire all handlers twice. (so don't do that)
public static method add takes unit member returns nothing
local ddBucket tempDat
local integer whichBucket=getBucket()
set tempDat=bucketDB[whichBucket]
set tempDat.bucketIndex=tempDat.bucketIndex+1
set tempDat.members[tempDat.bucketIndex]=member
call TriggerRegisterUnitEvent(tempDat.trig,member,EVENT_UNIT_DAMAGED)
endmethod
//* This is just an auxillary function for ADD_ALL_UNITS' implementation
private static method autoAddC takes nothing returns boolean
call .add(GetTriggerUnit())
return false
endmethod
//* This method is used to check if a given bucket is empty (and thus can be deallocated) - this is an auxillary process of the periodic cleanup system.
private static method bucketIsEmpty takes integer which returns boolean
local ddBucket tempDat=.bucketDB[which]
local integer index=0
loop
exitwhen index==BUCKET_SIZE
if GetUnitTypeId(tempDat.members[index])!=0 then //GetUnitTypeId(unit)==0 means that the unit has been removed.
return false
endif
set index=index+1
endloop
return true
endmethod
//* This method cleans up any empty buckets periodically by checking if it has been fully allocated and then checking if all its members no longer exist.
private static method perCleanup takes nothing returns nothing
local integer index=0
loop
exitwhen index>.maxDBIndex
if index!=.dbIndex and .bucketIsEmpty(index) then
call DestroyTrigger(.bucketDB[index].trig)
call .bucketDB[index].destroy()
set .bucketDB[index]=.bucketDB[.maxDBIndex]
set .maxDBIndex=.maxDBIndex-1
if .maxDBIndex==.dbIndex then
set .dbIndex=index
endif
set index=index-1
endif
set index=index+1
endloop
endmethod
//* This is a initialization function necessary for the setup of StructuredDD.
private static method onInit takes nothing returns nothing
local group grp
local region reg
local trigger autoAddUnits
local timer perCleanup
local unit FoG
static if ADD_ALL_UNITS then
//Add starting units
set grp=CreateGroup()
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
call .add(FoG)
call GroupRemoveUnit(grp,FoG)
endloop
//Add entering units
set autoAddUnits=CreateTrigger()
set reg=CreateRegion()
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(autoAddUnits,reg,null)
call TriggerAddCondition(autoAddUnits,Condition(function ddBucket.autoAddC))
set autoAddUnits=null
set reg=null
endif
//enable periodic cleanup:
set perCleanup=CreateTimer()
call TimerStart(perCleanup,PER_CLEANUP_TIMEOUT,true,function ddBucket.perCleanup)
set perCleanup=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//* Introduction:
//* DamageType is a resource which extends the functionality of StructuredDD, a damage detection engine. DamageType allows the client to detect the damage a unit
//* receives is:
//* -An Attack
//* -A Spell
//* -From Code
//* (As long as you deal code damage and check damage type using the API).
//* The advantage of this system is that it is incredibly user friendly, contains easy to follow code (for learning purposes), and runs in only about 85 lines of
//* code, which is additionally very optimized.
//*
//* Design Explanation:
//* On 2013.01.09, hiveworkshop.com user looking_for_help wrote up a detailed explanation about a new method of detecting damage type, which uses the Spell Damage
//* Reduction ability. The general idea is that all units have a modified version of this ability which has a 200% spell damage reduction, effectively inverting
//* all damage from spells. The proposed system would then simply deal twice as much damage back to the unit and then declare that it was spell damage. Before
//* this process was discovered, damage type detection had to be done with other methods like orb abilities and splash detection dummies. This is all mitigated
//* with the new design, but not without some flaws of its own (see Limitations).
//*
//* Limitations:
//* -DamageType requires vJass to pre-compile.
//* -DamageType requires StructuredDD and is therefore incompatible with other damage detection systems.
//* -This script uses Vexorian's Table library and thus other versions of Table may be incompatible.
//* -DamageType is not perfectly optmized as it aims to balance code quality and readability with optimization.
//* -The script requires a custom version of the 'Spell Damage Reduction' ability, thus complicating the implementation slightly.
//* -The scrpt does not contain code to calculate any second instance of the spell damage reduction ability, thus it is incompatible with maps that use this for
//* any other purpose, including the Runed Bracer item.
//* -Some standard warcraft spells may contain bugs, such as Carrion Swarm. I don't try to mitigate this as it just adds code complexity for what's certainly
//* a fixable issue.
//* -The system is designed to ignore damage values of 0. since many spells proc multiple damage events, for which one is usually a 0. damage event. Thus, if your
//* map uses units which deal 0 damage as a way of customized damage, or spells that do 0 damage as a way of checking their effect hitting only, you will have
//* to script your own pattern recognition, as I have no way of knowing which clients are doing either or neither of these methods. Support for this could be
//* added, however, and thus if you are interested, please contact me.
//* -The handler defined in this script must be the first handler added to the handler list using addHandler. Thus, if your map contains a library that also uses
//* a handler at map initialization, the library must 'require DamageType' in order to avoid a race condition.
//*
//* API:
//* integer DAMAGE_TYPE_CHECK_ID => rawcode of the spell damage redution ability.
//* integer NULLED => returned by get when the GetEventDamage() type can not be concluded.
//* integer ATTACK => returned by get when the GetEventDamage() is found to be a physical attack.
//* integer SPELL => returned by get when the GetEventDamage() is found to be a spell.
//* integer CODE => returned by get when the GetEventDamage() is found to be a spell.
//* public function get(real) => returns a DAMAGE_TYPE_* given GetEventDamage().
//* public function dealCodeDamage(unit,unit,real,damagetype) => causes the first unit to deal real damage of type damagetype to the second unit.
//*
//* Installation:
//* -Make sure you have an up-to-date copy of StructuredDD:
//* http://www.hiveworkshop.com/forums/jass-resources-412/system-structureddd-structured-damage-detection-216968/
//* -Make sure you have an up-to-date copy of Vexorian's Table: http://www.wc3c.net/showthread.php?t=101246
//* -Make sure you copy the DamageTypeCheck unit from the object editor, and set the value of DAMAGE_TYPE_CHECK_ID to your unit's rawcode.
//* -Make sure all damage dealt by triggers in your map against units referenced by StructuredDD is dealt using dealCodeDamage.
//* -If your map contains any libraries that use StructuredDD's addHanler, set those libraries to 'require DamageType' to avoid a race condition.
library DamageType initializer i requires StructuredDD, Table
//* This struct contains the data to deal damage to a target that would otherwise die when hit with double damage. This is to counter the effect of a unit
//* instantly healing some damage due to negative spell damage, without using any unnecessary triggers.
private struct delayDat
unit target
unit source
real damage
endstruct
globals
/// BEGIN CUSTOMIZE SECTION
//* Make sure this matches the rawcode/id of your version of the DamageTypeCheck ability (you must copy/paste it from the editor to your map)
private constant integer DAMAGE_TYPE_CHECK_ID='A000'
/// END CUSTOMIZE SECTION
//* One of the following 4 variables will be returned by get() depending on what type of damage was found.
public constant integer NULLED=-1
public constant integer ATTACK= 0
public constant integer SPELL = 1
public constant integer CODE = 2
private constant real DELAY_AMOUNT=0.
private integer lastDamageType=NULLED
private group grp=CreateGroup()
private HandleTable delayed
endglobals
public function get takes real sourceDamage returns integer
if lastDamageType==CODE then
return CODE
elseif sourceDamage>0. then
return ATTACK
elseif sourceDamage<0. then
return SPELL
endif
return NULLED
endfunction
public function dealCodeDamage takes unit who, unit target, real damage, damagetype how returns nothing
local integer prevType=lastDamageType
set lastDamageType=CODE
call UnitDamageTarget(who,target,damage,true,true,ATTACK_TYPE_NORMAL,how,null)
set lastDamageType=prevType
endfunction
//* One of the auxillary functions for adding the dummy ability to all units.
private function f takes nothing returns boolean
call UnitAddAbility(GetFilterUnit(),DAMAGE_TYPE_CHECK_ID)
return false
endfunction
//* One of the auxillary functions for adding the dummy ability to all units.
private function c takes nothing returns boolean
call UnitAddAbility(GetTriggerUnit(),DAMAGE_TYPE_CHECK_ID)
return false
endfunction
//* This is the function that occurs 0. seconds after damaging a unit, in the case that damaging it by twice as much at the same time would kill it
//* unexpectedly.
private function after takes nothing returns nothing
local timer time=GetExpiredTimer()
local delayDat tempDat=delayed[time]
call dealCodeDamage(tempDat.source,tempDat.target,tempDat.damage,DAMAGE_TYPE_UNIVERSAL)
call tempDat.destroy()
call DestroyTimer(time)
set time=null
endfunction
//* This is the function that will invert any negative spell damage. it must be StructuredDD_ddBucket.conditions[0] to function properly.
private function handler takes nothing returns nothing
local timer time
local delayDat tempDat
local real attemptedDamage=GetEventDamage()
local unit tU
local real sampledLife
if get(attemptedDamage)==SPELL then
set tU=GetTriggerUnit()
set sampledLife=GetWidgetLife(tU)
if sampledLife>-attemptedDamage and sampledLife<-2*attemptedDamage then
call SetWidgetLife(tU,sampledLife+attemptedDamage)
set time=CreateTimer()
set tempDat=delayDat.create()
set tempDat.target=tU
set tempDat.source=GetEventDamageSource()
set tempDat.damage=attemptedDamage
set delayed[time]=tempDat
call TimerStart(time,DELAY_AMOUNT,false,function after)
set time=null
else
call dealCodeDamage(GetEventDamageSource(),tU,2.*attemptedDamage,DAMAGE_TYPE_UNIVERSAL)
endif
set tU=null
endif
endfunction
//* Initialization function to enable the system.
private function i takes nothing returns nothing
local region reg=CreateRegion()
local trigger addBracer=CreateTrigger()
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(addBracer,reg,null)
call TriggerAddCondition(addBracer,Condition(function c))
call StructuredDD_ddBucket.addHandler(function handler)
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,Filter(function f))
set delayed=HandleTable.create()
set reg=null
set addBracer=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library tfConsts initializer i
globals
//* SETUP SECTION
public constant integer TF_ID ='H000' //unit ID of "Card Master" unit
public constant integer CASTER_DUMMY_ID ='h002' //unit ID of "Caster (Dummy)" unit
public constant integer EYE_DUMMY_ID ='h003' //unit ID of "Eye (Dummy)" unit
public constant integer CARD_DUMMY_ID ='h001' //unit ID of "Card (Dummy)" unit
public constant integer WILD_CARDS_ID ='A001' //ability ID of "Wild Cards" ability
public constant integer PICK_A_CARD_ID ='A002' //ability ID of "Pick a Card" ability
public constant integer DIS_PICK_A_CARD_ID='A006' //ability ID of "Pick a Card (disabled dummy ability" ability
public constant integer BLUE_CARD_ID ='A003' //ability ID of "Blue" ability
public constant integer DIS_BLUE_CARD_ID ='A007' //ability ID of "Blue (enabled card)" ability
public constant integer RED_CARD_ID ='A004' //ability ID of "Red" ability
public constant integer DIS_RED_CARD_ID ='A008' //ability ID of "Red (enabled card)" ability
public constant integer GOLD_CARD_ID ='A005' //ability ID of "Gold" ability
public constant integer DIS_GOLD_CARD_ID ='A009' //ability ID of "Gold (enabled card)" ability
public constant integer DUMMY_SLOW_ID ='A00B' //ability ID of "Slow (Red Card)" ability
public constant integer DUMMY_STUN_ID ='A00C' //ability ID of "Stun (Gold Card)" ability
public constant integer STACKED_DECK_ID ='A00D' //ability ID of "Stacked Deck" ability
public constant integer DESTINY_ID ='A00E' //ability ID of "Destiny" ability
public constant integer GATE_ID ='A00G' //ability ID of "Gate" ability
public constant integer DIS_GATE_ID ='A00F' //ability ID of "Gate (disabled)" ability
//* END SETUP SECTION
//* CUSTOMIZE SECTION
public constant real FPS =1./30. //The period for smooth abilities. should be between 1./100. and 1./20. depending on necessary
//performance. A good starting value for multiplayer should be 1./30.
//* END CUSTOMIZE SECTION
public constant integer BLUE_CARD=0
public constant integer RED_CARD =1
public constant integer GOLD_CARD=2
public integer array CARD_ID[3]
public HandleTable hiddenCard
private group grp=CreateGroup()
endglobals
private function f takes nothing returns boolean
local unit enum=GetEnumUnit()
if GetUnitTypeId(enum)==TF_ID then
set hiddenCard[enum]=BLUE_CARD
endif
set enum=null
return false
endfunction
private function c takes nothing returns boolean
local unit tU=GetTriggerUnit()
if GetUnitTypeId(tU)==TF_ID then
set hiddenCard[tU]=BLUE_CARD
endif
set tU=null
return false
endfunction
private function i takes nothing returns nothing
local trigger initializeNewTFs=CreateTrigger()
local region reg=CreateRegion()
set hiddenCard=HandleTable.create()
set CARD_ID[BLUE_CARD]=BLUE_CARD_ID
set CARD_ID[RED_CARD]=RED_CARD_ID
set CARD_ID[GOLD_CARD]=GOLD_CARD_ID
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(initializeNewTFs,reg,null)
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,Filter(function f))
call TriggerAddCondition(initializeNewTFs,Condition(function c))
call DestroyGroup(grp)
set grp=null
set reg=null
set initializeNewTFs=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope loadedDice initializer i
globals
private group grp=CreateGroup()
endglobals
private function c takes nothing returns boolean
local unit FoG
local unit kU=GetKillingUnit()
local player kUOwner=GetOwningPlayer(kU)
local boolean found=false
if IsUnitType(kU,UNIT_TYPE_HERO) then
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null or found
if GetUnitTypeId(FoG)==tfConsts_TF_ID and IsUnitType(FoG,UNIT_TYPE_DEAD)==false and IsUnitAlly(FoG,kUOwner) then
set found=true
endif
call GroupRemoveUnit(grp,FoG)
endloop
if found then
call SetPlayerState(kUOwner,PLAYER_STATE_RESOURCE_GOLD,GetPlayerState(kUOwner,PLAYER_STATE_RESOURCE_GOLD)+2)
call TextTag_GoldBounty(kU,2,kUOwner)
endif
endif
set kU=null
return false
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=47
//TESH.alwaysfold=0
scope wildCards initializer i
private struct cardDat
unit caster
unit array cards[3]
real array gradX[3]
real array gradY[3]
real damage
real traveled=0.
group struckUnits
endstruct
private struct cdrDat
unit fate
endstruct
globals
private constant real OFFSET_ANGLE=30.*bj_DEGTORAD
private constant real PROJECTILE_SPEED=700.
private constant real ENUM_RADIUS=125./2.
private constant real TOTAL_DISTANCE=1450.
private constant real COOLDOWN=6.
private cardDat array datDB
private integer dbIndex=-1
private timer time=CreateTimer()
private group grp=CreateGroup()
private HandleTable cdrTable
endglobals
private function setCardRandHue takes unit u returns nothing
local integer i=GetRandomInt(0,2)
if i==0 then
call SetUnitVertexColor(u,255,55,55,255)
elseif i==1 then
call SetUnitVertexColor(u,255,245,55,255)
else
call SetUnitVertexColor(u,80,80,255,255)
endif
endfunction
private function p takes nothing returns nothing
local integer index=0
local integer tripleLooper
local cardDat tempDat
local real uX
local real uY
local unit FoG
loop
exitwhen index>dbIndex
set tempDat=datDB[index]
set tripleLooper=0
loop
exitwhen tripleLooper>2
set uX=GetUnitX(tempDat.cards[tripleLooper])+tempDat.gradX[tripleLooper]
set uY=GetUnitY(tempDat.cards[tripleLooper])+tempDat.gradY[tripleLooper]
call SetUnitX(tempDat.cards[tripleLooper],uX)
call SetUnitY(tempDat.cards[tripleLooper],uY)
call GroupEnumUnitsInRange(grp,uX,uY,ENUM_RADIUS,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitEnemy(FoG,GetOwningPlayer(tempDat.caster)) and IsUnitType(FoG,UNIT_TYPE_DEAD)==false and IsUnitType(FoG,UNIT_TYPE_STRUCTURE)==false and IsUnitInGroup(FoG,tempDat.struckUnits)==false then
call DamageType_dealCodeDamage(tempDat.caster,FoG,tempDat.damage,DAMAGE_TYPE_MAGIC)
call GroupAddUnit(tempDat.struckUnits,FoG)
endif
call GroupRemoveUnit(grp,FoG)
endloop
set tripleLooper=tripleLooper+1
endloop
set tempDat.traveled=tempDat.traveled+PROJECTILE_SPEED*tfConsts_FPS
if tempDat.traveled>=TOTAL_DISTANCE then
call RemoveUnit(tempDat.cards[0])
call RemoveUnit(tempDat.cards[1])
call RemoveUnit(tempDat.cards[2])
call DestroyGroup(tempDat.struckUnits)
call tempDat.destroy()
set datDB[index]=datDB[dbIndex]
set index=index-1
set dbIndex=dbIndex-1
if dbIndex==-1 then
call PauseTimer(time)
endif
endif
set index=index+1
endloop
endfunction
private function cdr takes nothing returns nothing
local timer clock=GetExpiredTimer()
local cdrDat tempDat=cdrTable[clock]
call UnitRemoveAbility(tempDat.fate,tfConsts_WILD_CARDS_ID)
call UnitAddAbility(tempDat.fate,tfConsts_WILD_CARDS_ID)
call tempDat.destroy()
call cdrTable.flush(clock)
call DestroyTimer(clock)
set clock=null
endfunction
private function c takes nothing returns boolean
local cardDat tempDat
local cdrDat ridiculousUseOfStruct
local player owner
local unit tU
local real tX
local real tY
local real tUX
local real tUY
local real angle
local timer cdrClock
if GetSpellAbilityId()==tfConsts_WILD_CARDS_ID then
set tU=GetTriggerUnit()
set owner=GetOwningPlayer(tU)
set tX=GetSpellTargetX()
set tY=GetSpellTargetY()
set tUX=GetUnitX(tU)
set tUY=GetUnitY(tU)
set angle=Atan2(tY-tUY,tX-tUX)
set tempDat=cardDat.create()
set tempDat.cards[0]=CreateUnit(owner,tfConsts_CARD_DUMMY_ID,0.,0.,(angle-OFFSET_ANGLE)*bj_RADTODEG)
set tempDat.cards[1]=CreateUnit(owner,tfConsts_CARD_DUMMY_ID,0.,0.,angle*bj_RADTODEG)
set tempDat.cards[2]=CreateUnit(owner,tfConsts_CARD_DUMMY_ID,0.,0.,(angle+OFFSET_ANGLE)*bj_RADTODEG)
call setCardRandHue(tempDat.cards[0])
call setCardRandHue(tempDat.cards[1])
call setCardRandHue(tempDat.cards[2])
call SetUnitX(tempDat.cards[0],tUX)
call SetUnitX(tempDat.cards[1],tUX)
call SetUnitX(tempDat.cards[2],tUX)
call SetUnitY(tempDat.cards[0],tUY)
call SetUnitY(tempDat.cards[1],tUY)
call SetUnitY(tempDat.cards[2],tUY)
set tempDat.gradX[0]=PROJECTILE_SPEED*tfConsts_FPS*Cos(angle-OFFSET_ANGLE)
set tempDat.gradX[1]=PROJECTILE_SPEED*tfConsts_FPS*Cos(angle)
set tempDat.gradX[2]=PROJECTILE_SPEED*tfConsts_FPS*Cos(angle+OFFSET_ANGLE)
set tempDat.gradY[0]=PROJECTILE_SPEED*tfConsts_FPS*Sin(angle-OFFSET_ANGLE)
set tempDat.gradY[1]=PROJECTILE_SPEED*tfConsts_FPS*Sin(angle)
set tempDat.gradY[2]=PROJECTILE_SPEED*tfConsts_FPS*Sin(angle+OFFSET_ANGLE)
set tempDat.struckUnits=CreateGroup()
set tempDat.caster=tU
set tempDat.damage=10.+50.*GetUnitAbilityLevel(tU,tfConsts_WILD_CARDS_ID)+.65*GetHeroInt(tU,true)
set dbIndex=dbIndex+1
set datDB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(time,tfConsts_FPS,true,function p)
endif
if GetUnitAbilityLevel(tempDat.caster,tfConsts_STACKED_DECK_ID)>0 then
set cdrClock=CreateTimer()
set ridiculousUseOfStruct=cdrDat.create()
set ridiculousUseOfStruct.fate=tempDat.caster
set cdrTable[cdrClock]=ridiculousUseOfStruct
call TimerStart(cdrClock,COOLDOWN-COOLDOWN*0.03*GetUnitAbilityLevel(tempDat.caster,tfConsts_STACKED_DECK_ID),false,function cdr)
set cdrClock=null
endif
set tU=null
endif
return false
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
set cdrTable=HandleTable.create()
set t=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope pickACard initializer i
private struct pickDat
unit fate
integer revertAbilityLevel
real elapsedTime
boolean locked=false
boolean thrown=false
endstruct
private struct dummyCardDat
unit dummy
unit fate
endstruct
globals
private constant real CYCLE_MAX_DURATION=15.
private constant real COOLDOWN=6.
private constant real CARD_CYCLE_PERIOD=.6
private constant real RED_CARD_ENUM_RANGE=200.
private constant string TICKSOUND="Sound\\Interface\\MouseClick1.wav"
private constant string BLUESOUND="Abilities\\Spells\\Human\\Banish\\BanishCaster.wav"
private constant string BLUEFX ="Units\\NightElf\\Wisp\\WispExplode.mdl"
private constant string REDSOUND ="Abilities\\Spells\\Human\\Defend\\DefendCaster.wav"
private constant string REDFX ="Abilities\\Spells\\Human\\Feedback\\SpellBreakerAttack.mdl"
private constant string GOLDSOUND="Abilities\\Spells\\Human\\HolyBolt\\HolyBolt.mdl"
private constant string GOLDFX ="Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
private HandleTable fromTimer
private HandleTable fromFate
private sound snd
private dummyCardDat array dummyDB
private integer dummyDBIndex=-1
private timer dummyClock=CreateTimer()
private unit casterDummy
private group grp=CreateGroup()
endglobals
private function ddHandler takes nothing returns nothing
local real damage=GetEventDamage()
local unit dSource=GetEventDamageSource()
local unit tU
local unit FoG
local pickDat tempDat
local player owner
local sound colorSound
local real tX
local real tY
local real index
if DamageType_get(damage)==DamageType_ATTACK and GetUnitTypeId(dSource)==tfConsts_TF_ID then
set tempDat=fromFate[dSource]
set tU=GetTriggerUnit()
set owner=GetOwningPlayer(dSource)
if GetUnitAbilityLevel(dSource,tfConsts_DIS_BLUE_CARD_ID)>0 then
set colorSound=CreateSound(BLUESOUND,false,true,true,12700,12700,"")
call SetSoundVolume(colorSound,127)
call SetSoundPitch(colorSound,.75)
call SetSoundPosition(colorSound,GetUnitX(tU),GetUnitY(tU),100.)
call StartSound(colorSound)
call KillSoundWhenDone(colorSound)
if IsUnitType(tU,UNIT_TYPE_DEAD)==false then
call DamageType_dealCodeDamage(dSource,tU,20+20*tempDat.revertAbilityLevel+.4*GetHeroInt(dSource,true),DAMAGE_TYPE_MAGIC)
endif
call SetUnitState(dSource,UNIT_STATE_MANA,GetUnitState(dSource,UNIT_STATE_MANA)+46+2.9*GetHeroLevel(dSource)+13+13*tempDat.revertAbilityLevel)
call UnitRemoveAbility(dSource,tfConsts_DIS_BLUE_CARD_ID)
set tempDat.thrown=true
set tempDat.elapsedTime=CYCLE_MAX_DURATION
call DestroyEffect(AddSpecialEffect(BLUEFX,GetUnitX(tU),GetUnitY(tU)))
elseif GetUnitAbilityLevel(dSource,tfConsts_DIS_RED_CARD_ID)>0 then
set tX=GetUnitX(tU)
set tY=GetUnitY(tU)
set colorSound=CreateSound(REDSOUND,false,true,true,12700,12700,"")
call SetSoundVolume(colorSound,127)
call SetSoundPitch(colorSound,.75)
call SetSoundPosition(colorSound,tX,tY,100.)
call StartSound(colorSound)
call KillSoundWhenDone(colorSound)
call GroupEnumUnitsInRange(grp,GetUnitX(tU),GetUnitY(tU),RED_CARD_ENUM_RANGE,null)
if IsUnitType(tU,UNIT_TYPE_STRUCTURE)==false and IsUnitType(tU,UNIT_TYPE_DEAD)==false then
call DamageType_dealCodeDamage(dSource,tU,15+15*tempDat.revertAbilityLevel+.4*GetHeroInt(tempDat.fate,true),DAMAGE_TYPE_MAGIC)
endif
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitEnemy(FoG,owner) and IsUnitType(FoG,UNIT_TYPE_STRUCTURE)==false and IsUnitType(FoG,UNIT_TYPE_DEAD)==false then
if FoG!=tU then
call DamageType_dealCodeDamage(dSource,FoG,15+15*tempDat.revertAbilityLevel+.4*GetHeroInt(dSource,true)+46+2.9*GetHeroLevel(dSource),DAMAGE_TYPE_MAGIC)
endif
call SetUnitX(casterDummy,tX)
call SetUnitY(casterDummy,tY)
call UnitAddAbility(casterDummy,tfConsts_DUMMY_SLOW_ID)
call SetUnitAbilityLevel(casterDummy,tfConsts_DUMMY_SLOW_ID,tempDat.revertAbilityLevel)
call IssueTargetOrder(casterDummy,"slow",FoG)
call UnitRemoveAbility(casterDummy,tfConsts_DUMMY_SLOW_ID)
endif
call GroupRemoveUnit(grp,FoG)
endloop
call UnitRemoveAbility(dSource,tfConsts_DIS_RED_CARD_ID)
set tempDat.thrown=true
set tempDat.elapsedTime=CYCLE_MAX_DURATION
call DestroyEffect(AddSpecialEffect(REDFX,tX,tY))
set index=0.
loop
exitwhen index>bj_PI*2.
call DestroyEffect(AddSpecialEffect(REDFX,tX+RED_CARD_ENUM_RANGE/2.*Cos(index),tY+RED_CARD_ENUM_RANGE/2.*Sin(index)))
set index=index+bj_PI/9.
endloop
elseif GetUnitAbilityLevel(dSource,tfConsts_DIS_GOLD_CARD_ID)>0 then
set tX=GetUnitX(tU)
set tY=GetUnitY(tU)
set colorSound=CreateSound(GOLDSOUND,false,true,true,12700,12700,"")
call SetSoundVolume(colorSound,127)
call SetSoundPitch(colorSound,2.)
call SetSoundPosition(colorSound,tX,tY,100.)
call StartSound(colorSound)
call KillSoundWhenDone(colorSound)
if IsUnitType(tU,UNIT_TYPE_DEAD)==false then
call DamageType_dealCodeDamage(dSource,tU,7.5+7.5*tempDat.revertAbilityLevel+.4*GetHeroInt(tempDat.fate,true),DAMAGE_TYPE_MAGIC)
call SetUnitX(casterDummy,tX)
call SetUnitY(casterDummy,tY)
call UnitAddAbility(casterDummy,tfConsts_DUMMY_STUN_ID)
call SetUnitAbilityLevel(casterDummy,tfConsts_DUMMY_STUN_ID,tempDat.revertAbilityLevel)
call IssueTargetOrder(casterDummy,"thunderbolt",tU)
call UnitRemoveAbility(casterDummy,tfConsts_DUMMY_STUN_ID)
endif
call UnitRemoveAbility(dSource,tfConsts_DIS_GOLD_CARD_ID)
set tempDat.thrown=true
set tempDat.elapsedTime=CYCLE_MAX_DURATION
call DestroyEffect(AddSpecialEffect(GOLDFX,tX,tY))
endif
set tU=null
set colorSound=null
endif
set dSource=null
endfunction
private function dummyP takes nothing returns nothing
local integer index=0
local dummyCardDat tempDat
loop
exitwhen index>dummyDBIndex
set tempDat=dummyDB[index]
call SetUnitX(tempDat.dummy,GetUnitX(tempDat.fate))
call SetUnitY(tempDat.dummy,GetUnitY(tempDat.fate))
if GetUnitAbilityLevel(tempDat.fate,tfConsts_CARD_ID[tfConsts_BLUE_CARD])>0 or GetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_BLUE_CARD_ID)>0 then
call SetUnitVertexColor(tempDat.dummy,80,80,255,255)
elseif GetUnitAbilityLevel(tempDat.fate,tfConsts_CARD_ID[tfConsts_RED_CARD])>0 or GetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_RED_CARD_ID)>0 then
call SetUnitVertexColor(tempDat.dummy,255,55,55,255)
elseif GetUnitAbilityLevel(tempDat.fate,tfConsts_CARD_ID[tfConsts_GOLD_CARD])>0 or GetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_GOLD_CARD_ID)>0 then
call SetUnitVertexColor(tempDat.dummy,255,245,55,255)
else
call RemoveUnit(tempDat.dummy)
set tempDat.dummy=null
set tempDat.fate=null
call tempDat.destroy()
set dummyDB[index]=dummyDB[dummyDBIndex]
set dummyDBIndex=dummyDBIndex-1
set index=index-1
if dummyDBIndex==-1 then
call PauseTimer(dummyClock)
endif
endif
set index=index+1
endloop
endfunction
private function p takes nothing returns nothing
local timer time=GetExpiredTimer()
local pickDat tempDat=fromTimer[time]
local integer cardID=tfConsts_hiddenCard[tempDat.fate]
set tempDat.elapsedTime=tempDat.elapsedTime+CARD_CYCLE_PERIOD
if tempDat.elapsedTime<CYCLE_MAX_DURATION and tempDat.thrown==false then
if tempDat.locked==false and (GetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_BLUE_CARD_ID)>0 or GetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_RED_CARD_ID)>0 or GetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_GOLD_CARD_ID)>0) then
set tempDat.locked=true
endif
if tempDat.locked==false then
call UnitRemoveAbility(tempDat.fate,tfConsts_CARD_ID[cardID])
endif
if cardID==tfConsts_GOLD_CARD then
set cardID=tfConsts_BLUE_CARD
else
set cardID=cardID+1
endif
if tempDat.locked==false then
call UnitAddAbility(tempDat.fate,tfConsts_CARD_ID[cardID])
if GetLocalPlayer()==GetOwningPlayer(tempDat.fate) then
call StartSound(snd)
endif
endif
else
call UnitRemoveAbility(tempDat.fate,tfConsts_CARD_ID[cardID])
call UnitRemoveAbility(tempDat.fate,tfConsts_DIS_BLUE_CARD_ID)
call UnitRemoveAbility(tempDat.fate,tfConsts_DIS_RED_CARD_ID)
call UnitRemoveAbility(tempDat.fate,tfConsts_DIS_GOLD_CARD_ID)
call UnitAddAbility(tempDat.fate,tfConsts_DIS_PICK_A_CARD_ID)
endif
if tempDat.elapsedTime>CYCLE_MAX_DURATION+COOLDOWN-COOLDOWN*0.03*GetUnitAbilityLevel(tempDat.fate,tfConsts_STACKED_DECK_ID) then
call SetUnitAbilityLevel(tempDat.fate,tfConsts_PICK_A_CARD_ID,tempDat.revertAbilityLevel)
call UnitRemoveAbility(tempDat.fate,tfConsts_DIS_PICK_A_CARD_ID)
call tempDat.destroy()
call fromTimer.flush(time)
call DestroyTimer(time)
endif
set tfConsts_hiddenCard[tempDat.fate]=cardID
set time=null
endfunction
private function c takes nothing returns boolean
local pickDat tempDat
local dummyCardDat tempDCDat
local timer time
if GetSpellAbilityId()==tfConsts_PICK_A_CARD_ID then
//store all data in struct instance
set tempDat=pickDat.create()
set tempDat.fate=GetTriggerUnit()
set tempDat.revertAbilityLevel=GetUnitAbilityLevel(tempDat.fate,tfConsts_PICK_A_CARD_ID)
set tempDat.elapsedTime=0.
//make the ability unavailable
call SetUnitAbilityLevel(tempDat.fate,tfConsts_PICK_A_CARD_ID,5)
call IncUnitAbilityLevel(tempDat.fate,tfConsts_PICK_A_CARD_ID)
//add the proper ability depending on the current value stored in the table
call UnitAddAbility(tempDat.fate,tfConsts_CARD_ID[tfConsts_hiddenCard[tempDat.fate]])
//create new timer, because sharing a periodic timer with a card cycle period between 0.3 and 0.7 seconds will not only look poor,
//but potentially cause problems for TF vs TF situations.
//begin counting towards the duration. After picking a card, this same duration will be kept, and after the max is reached,
//or the card is thrown, the ability will reset to the original ability.
set time=CreateTimer()
set fromTimer[time]=tempDat
set fromFate[tempDat.fate]=tempDat
call TimerStart(time,CARD_CYCLE_PERIOD,true,function p)
//create the dummy card and give it the proper color. we need to attach it to a different, more smooth timer. This one will be stack based.
set tempDCDat=dummyCardDat.create()
set tempDCDat.fate=tempDat.fate
set tempDCDat.dummy=CreateUnit(GetOwningPlayer(tempDat.fate),tfConsts_CARD_DUMMY_ID,GetUnitX(tempDat.fate),GetUnitY(tempDat.fate),0.)
call UnitAddAbility(tempDCDat.dummy,'arav')
call UnitRemoveAbility(tempDCDat.dummy,'arav')
call SetUnitFlyHeight(tempDCDat.dummy,150.,0.)
call SetUnitTimeScale(tempDCDat.dummy,.5)
set dummyDBIndex=dummyDBIndex+1
set dummyDB[dummyDBIndex]=tempDCDat
if dummyDBIndex==0 then
call TimerStart(dummyClock,tfConsts_FPS,true,function dummyP)
endif
set time=null
endif
return false
endfunction
private function secondC takes nothing returns boolean
local integer id=GetSpellAbilityId()
local unit tU
if id==tfConsts_BLUE_CARD_ID then
set tU=GetTriggerUnit()
call UnitRemoveAbility(tU,tfConsts_BLUE_CARD_ID)
call UnitAddAbility(tU,tfConsts_DIS_BLUE_CARD_ID)
set tU=null
elseif id==tfConsts_RED_CARD_ID then
set tU=GetTriggerUnit()
call UnitRemoveAbility(tU,tfConsts_RED_CARD_ID)
call UnitAddAbility(tU,tfConsts_DIS_RED_CARD_ID)
set tU=null
elseif id==tfConsts_GOLD_CARD_ID then
set tU=GetTriggerUnit()
call UnitRemoveAbility(tU,tfConsts_GOLD_CARD_ID)
call UnitAddAbility(tU,tfConsts_DIS_GOLD_CARD_ID)
set tU=null
endif
return false
endfunction
private function i takes nothing returns nothing
local trigger initialCast=CreateTrigger()
local trigger secondCast=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(secondCast,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(secondCast,Condition(function secondC))
set fromTimer=HandleTable.create()
set fromFate =HandleTable.create()
set snd=CreateSound(TICKSOUND,false,false,false,12700,12700,"")
call SetSoundVolume(snd,127)
call SetSoundPitch(snd,0.5)
call StartSound(snd)
call TriggerRegisterAnyUnitEventBJ(initialCast,EVENT_PLAYER_UNIT_SPELL_FINISH)
call TriggerAddCondition(initialCast,Condition(function c))
call StructuredDD_ddBucket.addHandler(function ddHandler)
set casterDummy=CreateUnit(Player(15),tfConsts_CASTER_DUMMY_ID,0.,0.,0.)
set initialCast=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope stackedDeck initializer i
private struct stackDat
integer count
effect aura
endstruct
globals
private constant string NEXT_HIT_FX="Abilities\\Spells\\Undead\\RegenerationAura\\ObsidianRegenAura.mdl"
private constant string TARGET_FX="Abilities\\Spells\\Items\\AIil\\AIilTarget.mdl"
private constant string HIT_SOUND="Abilities\\Spells\\Human\\Polymorph\\PolymorphDone.wav"
private HandleTable attackCounts
endglobals
private function handler takes nothing returns nothing
local unit dSource=GetEventDamageSource()
local integer level=GetUnitAbilityLevel(dSource,tfConsts_STACKED_DECK_ID)
local integer count
local stackDat tempDat
local unit tU
local sound snd
if DamageType_get(GetEventDamage())==DamageType_ATTACK and level>0 then
if attackCounts.exists(dSource) then
set tempDat=attackCounts[dSource]
if tempDat.count==3 then //this attack is a proc, let's deal extra damage
set tU=GetTriggerUnit()
call DamageType_dealCodeDamage(dSource,tU,30.+25.*level+.4*GetHeroInt(dSource,true),DAMAGE_TYPE_MAGIC)
//then reset attackCount
set tempDat.count=0
//and remove the effect added when count was 2
call DestroyEffect(tempDat.aura)
call DestroyEffect(AddSpecialEffectTarget(TARGET_FX,tU,"origin"))
set snd=CreateSound(HIT_SOUND,false,true,true,12700,12700,"")
call SetSoundVolume(snd,127)
call SetSoundPitch(snd,1.25)
call SetSoundPosition(snd,GetUnitX(tU),GetUnitY(tU),100.)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd=null
set tU=null
elseif tempDat.count==2 then //the next attack we'll proc, so let's display an effect
set tempDat.aura=AddSpecialEffectTarget(NEXT_HIT_FX,dSource,"origin")
set tempDat.count=tempDat.count+1
else //this is attack 0 or 1 so just increment count
set tempDat.count=tempDat.count+1
endif
else //this is the first time the unit attacks with the ability so let's instanciate a stackDat and set the default count.
set tempDat=stackDat.create()
set tempDat.count=0
set attackCounts[dSource]=tempDat
endif
endif
set dSource=null
endfunction
private function i takes nothing returns nothing
call StructuredDD_ddBucket.addHandler(function handler)
set attackCounts=HandleTable.create()
endfunction
endscope
//TESH.scrollpos=81
//TESH.alwaysfold=0
scope destiny initializer i
private struct cdrDat
unit fate
endstruct
private struct target
unit who
endstruct
private struct destinyDat
unit fate
group visionDummies
real elapsedTime=0.
real duration
integer revertAbilityLevel
endstruct
globals
private constant string GATE_SOUND="Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.wav"
private destinyDat array destinyDB
private timer time=CreateTimer()
private integer dbIndex=-1
private group grp=CreateGroup()
private HandleTable attachments
private HandleTable cdrTable
endglobals
private function move takes nothing returns nothing
local unit enum=GetEnumUnit()
local target tempDat=attachments[enum]
call SetUnitX(enum,GetUnitX(tempDat.who))
call SetUnitY(enum,GetUnitY(tempDat.who))
set enum=null
endfunction
private function flusher takes nothing returns nothing
local unit enum=GetEnumUnit()
call attachments.flush(enum)
call RemoveUnit(enum)
set enum=null
endfunction
private function p takes nothing returns nothing
local integer index=0
local destinyDat tempDat
loop
exitwhen index>dbIndex
set tempDat=destinyDB[index]
set tempDat.elapsedTime=tempDat.elapsedTime+tfConsts_FPS
if tempDat.elapsedTime<tempDat.duration then
call ForGroup(tempDat.visionDummies,function move)
else
call UnitRemoveAbility(tempDat.fate,tfConsts_GATE_ID)
call UnitRemoveAbility(tempDat.fate,tfConsts_DIS_GATE_ID)
call SetUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID,tempDat.revertAbilityLevel)
call ForGroup(tempDat.visionDummies,function flusher)
call DestroyGroup(tempDat.visionDummies)
set destinyDB[index]=destinyDB[dbIndex]
set dbIndex=dbIndex-1
set index=index-1
if dbIndex==-1 then
call PauseTimer(time)
endif
endif
set index=index+1
endloop
endfunction
private function cdr takes nothing returns nothing
local timer clock=GetExpiredTimer()
local cdrDat tempDat=cdrTable[clock]
call UnitRemoveAbility(tempDat.fate,tfConsts_DESTINY_ID)
call UnitAddAbility(tempDat.fate,tfConsts_DESTINY_ID)
call tempDat.destroy()
call cdrTable.flush(clock)
call DestroyTimer(clock)
set clock=null
endfunction
private function c takes nothing returns boolean
local destinyDat tempDat
local cdrDat tempCDR
local target tStruct
local unit FoG
local unit u
local player who
local timer cdrClock
local real cooldown
if GetSpellAbilityId()==tfConsts_DESTINY_ID then
set tempDat=destinyDat.create()
set tempDat.fate=GetTriggerUnit()
set tempDat.visionDummies=CreateGroup()
set tempDat.revertAbilityLevel=GetUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID)
set tempDat.duration=4.+2.*tempDat.revertAbilityLevel
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
set who=GetOwningPlayer(tempDat.fate)
call SetUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID,3)
call IncUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID)
call UnitAddAbility(tempDat.fate,tfConsts_GATE_ID)
set cooldown=165.-15.*GetUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID)
if GetUnitAbilityLevel(tempDat.fate,tfConsts_STACKED_DECK_ID)>0 then
set cdrClock=CreateTimer()
set tempCDR=cdrDat.create()
set tempCDR.fate=tempDat.fate
set cdrTable[cdrClock]=tempCDR
call TimerStart(cdrClock,cooldown-cooldown*0.03*GetUnitAbilityLevel(tempDat.fate,tfConsts_STACKED_DECK_ID),false,function cdr)
set cdrClock=null
endif
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitType(FoG,UNIT_TYPE_HERO) and IsUnitEnemy(FoG,who) and UnitAlive(FoG) then
set u=CreateUnit(who,tfConsts_EYE_DUMMY_ID,GetUnitX(FoG),GetUnitY(FoG),270.)
call GroupAddUnit(tempDat.visionDummies,u)
set tStruct=target.create()
set tStruct.who=FoG
set attachments[u]=tStruct
set u=null
endif
call GroupRemoveUnit(grp,FoG)
endloop
set dbIndex=dbIndex+1
set destinyDB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(time,tfConsts_FPS,true,function p) //The only reason this timer has to be smooth is so that the effect (dummy) will track fast
//moving/gap closing/teleporting heroes.
endif
endif
return false
endfunction
private function gateC takes nothing returns boolean
local unit tU
local sound snd
if GetSpellAbilityId()==tfConsts_GATE_ID then
set tU=GetTriggerUnit()
set snd=CreateSound(GATE_SOUND,false,true,true,12700,12700,"")
call SetSoundVolume(snd,127)
call SetSoundPosition(snd,GetUnitX(tU),GetUnitY(tU),100.)
call SetSoundPitch(snd,2.)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd=null
call UnitRemoveAbility(tU,tfConsts_GATE_ID)
call UnitAddAbility(tU,tfConsts_DIS_GATE_ID)
set tU=null
endif
return false
endfunction
private function preloadSound takes nothing returns nothing
local sound s=CreateSound(GATE_SOUND,false,true,true,12700,12700,"")
call SetSoundVolume(s,1)
call SetSoundPosition(s,0.,0.,100.)
call StartSound(s)
call KillSoundWhenDone(s)
call DestroyTimer(GetExpiredTimer())
set s=null
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
local trigger gate=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gate,EVENT_PLAYER_UNIT_SPELL_FINISH)
call TriggerAddCondition(gate,Condition(function gateC))
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_FINISH)
call TriggerAddCondition(t,Condition(function c))
call TimerStart(CreateTimer(),0.01,false,function preloadSound)
set attachments=HandleTable.create()
set cdrTable =HandleTable.create()
set gate=null
set t=null
endfunction
endscope