HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

The Kraken

08-25-2006, 11:34 PM#1
moyack
The Kraken V 1.5
Second Place in spell session contest #7

Zoom (requires log in)

type of Spell: Channeling

Info: Ideal spell as a hero's ultimate ability. The caster selects an AOE where the Kraken's Tentacles will arise, attacking the enemies in the AOE. These tentacles has the bash ability and they can be attacked by the enemy, protecting in some way the caster unit. If an enemy enters to the AOE or a tantacle is killed, a new tentacle will rise from the water, and the opposite situation happens if one enemy dies or leaves the AOE. If the spell is casted in walkable pathing, the tentacles won't appear. Because is a channeling spell, it can be stopped via stun, casting other ability or killing the caster. the spell duraton is the duration of the spell animation x number of times the unit do the spell animation.

This spell should be seen as one that summon, control and remove dynamically allied units in a AOE. From version 1.1 you can change easily the summoned unit and the path type where they will be summoned.

I hope you like it ;)

LATEST UPDATE (25/10/2006): Now this spell has sound configuration, so you can change the sound produced by this spell.

changelog

Edit (25/10/2006)
  • Added sound functionality (AT LAST!!! HAHAAA) Thanks to Infrane for its sound function :D

Edit (09/09/2006)
  • Improved orderID detection (Thanks a lot Blu )
  • Improved micro AI, so tentacles attack the nearest enemy in the AOE, keeping them in the range and eliminating the "doubt" in tentacles about which enemy should it attack.
  • Removed Kraken scream, it creates a buggy behavior sometimes
  • Added installation instructions
  • Added 2 Water witch controlled by Neutral Hostile player in order to show compatibility with AI players.

Edit (05/09/2006)
  • Now use sound configuration and capabilities via GameCache
  • Fixed some leaks and code optimized (Blu_da_Noob suggestions).

Edit (03/09/2006)
  • Improved distance comparasion function.
  • Added sound configuration. Now you can change the sound path.
  • Now the kraken roar keeps until the spell finishes

Edit (27/08/2006)
  • Now really complies with the JESP (Thanks Rising_Dusk)
  • Code optimized with constant functions
  • Added level functionallity
  • Added Pathing modifier, now you can change the pathing type so it can be more usable in other maps
  • Code more readable

Edit (17/08/2006):
  • Removed Wander in tentacles
  • Added Birth animation
  • MUI fully implemented
  • complies with JESP standard (if not, please tell me )


Edit (13/08/2006)
  • Added Wander ability to give to tentacles an erratic behaviour
  • Added expiration time to the tentacles
  • Now the tentacles try to keep in the AOE, "surrounding" the enemy
  • Improved splash effect in tentacles
  • Fixed bug when the spell is casted between water and land, some tentacles appeared and dissappeared very fast
  • For testing, the Naga Summoner can cast Divine Shield, to avoid stun spells.


Edit (09/08/2006):
  • Tentacles are now spell immunes
  • Fixed bug with Storm Bolt, now any stunning ability can block the spell.
  • Multiinstanciability improved
  • Splash special effect added when the tentacle is created
  • Added Kraken Scream when the spell begins

Source Code (Updated 25/10/2006)

Collapse JASS:
//***************************************************************************************************************
//*                                                                                                             *
//*                                             The Kraken Spell.                                               *
//*                                                By Moyack.                                                   *
//*                                                  V.1.5.                                                     *
//*                                                                                                             *
//*                               Spell created for the Spell session contest 7                                 * 
//*                                               August 2006.                                                  *
//*                                                                                                             *
//***************************************************************************************************************

//***************************************************************************************************************
//* Here's the constant functions where you can modify the ability properties
//*
constant function Kraken_SpellID takes nothing returns integer 
    // Dummy Ability Rawcode. This spell works fine with an ability based on BLIZZARD.
    return 'A000' 
endfunction

constant function Kraken_Tentacle takes nothing returns integer
    // Tentacle Unit Rawcode
    return 'n001' 
