league, legends, aos, moba, twisted, fate, card, master, spellpack
Type:
Pack
Category:
vJASS
Twisted Fate the Card Master Spellpack
Introduction:
I decided to create this spellpack after seeing Lambdadelta's LeBlanc spellpack and realizing that I too would like to make a league of legends spellpack.
Notes:
Excuse the use of disabled ability icons which cause a green "missing icon" bug when the game is paused. I had the choice between choosing icons that had passive equivalents, importing icons, and this bugging method. You can obviously change these things if you prefer something else.
I've created this pack with the intention that the abilities matched the real ones in league of legends as close as possible. I have not modified game constants to make TF's stats at level 18 match those of league of legends identically, I just aimed for values that were "close enough" by choosing good stats and stats per level, etc.
The real twisted fate's stacked deck proc will register on blue cards but not on gold and red cards. This is something I could have done by implementing stacked deck within pick a card, but I chose not to because:
1: Most people don't even know that Twisted Fate works this way
2: I preferred code readability in this case over exact perfection
I've chosen not to follow exactly trivial code modification standards for his spells because this is designed to match twisted fate with extreme accuracy, not designed for others to modify.
Yes, this is fully MUI. Try not to drool on your keyboard.
Description of Spells:
Loaded Dice (Innate): Twisted Fate and his allies receive an additional 2 gold per unit kill. This bonus is lost while Twisted Fate is dead.
Wild Cards (Active): Twisted Fate fires 3 cards forward in a cone, passing through targets and dealing magic damage to enemy units in their way.
Pick a Card (Active): Cards flash over Twisted Fate's head in the following order: blue, then red, then gold, then blue again, starting randomly with any of the three and then continuing this pattern. When he uses the ability again, he picks the current card over his head, and the card picked adds a special effect to his next attack.
Blue Card: His next attack will deal magic damage and will restore mana equal to 65% of the damage done.
Red Card: His next attack will deal magic damage and will damage all enemies around the main target for the same amount as the target. All enemies hit will also be slowed for 2.5 seconds.
Gold Card: His next attack will deal magic damage and stun the target.
Stacked Deck (Passive): Twisted Fate gains bonus attack speed and cooldown reduction. In addition, every fourth autoattack will deal bonus magic damage.
Destiny (Active): Twisted Fate reveals all enemy champions, including stealthed champions, for a few seconds. While Destiny is active, Twisted Fate can use Gate once.
Gate (Active): After channeling for 2 seconds, Twisted Fate teleports to a target location, within a large radius.
library tfConsts initializer i globals //* SETUP SECTION publicconstantinteger TF_ID ='H000'//unit ID of "Card Master" unit publicconstantinteger CASTER_DUMMY_ID ='h002'//unit ID of "Caster (Dummy)" unit publicconstantinteger EYE_DUMMY_ID ='h003'//unit ID of "Eye (Dummy)" unit publicconstantinteger CARD_DUMMY_ID ='h001'//unit ID of "Card (Dummy)" unit
publicconstantinteger WILD_CARDS_ID ='A001'//ability ID of "Wild Cards" ability publicconstantinteger PICK_A_CARD_ID ='A002'//ability ID of "Pick a Card" ability publicconstantinteger DIS_PICK_A_CARD_ID='A006'//ability ID of "Pick a Card (disabled dummy ability" ability publicconstantinteger BLUE_CARD_ID ='A003'//ability ID of "Blue" ability publicconstantinteger DIS_BLUE_CARD_ID ='A007'//ability ID of "Blue (enabled card)" ability publicconstantinteger RED_CARD_ID ='A004'//ability ID of "Red" ability publicconstantinteger DIS_RED_CARD_ID ='A008'//ability ID of "Red (enabled card)" ability publicconstantinteger GOLD_CARD_ID ='A005'//ability ID of "Gold" ability publicconstantinteger DIS_GOLD_CARD_ID ='A009'//ability ID of "Gold (enabled card)" ability publicconstantinteger DUMMY_SLOW_ID ='A00B'//ability ID of "Slow (Red Card)" ability publicconstantinteger DUMMY_STUN_ID ='A00C'//ability ID of "Stun (Gold Card)" ability publicconstantinteger STACKED_DECK_ID ='A00D'//ability ID of "Stacked Deck" ability publicconstantinteger DESTINY_ID ='A00E'//ability ID of "Destiny" ability publicconstantinteger GATE_ID ='A00G'//ability ID of "Gate" ability publicconstantinteger DIS_GATE_ID ='A00F'//ability ID of "Gate (disabled)" ability //* END SETUP SECTION
//* CUSTOMIZE SECTION publicconstantreal FPS =1./30.//The period for smooth abilities. should be between 1./100. and 1./20. depending on necessary //performance. A good starting value for multiplayer should be 1./30. //* END CUSTOMIZE SECTION
privatefunction f takesnothingreturnsboolean localunit enum=GetEnumUnit() ifGetUnitTypeId(enum)==TF_ID then set hiddenCard[enum]=BLUE_CARD endif set enum=null returnfalse endfunction
privatefunction c takesnothingreturnsboolean localunit tU=GetTriggerUnit() ifGetUnitTypeId(tU)==TF_ID then set hiddenCard[tU]=BLUE_CARD endif set tU=null returnfalse endfunction
privatefunction i takesnothingreturnsnothing localtrigger initializeNewTFs=CreateTrigger() localregion reg=CreateRegion() set hiddenCard=HandleTable.create() set CARD_ID[BLUE_CARD]=BLUE_CARD_ID set CARD_ID[RED_CARD]=RED_CARD_ID set CARD_ID[GOLD_CARD]=GOLD_CARD_ID callRegionAddRect(reg,bj_mapInitialPlayableArea) callTriggerRegisterEnterRegion(initializeNewTFs,reg,null) callGroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,Filter(function f)) callTriggerAddCondition(initializeNewTFs,Condition(function c)) callDestroyGroup(grp) set grp=null set reg=null set initializeNewTFs=null endfunction endlibrary
loadedDice
Jass:
scope loadedDice initializer i globals privategroup grp=CreateGroup() endglobals
privatefunction c takesnothingreturnsboolean localunit FoG localunit kU=GetKillingUnit() localplayer kUOwner=GetOwningPlayer(kU) localboolean found=false ifIsUnitType(kU,UNIT_TYPE_HERO)then callGroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null) loop set FoG=FirstOfGroup(grp) exitwhen FoG==nullor found ifGetUnitTypeId(FoG)==tfConsts_TF_ID andIsUnitType(FoG,UNIT_TYPE_DEAD)==falseandIsUnitAlly(FoG,kUOwner)then set found=true endif callGroupRemoveUnit(grp,FoG) endloop if found then callSetPlayerState(kUOwner,PLAYER_STATE_RESOURCE_GOLD,GetPlayerState(kUOwner,PLAYER_STATE_RESOURCE_GOLD)+2) call TextTag_GoldBounty(kU,2,kUOwner) endif endif set kU=null returnfalse endfunction
privatefunction i takesnothingreturnsnothing localtrigger t=CreateTrigger() callTriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH) callTriggerAddCondition(t,Condition(function c)) set t=null endfunction endscope
wildCards
Jass:
scope wildCards initializer i privatestruct cardDat unit caster unitarray cards[3] realarray gradX[3] realarray gradY[3] real damage real traveled=0. group struckUnits endstruct
privatefunction setCardRandHue takesunit u returnsnothing localinteger i=GetRandomInt(0,2) if i==0then callSetUnitVertexColor(u,255,55,55,255) elseif i==1then callSetUnitVertexColor(u,255,245,55,255) else callSetUnitVertexColor(u,80,80,255,255) endif endfunction
privatefunction p takesnothingreturnsnothing localinteger index=0 localinteger tripleLooper local cardDat tempDat localreal uX localreal uY localunit FoG loop exitwhen index>dbIndex set tempDat=datDB[index] set tripleLooper=0 loop exitwhen tripleLooper>2 set uX=GetUnitX(tempDat.cards[tripleLooper])+tempDat.gradX[tripleLooper] set uY=GetUnitY(tempDat.cards[tripleLooper])+tempDat.gradY[tripleLooper] callSetUnitX(tempDat.cards[tripleLooper],uX) callSetUnitY(tempDat.cards[tripleLooper],uY) callGroupEnumUnitsInRange(grp,uX,uY,ENUM_RADIUS,null) loop set FoG=FirstOfGroup(grp) exitwhen FoG==null ifIsUnitEnemy(FoG,GetOwningPlayer(tempDat.caster))andIsUnitType(FoG,UNIT_TYPE_DEAD)==falseandIsUnitType(FoG,UNIT_TYPE_STRUCTURE)==falseandIsUnitInGroup(FoG,tempDat.struckUnits)==falsethen call DamageType_dealCodeDamage(tempDat.caster,FoG,tempDat.damage,DAMAGE_TYPE_MAGIC) callGroupAddUnit(tempDat.struckUnits,FoG) endif callGroupRemoveUnit(grp,FoG) endloop set tripleLooper=tripleLooper+1 endloop set tempDat.traveled=tempDat.traveled+PROJECTILE_SPEED*tfConsts_FPS if tempDat.traveled>=TOTAL_DISTANCE then callRemoveUnit(tempDat.cards[0]) callRemoveUnit(tempDat.cards[1]) callRemoveUnit(tempDat.cards[2]) callDestroyGroup(tempDat.struckUnits) call tempDat.destroy() set datDB[index]=datDB[dbIndex] set index=index-1 set dbIndex=dbIndex-1 if dbIndex==-1then callPauseTimer(time) endif endif set index=index+1 endloop endfunction
privatefunction cdr takesnothingreturnsnothing localtimer clock=GetExpiredTimer() local cdrDat tempDat=cdrTable[clock] callUnitRemoveAbility(tempDat.fate,tfConsts_WILD_CARDS_ID) callUnitAddAbility(tempDat.fate,tfConsts_WILD_CARDS_ID) call tempDat.destroy() call cdrTable.flush(clock) callDestroyTimer(clock) set clock=null endfunction
privatefunction c takesnothingreturnsboolean local cardDat tempDat local cdrDat ridiculousUseOfStruct localplayer owner localunit tU localreal tX localreal tY localreal tUX localreal tUY localreal angle localtimer cdrClock ifGetSpellAbilityId()==tfConsts_WILD_CARDS_ID then set tU=GetTriggerUnit() set owner=GetOwningPlayer(tU) set tX=GetSpellTargetX() set tY=GetSpellTargetY() set tUX=GetUnitX(tU) set tUY=GetUnitY(tU) set angle=Atan2(tY-tUY,tX-tUX) set tempDat=cardDat.create() set tempDat.cards[0]=CreateUnit(owner,tfConsts_CARD_DUMMY_ID,0.,0.,(angle-OFFSET_ANGLE)*bj_RADTODEG) set tempDat.cards[1]=CreateUnit(owner,tfConsts_CARD_DUMMY_ID,0.,0.,angle*bj_RADTODEG) set tempDat.cards[2]=CreateUnit(owner,tfConsts_CARD_DUMMY_ID,0.,0.,(angle+OFFSET_ANGLE)*bj_RADTODEG) call setCardRandHue(tempDat.cards[0]) call setCardRandHue(tempDat.cards[1]) call setCardRandHue(tempDat.cards[2]) callSetUnitX(tempDat.cards[0],tUX) callSetUnitX(tempDat.cards[1],tUX) callSetUnitX(tempDat.cards[2],tUX) callSetUnitY(tempDat.cards[0],tUY) callSetUnitY(tempDat.cards[1],tUY) callSetUnitY(tempDat.cards[2],tUY) set tempDat.gradX[0]=PROJECTILE_SPEED*tfConsts_FPS*Cos(angle-OFFSET_ANGLE) set tempDat.gradX[1]=PROJECTILE_SPEED*tfConsts_FPS*Cos(angle) set tempDat.gradX[2]=PROJECTILE_SPEED*tfConsts_FPS*Cos(angle+OFFSET_ANGLE) set tempDat.gradY[0]=PROJECTILE_SPEED*tfConsts_FPS*Sin(angle-OFFSET_ANGLE) set tempDat.gradY[1]=PROJECTILE_SPEED*tfConsts_FPS*Sin(angle) set tempDat.gradY[2]=PROJECTILE_SPEED*tfConsts_FPS*Sin(angle+OFFSET_ANGLE) set tempDat.struckUnits=CreateGroup() set tempDat.caster=tU set tempDat.damage=10.+50.*GetUnitAbilityLevel(tU,tfConsts_WILD_CARDS_ID)+.65*GetHeroInt(tU,true) set dbIndex=dbIndex+1 set datDB[dbIndex]=tempDat if dbIndex==0then callTimerStart(time,tfConsts_FPS,true,function p) endif ifGetUnitAbilityLevel(tempDat.caster,tfConsts_STACKED_DECK_ID)>0then set cdrClock=CreateTimer() set ridiculousUseOfStruct=cdrDat.create() set ridiculousUseOfStruct.fate=tempDat.caster set cdrTable[cdrClock]=ridiculousUseOfStruct callTimerStart(cdrClock,COOLDOWN-COOLDOWN*0.03*GetUnitAbilityLevel(tempDat.caster,tfConsts_STACKED_DECK_ID),false,function cdr) set cdrClock=null endif set tU=null endif returnfalse endfunction
privatefunction i takesnothingreturnsnothing localtrigger t=CreateTrigger() callTriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT) callTriggerAddCondition(t,Condition(function c)) set cdrTable=HandleTable.create() set t=null endfunction endscope
pickACard
Jass:
scope pickACard initializer i privatestruct pickDat unit fate integer revertAbilityLevel real elapsedTime boolean locked=false boolean thrown=false endstruct
privatestruct dummyCardDat unit dummy unit fate endstruct
privatefunction ddHandler takesnothingreturnsnothing localreal damage=GetEventDamage() localunit dSource=GetEventDamageSource() localunit tU localunit FoG local pickDat tempDat localplayer owner localsound colorSound localreal tX localreal tY localreal index if DamageType_get(damage)==DamageType_ATTACK andGetUnitTypeId(dSource)==tfConsts_TF_ID then set tempDat=fromFate[dSource] set tU=GetTriggerUnit() set owner=GetOwningPlayer(dSource) ifGetUnitAbilityLevel(dSource,tfConsts_DIS_BLUE_CARD_ID)>0then set colorSound=CreateSound(BLUESOUND,false,true,true,12700,12700,"") callSetSoundVolume(colorSound,127) callSetSoundPitch(colorSound,.75) callSetSoundPosition(colorSound,GetUnitX(tU),GetUnitY(tU),100.) callStartSound(colorSound) callKillSoundWhenDone(colorSound) ifIsUnitType(tU,UNIT_TYPE_DEAD)==falsethen call DamageType_dealCodeDamage(dSource,tU,20+20*tempDat.revertAbilityLevel+.4*GetHeroInt(dSource,true),DAMAGE_TYPE_MAGIC) endif callSetUnitState(dSource,UNIT_STATE_MANA,GetUnitState(dSource,UNIT_STATE_MANA)+46+2.9*GetHeroLevel(dSource)+13+13*tempDat.revertAbilityLevel) callUnitRemoveAbility(dSource,tfConsts_DIS_BLUE_CARD_ID) set tempDat.thrown=true set tempDat.elapsedTime=CYCLE_MAX_DURATION callDestroyEffect(AddSpecialEffect(BLUEFX,GetUnitX(tU),GetUnitY(tU))) elseifGetUnitAbilityLevel(dSource,tfConsts_DIS_RED_CARD_ID)>0then set tX=GetUnitX(tU) set tY=GetUnitY(tU) set colorSound=CreateSound(REDSOUND,false,true,true,12700,12700,"") callSetSoundVolume(colorSound,127) callSetSoundPitch(colorSound,.75) callSetSoundPosition(colorSound,tX,tY,100.) callStartSound(colorSound) callKillSoundWhenDone(colorSound) callGroupEnumUnitsInRange(grp,GetUnitX(tU),GetUnitY(tU),RED_CARD_ENUM_RANGE,null) ifIsUnitType(tU,UNIT_TYPE_STRUCTURE)==falseandIsUnitType(tU,UNIT_TYPE_DEAD)==falsethen call DamageType_dealCodeDamage(dSource,tU,15+15*tempDat.revertAbilityLevel+.4*GetHeroInt(tempDat.fate,true),DAMAGE_TYPE_MAGIC) endif loop set FoG=FirstOfGroup(grp) exitwhen FoG==null ifIsUnitEnemy(FoG,owner)andIsUnitType(FoG,UNIT_TYPE_STRUCTURE)==falseandIsUnitType(FoG,UNIT_TYPE_DEAD)==falsethen if FoG!=tU then call DamageType_dealCodeDamage(dSource,FoG,15+15*tempDat.revertAbilityLevel+.4*GetHeroInt(dSource,true)+46+2.9*GetHeroLevel(dSource),DAMAGE_TYPE_MAGIC) endif callSetUnitX(casterDummy,tX) callSetUnitY(casterDummy,tY) callUnitAddAbility(casterDummy,tfConsts_DUMMY_SLOW_ID) callSetUnitAbilityLevel(casterDummy,tfConsts_DUMMY_SLOW_ID,tempDat.revertAbilityLevel) callIssueTargetOrder(casterDummy,"slow",FoG) callUnitRemoveAbility(casterDummy,tfConsts_DUMMY_SLOW_ID) endif callGroupRemoveUnit(grp,FoG) endloop callUnitRemoveAbility(dSource,tfConsts_DIS_RED_CARD_ID) set tempDat.thrown=true set tempDat.elapsedTime=CYCLE_MAX_DURATION callDestroyEffect(AddSpecialEffect(REDFX,tX,tY)) set index=0. loop exitwhen index>bj_PI*2. callDestroyEffect(AddSpecialEffect(REDFX,tX+RED_CARD_ENUM_RANGE/2.*Cos(index),tY+RED_CARD_ENUM_RANGE/2.*Sin(index))) set index=index+bj_PI/9. endloop elseifGetUnitAbilityLevel(dSource,tfConsts_DIS_GOLD_CARD_ID)>0then set tX=GetUnitX(tU) set tY=GetUnitY(tU) set colorSound=CreateSound(GOLDSOUND,false,true,true,12700,12700,"") callSetSoundVolume(colorSound,127) callSetSoundPitch(colorSound,2.) callSetSoundPosition(colorSound,tX,tY,100.) callStartSound(colorSound) callKillSoundWhenDone(colorSound) ifIsUnitType(tU,UNIT_TYPE_DEAD)==falsethen call DamageType_dealCodeDamage(dSource,tU,7.5+7.5*tempDat.revertAbilityLevel+.4*GetHeroInt(tempDat.fate,true),DAMAGE_TYPE_MAGIC) callSetUnitX(casterDummy,tX) callSetUnitY(casterDummy,tY) callUnitAddAbility(casterDummy,tfConsts_DUMMY_STUN_ID) callSetUnitAbilityLevel(casterDummy,tfConsts_DUMMY_STUN_ID,tempDat.revertAbilityLevel) callIssueTargetOrder(casterDummy,"thunderbolt",tU) callUnitRemoveAbility(casterDummy,tfConsts_DUMMY_STUN_ID) endif callUnitRemoveAbility(dSource,tfConsts_DIS_GOLD_CARD_ID) set tempDat.thrown=true set tempDat.elapsedTime=CYCLE_MAX_DURATION callDestroyEffect(AddSpecialEffect(GOLDFX,tX,tY)) endif set tU=null set colorSound=null endif set dSource=null endfunction
privatefunction dummyP takesnothingreturnsnothing localinteger index=0 local dummyCardDat tempDat loop exitwhen index>dummyDBIndex set tempDat=dummyDB[index] callSetUnitX(tempDat.dummy,GetUnitX(tempDat.fate)) callSetUnitY(tempDat.dummy,GetUnitY(tempDat.fate)) ifGetUnitAbilityLevel(tempDat.fate,tfConsts_CARD_ID[tfConsts_BLUE_CARD])>0orGetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_BLUE_CARD_ID)>0then callSetUnitVertexColor(tempDat.dummy,80,80,255,255) elseifGetUnitAbilityLevel(tempDat.fate,tfConsts_CARD_ID[tfConsts_RED_CARD])>0orGetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_RED_CARD_ID)>0then callSetUnitVertexColor(tempDat.dummy,255,55,55,255) elseifGetUnitAbilityLevel(tempDat.fate,tfConsts_CARD_ID[tfConsts_GOLD_CARD])>0orGetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_GOLD_CARD_ID)>0then callSetUnitVertexColor(tempDat.dummy,255,245,55,255) else callRemoveUnit(tempDat.dummy) set tempDat.dummy=null set tempDat.fate=null call tempDat.destroy() set dummyDB[index]=dummyDB[dummyDBIndex] set dummyDBIndex=dummyDBIndex-1 set index=index-1 if dummyDBIndex==-1then callPauseTimer(dummyClock) endif endif set index=index+1 endloop endfunction
privatefunction p takesnothingreturnsnothing localtimer time=GetExpiredTimer() local pickDat tempDat=fromTimer[time] localinteger cardID=tfConsts_hiddenCard[tempDat.fate] set tempDat.elapsedTime=tempDat.elapsedTime+CARD_CYCLE_PERIOD if tempDat.elapsedTime<CYCLE_MAX_DURATION and tempDat.thrown==falsethen if tempDat.locked==falseand(GetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_BLUE_CARD_ID)>0orGetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_RED_CARD_ID)>0orGetUnitAbilityLevel(tempDat.fate,tfConsts_DIS_GOLD_CARD_ID)>0)then set tempDat.locked=true endif if tempDat.locked==falsethen callUnitRemoveAbility(tempDat.fate,tfConsts_CARD_ID[cardID]) endif if cardID==tfConsts_GOLD_CARD then set cardID=tfConsts_BLUE_CARD else set cardID=cardID+1 endif if tempDat.locked==falsethen callUnitAddAbility(tempDat.fate,tfConsts_CARD_ID[cardID]) ifGetLocalPlayer()==GetOwningPlayer(tempDat.fate)then callStartSound(snd) endif endif else callUnitRemoveAbility(tempDat.fate,tfConsts_CARD_ID[cardID]) callUnitRemoveAbility(tempDat.fate,tfConsts_DIS_BLUE_CARD_ID) callUnitRemoveAbility(tempDat.fate,tfConsts_DIS_RED_CARD_ID) callUnitRemoveAbility(tempDat.fate,tfConsts_DIS_GOLD_CARD_ID) callUnitAddAbility(tempDat.fate,tfConsts_DIS_PICK_A_CARD_ID) endif if tempDat.elapsedTime>CYCLE_MAX_DURATION+COOLDOWN-COOLDOWN*0.03*GetUnitAbilityLevel(tempDat.fate,tfConsts_STACKED_DECK_ID)then callSetUnitAbilityLevel(tempDat.fate,tfConsts_PICK_A_CARD_ID,tempDat.revertAbilityLevel) callUnitRemoveAbility(tempDat.fate,tfConsts_DIS_PICK_A_CARD_ID) call tempDat.destroy() call fromTimer.flush(time) callDestroyTimer(time) endif set tfConsts_hiddenCard[tempDat.fate]=cardID set time=null endfunction
privatefunction c takesnothingreturnsboolean local pickDat tempDat local dummyCardDat tempDCDat localtimer time ifGetSpellAbilityId()==tfConsts_PICK_A_CARD_ID then //store all data in struct instance set tempDat=pickDat.create() set tempDat.fate=GetTriggerUnit() set tempDat.revertAbilityLevel=GetUnitAbilityLevel(tempDat.fate,tfConsts_PICK_A_CARD_ID) set tempDat.elapsedTime=0. //make the ability unavailable callSetUnitAbilityLevel(tempDat.fate,tfConsts_PICK_A_CARD_ID,5) callIncUnitAbilityLevel(tempDat.fate,tfConsts_PICK_A_CARD_ID) //add the proper ability depending on the current value stored in the table callUnitAddAbility(tempDat.fate,tfConsts_CARD_ID[tfConsts_hiddenCard[tempDat.fate]]) //create new timer, because sharing a periodic timer with a card cycle period between 0.3 and 0.7 seconds will not only look poor, //but potentially cause problems for TF vs TF situations. //begin counting towards the duration. After picking a card, this same duration will be kept, and after the max is reached, //or the card is thrown, the ability will reset to the original ability. set time=CreateTimer() set fromTimer[time]=tempDat set fromFate[tempDat.fate]=tempDat callTimerStart(time,CARD_CYCLE_PERIOD,true,function p) //create the dummy card and give it the proper color. we need to attach it to a different, more smooth timer. This one will be stack based. set tempDCDat=dummyCardDat.create() set tempDCDat.fate=tempDat.fate set tempDCDat.dummy=CreateUnit(GetOwningPlayer(tempDat.fate),tfConsts_CARD_DUMMY_ID,GetUnitX(tempDat.fate),GetUnitY(tempDat.fate),0.) callUnitAddAbility(tempDCDat.dummy,'arav') callUnitRemoveAbility(tempDCDat.dummy,'arav') callSetUnitFlyHeight(tempDCDat.dummy,150.,0.) callSetUnitTimeScale(tempDCDat.dummy,.5) set dummyDBIndex=dummyDBIndex+1 set dummyDB[dummyDBIndex]=tempDCDat if dummyDBIndex==0then callTimerStart(dummyClock,tfConsts_FPS,true,function dummyP) endif set time=null endif returnfalse endfunction
privatefunction secondC takesnothingreturnsboolean localinteger id=GetSpellAbilityId() localunit tU if id==tfConsts_BLUE_CARD_ID then set tU=GetTriggerUnit() callUnitRemoveAbility(tU,tfConsts_BLUE_CARD_ID) callUnitAddAbility(tU,tfConsts_DIS_BLUE_CARD_ID) set tU=null elseif id==tfConsts_RED_CARD_ID then set tU=GetTriggerUnit() callUnitRemoveAbility(tU,tfConsts_RED_CARD_ID) callUnitAddAbility(tU,tfConsts_DIS_RED_CARD_ID) set tU=null elseif id==tfConsts_GOLD_CARD_ID then set tU=GetTriggerUnit() callUnitRemoveAbility(tU,tfConsts_GOLD_CARD_ID) callUnitAddAbility(tU,tfConsts_DIS_GOLD_CARD_ID) set tU=null endif returnfalse endfunction
privatefunction i takesnothingreturnsnothing localtrigger initialCast=CreateTrigger() localtrigger secondCast=CreateTrigger() callTriggerRegisterAnyUnitEventBJ(secondCast,EVENT_PLAYER_UNIT_SPELL_EFFECT) callTriggerAddCondition(secondCast,Condition(function secondC)) set fromTimer=HandleTable.create() set fromFate =HandleTable.create() set snd=CreateSound(TICKSOUND,false,false,false,12700,12700,"") callSetSoundVolume(snd,127) callSetSoundPitch(snd,0.5) callStartSound(snd) callTriggerRegisterAnyUnitEventBJ(initialCast,EVENT_PLAYER_UNIT_SPELL_FINISH) callTriggerAddCondition(initialCast,Condition(function c)) call StructuredDD_ddBucket.addHandler(function ddHandler) set casterDummy=CreateUnit(Player(15),tfConsts_CASTER_DUMMY_ID,0.,0.,0.) set initialCast=null endfunction endscope
privatefunction handler takesnothingreturnsnothing localunit dSource=GetEventDamageSource() localinteger level=GetUnitAbilityLevel(dSource,tfConsts_STACKED_DECK_ID) localinteger count local stackDat tempDat localunit tU localsound snd if DamageType_get(GetEventDamage())==DamageType_ATTACK and level>0then if attackCounts.exists(dSource)then set tempDat=attackCounts[dSource] if tempDat.count==3then//this attack is a proc, let's deal extra damage set tU=GetTriggerUnit() call DamageType_dealCodeDamage(dSource,tU,30.+25.*level+.4*GetHeroInt(dSource,true),DAMAGE_TYPE_MAGIC) //then reset attackCount set tempDat.count=0 //and remove the effect added when count was 2 callDestroyEffect(tempDat.aura) callDestroyEffect(AddSpecialEffectTarget(TARGET_FX,tU,"origin")) set snd=CreateSound(HIT_SOUND,false,true,true,12700,12700,"") callSetSoundVolume(snd,127) callSetSoundPitch(snd,1.25) callSetSoundPosition(snd,GetUnitX(tU),GetUnitY(tU),100.) callStartSound(snd) callKillSoundWhenDone(snd) set snd=null set tU=null elseif tempDat.count==2then//the next attack we'll proc, so let's display an effect set tempDat.aura=AddSpecialEffectTarget(NEXT_HIT_FX,dSource,"origin") set tempDat.count=tempDat.count+1 else//this is attack 0 or 1 so just increment count set tempDat.count=tempDat.count+1 endif else//this is the first time the unit attacks with the ability so let's instanciate a stackDat and set the default count. set tempDat=stackDat.create() set tempDat.count=0 set attackCounts[dSource]=tempDat endif endif set dSource=null endfunction
privatefunction i takesnothingreturnsnothing call StructuredDD_ddBucket.addHandler(function handler) set attackCounts=HandleTable.create() endfunction endscope
destiny
Jass:
scope destiny initializer i privatestruct cdrDat unit fate endstruct
privatestruct target unit who endstruct
privatestruct destinyDat unit fate group visionDummies real elapsedTime=0. real duration integer revertAbilityLevel endstruct
privatefunction p takesnothingreturnsnothing localinteger index=0 local destinyDat tempDat loop exitwhen index>dbIndex set tempDat=destinyDB[index] set tempDat.elapsedTime=tempDat.elapsedTime+tfConsts_FPS if tempDat.elapsedTime<tempDat.durationthen callForGroup(tempDat.visionDummies,function move) else callUnitRemoveAbility(tempDat.fate,tfConsts_GATE_ID) callUnitRemoveAbility(tempDat.fate,tfConsts_DIS_GATE_ID) callSetUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID,tempDat.revertAbilityLevel) callForGroup(tempDat.visionDummies,function flusher) callDestroyGroup(tempDat.visionDummies) set destinyDB[index]=destinyDB[dbIndex] set dbIndex=dbIndex-1 set index=index-1 if dbIndex==-1then callPauseTimer(time) endif endif set index=index+1 endloop endfunction
privatefunction cdr takesnothingreturnsnothing localtimer clock=GetExpiredTimer() local cdrDat tempDat=cdrTable[clock] callUnitRemoveAbility(tempDat.fate,tfConsts_DESTINY_ID) callUnitAddAbility(tempDat.fate,tfConsts_DESTINY_ID) call tempDat.destroy() call cdrTable.flush(clock) callDestroyTimer(clock) set clock=null endfunction
privatefunction c takesnothingreturnsboolean local destinyDat tempDat local cdrDat tempCDR local target tStruct localunit FoG localunit u localplayer who localtimer cdrClock localreal cooldown ifGetSpellAbilityId()==tfConsts_DESTINY_ID then set tempDat=destinyDat.create() set tempDat.fate=GetTriggerUnit() set tempDat.visionDummies=CreateGroup() set tempDat.revertAbilityLevel=GetUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID) set tempDat.duration=4.+2.*tempDat.revertAbilityLevel callGroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null) set who=GetOwningPlayer(tempDat.fate) callSetUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID,3) callIncUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID) callUnitAddAbility(tempDat.fate,tfConsts_GATE_ID) set cooldown=165.-15.*GetUnitAbilityLevel(tempDat.fate,tfConsts_DESTINY_ID) ifGetUnitAbilityLevel(tempDat.fate,tfConsts_STACKED_DECK_ID)>0then set cdrClock=CreateTimer() set tempCDR=cdrDat.create() set tempCDR.fate=tempDat.fate set cdrTable[cdrClock]=tempCDR callTimerStart(cdrClock,cooldown-cooldown*0.03*GetUnitAbilityLevel(tempDat.fate,tfConsts_STACKED_DECK_ID),false,function cdr) set cdrClock=null endif loop set FoG=FirstOfGroup(grp) exitwhen FoG==null ifIsUnitType(FoG,UNIT_TYPE_HERO)andIsUnitEnemy(FoG,who)and UnitAlive(FoG)then set u=CreateUnit(who,tfConsts_EYE_DUMMY_ID,GetUnitX(FoG),GetUnitY(FoG),270.) callGroupAddUnit(tempDat.visionDummies,u) set tStruct=target.create() set tStruct.who=FoG set attachments[u]=tStruct set u=null endif callGroupRemoveUnit(grp,FoG) endloop set dbIndex=dbIndex+1 set destinyDB[dbIndex]=tempDat if dbIndex==0then callTimerStart(time,tfConsts_FPS,true,function p)//The only reason this timer has to be smooth is so that the effect (dummy) will track fast //moving/gap closing/teleporting heroes. endif endif returnfalse endfunction
privatefunction gateC takesnothingreturnsboolean localunit tU localsound snd ifGetSpellAbilityId()==tfConsts_GATE_ID then set tU=GetTriggerUnit() set snd=CreateSound(GATE_SOUND,false,true,true,12700,12700,"") callSetSoundVolume(snd,127) callSetSoundPosition(snd,GetUnitX(tU),GetUnitY(tU),100.) callSetSoundPitch(snd,2.) callStartSound(snd) callKillSoundWhenDone(snd) set snd=null callUnitRemoveAbility(tU,tfConsts_GATE_ID) callUnitAddAbility(tU,tfConsts_DIS_GATE_ID) set tU=null endif returnfalse endfunction
Research tooltip coud list the next level after the spell's name
The passive buff status icon could list the effect
(active) and (passive) text in learned tooltips is not needed, we can see that from the icon
Learned tooltip could list only the stats of the current level, to make the tooltip cleaner
You could try running some animation (SetUnitAnimationIndex) on the caster when he casts Destiny or Pick a Card. Now the hero runs the anim for the casting time (0.1 s) and it doesn't look good.
Research tooltip coud list the next level after the spell's name
The passive buff status icon could list the effect
(active) and (passive) text in learned tooltips is not needed, we can see that from the icon
Good suggestions - I've updated the map now.
Quote:
Learned tooltip could list only the stats of the current level, to make the tooltip cleaner
There's a reason I did that - I wanted the blue/gold/red ability tooltips to match the standard tooltip - so I'd have to make 5 levels of blue/gold/red to do that while also displaying only current tooltip data.
I decided to bite the bullet though and just make blue/gold/red abilities display the upgrade tooltip, while the standard abilities will now display only their own level's stats now.
Quote:
You could try running some animation (SetUnitAnimationIndex) on the caster when he casts Destiny or Pick a Card. Now the hero runs the anim for the casting time (0.1 s) and it doesn't look good.
This is a bug and is not how twisted fate is supposed to work - should be fixed now.
Quote:
Originally Posted by Almia
btw,you dont like Linked Lists?They are faster than indexed arrays.
That is incorrect both in jass and in compiled programming languages as well.
Linked list implementations in vJass using structs is strictly limited to indexed arrays, and therefore is, at absolute fastest, still slower than using an indexed array directly.
In compiled programming languages, linked lists are still slower because each node of the linked list is typically allocated in memory at different times (and will thus never be contiguous sets).
That is not to say that linked lists are strictly worse than array lists - it depends on the implementation of whatever you're trying to do. The primary advantage of a linked list is O(1) insertion/removal without breaking order.
However, in these spells I never linSearch, nor do I insert/remove with the requirement that order remains the same.