Name | Type | is_array | initial_value |
Activate | trigger | No | |
dam | real | No | |
Deactivate | trigger | No | |
Defending | group | No | |
GDD__Integers | integer | Yes | |
GDD__LeftMapGroup | group | No | |
GDD__TriggerArray | trigger | Yes | |
GDD__UnitArray | unit | Yes | |
GDD_Damage | real | No | |
GDD_DamagedUnit | unit | No | |
GDD_DamageSource | unit | No | |
GDD_Event | real | No | |
t | timer | No | |
u | unit | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Configurables:
function ID takes nothing returns integer
// Write ID of custom "defend" in your map here, mine was 'A001'
return 'A001'
endfunction
function BashChance takes nothing returns integer
// This is the chance (%) to bash. For example, if the following line is "return 25", the chance to bash is 25%.
// Note: It is an integer
return 25
endfunction
function BashDistance takes nothing returns real
// To get an attacking enemy bashed, you have to get close enough, so this is the distance between the defending unit and
// it's attackers, from which it will bash. I choose 150.00 because it's melee range and pretty far so almost melee attackers
// can't avoid it
return 150.00
endfunction
function ManaCost takes nothing returns real
// Mana cost used here is 10.00, it's the amount of mana you use for blocking each attack
return 10.00
endfunction
function Angle takes nothing returns real
// This "determine" which direction is the front, from which defending units can block. For Example: if Angle returns 40.00,
// defending units can block attacks come from maximum of 40 degree from it's front to it's side. if it's 180.00, they can
// block attacks come from any directions.
return 40.00
endfunction
//===========================================================================
// Spell code:
function Trig_Deactivate_Conditions takes nothing returns boolean
if ( not ( GetIssuedOrderId() == String2OrderIdBJ("undefend") ) ) then
return false
endif
if ( not ( GetUnitAbilityLevel(GetTriggerUnit(), ID()) >= 1 ) ) then
return false
endif
return true
endfunction
function Trig_Deactivate_Actions takes nothing returns nothing
call GroupRemoveUnitSimple( GetOrderedUnit(), udg_Defending )
endfunction
//===========================================================================
function Trig_Activate_Conditions takes nothing returns boolean
if ( not ( GetIssuedOrderId() == String2OrderIdBJ("defend") ) ) then
return false
endif
if ( not ( GetUnitAbilityLevel(GetTriggerUnit(), ID()) >= 1 ) ) then
return false
endif
return true
endfunction
function Trig_Activate_Actions takes nothing returns nothing
call GroupAddUnitSimple( GetOrderedUnit(), udg_Defending )
endfunction
//===========================================================================
function AddHP takes nothing returns nothing
call SetUnitState( udg_u, UNIT_STATE_LIFE, ( GetUnitStateSwap(UNIT_STATE_LIFE, udg_u) + udg_dam ) )
endfunction
function Trig_Attack_Block_Conditions takes nothing returns boolean
return IsUnitInGroup(udg_GDD_DamagedUnit,udg_Defending)
endfunction
function Trig_Attack_Block_Actions takes nothing returns nothing
local unit dummy
local texttag txt = CreateTextTag()
local real x1 = GetLocationX(GetUnitLoc(udg_GDD_DamagedUnit))
local real y1 = GetLocationY(GetUnitLoc(udg_GDD_DamagedUnit))
local real x2 = GetLocationX(GetUnitLoc(udg_GDD_DamageSource))
local real y2 = GetLocationY(GetUnitLoc(udg_GDD_DamageSource))
local real A = bj_RADTODEG*Atan2(y2-y1,x2-x1)
local real maxlife = GetUnitStateSwap(UNIT_STATE_MAX_LIFE, udg_GDD_DamagedUnit)
local real life = GetUnitStateSwap(UNIT_STATE_LIFE, udg_GDD_DamagedUnit)
local real mana = GetUnitStateSwap(UNIT_STATE_MANA, udg_GDD_DamagedUnit)
local real deltaA = RAbsBJ(GetUnitFacing(udg_GDD_DamagedUnit)-A)
if ((deltaA <= Angle()) or ((360.00-deltaA) <= Angle())) then
set udg_dam = (udg_GDD_Damage - (maxlife - life))
call SetUnitState( udg_GDD_DamagedUnit, UNIT_STATE_LIFE, (life + udg_GDD_Damage))
call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Human\\Defend\\DefendCaster.mdl", udg_GDD_DamagedUnit, "origin" ) )
call SetTextTagText( txt, "blocked", 0.01955 )
call SetTextTagPos( txt, x2, y2, 0.00 )
call SetTextTagColor( txt, 255, 25, 25, 255)
call SetTextTagPermanent( txt, false )
call SetTextTagLifespan( txt, 2.00 )
call SetTextTagFadepoint( txt, 0.75 )
call SetTextTagVelocityBJ( txt, 64, 90 )
if (GetBooleanAnd((GetRandomInt(1, 100)<= BashChance()),GetBooleanAnd((SquareRoot((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))<= BashDistance()),GetBooleanAnd(not (IsUnitType(udg_GDD_DamageSource,UNIT_TYPE_STRUCTURE)),GetBooleanOr(IsUnitType(udg_GDD_DamageSource, UNIT_TYPE_GROUND), IsUnitType(udg_GDD_DamageSource, UNIT_TYPE_SNARED)))))) then
call StunUnit(udg_GDD_DamageSource, 1.00, false)
call UnitDamageTarget(udg_GDD_DamagedUnit, udg_GDD_DamageSource, 40, true, false, ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl", udg_GDD_DamagedUnit, "chest" ) )
endif
if ( GetUnitStateSwap(UNIT_STATE_MANA, udg_GDD_DamagedUnit) >= ManaCost() ) then
call SetUnitState( udg_GDD_DamagedUnit, UNIT_STATE_MANA, ( GetUnitStateSwap(UNIT_STATE_MANA, udg_GDD_DamagedUnit) - ManaCost() ) )
else
call SetUnitState( udg_GDD_DamagedUnit, UNIT_STATE_LIFE, ( GetUnitStateSwap(UNIT_STATE_LIFE, udg_GDD_DamagedUnit) - ( ( ( ManaCost() - GetUnitStateSwap(UNIT_STATE_MANA, udg_GDD_DamagedUnit) ) / ManaCost() ) * udg_GDD_Damage ) ) )
call IssueImmediateOrder( udg_GDD_DamagedUnit, "undefend" )
call SetUnitState( udg_GDD_DamagedUnit, UNIT_STATE_MANA, 0.00 )
endif
if ( udg_dam > 0.00 ) then
set udg_u = udg_GDD_DamagedUnit
call TimerStart(udg_t, 0.01, false, function AddHP )
endif
endif
set deltaA = 0.00
set A = 0.00
set mana = 0
set life = 0
set maxlife = 0
set x1 = 0.00
set y1 = 0.00
set x2 = 0.00
set y2 = 0.00
set txt = null
set dummy = null
endfunction
function InitTrig_Defend takes nothing returns nothing
set gg_trg_Defend = CreateTrigger( )
call TriggerRegisterVariableEvent( gg_trg_Defend, "udg_GDD_Event", EQUAL, 0 )
call TriggerAddCondition( gg_trg_Defend, Condition(function Trig_Attack_Block_Conditions))
call TriggerAddAction( gg_trg_Defend, function Trig_Attack_Block_Actions )
set udg_Deactivate = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( udg_Deactivate, EVENT_PLAYER_UNIT_ISSUED_ORDER )
call TriggerAddCondition( udg_Deactivate, Condition( function Trig_Deactivate_Conditions ) )
call TriggerAddAction( udg_Deactivate, function Trig_Deactivate_Actions )
set udg_Activate = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( udg_Activate, EVENT_PLAYER_UNIT_ISSUED_ORDER )
call TriggerAddCondition( udg_Activate, Condition( function Trig_Activate_Conditions ) )
call TriggerAddAction( udg_Activate, function Trig_Activate_Actions )
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
// GUI-Friendly Damage Detection -- v1.2.1 -- by Weep
// http:// www.thehelper.net/forums/showthread.php?t=137957
//
// Requires: only this trigger and its variables.
//
// -- What? --
// This snippet provides a leak-free, GUI-friendly implementation of an "any unit takes
// damage" event. It requires no JASS knowledge to use.
//
// It uses the Game - Value Of Real Variable event as its method of activating other
// triggers, and passes the event responses through a few globals.
//
// -- Why? --
// The traditional GUI method of setting up a trigger than runs when any unit is damaged
// leaks trigger events. This snippet is easy to implement and removes the need to do
// you own GUI damage detection setup.
//
// -- How To Implement --
// 0. Before you copy triggers that use GDD into a new map, you need to copy over GDD
// with its GDD Variable Creator trigger, or there will be a problem: the variables
// won't be automatically created correctly.
//
// 1. Be sure "Automatically create unknown variables while pasting trigger data" is
// enabled in the World Editor general preferences.
// 2. Copy this trigger category ("GDD") and paste it into your map.
// (Alternately: create the variables listed in the globals block below, create a
// trigger named "GUI Friendly Damage Detection", and paste in this entire text.)
// 3. Create your damage triggers using Game - Value Of Real Variable as the event,
// select GDD_Event as the variable, and leave the rest of the settings to the default
// "becomes Equal to 0.00".
// The event responses are the following variables:
// GDD_Damage is the amount of damage, replacing Event Response - Damage Taken.
// GDD_DamagedUnit is the damaged unit, replacing Event Response - Triggering Unit.
// Triggering Unit can still be used, if you need to use waits.
// Read the -- Notes -- section below for more info.
// GDD_DamageSource is the damaging unit, replacing Event Response - Damage Source.
//
// -- Notes --
// GDD's event response variables are not wait-safe; you can't use them after a wait in
// a trigger. If you need to use waits, Triggering Unit (a.k.a. GetTriggerUnit()) can
// be used in place of GDD_DamageSource. There is no usable wait-safe equivalent to
// Event Damage or Damage Source; you'll need to save the values yourself.
//
// Don't write any values to the variables used as the event responses, or it will mess
// up any other triggers using this snippet for their triggering. Only use their values.
//
// This uses arrays, so can detect damage for a maximum of 8190 units at a time, and
// cleans up data at a rate of 33.33 per second, by default. This should be enough for
// most maps, but if you want to change the rate, change the value returned in the
// GDD_RecycleRate function at the top of the code, below.
//
// By default, GDD will not register units that have Locust at the moment of their
// entering the game, and will not recognize when they take damage (which can only
// happen if the Locust ability is later removed from the unit.) To allow a unit to have
// Locust yet still cause GDD damage events if Locust is removed, you can either design
// the unit to not have Locust by default and add it via triggers after creation, or
// edit the GDD_Filter function at the top of the code, below.
//
// -- Credits --
// Captain Griffin on wc3c.net for the research and concept of GroupRefresh.
//
// Credit in your map not needed, but please include this README.
//
// -- Version History --
// 1.2.1: Minor code cleaning. Added configuration functions. Updated documentation.
// 1.2.0: Made this snippet work properly with recursive damage.
// 1.1.1: Added a check in order to not index units with the Locust ability (dummy units).
// If you wish to check for damage taken by a unit that is unselectable, do not
// give the unit-type Locust in the object editor; instead, add the Locust ability
// 'Aloc' via a trigger after its creation, then remove it.
// 1.1.0: Added a check in case a unit gets moved out of the map and back.
// 1.0.0: First release.
//===================================================================
// Configurables.
function GDD_RecycleRate takes nothing returns real //The rate at which the system checks units to see if they've been removed from the game
return 0.03
endfunction
function GDD_Filter takes unit u returns boolean //The condition a unit has to pass to have it registered for damage detection
return GetUnitAbilityLevel(u, 'Aloc') == 0 //By default, the system ignores Locust units, because they normally can't take damage anyway
endfunction
//===================================================================
// This is just for reference.
// If you use JassHelper, you could uncomment this section instead of creating the variables in the trigger editor.
// globals
// real udg_GDD_Event = 0.
// real udg_GDD_Damage = 0.
// unit udg_GDD_DamagedUnit
// unit udg_GDD_DamageSource
// trigger array udg_GDD__TriggerArray
// integer array udg_GDD__Integers
// unit array udg_GDD__UnitArray
// group udg_GDD__LeftMapGroup = CreateGroup()
// endglobals
//===================================================================
// System code follows. Don't touch!
function GDD_Event takes nothing returns boolean
local unit damagedcache = udg_GDD_DamagedUnit
local unit damagingcache = udg_GDD_DamageSource
local real damagecache = udg_GDD_Damage
set udg_GDD_DamagedUnit = GetTriggerUnit()
set udg_GDD_DamageSource = GetEventDamageSource()
set udg_GDD_Damage = GetEventDamage()
set udg_GDD_Event = 1.
set udg_GDD_Event = 0.
set udg_GDD_DamagedUnit = damagedcache
set udg_GDD_DamageSource = damagingcache
set udg_GDD_Damage = damagecache
set damagedcache = null
set damagingcache = null
return false
endfunction
function GDD_AddDetection takes nothing returns boolean
// if(udg_GDD__Integers[0] > 8190) then
// call BJDebugMsg("GDD: Too many damage events! Decrease number of units present in the map or increase recycle rate.")
// ***Recycle rate is specified in the GDD_RecycleRate function at the top of the code. Smaller is faster.***
// return
// endif
if(IsUnitInGroup(GetFilterUnit(), udg_GDD__LeftMapGroup)) then
call GroupRemoveUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
elseif(GDD_Filter(GetFilterUnit())) then
set udg_GDD__Integers[0] = udg_GDD__Integers[0]+1
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = GetFilterUnit()
set udg_GDD__TriggerArray[udg_GDD__Integers[0]] = CreateTrigger()
call TriggerRegisterUnitEvent(udg_GDD__TriggerArray[udg_GDD__Integers[0]], udg_GDD__UnitArray[udg_GDD__Integers[0]], EVENT_UNIT_DAMAGED)
call TriggerAddCondition(udg_GDD__TriggerArray[udg_GDD__Integers[0]], Condition(function GDD_Event))
endif
return false
endfunction
function GDD_PreplacedDetection takes nothing returns nothing
local group g = CreateGroup()
local integer i = 0
loop
call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function GDD_AddDetection))
set i = i+1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call DestroyGroup(g)
set g = null
endfunction
function GDD_GroupRefresh takes nothing returns nothing
// Based on GroupRefresh by Captain Griffen on wc3c.net
if (bj_slotControlUsed[5063] == true) then
call GroupClear(udg_GDD__LeftMapGroup)
set bj_slotControlUsed[5063] = false
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetEnumUnit())
endfunction
function GDD_Recycle takes nothing returns nothing
if(udg_GDD__Integers[0] <= 0) then
return
elseif(udg_GDD__Integers[1] <= 0) then
set udg_GDD__Integers[1] = udg_GDD__Integers[0]
endif
if(GetUnitTypeId(udg_GDD__UnitArray[udg_GDD__Integers[1]]) == 0) then
call DestroyTrigger(udg_GDD__TriggerArray[udg_GDD__Integers[1]])
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = null
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = udg_GDD__TriggerArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[1]] = udg_GDD__UnitArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = null
set udg_GDD__Integers[0] = udg_GDD__Integers[0]-1
endif
set udg_GDD__Integers[1] = udg_GDD__Integers[1]-1
endfunction
function GDD_LeaveMap takes nothing returns boolean
local boolean cached = bj_slotControlUsed[5063]
if(udg_GDD__Integers[2] < 64) then
set udg_GDD__Integers[2] = udg_GDD__Integers[2]+1
else
set bj_slotControlUsed[5063] = true
call ForGroup(udg_GDD__LeftMapGroup, function GDD_GroupRefresh)
set udg_GDD__Integers[2] = 0
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
set bj_slotControlUsed[5063] = cached
return false
endfunction
// ===========================================================================
function InitTrig_GUI_Friendly_Damage_Detection takes nothing returns nothing
local region r = CreateRegion()
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(), r, Condition(function GDD_AddDetection))
call TriggerRegisterLeaveRegion(CreateTrigger(), r, Condition(function GDD_LeaveMap))
call GDD_PreplacedDetection()
call TimerStart(CreateTimer(), GDD_RecycleRate(), true, function GDD_Recycle)
set r = null
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library StunSystem
// Stun Unit System by The_Witcher
//
// to import just copy this trigger into your map
//
// included functions:
//
// StunUnit( Victim, StunTime, OnlyIfMagicImmune? )
// unit real boolean
//
// IsUnitStunned ( Victim ) --> returns boolean
// unit
//
// SETUP
globals
// this is the special effect created on a stunned unit
private constant string SFX = "Abilities\\Spells\\Human\\Thunderclap\\ThunderclapTarget.mdl"
// this is the attachement point for sfx
private constant string ATTACHMENT_POINT = "overhead"
endglobals
//End of setup
private struct StunData
unit u
real t
real a
effect sfx
static StunData array StunnedUnits
static integer total = 0
static timer tim = CreateTimer()
static hashtable h = InitHashtable()
static method Stun takes nothing returns nothing
local integer i = 0
local StunData dat
loop
exitwhen i >= .total
set dat = .StunnedUnits[i]
if dat.t <= 0 or (IsUnitType(dat.u,UNIT_TYPE_DEAD) or GetUnitTypeId(dat.u) == 0 ) then
call dat.destroy()
set .StunnedUnits[i] = .StunnedUnits[.total]
set i = i - 1
else
set dat.t = dat.t - 0.01
call SetUnitPosition(dat.u,GetUnitX(dat.u),GetUnitY(dat.u))
call SetUnitFacing(dat.u,dat.a)
endif
set i = i + 1
endloop
if .total == 0 then
call PauseTimer(.tim)
endif
endmethod
static method create takes unit u, real time, boolean check returns StunData
local StunData dat = StunData.allocate()
set dat.t = time
set dat.u = u
set dat.a = GetUnitFacing(u)
set dat.sfx = AddSpecialEffectTarget(SFX,u,ATTACHMENT_POINT)
if .total == 0 then
call TimerStart(.tim,0.01,true,function StunData.Stun)
endif
call SaveInteger(.h,GetHandleId(u),0,1)
set .StunnedUnits[.total]=dat
set .total = .total + 1
return dat
endmethod
method onDestroy takes nothing returns nothing
call DestroyEffect(.sfx)
set .total = .total-1
call FlushChildHashtable(.h,GetHandleId(.u))
endmethod
endstruct
function IsUnitStunned takes unit u returns boolean
return LoadInteger(StunData.h,GetHandleId(u),0) != 0
endfunction
function StunUnit takes unit u, real time, boolean check returns nothing
if (not check or not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and not IsUnitStunned(u) then
call StunData.create(u, time, check)
endif
endfunction
endlibrary