| 10-26-2010, 05:08 AM | #1 |
I am pretty new to the whole JASS scene. I was looking around all the systems people have made and found JCast here: http://www.wc3c.net/showthread.php?t...ight=cast+time I also found the JESP standard and think I will try to use it. What I like the most about JCast is the casting time/spell cast progression and interruption. I was wondering if anyone has successfully combined JCast with a JESP spell. If they have, any advice or a test map? Perhaps JCast isn't the answer and I should consider something else? If so, any pointers or help would be appreciated. A simple link to a tutorial that I may have missed while searching would also be cool. |
| 10-26-2010, 03:37 PM | #2 |
JESP is outdated and shouldnt be used anymore. Your best chance is to ask Profet about a demo map, maybe he can show you some abilities he has made using JCast. If that doesnt help you, i think youll have to tell me what exactly you want to do. What do you want to use JCast for? |
| 10-27-2010, 01:08 AM | #3 |
Hmmm i see. I have a demo map for JCast featuring about 5 spells so I have a good base to start from. It just seemed to me that JESP was something to use since i saw it referenced a lot in the jass spells comments. Is there a better standard to follow? My main reasons for using JCast is for the dynamic cast progression/channeling features along with interruption/channel push back. The override functionality is also nice. I am currently trying to make JCast work with Shrapnade by Rising_Dusk from here: http://www.wc3c.net/showthread.php?t=99501 |
| 10-27-2010, 10:21 AM | #4 |
Well, if you use vJass correctly, your code is automatically better than JASS with JESP. So JESP really became obsolete through people using vJass almost exclusively. Just use vJass. If you want, you can post it here so either me or someone else can comment on it and see what could be improved. |
| 10-28-2010, 04:42 AM | #5 |
Alright. I modified the map that Rising_Dusk created to show off the spell I've been working with. I put in JCast and its dependencies. I got rid of "scope" in the spell and replaced it with "library" that has an initializer and some dependencies. Some things I wasn't sure about: - I changed the timer in the spell to be created using TimerUtils for the data storage aspect. I can't seem to start a Timer by giving it a non-static method inside a struct, so I made a static function called CallbackStart in the struct that looks in the timer for the data (the spell struct) and calls its non-static Callback. This seems excessive And there is probably a better way to do this? - I am not sure if I am using vJass or not. Thanks for the offer to comment on the code. Here is the spell code: JASS://*************************************************************************************************************** //* * //* S H R A P N A D E * //* Actual Code * //* v1.07 * //* * //* By: Rising_Dusk * //* Modified For Use with JCast * //*************************************************************************************************************** library Shrapnade initializer InitA uses AbilityCore, TimerUtils globals //********************************************************* //* These are the configuration constants for the spell //* //* AbilityID: The ability ID that triggers the spell //* DummyUnitID: The dummy unit's ID for who to attach effects to //* DamageLocal: Where the DamageEffect gets played on a unit //* DamageEffect: Which effect plays when you damage an enemy //* ExplodeEffect: Which effect plays when a Shrapnade explodes //* DummyUnitEffect: Which effect is attached to the dummy unit //* AttackType: What attack type the damage dealt is of //* DamageType: What damage type the damage dealt is of //* BaseSpeed: Smaller numbers make it move faster (Normal: 25) //* MinSpeed: Iteration speed minimum for fragmented shards //* MaxSpeed: Iteration speed maximum for fragmented shards //* BaseDamage: The damage the spell deals at level 1 //* DamagePerLevel: The damage each extra level makes the spell do //* Fragments: Base number of fragments each spell cast breaks into //* FragmentsPerLevel: How many extra fragments each level adds //* MinFragRange: Minimum range fragments will target from initial blast //* MaxFragRange: Maximum range fragments will target from initial blast //* BlastAreaOfEffect: How big of an area the damage is dealt within //* InitialBounceHeight: The max height of the spell-cast bomb //* MinBounceHeight: The minimum max height of the fragmented shards //* MaxBounceHeight: The maximum max height of the fragmented shards //* MomentumAngle: The angle spread in radians that fragments can have //* //private integer AbilityID = 'A003' private integer DummyUnitID = 'h000' private string DamageLocal = "chest" private string DamageEffect = "Abilities\\Weapons\\Rifle\\RifleImpact.mdl" private string ExplodeEffect = "Abilities\\Weapons\\Mortar\\MortarMissile.mdl" private string DummyUnitEffect = "Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl" private attacktype AttackType = ATTACK_TYPE_NORMAL private damagetype DamageType = DAMAGE_TYPE_UNIVERSAL private real BaseSpeed = 20. private real MinSpeed = 15. private real MaxSpeed = 35. private real BaseDamage = 80. private real DamagePerLevel = 20. private integer Fragments = 1 private integer FragmentsPerLevel = 1 private real MinFragRange = 100. private real MaxFragRange = 400. private real BlastAreaOfEffect = 200. private real InitialBounceHeight = 250. private real MinBounceHeight = 100. private real MaxBounceHeight = 350. private real MomentumAngle = 1.04719 //********************************************************* //* These are static constants used by the spell and shouldn't be changed //* //* Timer: The timer that runs all of the effects for the spell //* Counter: The counter for how many spell instances exist //* Nades: The array of all struct instances that exist //* TimerInterval: The interval for the timer that gets run. //* Boolexpr: The boolean expression used for damage checking. //* private timer Timer private integer Counter = 0 private integer array Nades private real TimerInterval = 0.04 private boolexpr Boolexpr = null //* This is the trigger for reference outside of the scope if desired //public trigger Trig = CreateTrigger() endglobals function InitA takes nothing returns nothing call BJDebugMsg("LIBRARY INIT") set Timer = NewTimer() endfunction //********************************************************* //* Support struct for the spell, I recommend leaving it alone private struct nade unit Dummy unit Caster effect DummyEffect integer Level boolean isPrimary real Speed real MaxHeight real DistanceCheck real DistanceTarget real Angle static method create takes unit u, real x, real y, integer level, real angle, real TotalDist, real max, real speed, boolean primary returns nade local nade n = nade.allocate() set n.Caster = u set n.Dummy = CreateUnit(GetOwningPlayer(u), DummyUnitID, x, y, angle) set n.DummyEffect = AddSpecialEffectTarget(DummyUnitEffect, n.Dummy, "origin") set n.DistanceCheck = TotalDist set n.DistanceTarget = TotalDist set n.isPrimary = primary set n.MaxHeight = max set n.Speed = speed set n.Angle = angle set n.Level = level call SetUnitFacing(n.Dummy, angle*57.29583) if IsUnitType(n.Dummy, UNIT_TYPE_GROUND) then call UnitAddAbility(n.Dummy, 'Amrf') call UnitRemoveAbility(n.Dummy, 'Amrf') endif return n endmethod private method onDestroy takes nothing returns nothing call BJDebugMsg("ON DESTROY SHRAPNADE") call DestroyEffect(AddSpecialEffect(ExplodeEffect, GetUnitX(this.Dummy), GetUnitY(this.Dummy))) call DestroyEffect(this.DummyEffect) call KillUnit(this.Dummy) endmethod endstruct // Struct for the actual spell, // extends IJCast for JCast functionality private struct spell extends IJCast //Ability's rawcode declaration (required for auto-generated spell's trigger) private static constant integer AbilityId = 'A004' //********************************************************* //* Some quick configuration functions //* This is the targeting BoolExpr for the spell (Who it hits) //* bj_meleeNearestMine references the spell caster private method Check takes nothing returns boolean return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(bj_meleeNearestMine)) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(), 'Avul') <= 0 endmethod //* This just does the calculation for the spell's damage private method FinalDamage takes integer lvl returns real return BaseDamage+(DamagePerLevel*(lvl-1)) endmethod //* This just does the calculation for the spell's fragmentation private method FragCount takes integer lvl returns integer return Fragments+(FragmentsPerLevel*(lvl-1)) endmethod //****************************************************************************** //* The spell code itself. private method Conditions takes nothing returns boolean return GetSpellAbilityId() == AbilityId endmethod //Im not sure why I need this, or if I do. //I couldn't find a way to make the timer call the regular Callback without //keeping it non-static. Is it a big deal if it is static or not in this case? I'm not sure really. public static method CallbackStart takes nothing returns nothing call spell(GetTimerData(GetExpiredTimer())).Callback() endmethod private method Callback takes nothing returns nothing local unit u local unit d local unit s local nade n = 0 local nade m = 0 local integer i = Counter - 1 local integer j = 0 local integer lvl local group g = CreateGroup() local real xi local real yi local real xf local real yf local real z local real di1 local real di2 //call BJDebugMsg("CALLBACK CALLLED: counter:"+I2S(Counter)+" i: "+I2S(i)) loop exitwhen i < 0 set n = Nades[i] set u = n.Caster set d = n.Dummy set di1 = n.DistanceCheck set di2 = n.DistanceTarget set lvl = n.Level set xi = GetUnitX(d) set yi = GetUnitY(d) // call BJDebugMsg("CALLBACK distance: di1:"+R2S(di1)) if di1 <= 0 then set bj_meleeNearestMine = u call GroupEnumUnitsInRange(g, xi, yi, BlastAreaOfEffect, Boolexpr) loop set s = FirstOfGroup(g) exitwhen s == null call DestroyEffect(AddSpecialEffectTarget(DamageEffect, s, DamageLocal)) call UnitDamageTarget(u, s, FinalDamage(lvl), false, false, AttackType, DamageType, null) call GroupRemoveUnit(g, s) endloop call GroupClear(g) if n.isPrimary then loop exitwhen j >= FragCount(lvl) set m = nade.create(u, xi, yi, lvl, GetRandomReal(n.Angle-MomentumAngle,n.Angle+MomentumAngle), GetRandomReal(MinFragRange,MaxFragRange), GetRandomReal(MinBounceHeight, MaxBounceHeight), GetRandomReal(MinSpeed, MaxSpeed), false) set Nades[Counter] = m set Counter = Counter + 1 set j = j + 1 endloop endif call n.destroy() set Counter = Counter - 1 if Counter <= 0 then call BJDebugMsg("PAUSE TIMER") call PauseTimer(Timer) set Counter = 0 else set Nades[i] = Nades[Counter] endif else set xf = xi + (di2/n.Speed) * Cos(n.Angle) set yf = yi + (di2/n.Speed) * Sin(n.Angle) set z = ((4*di1)/di2)*(1-(di1/di2))*n.MaxHeight set n.DistanceCheck = di1 - (di2/n.Speed) call SetUnitFacing(d, n.Angle*57.29583) call SetUnitFlyHeight(d, z, 0) call SetUnitX(d, xf) call SetUnitY(d, yf) endif set i = i - 1 endloop call GroupClear(g) call DestroyGroup(g) set g = null set u = null set d = null set s = null endmethod private method Actions takes nothing returns nothing local unit u = GetCaster() local integer lvl = GetUnitAbilityLevel(u, AbilityId) local real xi = GetUnitX(u) local real yi = GetUnitY(u) local real xf = GetTargetX() local real yf = GetTargetY() local real an = Atan2(yf - yi, xf - xi) local real di = SquareRoot((xi-xf)*(xi-xf) + (yi-yf)*(yi-yf)) local nade n = 0 //call BJDebugMsg("ACTIONS NOW, level? "+I2S(lvl)+" of: "+I2S(AbilityId)+" VS: "+I2S('A001')+" or: "+I2S('A003') +" real: "+I2S(GetUnitAbilityLevel(u, 'A001'))+" | "+I2S(GetUnitAbilityLevel(u, 'A003'))) //call BJDebugMsg("ACTIONS NOW, target: "+R2S(GetLocationX(l))+" "+R2S(GetLocationY(l))+" OR UNIT? "+GetUnitName(GetSpellTargetUnit())) if di == 0 then set di = 1 set an = GetUnitFacing(u) endif set n = nade.create(u, xi, yi, lvl, an, di, InitialBounceHeight, BaseSpeed, true) if Counter == 0 then call SetTimerData(Timer, integer(this)) call TimerStart(Timer, TimerInterval, true, function thistype.CallbackStart) endif set Nades[Counter] = n set Counter = Counter + 1 set u = null endmethod //*********************************************** //JCast Specific Functions //Overridden for more functionality, or comments //Define the spell's casting time as 1 second method GetCastTime takes nothing returns real return 1. endmethod //For now, require no specific conditions. Later could add something. method CanCast takes nothing returns boolean return true endmethod //Called when CanCast() returned FALSE. method OnFail takes nothing returns nothing call SimError(GetOwningPlayer(GetCaster()), "Please select a wounded unit") endmethod //Just play and animation when the cast starts. //Note: Don't forget to set the war3's spell 'Animation name' setting to "stand" if // you don't want the spell anim to be played when CanCast() returns FALSE. method OnCastStart takes nothing returns nothing call SetUnitAnimation(GetCaster(), "stand channel") call Demo_CastbarCreate(GetCaster(), GetAbilityName(AbilityId)) endmethod //Let's implement this method to update the casting bar. method OnCastUpdate takes real progress returns nothing //call BJDebugMsg("OnCastUpdate() progress: "+R2S(progress)) call Demo_CastbarUpdate(GetCaster(), progress) endmethod //The cast stage is over, then reset the casting bar. method OnCastEnd takes nothing returns nothing call Demo_CastbarReset(GetCaster()) endmethod method OnEnd takes nothing returns nothing call Demo_CastbarReset(GetCaster()) endmethod //The cast is complete and successful (CanCast method is evaluated again when cast is done), //then it's time to perform spell effects. method OnEffect takes nothing returns nothing call Actions() endmethod //Not used right now //When hit by an attack, cast progress is reduced by 0.5 second (infinite pushback) method ApplyCastPushback takes real progress, real castTime, real pushCount returns real return RMaxBJ(0., progress - 0.5) endmethod //The module must be implemented after the declaration of OnCastUpdate and OnChannelUpdate, else these methods //won't be taken into consideration by the vJass compiler. implement JCastModule endstruct endlibrary |
| 10-28-2010, 02:07 PM | #6 | ||
Quote:
Quote:
|
| 10-29-2010, 02:17 AM | #7 | |
If I reverse my changes I end up with something like this: JASS:globals //... private timer Timer = CreateTimer() //... endglobals struct spell extends IJcast private method Callback takes nothing returns nothing //... endmethod private method Actions takes nothing returns nothing //... TimerStart(Timer, TimerInterval, true, function thistype.Callback) endmethod endstruct This leads to a compile error telling me: Quote:
I didn't want to make callback static because it uses other non-static methods in the spell struct. When I first started, the working spell didn't have its functions inside a struct, but in a scope. I got rid of that and put everything in the struct so they can extend from IJCast and use its methods. There any way to make this work? |
| 10-29-2010, 08:30 PM | #8 |
Non-static methods implicitly take the struct instance as an argument; as such, they can not be used as timer callbacks since those must take no arguments. Typically, this is resolved by making the timer callback a static method that then adopts a struct instance: JASS:struct example timer t static method callback takes nothing returns nothing local example this = example(GetTimerData(GetExpiredTimer())) //this line makes our static method behave like a regular method call ReleaseTimer(.t) endmethod method runtimer takes real time returns nothing set .t=NewTimer() call SetTimerData(.t, this) call TimerStart(.t, time, false, function example.callback) endmethod endstruct |
| 10-29-2010, 09:21 PM | #9 |
Thank you, i will try this. By just looking at it im sure it will work fine. |
| 10-30-2010, 03:21 AM | #10 |
thats what he already does. the problem is not with the timer. Im pretty sure Dusk used a stack initially, but rogueteddybear changed it to try and make it use one timer per instance. The only reason why this still works is because the Callback method (which is not static in the code he posted) was static before and still works as if it was static (meaning that it doesnt make use of the local variable "this"). As i said, just reverse whatever you changed about that method and youll be just fine. You dont need TimerUtils to do what you want. |
| 10-30-2010, 11:07 AM | #11 |
Ah, I see, you have this weird combination of two approaches like you couldn't really decide which one to use: a timer per instance or one timer running all instances. It seems you initially had a setup where one timer ran all the instances, but then you attached a specific instance to the timer even though you still have a functional stack in the background and the timer callback is in fact still processing all the instances on the stack, not just the instance attached to it. I would recommend you use TimedLoop, it will handle the periodic timer management for you so you can simplify your code and avoid this confusion. |