endfunction

constant function Kraken_AOE takes nothing returns real
    // spell Area of Effect. It's recommended to use the same AOE from dummy ability
    return 400.00 
endfunction

constant function Kraken_SpellDuration takes nothing returns real
    // Spell animation time. Check in the caster model the spell animation time to set this value
    return 1.00 
endfunction

constant function Kraken_Waves takes integer level returns real
    // Number of times that the spell animation is played by the caster
    return 20.00 + 5.00*(level - 1.00) 
endfunction

constant function Kraken_MinTentacleScale takes nothing returns real
    //Set the tentacles' minimun scale
    return 0.5  
endfunction

constant function Kraken_MaxTentacleScale takes nothing returns real
    //Set the tentacles' maximun scale
    return 1.0  
endfunction

constant function Kraken_SummonEffect takes nothing returns string
    //Let you edit the summon effect model path
    return "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" 
endfunction

constant function Kraken_PathAllowed takes nothing returns pathingtype
    // Let you choose in which path type the spell should work.

    return PATHING_TYPE_FLOATABILITY // (Default) to change, add comment symbol to this line

    //return PATHING_TYPE_WALKABILITY
    //return PATHING_TYPE_ANY
    //                                Other pathing type are (just in case):
    //return PATHING_TYPE_AMPHIBIOUSPATHING
    //return PATHING_TYPE_BLIGHTPATHING
    //return PATHING_TYPE_BUILDABILITY
    //return PATHING_TYPE_FLYABILITY
    //return PATHING_TYPE_PEONHARVESTPATHING
endfunction

constant function Kraken_SoundFile takes nothing returns string
    //Let you edit the sound effect path
    return "Sound\\Ambient\\DoodadEffects\\SargerasRoar.wav" 
endfunction

function Kraken_TentacleAbilities takes unit SummonedUnit returns nothing 
    // You can add more abilities to the summoned tentacle with this function 
    call UnitAddAbility(SummonedUnit, 'ACmi')//Makes tentacles resistant to spells.
    call UnitAddAbility(SummonedUnit, 'ACbh')//Add Bash(neutral hostile) ability to unit
endfunction

//***************************************************************************************************************
//*                                                                                                             *
//*                                             Custom Functions                                                *
//*                                                                                                             *
//***************************************************************************************************************

//***************************************************************************************************************
//* This function kills the tentacles when finished the spell
//*
function Kraken_KillTentacles takes nothing returns nothing
    local unit u = GetEnumUnit()
    local location l = GetUnitLoc(u)
    call DestroyEffect(AddSpecialEffectLoc( Kraken_SummonEffect(), l ) )
    call RemoveUnit( GetEnumUnit() )
    call RemoveLocation(l)
    set l = null
    set u = null
endfunction

//***************************************************************************************************************
//* Detects if a matching enemy unit is alive
//*
function Kraken_Is_Enemy_Alive takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction

//***************************************************************************************************************
//* Determines if a location has the pathing condition
//* (used to detect if the tentacle can be summoned in that location) 
//*
function Kraken_IsPointNotAllowed takes location Point returns boolean 
    return IsTerrainPathable(GetLocationX(Point),GetLocationY(Point), Kraken_PathAllowed())
endfunction

//***************************************************************************************************************
//* Create a group copy
//*
function Kraken_CopyGroup takes group G returns group
    set bj_groupAddGroupDest = CreateGroup()
    call ForGroup(G, function GroupAddGroupEnum)
    return bj_groupAddGroupDest
endfunction

//***************************************************************************************************************
//* Distance function, just for calculations
//* Function writen by blu_da_noob. Modified from Rising_Dusk's distance function.
//*

