| 07-22-2008, 10:52 AM | #1 |
Hi friends...I spent 18 hours without sleeping last night to learn Jass. I read all great tutorials of Vexorain, Daelin, Wyrmlord, DarkWolf...and i found JASS Very interesting. Here is my first simple spell and i hope you can take a look at it..to show me how stupid i am .I can save it but it does not work as i want +Holy Incantation: Passive - gives thorn aura and a (2 * level of Sacred Strength (ultimate))% chance to convert an undead creature to your side on attack under the effect of Holy Incantation (buff) -Can only convert one creature at one time. <In GUI, i set group of type undead for caster and destroy them all before change owner of the target, but i dont know how to do that in JASS, So i added expiration timer (i dont like this)> -And here is the code, hix: JASS:function Trig_Conversion_Conditions takes nothing returns boolean return GetUnitTypeId(GetAttacker()) =='Hpal' return IsUnitType(GetAttackedUnitBJ(), UNIT_TYPE_UNDEAD) == true return GetUnitAbilityLevelSwapped('A007', GetAttacker()) >=1 return UnitHasBuffBJ(GetAttacker(), 'B000') == true endfunction //.................. function Trig_Conversion_Actions takes nothing returns nothing local integer Int_Conversion = GetRandomInt(1, 100) local unit Caster_Conversion = GetAttacker() local unit Target_Conversion = GetAttackedUnitBJ() local integer Int_Chance = 2 * GetUnitAbilityLevelSwapped('A007', Caster_Conversion) if ( Int_Conversion <= Int_Chance ) then call SetUnitAnimation(Caster_Conversion, "Attack") call AddSpecialEffectTargetUnitBJ("origin", Target_Conversion, "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" ) call DestroyEffectBJ(GetLastCreatedEffectBJ()) call SetUnitOwner(Target_Conversion, GetOwningPlayer(Caster_Conversion), true) call UnitApplyTimedLifeBJ( 60, 'BTLF', Target_Conversion) set Caster_Conversion = null set Target_Conversion = null call DisableTrigger(GetTriggeringTrigger()) call TriggerSleepAction(20.0) call EnableTrigger(GetTriggeringTrigger()) else call DoNothing() endif endfunction //.......................... function InitTrig_Conversion takes nothing returns nothing call TriggerRegisterAnyUnitEventBJ(gg_trg_Conversion, EVENT_PLAYER_UNIT_ATTACKED) call TriggerAddCondition(gg_trg_Conversion, Condition(function Trig_Conversion_Conditions)) call TriggerAddAction(gg_trg_Conversion, function Trig_Conversion_Actions) endfunction -BTW, do you have any Demo map which is good for learning creating spells with JASS?? |
| 07-22-2008, 11:16 AM | #2 | |
Quote:
Please use the [.jass] and [./jass] tags. (without the dot, of course) It makes your code readable. Once you use JASS tags I can probably do some tweaking to your code. If I can't make it work I can at least fix some things the GUI left behind. |
| 07-22-2008, 11:36 AM | #3 |
Yes..and please send me a list of commands, functions that GUI does not has, thanks my friend... |
| 07-22-2008, 11:50 AM | #4 |
I'm purdy sure the common.j list is somewhere on this site... any 3rd party syntax checker would have it and that'd be in/attached to it. common.j has ALL commands WC3 has available. |
| 07-22-2008, 12:05 PM | #5 |
| 07-22-2008, 12:05 PM | #6 |
Please, use jass tags: JASS:function Trig_Conversion_Conditions takes nothing returns boolean return GetUnitTypeId(GetAttacker()) =='Hpal' return IsUnitType(GetAttackedUnitBJ(), UNIT_TYPE_UNDEAD) == true return GetUnitAbilityLevelSwapped('A007', GetAttacker()) >=1 return UnitHasBuffBJ(GetAttacker(), 'B000') == true endfunction function Trig_Conversion_Actions takes nothing returns nothing local integer Int_Conversion = GetRandomInt(1, 100) local unit Caster_Conversion = GetAttacker() local unit Target_Conversion = GetAttackedUnitBJ() local integer Int_Chance = 2 * GetUnitAbilityLevelSwapped('A007', Caster_Conversion) if ( Int_Conversion <= Int_Chance ) then call SetUnitAnimation(Caster_Conversion, "Attack") call AddSpecialEffectTargetUnitBJ("origin", Target_Conversion, "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" ) call DestroyEffectBJ(GetLastCreatedEffectBJ()) call SetUnitOwner(Target_Conversion, GetOwningPlayer(Caster_Conversion), true) call UnitApplyTimedLifeBJ( 60, 'BTLF', Target_Conversion) set Caster_Conversion = null set Target_Conversion = null call DisableTrigger(GetTriggeringTrigger()) call TriggerSleepAction(20.0) call EnableTrigger(GetTriggeringTrigger()) else call DoNothing() endif endfunction function InitTrig_Conversion takes nothing returns nothing call TriggerRegisterAnyUnitEventBJ(gg_trg_Conversion, EVENT_PLAYER_UNIT_ATTACKED) call TriggerAddCondition(gg_trg_Conversion, Condition(function Trig_Conversion_Conditions)) call TriggerAddAction(gg_trg_Conversion, function Trig_Conversion_Actions) endfunction First of all, very good coding for a beginner, I see you'll advanced very quickly. Just some corrections/improvements (sorry I can't help it, I like doing that): I really don't understand your conditions, with that structure, only the first line will work (the one that checks if the attacker is a Paladin). I suppose you wanted to apply all those conditions, in that case you connect them with an and: JASS:function Trig_Conversion_Conditions takes nothing returns boolean return GetUnitTypeId(GetAttacker()) =='Hpal' and IsUnitType(GetAttackedUnitBJ(), UNIT_TYPE_UNDEAD) == true and GetUnitAbilityLevelSwapped('A007', GetAttacker()) >=1 and UnitHasBuffBJ(GetAttacker(), 'B000') == true endfunction Second, let's get rid of those BJs (in one of Daelin's tuts /Jass Advance Tips/ you have BJs explained): GetUnitAbilityLevelSwapped('A007', GetAttacker()) >=1 Is same as: GetUnitAbilityLevel(GetAttacker(), 'A007') >=1 But it's a bit faster. The only difference is that it doesn't have the Swapped suffix and the parameters aren't swapped (GetUnitAbilityLevel is a native). Also: GetAttackedUnitBJ() Is equal to: GetTriggerUnit() But it's faster. It's same as GUI, instead of all those Casting unit, Attacked unit etc. you can just use Triggering unit. UnitHasBuffBJ is a bit different, basically, it actually checks if the unit has that ability (in JASS buffs can be considered as abilities too). I suppose you have some kind of a JASS editor, so you can look what the function actually does: JASS:function UnitHasBuffBJ takes unit whichUnit, integer buffcode returns boolean return (GetUnitAbilityLevel(whichUnit, buffcode) > 0) endfunction So you can replace this: UnitHasBuffBJ(GetAttacker(), 'B000') == true With this: GetUnitAbilityLevel(GetAttacker(), 'B000') > 0 Also, when checking for boolean values, you don't have to add that == true part, that is unnecessary, so you can replace this: IsUnitType(GetAttackedUnitBJ(), UNIT_TYPE_UNDEAD) == true With this (remember: I said GetAttackedUnitBJ is same as GetTriggerUnit, so we can replace that): IsUnitType(GetTriggerUnit(), UNIT_TYPE_UNDEAD) In case you want the condition to not be true, just put a not behind it: not IsUnitType(GetTriggerUnit(), UNIT_TYPE_UNDEAD) So the final condition function should look like this: JASS:function Trig_Conversion_Conditions takes nothing returns boolean return GetUnitTypeId(GetAttacker()) =='Hpal' and IsUnitType(GetTriggerUnit(), UNIT_TYPE_UNDEAD) and GetUnitAbilityLevel(GetAttacker(), 'A007') >=1 and GetUnitAbilityLevel(GetAttacker(), 'B000') > 0 endfunction Note: >=1 is equal to >0 so can use one of those when you code, instead of using both of them, because otherwise it may confuse you. Now, let's move forward. When you're naming variables (this is only for locals, not globals), I strongly suggest you give them really short names, because that will allow you to code much faster and it will probably be much easier to read, so I suggest naming the first 4 variables to something like this: JASS:local integer Int_Conversion == local integer ir local unit Caster_Conversion == local unit c local unit Target_Conversion == local unit t local integer Int_Chance == local integer ic Explanation: ir as in integer random, c as in caster, t as in target and ic as in integer chance. Unlike globals, you can give locals really short names because you don't have to worry of name interference (you can't have two globals of the same name, but you can have two locals of the same name, but in separate functions). AddSpecialEffectTargetUnitBJ is a really stupid BJ, because all it does is it saves the last created special effect in a global variable (which you can get with GetLastCreatedEffectBJ()). Instead you can use the AddSpecialEffectTarget function (with a different paramter order). That function returns the effect, so if you have a situation when you have to create an effect and then instantly destroy it, you can just use a single line: call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl", t, "origin") Note that I replaced DestroyEffectBJ with DestroyEffect, because those two functions are exactly the same, but DestroyEffect is faster (since DestroyEffectBJ is a BJ). So now you destroy a created effect, I hope you understand it. If not, try to think of AddSpecialEffectTarget as an effect variable, but instead of putting it in a variable and destroying the variable, you instantly destroy it because it's faster. This: call UnitApplyTimedLifeBJ( 60, 'BTLF', Target_Conversion) Is same as this: call UnitApplyTimedLifeBJ(t, 'BTLF', 60) But the second one is faster. Note that I replaced Target_Conversion with t, because I changed those variable names. I suggest you do this: JASS:set Caster_Conversion = null set Target_Conversion = null After the endif because the values are assigned to variables outside the if/then/else, so if the actions in the if block don't happen, the variables will stay non-nulified (causing a leak). Two more advices:[list][*]never use DoNothing(), because it would be the same if you didn't put any action, plus DoNothing() is really slow[*]when you don't have any action under else, you can just leave the else out Also, when using values that are constant, you can use constant functions, because when you have to change a certain constant value, you would have to change each line with that value. In this case you can just change the constant: JASS:constant function Conversion_SpellId takes nothing returns integer return 'A007' endfunction constant function Conversion_BuffId takes nothing returns integer return 'B000' endfunction So instead of putting 'A007' or 'B000', you can call that function. Also, I strongly recommend you stay away from TriggerSleepAction, because it really sucks (I don't feel like explaining why), use timers instead (but in this case, it's kinda tricky to use timers, because you would have to transfer the trigger with some kind of a system and I don't really want to go through all that now). Now, the final code should look something like this: JASS:constant function Conversion_SpellId takes nothing returns integer return 'A007' endfunction constant function Conversion_BuffId takes nothing returns integer return 'B000' endfunction function Trig_Conversion_Conditions takes nothing returns boolean return GetUnitTypeId(GetAttacker()) =='Hpal' and IsUnitType(GetTriggerUnit(), UNIT_TYPE_UNDEAD) and GetUnitAbilityLevel(GetAttacker(), Conversion_SpellId()) >=1 and GetUnitAbilityLevel(GetAttacker(), Conversion_BuffId()) > 0 endfunction function Trig_Conversion_Actions takes nothing returns nothing local integer ir = GetRandomInt(1, 100) local unit c = GetAttacker() local unit t = GetTriggerUnit() local integer ic = 2 * GetUnitAbilityLevel(c, Conversion_SpellId()) if ir <= ic then // those "()" are not needed call SetUnitAnimation(c, "attack") // I'm pretty sure that the string // should be decapitalized ("attack") call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl", t, "origin")) call SetUnitOwner(t, GetOwningPlayer(c), true) call UnitApplyTimedLife(t, 'BTLF', 60) call DisableTrigger(GetTriggeringTrigger()) call TriggerSleepAction(20.0) call EnableTrigger(GetTriggeringTrigger()) endif set c = null set t = null endfunction function InitTrig_Conversion takes nothing returns nothing call TriggerRegisterAnyUnitEventBJ(gg_trg_Conversion, EVENT_PLAYER_UNIT_ATTACKED) call TriggerAddCondition(gg_trg_Conversion, Condition(function Trig_Conversion_Conditions)) call TriggerAddAction(gg_trg_Conversion, function Trig_Conversion_Actions) endfunction There, I also commented the code a bit. EDIT: Wow, even 3 people beat me to it. |
| 07-22-2008, 05:35 PM | #7 |
Damn you, I was going to do that. |
| 07-22-2008, 07:21 PM | #8 |
Just on a side note, since you are transferring from GUI to JASS, I highly recommend you to read this (not very long) tutorial: http://www.thehelper.net/forums/showthread.php?t=80241 It shows you why you should directly start coding vJass without learning "normal"/"old" jass first, and gives a few example-spells. |
| 07-23-2008, 01:13 AM | #9 | ||
Wow....Silvernon...I must call you my brother...really really helpful..from your explanation, i have learned much. Darkwulf, you still have chance to show yourself -Well, yes, firstly i have to convert GUI to JASS for learning, and try to research and optimize it....but later on, when my knowledge is better, i will try to write my own code. I also thank you all friends for your help. -Here is the list of my next spells..But i should introduce they abit, right...so you can imagine how they work and see if my code is going right. -Lay on Hands: A holy light that can heal a friendly unit or deal half damage to a target foe. -And the code: JASS:function Trig_Lay_on_Hands_Conditions takes nothing returns boolean return GetSpellAbilityId() == 'A003' endfunction function Trig_Lay_on_Hands_Actions takes nothing returns nothing local unit Caster_LOH = GetTriggerUnit() local integer Int_LOH = GetUnitAbilityLevel(Caster_LOH, 'A003') local unit Target_LOH = GetSpellTargetUnit() call TriggerSleepAction(0.50) if IsUnitAlly(Target_LOH, GetOwningPlayer(Caster_LOH))then call SetUnitState(Target_LOH, UNIT_STATE_LIFE, GetUnitState(Target_LOH, UNIT_STATE_LIFE) + (Int_LOH * 80. + udg_Bonus_LOH)) else call UnitDamageTargetBJ(Caster_LOH, Target_LOH, (80. * Int_LOH + udg_Bonus_LOH), ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL) endif set Caster_LOH = null set Target_LOH = null endfunction //=========================================================================== function InitTrig_Lay_on_Hands takes nothing returns nothing set gg_trg_Lay_on_Hands = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( gg_trg_Lay_on_Hands, EVENT_PLAYER_UNIT_SPELL_CHANNEL ) call TriggerAddCondition( gg_trg_Lay_on_Hands, Condition( function Trig_Lay_on_Hands_Conditions ) ) call TriggerAddAction( gg_trg_Lay_on_Hands, function Trig_Lay_on_Hands_Actions ) endfunction -Holy Incantation: Passive - An aura that gives friendly nearby units a damage shield, which returns a percentage of a melee attacker's damage back to it. -And the code for Conversion (For multiple conditions, we use "and", right? So i added this line at the end of the Condition Function: and not IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO : JASS:constant function Conversion_SpellId takes nothing returns integer return 'A007' endfunction constant function Conversion_BuffId takes nothing returns integer return 'AeAh' endfunction constant function Conversion_Dum takes nothing returns integer return 'A004' endfunction function Trig_Conversion_Conditions takes nothing returns boolean return GetUnitTypeId(GetAttacker()) =='Hpal' and IsUnitType(GetTriggerUnit(), UNIT_TYPE_UNDEAD) and GetUnitAbilityLevel(GetAttacker(), Conversion_SpellId()) >=1 and GetUnitAbilityLevel(GetAttacker(), Conversion_BuffId()) >0 and not IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO ) endfunction function Trig_Conversion_Actions takes nothing returns nothing local unit c = GetAttacker() local unit t = GetTriggerUnit() local integer ir = GetRandomInt(1,100) local integer ic = 2 * GetUnitAbilityLevel(c, Conversion_SpellId()) if ir <= ic then call SetUnitAnimation(c, "attack") call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl", t, "origin")) call SetUnitOwner(t, GetOwningPlayer(c), true) call UnitAddAbility(t, Conversion_Dum()) endif set c = null set t = null endfunction function InitTrig_Conversion takes nothing returns nothing call TriggerRegisterAnyUnitEventBJ(gg_trg_Conversion, EVENT_PLAYER_UNIT_ATTACKED) call TriggerAddCondition(gg_trg_Conversion, Condition(function Trig_Conversion_Conditions)) call TriggerAddAction(gg_trg_Conversion, function Trig_Conversion_Actions) endfunction -Judicial Chop: The Paladin focus his holy strength to jolt a hot light stream at a target enemy unit, causing damage and ministun. -Sacred Strength: Passive - The Paladin channels divine power to gain bonus Strength. Also adds more points to Lay on Hands, gives extra damage VS Undead to Judicial Chop, completely immune to Disease and has a chance to convert Undead creatures to your side on attacks under the effect of Holy Incantation -And the Code for Extra damage for Judicial Chop(I make the game wait 0.5 sec before triggering damage and creating floating text because the caster need a small time to cast his spell or i should use event "A unit finish casting a spell" ?): JASS:function Trig_Judicial_Chop_Conditions takes nothing returns boolean return GetSpellAbilityId() == 'AHtb' endfunction function Trig_Judicial_Chop_Actions takes nothing returns nothing local unit c = GetTriggerUnit() local unit t = GetSpellTargetUnit() local integer i = 100 * GetUnitAbilityLevel(c, 'A007') call TriggerSleepAction(0.50) call UnitDamageTargetBJ(c, t, i, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL) call CreateTextTagUnitBJ( ( "+" + I2S(i) ), t, 0, 10, 100, 0.00, 0.00, 0 ) call SetTextTagVelocityBJ( GetLastCreatedTextTag(), 40.00, 90 ) call SetTextTagPermanentBJ( GetLastCreatedTextTag(), false ) call SetTextTagLifespanBJ( GetLastCreatedTextTag(), 3.00 ) set c = null set t = null endfunction //=========================================================================== function InitTrig_Judicial_Chop takes nothing returns nothing set gg_trg_Judicial_Chop = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( gg_trg_Judicial_Chop, EVENT_PLAYER_UNIT_SPELL_CAST ) call TriggerAddCondition( gg_trg_Judicial_Chop, Condition( function Trig_Judicial_Chop_Conditions ) ) call TriggerAddAction( gg_trg_Judicial_Chop, function Trig_Judicial_Chop_Actions ) endfunction -The Code for Extra Strength and bonus point to Lay on Hands: JASS:function Trig_Sacred_Strength_Conditions takes nothing returns boolean return GetLearnedSkillBJ() == 'A007'and GetUnitTypeId(GetTriggerUnit()) == 'Hpal' endfunction function Trig_Sacred_Strength_Actions takes nothing returns nothing local unit c = GetTriggerUnit() local integer i = GetUnitAbilityLevel(GetTriggerUnit(), 'A007') set udg_Bonus_LOH = 3 * i if i == 1 then call UnitAddAbility(c, 'A008') elseif i ==2 then call UnitRemoveAbility(c, 'A008') call UnitAddAbility(c, 'A009') elseif i == 3 then call UnitRemoveAbility(c, 'A009') call UnitAddAbility(c, 'A00A') endif set c = null endfunction //=========================================================================== function InitTrig_Sacred_Strength takes nothing returns nothing set gg_trg_Sacred_Strength = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( gg_trg_Sacred_Strength, EVENT_PLAYER_HERO_SKILL ) call TriggerAddCondition( gg_trg_Sacred_Strength, Condition( function Trig_Sacred_Strength_Conditions ) ) call TriggerAddAction( gg_trg_Sacred_Strength, function Trig_Sacred_Strength_Actions ) endfunction Well, i saved it, the map report no bug, but except Lay on Hands i see it clearly works...The other i am not sure, and the Conversion action still does not work, hix. I attach the map here, so if you have time, can you take a look at it? Also, if my spells have any typo or grammar error, i hope you could help me fix At this corner is a group of Undead for testing:
And i want to bother you one more thing, how to create a game message like Blizzard's one, like this:
I will play here around to waiting for answerm hix |
| 07-23-2008, 08:59 AM | #10 | |
Quote:
JASS:native DisplayTextToPlayer takes player toPlayer, real x, real y, string message returns nothing |
| 07-23-2008, 01:08 PM | #11 |
hu' hu'....anybody there ???....................... |
| 07-23-2008, 03:12 PM | #12 |
Hmmm, I don't see why is Lay on Hands a channeling ability, mind explaining why? The code seems fine, what ability did you base it on? Also, what's 'A004'? Maybe the spell doesn't work because of the low chance? Try setting it to 100%. I suggest you use EVENT_PLAYER_UNIT_SPELL_EFFECT instead of EVENT_PLAYER_UNIT_SPELL_CAST, and you should probably use it instead of the channeling event also. That's all I can see for now, can you be more specific on what exactly works and what doesn't. I don't know why Conversion wouldn't work... Maybe it's just the low chance. Try adding a call BJDebugMsg("msg") line right after local declarations before the if (in the Conversion trigger) and see if the message will show in game. P. S. Sorry, I can't take a look at the map because I don't have wc3 installed. |
| 07-24-2008, 01:47 AM | #13 |
Ah...yes.. Silvernon, my brother i think so..i checked the code thoroughly and fixed a small bug..it works now..blah ^^ Now i have a question: How to remove a buff from a unit, i trying to make the paladin immune to disease whenever a unit cast it on him. Jass define the buff as spell and the code looks like this, right: JASS:call RemoveUnitAbility(unit, buffcode) But i cant know why it does not work. Also about timer, Rising_Dusk said a way to use CS Safety of Vexorian...(The very first use he introduced is to use 2 functions where global vars are used>>this is not MUI)..but how to use it? thats the problem, sorry if the question is stupid just forget it Ah, also thanks to Pyrogasm for his text help. |
| 07-24-2008, 09:03 AM | #14 |
You could at least spell my name right if you're calling me your brother :) Many people have a wrong opinion on buffs, they think that once you remove the buff, you removed the spell effect. NO! It just removes the effect that buff has been causing and the buff icon from the unit status. For removing the actual effect you would have to dispel somehow, or make the spell so you won't have to remove the spell effect before time. If you want to make somebody spell immune, make a dummy caster that will cast a (modified) magic shell or something. As for the timers, you could use CSSafety, that's a good idea. Rising_Dusk also made a fine timer tutorial. You could also learn a bit of handle vars (1, 2). Well, that's enough linking to get you started :) |
| 07-24-2008, 10:45 AM | #15 |
Woops...nice help indeed..I will try all those tutorials..brother Silvenon ^^ Any way +Rep for your great kind. |
