Name | Type | is_array | initial_value |
//TESH.scrollpos=12
//TESH.alwaysfold=0
//! zinc
/***************************************************************************************************/
/* Beacon by 'Ciebron' v1.0 ************************************************************************/
/***************************************************************************************************/
/***************************************************************************************************/
/***************************************************************************************************/
/* Beacon is a mix between damage and healing ******************************************************/
/* When cast it will check the units around the hero if they are an enemy or ally ******************/
/* If there are mostly friendlys it will heal the ally with lowest hp in range *********************/
/* If there are mostly enemys it will damage the the target with least health **********************/
/* If there are equally ally and enemys it will heal ***********************************************/
/***************************************************************************************************/
/* This spell where mostly made cause i wanted too try zinc ****************************************/
/* Which i enjoy more then Jass since it more flexible *********************************************/
/* Enjoy *******************************************************************************************/
/***************************************************************************************************/
library Beacon requires TimerUtils, Table
{
private
{
/* RawCode of the spell */
constant integer Abil_Id = 'A000';
/* How many levels the spell have (you need to setup them in the setup function if you add more) */
constant integer Levels = 3;
/* Effect on the hero when he heals */
constant string HealSfx = "Abilities\\Spells\\NightElf\\Rejuvenation\\RejuvenationTarget.mdl";
/* Effect on the hero when he damages */
constant string DmgSfx = "war3mapImported\\GrudgeAura.mdx";
/* Effect on the targets of the heal */
constant string HealHitSfx = "Abilities\\Spells\\Items\\ResourceItems\\ResourceEffectTarget.mdl";
/* Effect on the targets when it damages */
constant string DmgHitSfx = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl";
/* Where on the caster the heal effect will attach */
constant string HealAttachPoint = "overhead";
/* Where on the caster the dmg effect will attach */
constant string DmgAttachPoint = "origin";
/* Damagetype it will deal (Duh!) */
constant damagetype DamageType = DAMAGE_TYPE_MAGIC;
/* AttackType it will deal */
constant attacktype AttackType = ATTACK_TYPE_MAGIC;
/* Dont touch these if you dont know what you are doing */
real Amount[Levels], Radius[Levels], Interval[Levels];
integer Count[Levels];
}
function Setup()
{
/* Amount it will heal and damage */
Amount[1] = 35;
Amount[2] = 50;
Amount[3] = 65;
/* The radius it will heal and damage in */
Radius[1] = 450;
Radius[2] = 500;
Radius[3] = 550;
/* How often it will heal or damage */
Interval[1] = 0.75;
Interval[2] = 0.65;
Interval[3] = 0.55;
/* how many times it will damage or heal before it ends */
Count[1] = 12;
Count[2] = 15;
Count[3] = 20;
}
/* Below this line its the filterfunction for the units it will effect */
/* This filter check when the spell is cast (when check how many enemys and friendlys there are */
function Match() -> boolean
{
unit f = GetFilterUnit();
unit t = GetTriggerUnit();
boolean ok = f != t && ! IsUnitType(f,UNIT_TYPE_STRUCTURE);
f = null;
t = null;
return ok;
}
/* This filter check which units too heal */
function HealMatch() -> boolean
{
unit f = GetFilterUnit();
Data this = Dat;
boolean ok = GetWidgetLife(f) > .305 && IsUnitAlly(f,this.Player) && ! IsUnitType(f,UNIT_TYPE_MAGIC_IMMUNE) && ! IsUnitType(f,UNIT_TYPE_STRUCTURE) && GetWidgetLife(f) != GetUnitState(f,UNIT_STATE_MAX_LIFE);
f = null;
return ok;
}
/* This filter check which units too damage */
function DmgMatch() -> boolean
{
unit f = GetFilterUnit();
Data this = Dat;
boolean ok = GetWidgetLife(f) > .305 && IsUnitEnemy(f,this.Player) && ! IsUnitType(f,UNIT_TYPE_MAGIC_IMMUNE) && ! IsUnitType(f,UNIT_TYPE_STRUCTURE);
f = null;
return ok;
}
/***************************************/
/******* Here does the Setup end *******/
/***************************************/
private
{
boolexpr Bool, HealBool, DmgBool;
integer Enemy_Count, Friendly_Count;
player Player_Pass;
unit TempU, TempLow;
real TempR, TempRLow;
Data Dat;
HandleTable Table;
}
private struct Data
{
unit caster;
integer lvl, count;
effect Sfx;
player Player;
timer Timer;
static group Grp = CreateGroup();
static method create(unit u) -> thistype
{
thistype this = thistype.allocate();
this.caster = u;
this.Player = GetOwningPlayer(this.caster);
this.lvl = GetUnitAbilityLevel(this.caster,Abil_Id);
this.Timer = CreateTimer();
this.count = Count[this.lvl];
GroupEnumUnitsInRange(Grp,GetUnitX(this.caster),GetUnitY(this.caster),Radius[this.lvl],Bool);
Player_Pass = this.Player;
Enemy_Count = 0;
Friendly_Count = 0;
ForGroup(Grp,function()
{
if (IsUnitEnemy(GetEnumUnit(),Player_Pass))
{
Enemy_Count = Enemy_Count + 1;
}
else
{
Friendly_Count = Friendly_Count + 1;
}
} );
SetTimerData(this.Timer,this);
if (Friendly_Count >= Enemy_Count)
{
TimerStart(this.Timer,Interval[this.lvl],true,function thistype.HealLoop);
this.Sfx = AddSpecialEffectTarget(HealSfx,this.caster,HealAttachPoint);
}
else
{
TimerStart(this.Timer,Interval[this.lvl],true,function thistype.DmgLoop);
this.Sfx = AddSpecialEffectTarget(DmgSfx,this.caster,DmgAttachPoint);
}
Table[this.caster] = this;
return this;
}
static method HealLoop()
{
timer Timer = GetExpiredTimer();
thistype this = GetTimerData(Timer);
Dat = this;
GroupEnumUnitsInRange(Grp,GetUnitX(this.caster),GetUnitY(this.caster),Radius[this.lvl],HealBool);
TempLow = FirstOfGroup(Grp);
TempRLow = GetWidgetLife(TempLow);
ForGroup(Grp,function()
{
unit Enum = GetEnumUnit();
TempR = GetWidgetLife(Enum);
if (TempR < TempRLow)
{
TempRLow = TempR;
TempLow = Enum;
}
Enum = null;
});
SetWidgetLife(TempLow,GetWidgetLife(TempLow)+Amount[this.lvl]);
DestroyEffect(AddSpecialEffectTarget(HealHitSfx,TempLow,"origin"));
this.count = this.count - 1;
if (this.count <= 0)
{
this.destroy();
}
}
static method DmgLoop()
{
timer Timer = GetExpiredTimer();
thistype this = GetTimerData(Timer);
Dat = this;
GroupEnumUnitsInRange(Grp,GetUnitX(this.caster),GetUnitY(this.caster),Radius[this.lvl],DmgBool);
TempLow = FirstOfGroup(Grp);
TempRLow = GetWidgetLife(TempLow);
ForGroup(Grp,function()
{
unit Enum = GetEnumUnit();
TempR = GetWidgetLife(Enum);
if (TempR < TempRLow)
{
TempRLow = TempR;
TempLow = Enum;
}
Enum = null;
});
UnitDamageTarget(this.caster,TempLow,Amount[this.lvl],false,false,AttackType,DamageType,null);
DestroyEffect(AddSpecialEffectTarget(DmgHitSfx,TempLow,"origin"));
this.count = this.count - 1;
if (this.count <= 0)
{
this.destroy();
}
}
method onDestroy()
{
DestroyEffect(this.Sfx);
PauseTimer(this.Timer);
DestroyTimer(this.Timer);
Table.flush(this.caster);
}
}
function OnCast() -> boolean
{
Data this;
unit caster;
if (GetSpellAbilityId()==Abil_Id)
{
caster = GetTriggerUnit();
if (Table.exists(caster))
{
this = Table[caster];
this.destroy();
}
Data.create(caster);
caster = null;
}
return false;
}
function onInit()
{
trigger trig = CreateTrigger();
TriggerAddCondition(trig,Condition(function OnCast));
TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_EFFECT);
Bool = Filter(function Match);
DmgBool = Filter(function DmgMatch);
HealBool = Filter(function HealMatch);
static if(LIBRARY_Table)
{
Table = HandleTable.create();
}
Setup();
}
}
//! endzinc
//TESH.scrollpos=37
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.0
//* ------------
//*
//* 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
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
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht = InitHashtable()
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=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//* 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.wc3c.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.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = false
private constant boolean USE_FLEXIBLE_OFFSET = true
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
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
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
set tT[0]=CreateTimer()
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
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==ARRAY_SIZE) 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
local integer i=0
local integer o=-1
local boolean oops = false
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Initializer initializer Init
private function Init takes nothing returns nothing
call FogEnable(false)
call FogMaskEnable(false)
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 12)
call SuspendTimeOfDay(true)
call CreateUnit(Player(0),'Hpal',0,0,0)
endfunction
endlibrary