//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once TimerUtils initializer init
//*********************************************************************
//* TimerUtils (Blue flavor for 1.23b or later)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//* limit, which means that if more than 408000 handle ids
//* are used in your map, TimerUtils might fail, this
//* value is quite big and it is much bigger than the
//* timer limit in Red flavor.
//*
//********************************************************************
//==================================================================================================
globals
private hashtable hasht //I <3 blizz
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
call SaveInteger(hasht,0, GetHandleId(t), value)
endfunction
function GetTimerData takes timer t returns integer
return LoadInteger(hasht, 0, GetHandleId(t))
endfunction
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
set tT[0]=CreateTimer()
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==8191) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
set hasht = InitHashtable()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
This is the JESP standard document, if a map contains this document it means that there are
spells that follow this standard.
Spells of this map that follow the standard:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- "Sun Ray"
Advantages of the Standard
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- Implementing spells that follow the standard is relatively easier than implementing JASS
spells that don't follow the standard.
- Configuring/Balancing spells that follow the standard is relatively easier than
implementing JASS spells that don't follow the standard.
- Users may do the following procedure to make a new ability that uses the spell's script :
* Create a new Trigger with a name (case sensitive)
* Convert that trigger to custom text.
* Copy the spell's script to a text editor like Notepad or your OS equivalent.
* Replace the spell's Code name with the name you used on the trigger.
* Copy the new text to the new trigger
* Duplicate the Spell's original objects to have new ones for the new spell script.
You are now able to use that new version of the spell.
- In case two guys give the same name to 2 different spells, there are no conflict problems
because you can easily change the name of one of them
What is the JESP Standard?
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The JESP standard was designed to make spell sharing much better. And to make sure JASS
enhanced spells follow a rule, to prevent chaos.
What does JESP Standard stands for?
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
JASS
Enhanced
Spell
Pseudotemplate
Requirements for a spell to follow the JESP Standard
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- The spell is written in JASS
- The spell is 100% multi instanceable.
- The spell script is ready to support spells of any number of levels.
(default config header is not required to support all of them)
- The Spell has an specific code name.
- The Spell's trigger must have the spell's codename as name
- The Spell's InitTrig function must be named: InitTrig_<CodeName>
- The spell has a configuration header.
- It is mandatory that rawcodes of objects are configurable in the header.
- All the spell's specific code is inside the spell's "Trigger" (Trigger== that custom text
slot that world editor calls Trigger, the spell may use as many 'trigger' OBJECTS as needed)
- Every spell-specific single identifier or key works in such a way that reproducing the
spell's trigger but after performing a text-replace of codename with another name (and thus
renaming the cloned trigger to the new code name) it won't cause compile errors / conflicts
when playing the map.
- There is no code inside the spell's "Trigger" that is not specific to the spell.
- There are no requirements for GUI variables that are specific to the spell. If a system
used by the spell requires GUI variables the code for the system must be outside the "Trigger"
- Eyecandy and spell's balance have to be easy to configure
- The name of the author should be included in the spell's script.
- The reason to exist of this standard is spell sharing. This document should be included
within the map. And it should specify which spell follows the standard, in the top list.
//TESH.scrollpos=0
//TESH.alwaysfold=0
//===========================================================================
//The caster uses his light powers to summon waves of Light Rays around him,
//damaging enemy Undead units nearby, and healing allied units
//
//Requires TimerUtils
//
//@author Flame_Phoenix
//
//@credits
//- Blackroot (aka Modeler), the first creator, gave me the basic idea and concept
//- the-thingy, by telling me where to use bj_DEGTORAD and why
//- Alexander244, for helping me fixing a bug with parenthesis ... lol
//- Anitarf, for suggesting a slight improvement for the code
//- My first teacher of vJASS: Blue_Jeans
//- All other people I forgot or ignored
//
//@version 1.5
//===========================================================================
scope SunRay initializer Init
//needed for spell's core, don't change
private keyword tmpPlayer
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
globals
private constant integer AID = 'A000' //raecode of ability
private constant real LIGHT_RADIUS = 100 //radius of each single light
private constant string LIGHT_EFFECT = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" //the effect of the light
private constant real LIGHT_INTERVAL = 0.2 //the interval that separates each light
private constant boolean CASTER_ANG = true //if true we consider the caster's facing angle, else we don't
endglobals
private constant function Damage takes integer level returns real
return level * 5. //the damage the enemy undead units will take
endfunction
private constant function Heal takes integer level returns real
return level * 10. //the heal your allied units will receive
endfunction
private constant function LightNumber takes integer level returns integer
//each Sun Ray of (4 + level) lights
return 4 + level
endfunction
private constant function RayNumber takes integer level returns integer
//the number of Sun Rays of the spell
return 5 + level
endfunction
private function AllyTargets takes nothing returns boolean
//what allied units will be affected by the spell
return IsUnitAlly(GetFilterUnit(), tmpPlayer) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
private function EnemyTargets takes nothing returns boolean
//what enemy units will be affected by the spell
return IsUnitEnemy(GetFilterUnit(), tmpPlayer) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false) and (GetUnitRace(GetFilterUnit()) == RACE_UNDEAD) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================
globals
private player tmpPlayer
private group g
private boolexpr enemies
private boolexpr allies
endglobals
private struct MyStruct
unit caster
real casterX
real casterY
integer level
timer t
real distInc
integer lightsCreated
static method create takes unit caster returns MyStruct
local MyStruct data = MyStruct.allocate()
//setting variables
set data.caster = caster
set data.casterX = GetUnitX(caster)
set data.casterY = GetUnitY(caster)
set data.level = GetUnitAbilityLevel(caster, AID)
set data.t = NewTimer() //this will create the lights =)
set data.distInc = LIGHT_RADIUS
set data.lightsCreated = 0
return data
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
//===========================================================================
private function CreateLights takes nothing returns nothing
local MyStruct data = MyStruct(GetTimerData(GetExpiredTimer()))
local unit f
local real x //the X position of the effect
local real y //the Y position of the effect
local real angle
local integer i //a counter for the loop
//we set this variable depending if we want to consider the
//caster's angle or not
if CASTER_ANG then
set angle = GetUnitFacing(data.caster)
else
set angle = 0
endif
//if we didn't pass the number of lights, than we create lights
//again, however, if we did, we end everything
if data.lightsCreated < LightNumber(data.level) then
//in this outer loop we create the Sun Rays
set i = 0
loop
exitwhen(i == RayNumber(data.level))
set x = data.casterX + data.distInc * Cos(angle)
set y = data.casterY + data.distInc * Sin(angle)
call DestroyEffect(AddSpecialEffect(LIGHT_EFFECT, x, y))
//in this first double inner loop we select the enemy units and damage them
set tmpPlayer = GetOwningPlayer(data.caster)
call GroupEnumUnitsInRange(g, x, y, LIGHT_RADIUS, enemies)
loop
set f = FirstOfGroup(g)
exitwhen(f == null)
call GroupRemoveUnit(g, f)
call UnitDamageTarget(data.caster, f, Damage(data.level), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
endloop
//in this second double inner loop we select the allied units and heal them
set tmpPlayer = GetOwningPlayer(data.caster)
call GroupEnumUnitsInRange(g, x, y, LIGHT_RADIUS, allies)
loop
set f = FirstOfGroup(g)
exitwhen(f == null)
call GroupRemoveUnit(g, f)
call SetWidgetLife(f, GetWidgetLife(f) + Heal(data.level))
endloop
set angle = angle + (360 / RayNumber(data.level)) * bj_DEGTORAD
set i = i + 1
endloop
set data.distInc = data.distInc + LIGHT_RADIUS
set data.lightsCreated = data.lightsCreated + 1
else
call data.destroy()
endif
endfunction
//===========================================================================
private function Conditions takes nothing returns boolean
local MyStruct data
if GetSpellAbilityId() == AID then
set data = MyStruct.create(GetTriggerUnit())
//now we start the timer !
call SetTimerData(data.t, integer(data))
call TimerStart(data.t, LIGHT_INTERVAL, true, function CreateLights)
endif
return false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger SunRayTrg = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( SunRayTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( SunRayTrg, Condition( function Conditions ) )
set SunRayTrg = null
//setting globals
set g = CreateGroup()
set enemies = Condition(function EnemyTargets)
set allies = Condition(function AllyTargets)
//preloading effect
call Preload(LIGHT_EFFECT)
endfunction
endscope