function Kraken_Square_Distance_Function takes real x1, real y1, real x2, real y2 returns real
    return ((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
endfunction

function Kraken_IsNotPointInRange takes real x1, real y1, real x2, real y2, real range returns boolean
    return Kraken_Square_Distance_Function( x1, y1, x2, y2 ) >= range * range
endfunction

//***************************************************************************************************************
//* Gets the nearest enemy in the AOE and order attacker to attack it.
//*
function Kraken_AttackNearestEnemy takes unit Attacker, location center returns nothing
    local location p = GetUnitLoc(Attacker)
    local unit u
    local location pu
    local real ux
    local real uy
    local real Ax = GetLocationX(p)
    local real Ay = GetLocationY(p)
    local group E = GetUnitsInRangeOfLocMatching(Kraken_AOE(), center, Condition(function Kraken_Is_Enemy_Alive))
    local real d
    local real md = Kraken_AOE()
    local unit nu = null
    local location po
    loop
        set u = FirstOfGroup(E)
        exitwhen u == null
        set pu = GetUnitLoc(u)
        set ux = GetLocationX(pu)
        set uy = GetLocationY(pu)
        set d = Kraken_Square_Distance_Function(Ax, Ay, ux, uy)
        if d <= md then
            set md = d
            set nu = u
        endif
        call GroupRemoveUnit(E, u)
        call RemoveLocation(pu)
    endloop
    if nu == null then
        set po = PolarProjectionBJ(center, GetRandomReal(Kraken_AOE()*0.50, Kraken_AOE()*0.7), GetRandomReal(0, 360))
        call IssuePointOrderLoc(Attacker, "move", po)
        call RemoveLocation(po)
    else
        call IssueTargetOrder( Attacker, "attack", nu )
    endif
    call RemoveLocation(p)
    call DestroyGroup(E)
    set p = null
    set pu = null
    set po = null
    set E = null
    set nu = null 
endfunction

//***************************************************************************************************************
//* This is a micro AI that avoid tentacles to leave the AOE
//*
function Kraken_TentacleIsInRange takes location Loc, real Range, group G returns nothing
    local group Temp = Kraken_CopyGroup(G)
    local unit u
    local real ux
    local real uy
    local real lx = GetLocationX(Loc)
    local real ly = GetLocationY(Loc)
    local location l
    loop
        set u = FirstOfGroup(Temp)
        set l = GetUnitLoc(u)
        exitwhen u == null
        if Kraken_IsPointNotAllowed(l) then
            call KillUnit(u)
        endif
        set ux = GetLocationX(l)
        set uy = GetLocationY(l)
        if Kraken_IsNotPointInRange( ux, uy, lx, ly, 0.9 * Range ) then
            call Kraken_AttackNearestEnemy(u, Loc)
        endif
        call GroupRemoveUnit(Temp, u)
        call RemoveLocation(l)
    endloop
    call DestroyGroup( Temp )
    set Temp = null
    set l = null
    set u = null
endfunction

//***************************************************************************************************************
//* Checks if the caster is alive, is still casting the spell or if it has been stunned
//*
function Kraken_CheckConditions takes timer Tempo, unit CasterUnit, integer SpellOrderID returns real
    local real value = TimerGetRemaining(Tempo)
    // checks if the caster has changed its order. 
    if GetUnitCurrentOrder(CasterUnit) != SpellOrderID then
        set value = 0.0
    endif
    // checks if the caster is dead
    if GetWidgetLife( CasterUnit ) <= 0.405 then 
        set value = 0.0
    endif
    return value
endfunction

//***************************************************************************************************************
//* Add sound to the tentacle. Script made by iNfraNe
//*
function Kraken_AddSound takes unit u, string file returns sound
  local sound s = CreateSound(file, false, true, false, 10, 10, "")
  call SetSoundPitch(s,GetRandomReal(0.75,1.25))
  call SetSoundVolume(s,128)
  call SetSoundDistances(s,600,10000)
  call SetSoundDistanceCutoff(s,3000)
  call AttachSoundToUnit(s,u)
  call StartSound(s)
  call KillSoundWhenDone(s)
  return s
endfunction


//***************************************************************************************************************
//*                                                                                                             *
//*                                            End Custom Functions                                             *
//*                                                                                                             *
//***************************************************************************************************************

//***************************************************************************************************************
//*                                                                                                             *
//*                                          Kraken Casting Functions                                           *
//*                                                                                                             *
//***************************************************************************************************************

//***************************************************************************************************************
//* Kraken casting condition
//*
function Kraken_Cast_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Kraken_SpellID() // Check Ability code
endfunction

//***************************************************************************************************************
//* Kraken casting Actions
//*
function Kraken_Cast_Actions takes nothing returns nothing
    local real SpellDuration = Kraken_SpellDuration()  
    // Spell duration will be SpellDuration*Kraken_Waves().
    local unit Caster = GetTriggerUnit()
    local player CasterPlayer = GetOwningPlayer(Caster)
    local unit Tent
    local location CastingPoint = GetSpellTargetLoc()
    local location TempPoint
    local group Enemies
    local group Tentacles = CreateGroup()
    local integer CountEnemies
    local integer CountTentacles
    local timer t
    local real ScaleTentacle
    local integer SpellOrder = GetUnitCurrentOrder(Caster)
    local sound s
    set t = CreateTimer()
    call TimerStart(t, SpellDuration * Kraken_Waves(GetUnitAbilityLevel(Caster, Kraken_SpellID())), false, null)
    loop
        set SpellDuration = Kraken_CheckConditions(t, Caster, SpellOrder)
        exitwhen SpellDuration <= 0.0
        set Enemies = GetUnitsInRangeOfLocMatching(Kraken_AOE(), CastingPoint, Condition(function Kraken_Is_Enemy_Alive)) 
        set CountEnemies = CountUnitsInGroup(Enemies)
        set CountTentacles = CountUnitsInGroup(Tentacles)
        if CountEnemies > CountTentacles then
            set TempPoint = PolarProjectionBJ(CastingPoint, GetRandomReal(Kraken_AOE()*0.50, Kraken_AOE()), GetRandomReal(0, 360))
            if not Kraken_IsPointNotAllowed(TempPoint) then
                set ScaleTentacle = GetRandomReal(Kraken_MinTentacleScale(), Kraken_MaxTentacleScale())
                call DestroyEffect(AddSpecialEffectLoc( Kraken_SummonEffect(), TempPoint ) )
                set Tent = CreateUnitAtLoc( CasterPlayer, Kraken_Tentacle(), TempPoint, GetRandomReal(0, 360))
                call SetUnitScale( Tent, ScaleTentacle, ScaleTentacle, ScaleTentacle )
                call GroupAddUnit( Tentacles, Tent )
                call UnitAddAbility( Tent, 'Aloc' )//Makes tentacles unselectable.
                call UnitRemoveAbility( Tent, 'Aloc' )//Make Tentacles vulnerables.
                call Kraken_TentacleAbilities( Tent )//Add abilities to the tentacle
                call UnitApplyTimedLife( Tent, 'BHwe', SpellDuration ) //Sets the tentacles' Expiration Time
                call SetUnitAnimation( Tent, "birth" )
                call TriggerSleepAction( 0.10 )
                call SetUnitAnimation( Tent, "stand" )
                set s = Kraken_AddSound(Tent, Kraken_SoundFile())
            endif
            call RemoveLocation( TempPoint )
        endif
        if CountEnemies < CountTentacles then
            set Tent = GroupPickRandomUnit( Tentacles )
            set TempPoint = GetUnitLoc(Tent)
            call DestroyEffect(AddSpecialEffectLoc( Kraken_SummonEffect(), TempPoint ) )
            call KillUnit( Tent )
            call RemoveLocation( TempPoint )
        endif
        call Kraken_TentacleIsInRange(CastingPoint, Kraken_AOE(), Tentacles)
        call DestroyGroup( Enemies )
        call TriggerSleepAction(0.10)
    endloop
    call ForGroup( Tentacles, function Kraken_KillTentacles )
    call DestroyTimer(t)
    call DestroyGroup( Tentacles )
    call RemoveLocation( CastingPoint )
    set t = null
    set Enemies = null
    set Tentacles = null
    set TempPoint = null
    set CastingPoint = null
    set Caster = null
    set Tent = null
    set CasterPlayer = null
    set s = null
endfunction

//********************************************************************
//********************************************************************
function InitTrig_Kraken takes nothing returns nothing // Trigger Initialization. DO NOT MODIFY
    local trigger Kraken = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( Kraken, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( Kraken, Condition( function Kraken_Cast_Conditions ) )
    call TriggerAddAction( Kraken, function Kraken_Cast_Actions )
    call Preload(Kraken_SummonEffect())
    call Preload(Kraken_SoundFile())
    set Kraken = null
endfunction

Attached Images
File type: jpgThe Kraken.jpg (122.7 KB)
Attached Files
File type: w3x(1)The Kraken.w3x (68.5 KB)
08-26-2006, 12:06 AM#2
Game_Slave
there aren't too many maps that could an ability like this because it requires so much water, also it looks unorganized... of course the kraken's tentacles aren't organized anyways but..
08-26-2006, 12:14 AM#3
sluggen
aye if its only useable in water then its use is verry limited, anyways looks like a nice spell.
08-26-2006, 12:30 AM#4
Rising_Dusk
Okay, first of all.
JESP manifest requires it be present in your map.
Follow this link and put that in the map in a disabled trigger somewhere.

All of the functions in the spell should have some sort of unique prefix.
This way you can easily change the prefix for a new spell or some such.

Also, you should use 'constant function's for your configuration functions.
Like --
Collapse JASS:
constant function YourPrefix_SpellID takes nothing returns integer // to complies with the JESP Standard
    return 'A000'
endfunction

Also, you should combine your boolean functions.
They're entirely too inefficient as is.
This --
Collapse JASS:
function Is_Enemy takes nothing returns boolean
    return ( IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) == true )
endfunction
function Is_Alive takes nothing returns boolean
    return ( GetWidgetLife(GetFilterUnit()) > 0.405 )
endfunction
function Is_Enemy_Alive takes nothing returns boolean
    return GetBooleanAnd( Is_Enemy(), Is_Alive() )
endfunction
Can be made into this --
Collapse JASS:
function YourPrefix_Check takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction
The BJ GetBooleanAnd() is pretty pointless.

I recommend optimizing your TentacleIsInRange function to not use the DistanceBetweenPoints() BJ.
This is a function I use in AotZ, you can skip the function call and just use what it does.
Collapse JASS:
function DistanceBetweenPointsXY takes real x1, real y1, real x2, real y2 returns real
    return SquareRoot((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
endfunction
(It uses the distance formula)

"UnitHasBuffBJ()" can be broken down into using "GetUnitAbilityLevel(YourUnit, YourBuffRaw) > 0"

"IsUnitDeadBJ()" can be broken down into "GetWidgetLife(YourUnit) < .405"

Everytime you use "set TempPoint = SomeLocation" you need to RemoveLocation that location.
Just removing it once after 50 declarations leaks the first 49.

There are lots and lots of BJs and a bunch of location leaks you need to work out.
Most of them are in the Trig_Kraken_Starts_Actions function.

Also, why do you use "bj_POLLED_WAIT_INTERVAL"?
I dont see any reason to use it over 0.0 or some such. (I didn't even read what it was used for, just saw it and wondered why)
08-26-2006, 12:44 AM#5
moyack
Quote:
Originally Posted by Game_Slave
there aren't too many maps that could an ability like this because it requires so much water, also it looks unorganized... of course the kraken's tentacles aren't organized anyways but..
Well, I tried to make a function that keeps units more or less arranged, but believe me, it's terribly laggy, it's a comparation of multiple distances made every 0.1 seconds, so it has two options, lag or not working, it won the second one :(

Quote:
Originally Posted by sluggen
aye if its only useable in water then its use is verry limited, anyways looks like a nice spell.
Well, I want to think this spell as a one that create and remove dynamically uncontrollable allied warriors that will attack in a AOE according to the number of enemies in that AOE, so if you remove the pathing condition from the script, it will work in normal terrain and it will be usable.

Edit: Thinking in that, I'll put a conditional to detect in which kind of path it should work.

@ Rising_Dusk: Wow, I have a lot of thing to modify :) I'll follow your recommendations and I'll update the map ASAP.
08-26-2006, 08:43 AM#6
Tim.
Also that image is huge. Fix please kay thanks bye.
08-26-2006, 01:42 PM#7
moyack
Screenshot fixed, code upgrade in progress.

Quote:
Originally Posted by Tim.
kay thanks bye.
08-26-2006, 01:53 PM#8
Rising_Dusk
Tim.'s trying to be all ninja-like.
Don't mind him. :P
08-26-2006, 06:19 PM#9
Tim.
Better, thanks.
08-27-2006, 11:32 PM#10
THE_END
Wow now THAT is a cool water spell, good job
08-28-2006, 03:34 AM#11
moyack
Just to say that I've updated my spell to 1.1.

Changelog 27/08/2006:
  • Now really complies with the JESP (Thanks Rising_Dusk)
  • Code optimized with constant functions
  • Added level functionallity
  • Added Pathing modifier, now you can change the pathing type so it can be more usable in other maps
  • Code more readable
08-28-2006, 04:31 AM#12
Rising_Dusk
You're getting closer. :D

Okay, to start it off, you're still missing your prefix in front of most of your functions.
The JESP manifest says ALL functions in your spell need to use the prefix unless they're widely used (Like KaTTaNa's handle vars or SimError).
So a function like Trig_Kraken_Actions you may consider renaming as just Kraken_Actions.
Or your TentacleIsInRange should be Kraken_TentacleIsInRange.

You picked up on the constant functions really well, you've even got them taking level parameters.
Your spell code is a lot easier to read as well, you added lots of description; well done there.

Most of your BJ usage is for ease of reading and the ones you use don't leak so far as I can tell.
So those should be fine.
Aside from that, the code looks great. Good job. :D

I noticed you use a global sound variable declared in the Sound Editor, that sort of thing might be useful to add to implementation.
(I noticed you didn't make mention of it in the implementation area)

Just fix those small things I mentioned like the prefix, etc. and you should be good to go.
08-28-2006, 03:03 PM#13
moyack
so close, so close!!!! :)

My godness, I forgot to change that, probably it was because I' was implementing the leveling option. I'll make that changes.

About the sound, I don't know how to call the sound files via JASS, is there a way to get the sound path in order to change it?? because I was thinking in adding the option to change the sound.
08-28-2006, 03:35 PM#14
blu_da_noob
I go away from home for a couple of days, and this happens? Hax.

I'll check it out a little later (after homework) and if it's all in order (thanks Dusk ^_^), approve.

Edit: ok

In this usage of DistanceBetweenPoints, you are using a comparison. You could speed it up by removing the square root (squaring both sides). So modifying Dusk's function:

Collapse JASS:
function IsPointInRange takes real x1, real y1, real x2, real y2, real range returns boolean
    return ((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) <= range * range
endfunction

I would recomend some constant functions so people can change the special effects created (currently naga building death). Also some constants to set the rawcode of abilities to add for spell immunity etc.

Your 'micro tentacle AI' won't actually prevent them leaving the AoE. If it sees a unit within its aquisition range to attack, the attack move command will allow it to follow that unit even if it isn't going in the direction of the attack move command.

Get rid of useless BJs.
08-28-2006, 07:23 PM#15
moyack
I'll do the changes as soon as I get home. Thanks blu :)

About the tentacle AI, I can tell you that it really works, without that, the tentacles would spread horribly, now they are more forced to stay in the range. ATM I'm thinking in a function not so heavy that detects the nearest enemy in the range. I'm going to see if it works