Viikuna
No Marlo no game.
- Reaction score
- 265
And so we return and beging again.
I decided to make and submit a spell again, because its been so long since Ive done anything
Anyways, spell is called Quicksand and its pretty cool and eveything.
The spell is build of:
- 2 textures, made by me.
- One cool special effect model made by me.
- an awesome spell code made by me.
- 4 cool libraries from wc3c.net. ( I tried to use all
the standard systems, that people use in wc3c.net)
Required stuff: -vJass
Alright, I think there was all the needed information, so here comes the screenie:
That creenie is pretty bad, and you probably have no idea whats happening in it, so I try to tell what this spell is about.
When channeled, the spell turns soil under the enemy units in to deadly quicksand that swallows them under the ground. When channeling ends, units are violently pulled back to the surface through the hard rock. Deals damage and slows enemies.
-Well, that was maybe not so clear, but it basicly sucks units under the ground, which is kinda cool IMO. ( The one reason why I did this, was because I remember that Uberplayer once wondered how to move units below terrain level. )
Okay, here is the test map, go and play it: View attachment [Spell]Quicksand1.1.w3x
Note that I just finished making this spell few minutes ago, so its probably full of bugs. What I need is you to test it and tell me what to change.
I also post code here, so all those cool vJasser guyes from this site can read my code and tell me how to impro it:
I wanna thank all those cool guyes who did NewGen and vJass and all other modding community people and their families and all the guyes who have somehow affected to making of this spell.
Special credits go to Anitarf, Grim001, and Vexorian, because these guys made the systems this spell uses. Also I wanna thank Kitabatake, who helped me with some mdl editing stuff.
All kind of comments and spamming posts are welcome.
I decided to make and submit a spell again, because its been so long since Ive done anything
Anyways, spell is called Quicksand and its pretty cool and eveything.
The spell is build of:
- 2 textures, made by me.
- One cool special effect model made by me.
- an awesome spell code made by me.
- 4 cool libraries from wc3c.net. ( I tried to use all
the standard systems, that people use in wc3c.net)
Required stuff: -vJass
Alright, I think there was all the needed information, so here comes the screenie:
That creenie is pretty bad, and you probably have no idea whats happening in it, so I try to tell what this spell is about.
When channeled, the spell turns soil under the enemy units in to deadly quicksand that swallows them under the ground. When channeling ends, units are violently pulled back to the surface through the hard rock. Deals damage and slows enemies.
-Well, that was maybe not so clear, but it basicly sucks units under the ground, which is kinda cool IMO. ( The one reason why I did this, was because I remember that Uberplayer once wondered how to move units below terrain level. )
Okay, here is the test map, go and play it: View attachment [Spell]Quicksand1.1.w3x
Note that I just finished making this spell few minutes ago, so its probably full of bugs. What I need is you to test it and tell me what to change.
I also post code here, so all those cool vJasser guyes from this site can read my code and tell me how to impro it:
JASS:
scope Quicksand initializer init/* requires Table, TimerUtils,
... AutoIndex, SpellEvent */
globals
private constant integer ABILITY_ID = 039;Qsnd039;
private constant real ABILITY_TIMER_PERIOD = .75
// Dealer:
private constant integer DUMMY_CASTER_ID = 039;deal039;
// SlowBuff stuff:
private constant integer SLOW_ABILITY_ID = 039;Qslw039;
private constant string SLOW_ORDER = "slow"
// ImpaleStuff:
private constant integer IMPALE_ABILITY_ID = 039;AImp039;
private constant string IMPALE_ORDER = "impale"
private constant integer IMPALE_BUFF_ID =039;BImp039;
// Effects:
private constant string SAND_EFFECT /*
*/ = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
private constant string DAMAGE_EFFECT /*
*/ = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
private constant string DAMAGE_EFFECT_ATTACH_POINT = "chest"
private constant real PRELOAD_X = 0.0
private constant real PRELOAD_Y = 0.0
// Image stuff:
private constant real IMAGE_TIMER_PERIOD = .025
// the duration that it takes for sand texture to fade in
private constant string SAND_FILE_PATH = "Quicksand.blp"
private constant real SAND_FADE_IN_TIME = .5
// the duration that it takes for ground texture to dissapear
private constant string GROUND_FILE_PATH = "SolidSurface.blp"
private constant real GROUND_FADE_IN_TIME = 1.
private constant real GROUND_FADE_OUT_TIME = 4.
endglobals
// Spells Area of Effect is calculated using this formula:
private constant function RADIUS takes integer level returns real
return 275. + ( level * 0. )
endfunction
// The amount of damage spell deals, is calculated using this formula:
private constant function DAMAGE takes integer level returns real
return 30. + ( level + 50. )
endfunction
private constant function IMAGE_SIZE takes real radius returns real
return 600. + (radius * 0.0)
endfunction
// A wrapper function for your very own death check
private function IsUnitAlive takes unit u returns boolean
return GetWidgetLife(u) > .405
endfunction
// for your UnitDamageTargetEx function, or whatever thing you use
private function SpellDamageTarget takes unit damager, unit damaged, real damage returns nothing
call UnitDamageTarget(damager,damaged,damage,false,false, /*
*/ ATTACK_TYPE_NORMAL,DAMAGE_TYPE_MAGIC,WEAPON_TYPE_WHOKNOWS)
endfunction
/*
====================================================================================
====================================================================================
And so we return and begin again
spell code v1.1 made by Viikuna
====================================================================================
====================================================================================
*/
private struct Image
// this is a simple image struct,
// which currently has no other use than
// an ability to change images alpha value overtime
// if you have some better image struct with loads of cool stuff,
// you can get rid of this and use it instead
image image
private real a = 255.
private real change = 0
private integer ticks = 0
private real target = 0
private static timer timer = CreateTimer()
private static integer Count = 0
private static Image array array
static method create takes string path, real x, real y, real size, integer alpha, integer imagetype returns Image
local Image this=Image.allocate()
set .image=CreateImage(path,size,size,0,x-(size/2),y-(size/2),0,0,0,0,imagetype)
call SetImageRenderAlways(.image,true)
call ShowImage(.image,true)
set .a=alpha
call SetImageColor(.image,255,255,255,alpha)
return this
endmethod
private static method periodic takes nothing returns nothing
local integer i=0
local Image this
loop
exitwhen i>=.Count
set this=.array<i>
if .ticks>0 then
set .a=.a+.change
set .ticks=.ticks-1
else
set .a=.target
set .Count=.Count-1
if .Count>0 then
set .array<i>=.array[.Count]
set i=i-1
else
call PauseTimer(.timer)
endif
endif
call SetImageColor(.image,255,255,255,R2I(.a))
set i=i+1
endloop
endmethod
method operator alpha takes nothing returns integer
return R2I(.a)
endmethod
method fade takes integer alpha, real duration returns nothing
if duration<=0.0 then
set .a=alpha
call SetImageColor(.image,255,255,255,alpha)
else
set .target=alpha
set .ticks=R2I(duration/IMAGE_TIMER_PERIOD)
set .change=(alpha-.a)/.ticks
if .Count==0 then
call TimerStart(.timer,IMAGE_TIMER_PERIOD,true,function Image.periodic)
endif
set .array[.Count]=this
set .Count=.Count+1
endif
endmethod
private method onDestroy takes nothing returns nothing
call DestroyImage(.image)
endmethod
endstruct
private struct DummyCaster
// This little dummy caster struct can only cast
// unit targetted and non channeling spells,
// but thats everything we need for this ability anyways.
// If you have some cool DummyCaster struct with lots of nice and smooth methods
// feel free to get rid of this and use it instead.
readonly integer abilityId = 0
readonly integer orderId = 0
private integer lvl = 1
private player player = Player(13)
private unit unit
method operator owner takes nothing returns player
return .player
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(.unit,p,false)
set .player=p
endmethod
method operator level takes nothing returns integer
return .lvl
endmethod
method operator level= takes integer i returns nothing
call SetUnitAbilityLevel(.unit,.abilityId,i)
set .lvl=i
endmethod
static method create takes integer abilityId, integer orderId returns DummyCaster
local DummyCaster this=DummyCaster.allocate()
set .unit=CreateUnit(Player(13),DUMMY_CASTER_ID,0.0,0.0,0.0)
call UnitAddAbility(.unit,abilityId)
set .abilityId=abilityId
set .orderId=orderId
return this
endmethod
method castOnUnit takes unit target returns nothing
call SetUnitX(.unit,GetUnitX(target))
call SetUnitY(.unit,GetUnitY(target))
call IssueTargetOrderById(.unit,.orderId,target)
endmethod
endstruct
private struct ability
// This struct contains the actual spell stuff
unit caster
integer level
real x
real y
real radius
real size
real damage
private timer timer
private Image sand
private Image ground
private boolean hasImpale
private DummyCaster Impale
private static DummyCaster Slow
private static group EnumGroup=CreateGroup()
private static filterfunc DrownToSandFilter
private static filterfunc DamageAndSlowActions
private static ability Temp
private static unit TempUnit
static ability array UnitData
private static method drownToSandFilterFunction takes nothing returns boolean
local ability this=.Temp
set .TempUnit=GetFilterUnit()
return /*
*/ IsUnitAlive(.TempUnit) /*
*/ and GetUnitFlyHeight(.TempUnit) == 0.0 /*
*/ and IsUnitEnemy(.TempUnit,GetOwningPlayer(.caster)) /*
*/ and not IsUnitType(.TempUnit,UNIT_TYPE_STRUCTURE) /*
*/ and GetUnitAbilityLevel(.TempUnit,IMPALE_BUFF_ID) == 0
endmethod
private method drownToSandActions takes nothing returns nothing
local unit u
local integer i=0
set .Temp=this
call GroupEnumUnitsInRange(.EnumGroup,.x,.y,.radius,.DrownToSandFilter)
set u=GroupPickRandomUnit(.EnumGroup)
if u!=null then
call .Impale.castOnUnit( u )
call DestroyEffect(AddSpecialEffect(SAND_EFFECT,GetUnitX(u),GetUnitY(u)))
call GroupRemoveUnit(.EnumGroup,u)
endif
set u=null
endmethod
private static method periodic takes nothing returns nothing
local timer t=GetExpiredTimer()
local ability this=GetTimerData(t)
call .drownToSandActions()
endmethod
static method startEffect takes nothing returns nothing
local timer t=GetExpiredTimer()
local ability this=GetTimerData(t)
call .drownToSandActions()
call TimerStart(t,ABILITY_TIMER_PERIOD,true,function ability.periodic)
endmethod
static method create takes unit caster, real x, real y returns ability
local ability this=ability.allocate()
set .caster=caster
set .x=x
set .y=y
set .level=GetUnitAbilityLevel(.caster,ABILITY_ID)
set .radius=RADIUS(.level)
set .damage=DAMAGE(.level)
set .size=IMAGE_SIZE(.radius)
if not .hasImpale then
set .hasImpale=true
set .Impale=DummyCaster.create(IMPALE_ABILITY_ID,OrderId(IMPALE_ORDER))
endif
set .timer=NewTimer()
set .sand=Image.create(SAND_FILE_PATH,.x,.y,.size,0,2)
call .sand.fade(255,SAND_FADE_IN_TIME )
call SetTimerData(.timer,this)
call TimerStart(.timer,SAND_FADE_IN_TIME ,false,function ability.startEffect)
return this
endmethod
static method damageAndSlow takes nothing returns boolean
local unit u=GetFilterUnit()
local ability this=ability.Temp
if GetUnitAbilityLevel(u,IMPALE_BUFF_ID) > 0 then
call UnitRemoveAbility(u,IMPALE_BUFF_ID)
call SpellDamageTarget(.caster,u,.damage)
call .Slow.castOnUnit(u)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT,u,DAMAGE_EFFECT_ATTACH_POINT))
endif
set u=null
return false
endmethod
static method didAndGoon takes nothing returns nothing
local timer t=GetExpiredTimer()
local ability this=GetTimerData(t)
call .ground.destroy()
call .sand.destroy()
call .destroy()
call ReleaseTimer(t)
endmethod
static method fadeGround takes nothing returns nothing
local timer t=GetExpiredTimer()
local ability this=GetTimerData(t)
call .ground.fade(0,GROUND_FADE_OUT_TIME)
call TimerStart(.timer,GROUND_FADE_OUT_TIME,false,function ability.didAndGoon)
endmethod
static method endEffect takes nothing returns nothing
local timer t=GetExpiredTimer()
local ability this=GetTimerData(t)
set .Temp=this
set .Slow.level=.level
call GroupEnumUnitsInRange(.EnumGroup,.x,.y,.radius,.DamageAndSlowActions)
call TimerStart(.timer,1.,false,function ability.fadeGround)
endmethod
method endSpell takes nothing returns nothing
call PauseTimer(.timer)
set .ground=Image.create(GROUND_FILE_PATH,.x,.y,.size,0,2)
call .ground.fade(255,GROUND_FADE_IN_TIME)
call .sand.fade(0,GROUND_FADE_IN_TIME)
call TimerStart(.timer,GROUND_FADE_IN_TIME,false,function ability.endEffect)
endmethod
static method onInit takes nothing returns nothing
set .Slow = DummyCaster.create(SLOW_ABILITY_ID,OrderId(SLOW_ORDER))
set .DrownToSandFilter = Filter( function ability.drownToSandFilterFunction)
set .DamageAndSlowActions = Filter( function ability.damageAndSlow)
endmethod
endstruct
// These function would go to ability struct,
// but I couldnt get function interfaces to work with static methods,
// so here they are.
private function onEndCast takes nothing returns nothing
local ability this = ability.UnitData[ GetUnitId(SpellEvent.CastingUnit) ]
call this.endSpell()
endfunction
private function onEffect takes nothing returns nothing
set ability.UnitData [ GetUnitId(SpellEvent.CastingUnit) ] /*
*/ = ability.create /*
*/ (SpellEvent.CastingUnit,SpellEvent.TargetX,SpellEvent.TargetY)
endfunction
private function init takes nothing returns nothing
call RegisterSpellEffectResponse(ABILITY_ID,onEffect)
call RegisterSpellEndCastResponse(ABILITY_ID,onEndCast)
call DestroyEffect(AddSpecialEffect(SAND_EFFECT,PRELOAD_X,PRELOAD_Y))
call DestroyEffect(AddSpecialEffect(DAMAGE_EFFECT,PRELOAD_X,PRELOAD_Y))
endfunction
endscope</i></i>
I wanna thank all those cool guyes who did NewGen and vJass and all other modding community people and their families and all the guyes who have somehow affected to making of this spell.
Special credits go to Anitarf, Grim001, and Vexorian, because these guys made the systems this spell uses. Also I wanna thank Kitabatake, who helped me with some mdl editing stuff.
All kind of comments and spamming posts are welcome.