//***************************************************************************
//***************************************************************************
//***************************************************************************
//
// * * * * * *
// * * * * * * * *
// * * * * * * . *
// Warlike Spirit
// ***** ----------------
// By: Hara
//
// - Sepll Description:
// ` - Call a force of spirit that fly around the caster,
// spirit attacks the enemy in 1000 range and also steal 6/7/8/9% of
// their hit points and return it to the caster.
// But if the enemy die after spirit has stealed their hit points
// a Warlike Spirit will be created. Spirit lasts 5.5/6.5/7.5/8.5 seconds.
//
// - Installation:
// - Import/copy Warlike Spirit code and Custom Units/Abilities/Buffs to your map
// - Change the SPELL_ID, WARLIKE_SPIRIT_ID, WS_STUN_ABI, DUMMY_ID if needed
// - You may view the raw ID of the objects by pressing CTRL+D in the object editor
// - You may play with the configurables below
//
//
//***************************************************************************
//***************************************************************************
//***************************************************************************
//! zinc
library WarlikeSpirit{
//------------ CONFIGURATION ------------//
//Spirit count called per cast
private constant integer SPIRIT_COUNT = 10;
//Dummy rawcode, change if needed
private constant integer DUMMY_ID = 'e000';
//Warlike spirit rawcode, change if needed
private constant integer WARLIKE_SPIRIT_ID = 'e001';
//Spell rawcode, change if needed
private constant integer SPELL_ID = 'A000';
//Warlke Spirit's stun rawcode, change if needed
private constant integer WS_STUN_ABI = 'A001';
//An effect when spirit hits enemy
private constant string HIT_EFFECT = "Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl";
//Enemy's effect attachment
private constant string HIT_ATTACHMENT = "chest" ;
//An effect when spirit return hit points to the caster
private constant string HIT_CASTER = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl";
//Caster's effect attachment
private constant string CASTER_ATTACHMENT = "origin" ;
//Distance between the caster and the force of spirit
private constant real DISTANCE = 250;
//Hit distance between spirit and enemy
private constant real HIT_DISTANCE = 3;
//Spirit missle speed/PERIODIC when it attacks enemy
private constant real TARGET_SPEED = 25;
//Spirit swivel speed/PERIODIC
private constant real SPIRIT_SPEED = 5;
//Spirit's enemy detection range
private constant real RANGE = 1000;
//Time before fire spirit to the enemy
private constant real SHOOT_TIME = 0.5;
//Default scale value of warlike spirit
private constant real WS_DEFAULT_SCALE = 1.5;
//Timer period
private constant real PERIODIC = .0312500;
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC;
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_COLD;
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_METAL_HEAVY_BASH;
//***************************************//
//------------ NON-CONFIGURATION --------//
private constant timer TIMER = CreateTimer();
private constant real inBetween = 6.283188 / SPIRIT_COUNT ; //6.283188 = bj_PI*2
private constant group G = CreateGroup();
private constant location locZ = Location(0,0);
private integer MUI = -1;
private integer MUI2[];
private unit Caster;
private player CasterOwner;
//***************************************//
//IsUnitAlive func
function UnitAlive (unit filterUnit) -> boolean{
return (!IsUnitType(filterUnit,UNIT_TYPE_DEAD) && GetUnitTypeId(filterUnit)!=0);
}
//Spell damage
function getDamagePercentage (integer abiLevel) -> real{
return (abiLevel + 5.);
}
//Spell duration
function getDuration (integer abiLevel) -> real {
return (abiLevel + 4.5);
}
//Summon warlike spirit duration
function getWarlikeDuration (integer abiLevel) -> real {
return (abiLevel + 1.5);
}
//--------------------------------
function DistanceBetween (unit A, unit B) -> real {
real dx = GetUnitX(B) - GetUnitX(A);
real dy = GetUnitY(B) - GetUnitY(A);
return SquareRoot(dx * dx + dy * dy);
}
function filterFunc() -> boolean{
return UnitAlive(GetFilterUnit());
}
function GroupPickEnum (){
unit tempU = GetEnumUnit();
if(UnitAlive(tempU) && IsUnitEnemy(tempU,CasterOwner)) {
bj_groupRandomConsidered = bj_groupRandomConsidered + 1;
if (GetRandomInt(1,bj_groupRandomConsidered) == 1)
bj_groupRandomCurrentPick = tempU;
}
tempU = null;
}
function RandomUnit (group whichGroup) -> unit{
bj_groupRandomConsidered = 0;
bj_groupRandomCurrentPick = null;
ForGroup(whichGroup, function GroupPickEnum);
return bj_groupRandomCurrentPick;
}
//-----------------------------------
private struct WarlikeSpiritPlugin{
unit caster;
unit dummy;
unit target;
unit warlike_spirit=null;
real angle;
real height = 0.;
real time = 0.;
real maxDis;
real maxHei;
real dmg;
real ws_dur;
integer abiLevel;
player casterOwner;
boolean fire = false;
boolean back = false;
boolean pause = false;
static method release( integer tempInt ){
//Recycle
MUI2[tempInt] = MUI2[MUI];
MUI2[MUI] = -1;
MUI = MUI - 1;
if( MUI == -1 )
PauseTimer(TIMER);
}
method getHeight(boolean b,real tempX,real tempY) -> real{
real tempA;
if( b ){
tempA = GetUnitFlyHeight(target) + 1.;
height = tempA - (tempA * ( (DistanceBetween(target , dummy)/maxDis) ));
return bj_RADTODEG*Atan2(GetUnitY(target)-tempY,GetUnitX(target)-tempX ) * bj_DEGTORAD;
}else{
tempA = GetUnitFlyHeight(dummy) + 1.;
height = maxHei * ( (DistanceBetween(caster,dummy)/maxDis) );
return bj_RADTODEG*Atan2(GetUnitY(caster)-tempY,GetUnitX(caster)-tempX ) * bj_DEGTORAD;
}
}
method shootBack(){
maxHei = GetUnitFlyHeight(dummy);
maxDis = DistanceBetween(caster , dummy);
back = true;
target = null;
}
static method onPeriodic(){
thistype this;
integer tempInt = 0;
integer tempInt2;
real tempX;
real tempY;
real tempA;
unit tempU = null;
while(tempInt <= MUI){
this = MUI2[tempInt];
//
if( UnitAlive(dummy) && UnitAlive(caster) ){
if( fire ) {
if( !pause ){
tempX = GetUnitX(dummy);
tempY = GetUnitY(dummy);
tempA = getHeight(target != null,tempX,tempY);
tempX = tempX + TARGET_SPEED * Cos(tempA);
tempY = tempY + TARGET_SPEED * Sin(tempA);
if( !back ){
if( UnitAlive(target) ){
if( IsUnitInRangeXY(target,tempX,tempY,HIT_DISTANCE) ){
DestroyEffect(AddSpecialEffectTarget(HIT_EFFECT,target,HIT_ATTACHMENT));
dmg=(GetUnitState(target,UNIT_STATE_MAX_LIFE)*dmg)/100.;
UnitDamageTarget(caster,target,dmg,true,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE);
//Check if the enemy is alive
if(!UnitAlive(target)){
//Create warlike spirit
warlike_spirit = CreateUnit(casterOwner,WARLIKE_SPIRIT_ID,tempX,tempY,0.);
SetUnitAbilityLevel(warlike_spirit,WS_STUN_ABI,abiLevel);
UnitApplyTimedLife(warlike_spirit,0x0,ws_dur);
SetUnitScale(dummy,0.,0.,0.);
pause = true;
}else
shootBack();
}
}else
KillUnit(dummy);
}else{
if( IsUnitInRangeXY(caster,tempX,tempY,HIT_DISTANCE) ){
DestroyEffect(AddSpecialEffectTarget(HIT_CASTER,caster,CASTER_ATTACHMENT));
if( warlike_spirit != null ){
dmg = dmg/2;
warlike_spirit = null;
}
SetWidgetLife(caster,GetWidgetLife(caster)+dmg);
RemoveUnit(dummy);
dummy = null;
caster = null;
casterOwner = null;
this.destroy();
release(tempInt);
}
}
}else if ( !UnitAlive( warlike_spirit ) ){
pause = false;
SetUnitX(dummy,GetUnitX(warlike_spirit));
SetUnitY(dummy,GetUnitY(warlike_spirit));
SetUnitScale(dummy,WS_DEFAULT_SCALE,WS_DEFAULT_SCALE,0.);
shootBack();
}
}else {
//
angle = angle + SPIRIT_SPEED;
tempA = angle * bj_DEGTORAD;
tempX = GetUnitX(caster) + DISTANCE * Cos(tempA);
tempY = GetUnitY(caster) + DISTANCE * Sin(tempA);
//
if( target == null ){
CasterOwner = casterOwner;
GroupEnumUnitsInRange(G,tempX,tempY,RANGE,function filterFunc);
target = RandomUnit(G);
GroupClear(G);
}else{
if( time <= 0. ){
fire = true;
maxDis = DistanceBetween(target , dummy);
UnitPauseTimedLife(dummy,true);
}else
time = time - PERIODIC;
}
}
if( !pause ){
MoveLocation(locZ,tempX,tempY);
SetUnitX(dummy,tempX);
SetUnitY(dummy,tempY);
SetUnitFlyHeight(dummy,GetLocationZ(locZ)+height,99999.);
}
}else{
this.destroy();
release(tempInt);
}
//
tempInt = tempInt + 1;
}
}
static method new_WarlikeWpirit(unit whichDummy , real whichAngle, real shootTime){
thistype this = allocate();
MUI = MUI + 1;
MUI2[MUI] = this;
caster = Caster;
dummy = whichDummy;
abiLevel = GetUnitAbilityLevel(caster,SPELL_ID);
angle = whichAngle;
casterOwner = CasterOwner;
time = shootTime;
dmg = getDamagePercentage(abiLevel);
ws_dur = getWarlikeDuration(abiLevel);
UnitApplyTimedLife(dummy,0x0,getDuration(abiLevel));
if(MUI == 0){
TimerStart(TIMER,PERIODIC,true,function thistype.onPeriodic);
}
}
}
//
function onCast() -> boolean{
real angle = 360./SPIRIT_COUNT;
real x;
real y;
real tempX;
real tempY;
real tempA;
real tempT = 0.;
integer temp = 1;
Caster = GetTriggerUnit();
CasterOwner = GetTriggerPlayer();
tempX = GetUnitX(Caster);
tempY = GetUnitY(Caster);
while(temp <= SPIRIT_COUNT){
tempA = temp * inBetween;
x = tempX + DISTANCE * Cos(tempA);
y = tempY + DISTANCE * Sin(tempA);
//
tempT = tempT + SHOOT_TIME;
WarlikeSpiritPlugin.new_WarlikeWpirit( CreateUnit(CasterOwner,DUMMY_ID,x,y,0.) , bj_RADTODEG*Atan2(y-tempY,x-tempX ) , tempT );
//
temp = temp + 1;
}
return false;
}
function onInit(){
integer i = 0;
trigger trig = CreateTrigger();
while(i<bj_MAX_PLAYERS){
TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null);
i = i + 1;
}
TriggerAddCondition(trig,function onCast);
}
}
//! endzinc