• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Relativistic Missiles [vJASS][LUA][GUI]

This bundle is marked as high quality. It exceeds standards and is highly desirable.
Relativistic Missiles
v2.8 by Chopinski
Introduction
First of all, Credits and thanks to BPower, Dirac, and Vexorian for their missile systems at which I based this system upon, and to AGD for the help with effect orientation methods.

This is a Missile system that I put together initially to fulfill my resources needs. Very often while I was creating system, spells and maps I found myself in need of a such systems, but all the available options usually have a little flaw that forced me to used something else.

For example, let's say I wanted a missile that can arc and homing to a target, well there goes BPower's system. It can arc/curve but can't do homing with arc/curve natively. So the options left are Vexorian xemissile which can arc and do homing but cant curve, and Dirac's Missile system. Then again more problems with those, because Vexorian system do not have a lot of the useful events like Dirac's and BPower's systems. Dirac's system can arc/curve and do homing but it's T32 timer is never stopped and it do not have some useful events that the BPower system has. So I decided to get all three system together and build my own system with the stuff I think is useful and with the syntax I think is the most friendly, because I'm not a genius of vJASS myself.

What's different in this system?
Ok, so why it's called "Relativistic Missiles"? Well because mid development I realize that the way missiles were being processed could be optimized and the Einstein postulates about Time Dilation by Gravity gave the idea behind the implementation. Yeah, I know, WTH?

When it comes to Missile systems, performance is everything, and all missiles systems do basically the same thing when processing missiles instances, they loop every set period, which is usually 1/32, through all instances and do their calculations and event calls. The problem with it, is that the more instances existing at a given moment the greater will be the tanking of the game frame rate because the game will lower its fps so all that's need to be done before the next frame gets drawn have time to do so, and that's where the Einstein postulates gave me an idea.

See, when we study Relativity, we learn that gravity dilates time, the greater the gravitational potential (more or less mass) the slower time passes relative to an outside point of reference. Gravity in this analogy would be the amount of missiles instances currently existing, so instead of letting the game frame rate drops drastically to move all those instances, I make the missile instances have their time dilated.

Ok, but how?? Simple, first we need to find what I'll be calling the system Sweet Spot, which is the amount of missiles the game can handle without dropping to much performance per given period, which I found out to be between 150 and 200 missiles. Then we limit the amount of missiles processed per period to this sweet spot, and move the next missile in the line to be processed and all other behind it to the front, In terms of data structures, think of a circular list with a head and tail, and we move the head and tail to change the initial and final positions. I did it with arrays because they are a bit faster and because I'm lazy :p.

But that's not all, because by limiting the amount of missiles processed, we delay their next process call by (total amount)/(sweet spot), so now their speed needs to be corrected and relativity comes full circle now, because time dilation has it's relation with Length Contraction, so we increment the missiles speed by this factor.

Also, something very important is that this system is using only effects as missiles, thanks to the new natives introduced in 1.31, so no dummy units, no CreateUnit() and SetUnitFacing() blasphemy. I've included in the test map a version of this system that do use the dummy method for those that do not have the newer versions of the game. I Also included a version of this system that uses the usual process method, so processing all missiles in a single blow.

Results
I've taken the liberty to test this system against the king of Missile systems for some time, BPower's System. Below, there are 3 videos I make for stress test. The first from the left is BPower's Missile System reworked by AGD. Amazing job btw. You will be able to notice what I was talking about earlier about the sweet spot, when there are more than 150 or so missiles being processed, the fps goes basically to 0 and it freezes until the missiles are finished.

The video in the middle is my system in its normal version (not relativistic). It performs a little better (I've build it a little differently), but in the same way, when the missile count near 250, the fps starts to go down hill real quick.

The video in the right is the relativistic version, notice the missile count and fps compared to the 2 other system. At about 600 missiles fps get really low, but not so much because of the amount of missiles being processed but because of the amount of visual effects in the screen. If the screen was moved to the side where not so many stuff is visible, fps goes up. In my internal tests I was reaching more than 1200 missiles with about 16 fps but with no screen freezes.

And Last but not Least, a video with the examples.

vJASS:
event onPeriod takes nothing returns boolean
    -> if declared will run whenever every missile period

event onHit takes unit hit returns boolean
    -> if declared and collision is greater than 0
    -> will run whenever the missile collides with a unit

event onMissile takes Missiles missile returns boolean
    -> if declared and collision is greater than 0
    -> will run whenever the missile collides with another missile

event onDestructable takes destructable dest returns boolean
    -> if declared and collision is greater than 0
    -> will run whenever the missile collides with a destructable

event onItem takes item i returns boolean
    -> if declared and collision is greater than 0
    -> will run whenever the missile collides with an item

event onCliff takes nothing returns boolean
    -> if declared, will run whenever the missile collides with a cliff wall
    -> with height greater than the current missile height

event onTerrain takes nothing returns boolean
    -> if declared, will run whenever the missile collides with terrain

event onTileset takes nothing returns boolean
    -> if declared, will run whenever the missile changes tileset types

event onFinish takes nothing returns boolean
    -> if declared, will run when the missiles reaches its destination

event onBoundaries takes nothing returns boolean
    -> if declared, will run whenever the missile tries to go out
    -> of the map boundaries.

event onPause takes nothing returns boolean
    -> if declared, will run when the missile is paused

event onResume takes nothing returns boolean
    -> if declared, will run when the missile is unpaused

event onRemove takes nothing returns nothing
    -> if declared, will run when the missile is removed
vJASS:
x             -> The current x coordinate of the missile (readonly)
y             -> The current y coordinate of the missile (readonly)
z             -> The current z coordinate of the missile (readonly)
prevX         -> The previous x coordinate of the missile (readonly)
prevY         -> The previous y coordinate of the missile (readonly)
prevZ         -> The previous z coordinate of the missile (readonly)
nextX         -> The next x coordinate of the missile (readonly)
nextY         -> The next y coordinate of the missile (readonly)
nextZ         -> The next z coordinate of the missile (readonly)
travel        -> The current traveled distance of the missile (readonly)
source        -> The source unit for the missile (read and write)
target        -> The target unit for the missile. If set the missile will be roaming (read and write)
owner         -> The owning player of the missile (read and write)
vision        -> The sight range of the missile (read and write)
collideZ      -> If true, the missile will take into consideration the unit collision size, destructable oclusion height and item size when running the onHit, onDestructable, onItem events (read and write)
collision     -> The collision size of the missile. If not set, the events onHit, onDestructable, onItem and onMissile will no run (read and write)
damage        -> Stores the amount of damage to be used inside an event (read and write)
acceleration  -> The missile acceleration (read and write)
data          -> Stores information to be used inside any event (read and write)
model         -> The model of the missile (read and write)
curve         -> The curve of the missile (read and write)
arc           -> The Arc of the missile (read and write)
scale         -> The scale of the missile (read and write)
speed         -> The speed of the missile (read and write)
duration      -> Set the speed of the missile to match a duration (read and write)
roll          -> If true, the missile will roll as well (1.31+ only) (read and write)
type          -> Integer that can be used to diferentiate missiles (read and write)
paused        -> True when the missile is paused, false otherwise (read and write)
tileset       -> The current terrain type under the missile (readonly)
timeScale     -> The time scale of the effect model (read and write) (1.31+ non dummmy version only)
alpha         -> The effect model alpha value (read and write) (1.31+ non dummmy version only)
playerColor   -> The effect model player color (read and write) (1.31+ non dummmy version only)
animation     -> The effect model animation type (read and write) (1.31+ non dummmy version only)
impact        -> The object representing the impact coordinates (x, y, z, angle, distance, square, slope, alpha) (read and write)
origin        -> The object representing the origin coordinates (x, y, z, angle, distance, square, slope, alpha) (read and write)
effect        -> The object representing the missile (size, yaw, pitch, roll, path, effect, attachments), and attachments has (x, y, z, size, yaw, pitch, roll, path, effect)
vJASS:
x             -> The current x coordinate of the missile (readonly)
y             -> The current y coordinate of the missile (readonly)
z             -> The current z coordinate of the missile (readonly)
prevX         -> The previous x coordinate of the missile (readonly)
prevY         -> The previous y coordinate of the missile (readonly)
prevZ         -> The previous z coordinate of the missile (readonly)
nextX         -> The next x coordinate of the missile (readonly)
nextY         -> The next y coordinate of the missile (readonly)
nextZ         -> The next z coordinate of the missile (readonly)
travel        -> The current traveled distance of the missile (readonly)
source        -> The source unit for the missile (read and write)
target        -> The target unit for the missile. If set the missile will be roaming (read and write)
owner         -> The owning player of the missile (read and write)
collideZ      -> If true, the missile will take into consideration the unit collision size, destructable oclusion height and item size when running the onHit, onDestructable, onItem events (read and write)
collision     -> The collision size of the missile. If not set, the events onHit, onDestructable, onItem and onMissile will no run (read and write)
damage        -> Stores the amount of damage to be used inside an event (read and write)
acceleration  -> The missile acceleration (read and write)
data          -> Stores information to be used inside any event (read and write)
model         -> The model of the missile (to set use yourMissile:model(string), to get use yourMissile.Model)
curve         -> The curve of the missile (to set use yourMissile:curve(real), to get use yourMissile.Curve)
arc           -> The Arc of the missile (to set use yourMissile:arc(real), to get use yourMissile.Arc)
scale         -> The scale of the missile (to set use yourMissile:scale(real), to get use yourMissile.Scale)
speed         -> The speed of the missile (to set use yourMissile:speed(real), to get use yourMissile.Speed)
duration      -> Set the speed of the missile to match a duration (to set use yourMissile:duration(real), to get use yourMissile.Duration)
vision        -> The sight range of the missile (to set use yourMissile:vision(real), to get use yourMissile.Vision)
roll          -> If true, the missile will roll as well (1.31+ only) (read and write)
type          -> Integer that can be used to diferentiate missiles (read and write)
paused        -> True when the missile is paused, false otherwise (read and write)
timeScale     -> The time scale of the effect model (to set use yourMissile:timeScale(real), to get use yourMissile.TimeScale)
alpha         -> The effect model alpha value (to set use yourMissile:alpha(integer), to get use yourMissile.Alpha)
playerColor   -> The effect model player color (to set use yourMissile:playerColor(integer), to get use yourMissile.playercolor)
animation     -> The effect model animation type (to set use yourMissile:animation(integer), to get use yourMissile.Animation)
tileset       -> The current terrain type under the missile (readonly)
impact        -> The object representing the impact coordinates (x, y, z, angle, distance, square, slope, alpha) (read and write)
origin        -> The object representing the origin coordinates (x, y, z, angle, distance, square, slope, alpha) (read and write)
effect        -> The object representing the missile (size, yaw, pitch, roll, path, effect, attachments), and attachments has (x, y, z, size, yaw, pitch, roll, path, effect)
vJASS:
Missile                 -> The missile
MissileDestroy          -> Set this variable to true inside a missile event trigger to destroy the missile
MissileStart            -> The starting point of the missile. Set this variable before running the MissileCreate trigger
MissileStartZ           -> The starting Z of the missile. Set this variable before running the MissileCreate trigger
MissileFinish           -> The end point of the missile. Set this variable before running the MissileCreate trigger
MissileFinishZ          -> The end Z of the missile. Set this variable before running the MissileCreate trigger
MissileModel            -> The missile model. Set this variable before running the MissileCreate trigger. You can set this varible to change the model at any point inside a missile event trigger
MissileScale            -> The missile model scale. Set this variable before running the MissileCreate trigger. You can set this varible to change the scale at any point inside a missile event trigger
MissileSpeed            -> The missile speed. Set this variable before running the MissileCreate trigger. You can set this varible to change the speed at any point inside a missile event trigger
MissileDuration         -> The missile duration. Set this variable before running the MissileCreate trigger. You can set this varible to change the duration at any point inside a missile event trigger
MissileArc              -> The missile arc. Set this variable before running the MissileCreate trigger. You can set this varible to change the arc at any point inside a missile event trigger
MissileCurve            -> The missile curve. Set this variable before running the MissileCreate trigger. You can set this varible to change the curve at any point inside a missile event trigger
MissileVision           -> The missile sight range. Set this variable before running the MissileCreate trigger. You can set this varible to change the sight range at any point inside a missile event trigger
MissileDamage           -> Stores the damage. Set this variable before running the MissileCreate trigger. You can set this varible to change the damage at any point inside a missile event trigger
MissileCollision        -> The missile collision size. Set this variable before running the MissileCreate trigger. You can set this varible to change the collision at any point inside a missile event trigger
MissileAcceleration     -> The missile acceleration. Set this variable before running the MissileCreate trigger. You can set this varible to change the acceleration at any point inside a missile event trigger
MissileCollideZ         -> If true, the missile will check Z height when colliding. Set this variable before running the MissileCreate trigger. You can set this varible to change the model at any point inside a missile event trigger
MissileSource           -> The missile source unit. Set this variable before running the MissileCreate trigger. You can set this varible to change the source at any point inside a missile event trigger
MissileTarget           -> The missile target unit. If set the missile will roam to the target. Set this variable before running the MissileCreate trigger. You can set this varible to change the target at any point inside a missile event trigger
MissileOwner            -> The owner of the missile. Set this variable before running the MissileCreate trigger. You can set this varible to change the owner at any point inside a missile event trigger
MissilePaused           -> True if the missile is paused, false otherwise
MissileRoll             -> If true the missile will use roll (1.31+)
MissileType             -> Set if you want to categorize the missile. Set this variable before running the MissileCreate trigger. You can set this varible to change the type at any point inside a missile event trigger
MissilePosition         -> The current missile position
MissileZ                -> The current missile Z
MissileLastPosition     -> The last missile position
MissilePrevZ            -> The last missile Z
MissileNextPosition     -> The next missile position
MissileNextZ            -> The next missile Z
MissileVelocity         -> The missile velocity. Velocity is the speed over the system period.
MissileTravelled        -> The missile travelled distance. It is reseted if you deflect or bounce the missile.
MissileRemoveLocations  -> Set to true before running MissileCreate trigger to remove the MissileStart and MissileFinish points automatically.
MissileHitUnit          -> The hitted unit inside an onHit event trigger
MissileHitDestructable  -> The hitted destructable inside an onDestructable event trigger
MissileHitItem          -> The hitted item inside an onItem event trigger
MissileHitMissile       -> The hitted missile inside an onMissile event trigger
MissileTileset          -> The terrain type under the missile
MissileData             -> The missile data variable can be used to hold information (integer)
MissileTimeScale        -> The missile effect time scale. Set this variable before running the MissileCreate trigger. You can set this varible to change the time scale at any point inside a missile event trigger (1.31+ non dummmy version only)
MissileAlpha            -> The missile effect alpha value. Set this variable before running the MissileCreate trigger. You can set this varible to change the alpha value at any point inside a missile event trigger (1.31+ non dummmy version only)
MissilePlayerColor      -> The missile effect player color. Set this variable before running the MissileCreate trigger. You can set this varible to change the player color of the effect at any point inside a missile event trigger (1.31+ non dummmy version only)
MissileAnimation        -> The missile effect animation type. Set this variable before running the MissileCreate trigger. You can set this varible to change the animation type of the effect at any point inside a missile event trigger (1.31+ non dummmy version only)
MissileDeflectTarget    -> Set this variable before running the MissileDeflectTarget trigger to deflect the missile to the unit assigned to this variable
MissileRed              -> Set this variable (and green and blue) after the missile is created and then run the MissileColor trigger to change the missile effect color values (1.31+ non dummmy version only)
MissileGreen            -> Set this variable (and red and blue) after the missile is created and then run the MissileColor trigger to change the missile effect color values (1.31+ non dummmy version only)
MissileBlue             -> Set this variable (and red and green) after the missile is created and then run the MissileColor trigger to change the missile effect color values (1.31+ non dummmy version only)
Missile_onPeriod        -> Set this variable to a trigger that you want to run on every missile period
Missile_onHit           -> Set this variable to a trigger that you want to run when the missile hits an unit. Use the MissileHitUnit variable to access the hitted unit
Missile_onDestructable  -> Set this variable to a trigger that you want to run when the missile hits a destructable. Use the MissileHitDestructable variable to access the hitted destructable
Missile_onItem          -> Set this variable to a trigger that you want to run when the missile hits an item. Use the MissileHitItem variable to access the hitted item
Missile_onMissile       -> Set this variable to a trigger that you want to run when the missile hits another Missile. Use the MissileHitMissile variable to access the hitted Missile
Missile_onCliff         -> Set this variable to a trigger that you want to run when the missile collides with a cliff wall with height greater than the current missile height
Missile_onTerrain       -> Set this variable to a trigger that you want to run when the missile collides with terrain
Missile_onTileset       -> Set this variable to a trigger that you want to run when the missile changes tileset types
Missile_onFinish        -> Set this variable to a trigger that you want to run when the missile reaches its destination
Missile_onBoundaries    -> Set this variable to a trigger that you want to run when the missile tries to leave the map
Missile_onPause         -> Set this variable to a trigger that you want to run when the missile is paused
Missile_onResume        -> Set this variable to a trigger that you want to run when the missile is unpaused
Missile_onRemove        -> Set this variable to a trigger that you want to run when the missile is removed
MissileEvent            -> This variable represents which event is being executed. Compere it to one of the following variables inside a trigger to decide what to do in each event
MissileOnPeriod         -> MissileEvent is set to the value of this variable when Missile_onPeriod is set to a trigger
MissileOnHit            -> MissileEvent is set to the value of this variable when Missile_onHit is set to a trigger
MissileOnDestructable   -> MissileEvent is set to the value of this variable when Missile_onDestructable is set to a trigger
MissileOnItem           -> MissileEvent is set to the value of this variable when Missile_onItem is set to a trigger
MissileOnMissile        -> MissileEvent is set to the value of this variable when Missile_onMissile is set to a trigger
MissileOnCliff          -> MissileEvent is set to the value of this variable when Missile_onCliff is set to a trigger
MissileOnTerrain        -> MissileEvent is set to the value of this variable when Missile_onTerrain is set to a trigger
MissileOnTileset        -> MissileEvent is set to the value of this variable when Missile_onTileset is set to a trigger
MissileOnFinish         -> MissileEvent is set to the value of this variable when Missile_onFinish is set to a trigger
MissileOnBoundaries     -> MissileEvent is set to the value of this variable when Missile_onBoundaries is set to a trigger
MissileOnPause          -> MissileEvent is set to the value of this variable when Missile_onPause is set to a trigger
MissileOnResume         -> MissileEvent is set to the value of this variable when Missile_onResume is set to a trigger
MissileOnRemove         -> MissileEvent is set to the value of this variable when Missile_onRemove is set to a trigger
vJASS:
method bounce takes nothing returns nothing
    -> Bounces the missile, reseting angles

method deflect takes real x, real y, real z returns nothing
    -> Deflects the missile to the x, y and z coordinates

method deflectTarget takes unit u returns nothing
    -> Deflects the missile to a unit

method flushAll takes nothing returns nothing
    -> Flushes the hit group for the missile

method flush takes widget w returns nothing
    -> Remove a unit, destructable, item or missile from the hit group

method hitted takes widget w returns boolean
    -> Returns true if the unit, destructable or item was hit by the missile

method attach takes string model, real dx, real dy, real dz, real scale returns effect
    -> Attach an effect to the missile and return the effect attached
    -> dx, dy, and dz are offsets from the main missile coordinates

method detach takes effect attachment returns nothing
    -> Detach an effect attached to the missile

method pause takes boolean flag returns nothing
    -> If true will pause the misisle, else will unpause it

method color takes integer red, integer green, integer blue returns nothing
    -> Changes the missile effect model color (1.31+ non dummy version only)
vJASS:
MissileDeflectPosition
MissileDeflectZ
MissileDeflectTarget
Set the above variables then run the MissileDeflect or MissileDeflectTarget triggers to deflect the missile

MissileFlushUnit
Set the above variable then run the MissileFlush to make the set unit be able to be hitted again by the missile

MissileHittedUnit
MissileHitted
Set the MissileHittedUnit to a unit then run the MissileHitted trigger. After that you can use the MissileHitted varible to check if the unit was already hitted by the missile

MissileAttachModel
MissileAttachX
MissileAttachY
MissileAttachZ
MissileAttachScale
MissileDetachEffect
Set the above varibles then run the MissileAttach trigger to attach an effect to the missile with MissileAttach(Model, X, Y, Z, Scale)

MissileDetachEffect
Set the above variable to the effect that was previously attached to the missile then run the MissileDetach trigger

Run the MissileBounce trigger to bounce the missile and reset its travalled distance

Run the MissileFlushAll trigger flush all units hitted by the missile

Run the MissilePause trigger pause the missile

Run the MissileResume trigger unpause the missile

Run the MissileBounce trigger to bounce the missile and reset its travalled distance

MissileRed
MissileGreen
MissileBlue
Set the above variables and then run the MissileColor trigger to change the missile effect model color (1.31+ non dummy version only)
vJASS:
function CreateMissileGroup takes nothing returns MissileGroup
    -> Creates a missile group and return it

function DestroyMissileGroup takes MissileGroup missiles returns nothing
    -> Destroys the misisle group

function MissileGroupGetSize takes MissileGroup missiles returns integer
    -> Returns the size of the missile group

function GroupMissileAt takes MissileGroup missiles, integer position returns Missiles
    -> Returns the missile at the given index.

function ClearMissileGroup takes MissileGroup missiles returns nothing
    -> Clear the missile group

function IsMissileInGroup takes Missiles missile, MissileGroup missiles returns boolean
    -> True if the missile is inside the missile group

function GroupRemoveMissile takes MissileGroup missiles, Missiles missile returns nothing
    -> Removes the missile from the missile group

function GroupAddMissile takes MissileGroup missiles, Missiles missile returns nothing
    -> Add the missile to the missile group

function GroupPickRandomMissile takes MissileGroup missiles returns Missiles
    -> Returns a random missile from the misisle group. 0 if empty

function FirstOfMissileGroup takes MissileGroup missiles returns Missiles
    -> Returns the first missile inside the missile group. 0 if empty

function GroupAddMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
    -> Add the missiles that are in the source group into the destiny group

function GroupRemoveMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
    -> Remove the missiles that are in the source group from the destiny group

function GroupEnumMissilesOfType takes MissileGroup missiles, integer whichType returns nothing
    -> Enumerates the missiles of matching type

function GroupEnumMissilesOfTypeCounted takes MissileGroup missiles, integer whichType, integer amount returns nothing
    -> Enumerates up to amount of missiles of matching type

function GroupEnumMissilesOfPlayer takes MissileGroup missiles, player p returns nothing
    -> Enumerates the missiles that belongs to a player

function GroupEnumMissilesOfPlayerCounted takes MissileGroup missiles, player p, integer amount returns nothing
    -> Enumerates up to amount of missiles that belongs to a player

function GroupEnumMissilesInRect takes MissileGroup missiles, rect r returns nothing
    -> Enumerates the missiles inside a region

function GroupEnumMissilesInRectCounted takes MissileGroup missiles, rect r, integer amount returns nothing
    -> Enumerates up to amount of missiles inside a region

function GroupEnumMissilesInRangeOfLoc takes MissileGroup missiles, location loc, real radius returns nothing
    -> Enumerates the missiles within range of a location

function GroupEnumMissilesInRangeOfLocCounted takes MissileGroup missiles, location loc, real radius, integer amount returns nothing
    -> Enumerates up to amount of missiles within range of a location

function GroupEnumMissilesInRange takes MissileGroup missiles, real x, real y, real radius returns nothing
    -> Enumerates the missiles within range of x, y

function GroupEnumMissilesInRangeCounted takes MissileGroup missiles, real x, real y, real radius, integer amount returns nothing
    -> Enumerates up to amount of missiles within range of x, y

  • BPower
  • Dirac
  • Vexorian
  • AGD
  • emjlr3
  • AceHart
  • Nestharus
  • Maghteridon96
  • Bribe
  • Flux
  • Forsakn

(v1.0)
  • Release
(v1.1)
  • Merged Normal and Relativistic code for both Legacy and Current versions.
  • You can use the SWEET_SPOT constant to determine the system behavior
    • SWEET_SPOT <= 0 -> normal method
    • SWEET_SPOT > 0 -> relativistic method
  • Included support for DummyRecycler by Flux for the Legacy version. This should improve its performance even further.
(v1.2)
  • Fixed a bug for homing missiles height
  • New configuration member boolean collideZ -> set to true to make the new missile consider z collisions
  • New Global Configuration constant boolean ROLL -> If true missile will consider roll in orientation. Roll can look really fishy for some users and was never possible until patch 1.31, so if you want it set it to true in the globlas block (1.31+ version only).
  • You can now attach effects to the main missile effect with an offset through 2 new methods (1.31+ version only)
    • // dx, dy, and dz are offsets from the main missile coordinates method attach takes string model, real dx, real dy, real dz, real scale returns effect
    • method detach takes effect attachment returns nothing
  • Few minor optmizations.
(v1.3)
  • Fix a minor rect leak
(v1.4)
  • Minor optmizations.
  • WorldBounds not required anymore.
(v1.5)
  • WorldBounds now is required (Centralized requirements and outsourced)
  • New optional event (See Available Events for more info)
    • method onBoundaries takes nothing returns boolean
  • Fixed a spelling mistake on the detach functionality.
(v1.6)
  • MissileEffect library updated to use the BlzSetSpecialEffectOrientation native. (v1.31+ only)
(v1.7)
  • Standardized getting and setting of the members speed, arc and curve. They now read and write in degrees.
(v1.8)
  • Made a system function (GetLocZ) private to avoid conflict with other resources.
(v1.9)
  • System ported to LUA (Credits to Forsakn for the first port).
  • Added instant facing when creating missiles instances.
  • Included a version of Missiles that uses the dummy method and a Dummy Pool utilizing the new natives. This version by default will not let missiles clip through terrain.
  • Code formatting and cleaning.
(v2.0)
  • System ported to GUI!
  • Implemented ways to enumerate missiles (Missile Groups)
  • New method: pause. When called pauses or unpauses the missile
  • 2 new events: onPause and onResume
  • 3 new members: roll, type and paused
  • Added the attach and detach methods to the LUA version
  • Fixed a bug on the collideZ
  • Fixed a bug when deflecting missiles
  • Refactored code to be more readable and easy to maintain
(v2.1)
  • Removed the usage of a post 1.30 native being used in the 1.30 version of the system. Now the 1.30- version should work for patches below that
  • Fixed a bug in the LUA GUI version for the onPause and the onRemove events
  • Added an alternative method of setting up events for the GUI version. It's more compact, you can set up all events you want inside only 1 trigger (See the Trigger Preview for examples on how to do it)
(v2.2)
  • Adjusted the initial position of the missile in the versions that use the dummy method.
(v2.3)
  • 2 new events:
    • onCliff -> Run whenever the missiles collides with a cliff wall and it's height is greater than the current missile height
    • onTileset -> Run whenever the missile changes terrain types
  • 4 new members
    • nextX -> The next missile x value
    • nextY -> The next missile y value
    • nextZ -> The next missile z value
    • tileset -> the current terrain type under the missile
  • Fixed the missile movement on higher ground levels
  • Fixed the movement of missiles when going over terrain/cliffs for the dummy versions
  • Separated the GUI version into vJASS and LUA implementations. For those who want to choose between them.
(v2.4)
  • Missiles can now grant vision using the new member "vision"
    • vJASS:
      // To set (The missile must have a source or a owner)
      yourMissile.vision = value
      
      // To get
      var = yourMissile.vision

    • Lua:
      -- To set (The missile must have a source or a owner)
      yourMissile:vision(value)
      
      -- To get
      var = yoruMissile.Vision
      • Example
        • Actions
          • -- Set the MissileVision variable before running the MissileCreate trigger ou at any event trigger (The missile must have a source or a owner) --
          • Set VariableSet MissileSource = (Triggering unit)
          • Set VariableSet MissileVision = 800.00
          • Creates the missile
          • Trigger - Run MissileCreate <gen> (ignoring conditions)
  • New required library: Alloc
  • Fixed a few bugs in the GUI version
(v2.5)
  • Missiles can now be paused within the onFinish event.
  • Removed the deflectZ method, now the deflect method expects x, y and z parameters
  • New method deflectTarget deflects the missile to a unit
  • New GUI variable MissileData can be used to store information as needed (integer type)
  • version 1.31+ non dummy only changes:
    • 4 new method operator (operators in vJASS, functions in Lua)
      • yourMissile.timeScale => changes the time scale of the effect model (animation speed)
      • yourMissile.alpha => changes the alpha value of the effect model (transparency)
      • yourMissile.playerColor => changes the player color of the effect model
      • yourMissile.animation => changes the animation type used by the effect model
    • 1 new method
      • yourMissile.color(integer, integer, integer) => changes the effect model color (vJASS)
      • yourMissile:color(integer, integer, integer) => changes the effect model color (Lua)
    • vJASS:
      // To set
          yourMissile.timeScale = 3.
          yourMissile.alpha = 128
          yourMissile.playerColor = GetPlayerId(GetOwningPlayer(unit))
          yourMisisle.animation = 3 // ConvertAnimType(3) under the hood
      // To get
          variable = yourMissile.timeScale
          variable = yourMissile.alpha
          variable = yourMissile.playerColor
          variable = yourMisisle.animation
    • Lua:
      -- To set
          yourMissile:timeScale(3.)
          yourMissile:alpha(128)
          yourMissile:playerColor(GetPlayerId(GetOwningPlayer(unit)))
          yourMisisle:animation(3) -- ConvertAnimType(3) under the hood
      -- To get
          variable = yourMissile.TimeScale
          variable = yourMissile.Alpha
          variable = yourMissile.playerColor
          variable = yourMisisle.Animation
      • GUIExamle
        • Actions
          • Set VariableSet MissileAnimation = 5
          • Set VariableSet MissileTimeScale = 3.00
          • Set VariableSet MissilePlayerColor = 3
          • Set VariableSet MissileAlpha = 128
          • Trigger - Run MissileCreate <gen> (ignoring conditions)
          • -------- CHANGING THE COLOR. MUST BE DONE AFTER THE MISSILE IS CREATED (1.31+ NON DUMMY VERSION ONLY) --------
          • Set VariableSet MissileRed = 123
          • Set VariableSet MissileGreen = 67
          • Set VariableSet MissileBlue = 32
          • Trigger - Run MissileColor <gen> (ignoring conditions)
(v2.6)
  • Fixed a bug when pausing missiles inside an onFinish event.
(v2.7)
  • Minor improvements.
(v2.8)
  • Fixed a bug in the deflect method when the onTerrain event is declared.
  • Fixed a bug in the flushAll method in the LUA version.
Contents

Missiles (Map)

Missiles (Map)

Missiles (Map)

Missiles (Map)

Reviews
Wrda
Upgraded to High quality due to how great the implementation and usefulness this offers.

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
I have a few issues with your API. It's great don't get me wrong, but a few things made me implement my own method instead of just using yours. First, like JAKEZINC said is the orientation problem, it produces wrong results with the method i implemented. I did suggested it in the thread for you to add a orientation method that would use the direct yaw, pitch and roll alongside the one already there, but you didn't add it. Second is ease of use, as i said i wanted this system to be as friendly as possible to entry level users, and your system, although very sophisticated, looks somewhat scary, specially for those who never seen a struct or are just beginning to learn it. I also think it could be more intuitive to use, for example, in my own, although very simple, method names are straight to the point, "attach", "detach" and effects are matched by handle instead of by model name, which is also a downside since your system only allows for 1 model of a type. Finally i wanted it to be as lightweight as possible, and when the user wanted a more sophisticated functionality he/she could implement him/herself for a specific missile.
Comparing the API
yours:
JASS:
method attach takes string fxpath, real dx, real dy, real dz, real scale returns effect

method dettach takes effect sfx returns nothing

method orient takes real yaw, real pitch, real roll returns nothing

method move takes real x, real y, real z returns nothing

method scale takes effect sfx, real scale returns nothing
/*
    If it's only meant to scale a single effect handle the user can simply do this with a native call
*/
...

equivalents in mine:
JASS:
method addModel takes string model returns effect

method removeModel takes string model returns nothing

method setOrientation takes real yaw, real pitch, real roll returns nothing

method move takes real x, real y, real z returns nothing

call BlzSetSpecialEffectScale(sfx.addModel(model), scale)
// Equivalent of my scaling to yours

...

Looking at the above, I honestly don't see what makes the later one scarier over the former. I find both names 'to the point'. And regarding the ease of use, I can argue that it's easier for user to just specify the model both when attaching and removing attachments than to also specify position offsets when attaching an then having to save it to an effect variable just so you can dettach it. Note that multiple similar models being attached is the rare case rather than the norm, so if I were to appeal to ease of use for majority of users, I'd still go with the later API.
Now the point that I want to get across is not which API here is 'truly' better because honestly, it can be very subjective. Even me when looking at already established resources, I can find a lot of them where I want to recreate their APIs into something 'I think' (the keyword there) is better and more intuitive. But then again, idea coming from me would ofcourse be intuitive to me. That's why I refrained from mentioning API initially in my comments since it is very subjective and instead emphasized the result which would be redundancy of resources from the user's side when importing.

As for the use of lite-ness as a reason for reimplementation, one can also use this for most of the resources here in Hive actually, such as damage systems, unit indexers, etc., to make lite-variants of those various systems. But I don't think it would be good practice.

Regarding the orientation, it didn't made a difference in my tests (JAKEZINC also said it didn't). But since you emphasized it and since both of us haven't tested it in Reforged, I think I'll change it to be safe.

private modules are working correctly? I remember some time ago they were not, i will add the private to them so it will not conflict with external libs.
Yes they work perfectly.
 
Level 20
Joined
May 16, 2012
Messages
635
Comparing the API
yours:
JASS:
method attach takes string fxpath, real dx, real dy, real dz, real scale returns effect

method dettach takes effect sfx returns nothing

method orient takes real yaw, real pitch, real roll returns nothing

method move takes real x, real y, real z returns nothing

method scale takes effect sfx, real scale returns nothing
/*
    If it's only meant to scale a single effect handle the user can simply do this with a native call
*/
...

equivalents in mine:
JASS:
method addModel takes string model returns effect

method removeModel takes string model returns nothing

method setOrientation takes real yaw, real pitch, real roll returns nothing

method move takes real x, real y, real z returns nothing

call BlzSetSpecialEffectScale(sfx.addModel(model), scale)
// Equivalent of my scaling to yours

...

Looking at the above, I honestly don't see what makes the later one scarier over the former. I find both names 'to the point'. And regarding the ease of use, I can argue that it's easier for user to just specify the model both when attaching and removing attachments than to also specify position offsets when attaching an then having to save it to an effect variable just so you can dettach it. Note that multiple similar models being attached is the rare case rather than the norm, so if I were to appeal to ease of use for majority of users, I'd still go with the later API.
Now the point that I want to get across is not which API here is 'truly' better because honestly, it can be very subjective. Even me when looking at already established resources, I can find a lot of them where I want to recreate their APIs into something 'I think' (the keyword there) is better and more intuitive. But then again, idea coming from me would ofcourse be intuitive to me. That's why I refrained from mentioning API initially in my comments since it is very subjective and instead emphasized the result which would be redundancy of resources from the user's side when importing.

As for the use of lite-ness as a reason for reimplementation, one can also use this for most of the resources here in Hive actually, such as damage systems, unit indexers, etc., to make lite-variants of those various systems. But I don't think it would be good practice.

Regarding the orientation, it didn't made a difference in my tests (JAKEZINC also said it didn't). But since you emphasized it and since both of us haven't tested it in Reforged, I think I'll change it to be safe.

Yes they work perfectly.

All fair points, but i still think providing the ability to create multiple effects of the same type is a good addition as well as enabling offsets to them even if its not the normal case, and if someone can save a string to remove a model later they can save an effect just as well. I don't think my version is better than your, on the contrary, but it's the way i found to deal with the problem and it performs well. I don't think you should change stuff on your code (orientation) just to accommodate a difference in my system, since I've already created something of my own and don't intend in rewriting a lot of code.
 
Level 7
Joined
Aug 11, 2010
Messages
269
I've found out how to tie this system very rudimentary to GUI, which is what I'm most comfortable/familiar with. One thing that I'm not able to solve though, and I feel silly for it: using the Q example (Firebolt) I've been trying to figure out how to get it to travel towards the targeted area for a set distance. (As it is now, if you fire a Firebolt right next to your Hero the bolt is destroyed. I'd like to know how to set the minimum/maximum distance to 600 towards the cast point, for example.)

I'm not familiar at all with JASS; honestly. That's probably why I'm struggling. I tried converting some GUI into JASS hoping to jury rig something together, but I wasn't successful. Could you provide any tips?
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
^ Before converting a GUI script into JASS, make sure to understand the basics of JASS first.
Once that script gets converted, it would only show you some toppings such as BJs and implicit
boolean operations which aren't that complicated once you get to learn the basics honestly.

There are lots of tutorials here to cover that,
but I wasn't successful
And it doesn't mean you give up that easily...
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
@chopinski , can you show a GIF or similar showing a case where BlzSetSpecialEffectOrientation() yields incorrect results compared to BlzSetSpecialEffectYaw/Pitch/Roll()? So I can test it also myself because so far there has been no difference except for the one below, which shows that the later method actually has problems.

You will see in the gif that the later method sometimes causes flickering when the pitch is at 90 degrees (facing down) or at 270 degrees (facing up). It also moves the effects up and down a bit. On the other hand, effects remain steady when using BlzSetSpecialEffectOrientation().
left = uses BlzSetSpecialEffectYaw/Pitch/Roll()
right = uses BlzSetSpecialEffectOrientation()

sfx_orientation_flickering-gif.361974
sfx_orientation_no_flicker-gif.361975


You can test it using the code below
JASS:
scope EffectOrientationTest initializer OnInit

    globals
        /*
        *   true -> Uses BlzSetSpecialEffectOrientation()
        *   false -> Uses BlzSetSpecialEffectYaw/Pitch/Roll()
        */
        private constant boolean SWITCH = true

        private constant real height = 300.00
        private constant integer sfxCount = 8
        private constant real spacing = 200.00
        private constant real width = spacing*sfxCount

        private effect array sfx
        private real array yaw
        private real array pitch
        private real array roll
    endglobals

    private function OnOrientationPeriod takes nothing returns nothing
        local integer i = sfxCount
        local real x = GetCameraTargetPositionX()
        local real y = GetCameraTargetPositionY()
        loop
            exitwhen i == 0

            set yaw[i] = yaw[i] + 0.00
            set pitch[i] = ModuloReal(pitch[i] + 2*bj_DEGTORAD, 2.*bj_PI)
            set roll[i] = 90.*bj_DEGTORAD

            static if SWITCH then
                call BlzSetSpecialEffectOrientation(sfx[i], yaw[i], pitch[i], roll[i])
            else
                call BlzSetSpecialEffectYaw(sfx[i], yaw[i])
                call BlzSetSpecialEffectPitch(sfx[i], pitch[i])
                call BlzSetSpecialEffectRoll(sfx[i], roll[i])
            endif

            call BlzSetSpecialEffectX(sfx[i], x + spacing*i - width/2)
            call BlzSetSpecialEffectY(sfx[i], y)
            call BlzSetSpecialEffectHeight(sfx[i], height)

            set i = i - 1
        endloop
    endfunction

    private function InitOrientationTest takes nothing returns nothing
        local integer i = sfxCount
        local real x = GetCameraTargetPositionX()
        local real y = GetCameraTargetPositionY()
        loop
            exitwhen i == 0
            set sfx[i] = AddSpecialEffect("Abilities\\Weapons\\GargoyleMissile\\GargoyleMissile.mdl", x + spacing*i - width/2, y)
            call BlzSetSpecialEffectHeight(sfx[i], height)
            set i = i - 1
        endloop
        call TimerStart(CreateTimer(), 1./32., true, function OnOrientationPeriod)
    endfunction

    private function OnInit takes nothing returns nothing
        call FogEnable(false)
        call FogMaskEnable(false)
        call InitOrientationTest()
    endfunction

endscope
 
Level 20
Joined
May 16, 2012
Messages
635
@chopinski , can you show a GIF or similar showing a case where BlzSetSpecialEffectOrientation() yields incorrect results compared to BlzSetSpecialEffectYaw/Pitch/Roll()? So I can test it also myself because so far there has been no difference except for the one below, which shows that the later method actually has problems.

You will see in the gif that the later method sometimes causes flickering when the pitch is at 90 degrees (facing down) or at 270 degrees (facing up). It also moves the effects up and down a bit. On the other hand, effects remain steady when using BlzSetSpecialEffectOrientation().
left = uses BlzSetSpecialEffectYaw/Pitch/Roll()
right = uses BlzSetSpecialEffectOrientation()

sfx_orientation_flickering-gif.361974
sfx_orientation_no_flicker-gif.361975


You can test it using the code below
JASS:
scope EffectOrientationTest initializer OnInit

    globals
        /*
        *   true -> Uses BlzSetSpecialEffectOrientation()
        *   false -> Uses BlzSetSpecialEffectYaw/Pitch/Roll()
        */
        private constant boolean SWITCH = true

        private constant real height = 300.00
        private constant integer sfxCount = 8
        private constant real spacing = 200.00
        private constant real width = spacing*sfxCount

        private effect array sfx
        private real array yaw
        private real array pitch
        private real array roll
    endglobals

    private function OnOrientationPeriod takes nothing returns nothing
        local integer i = sfxCount
        local real x = GetCameraTargetPositionX()
        local real y = GetCameraTargetPositionY()
        loop
            exitwhen i == 0

            set yaw[i] = yaw[i] + 0.00
            set pitch[i] = ModuloReal(pitch[i] + 2*bj_DEGTORAD, 2.*bj_PI)
            set roll[i] = 90.*bj_DEGTORAD

            static if SWITCH then
                call BlzSetSpecialEffectOrientation(sfx[i], yaw[i], pitch[i], roll[i])
            else
                call BlzSetSpecialEffectYaw(sfx[i], yaw[i])
                call BlzSetSpecialEffectPitch(sfx[i], pitch[i])
                call BlzSetSpecialEffectRoll(sfx[i], roll[i])
            endif

            call BlzSetSpecialEffectX(sfx[i], x + spacing*i - width/2)
            call BlzSetSpecialEffectY(sfx[i], y)
            call BlzSetSpecialEffectHeight(sfx[i], height)

            set i = i - 1
        endloop
    endfunction

    private function InitOrientationTest takes nothing returns nothing
        local integer i = sfxCount
        local real x = GetCameraTargetPositionX()
        local real y = GetCameraTargetPositionY()
        loop
            exitwhen i == 0
            set sfx[i] = AddSpecialEffect("Abilities\\Weapons\\GargoyleMissile\\GargoyleMissile.mdl", x + spacing*i - width/2, y)
            call BlzSetSpecialEffectHeight(sfx[i], height)
            set i = i - 1
        endloop
        call TimerStart(CreateTimer(), 1./32., true, function OnOrientationPeriod)
    endfunction

    private function OnInit takes nothing returns nothing
        call FogEnable(false)
        call FogMaskEnable(false)
        call InitOrientationTest()
    endfunction

endscope

@AGD I've tested the orientation native in my missile system and in a few of my Hero Concepts and apparently Blizzard have patched the native because now it produces the exact same result as in using the Yaw, Pitch and Roll natives, at least for the orientation method i implemented (Dirac's method), even with no roll (in my system its optional). I remember it being an issue when i reported it here but now it seems to be solved, which is good, because your SpecialEffect Lib do not need to have any change now. I'll change my MissileEffect lib to use the native as well since its less lines of code. I cant say at which version they change it, but I tested it in reforged as well and they are producing the same result.
 
Last edited:
Level 20
Joined
Aug 13, 2013
Messages
1,696
It looks like they do fixings like these without mentioning in the patch notes. -,-

I mean if they did fixed something that's troublesome for us before they should inform at least. (whether it's a minor or major thing)
As it could help us to point and revert the changes we've made just to workaround with these and to avoid such confusions like this.

There are even some of the reported broken natives that weren't broken before and can crash a game.
I believe these are worth of mentioning in the patch changelog list.

Regardless, it's good to know that these orientation natives are now fixed in the latest patch though. This just implies they do listen.
(I just don't like their idea of being implicit when it comes to these things, they should speak somehow to fulfill good communication)
 
Level 20
Joined
May 16, 2012
Messages
635
It looks like they do fixings like these without mentioning in the patch notes. -,-

I mean if they did fixed something that's troublesome for us before they should inform at least. (whether it's a minor or major thing)
As it could help us to point and revert the changes we've made just to workaround with these and to avoid such confusions like this.

There are even some of the reported broken natives that weren't broken before and can crash a game.
I believe these are worth of mentioning in the patch changelog list.

Regardless, it's good to know that these orientation natives are now fixed in the latest patch though. This just implies they do listen.
(I just don't like their idea of being implicit when it comes to these things, they should speak somehow to fulfill good communication)

Fully agreed, and it seems that they are leaving The World Editor/ Jass changes in the dark, so we must find out whats different ourselves, which sucks, because those were not the only changes to happen to jass in the latest patches.
 
Level 20
Joined
May 16, 2012
Messages
635
Could someone make an example spell that only has onunit collision testing the limits of this system for the non-reforged version?

Sorry for the delay response. Hold your horses for a bit longer, things are a bit chaotic here right now, but as soon as i can i'll do what you asked. As a spoiler, the performance will not be much different, specially if you use the dummy recycler library. If you go to page 2 of this thread and look for a video i posted in response for IcemanBo comment you will be able to see the Barrage spell that has onHit and onDestructible events and still are able to run with thousands of missiles without breaking the game.

Edit: @Dat-C3 Below is a simple spell like you asked. I also attached a test map. What the spell do is when you cast it, from the position of the caster every 0.05 seconds a fire ball will be launched to you current mouse position. The missile has only an onHit declared.

JASS:
scope Example
    // The following Spell struct represents our missile.
    // To make it behave like a Missile just make it extend Missiles.
    // After that you will be able to optionally declare any event implemented.
    private struct Spell extends Missiles
        // the onHit event is one of the events that you can
        // implement. You must declare it like this.
        // It will only happen if your missile has a collision
        // size greater than 0.
        // To see how and what events you can implement and
        // what they do see the Events section in the thread.
        method onHit takes unit hit returns boolean
            // owner, source and damage are all members of the main missile system
            // that you can set when creating it. To see what members you can have access
            // see the Members section of the thread. That's it, now your missile will do
            // damage to any enemy unit within it's collision size
            if IsUnitEnemy(hit, owner) then
                call UnitDamageTarget(source, hit, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
            endif

            // returning true will destroy the missile.
            return false
        endmethod
    endstruct

    private struct Example
        static timer   timer  = CreateTimer()
        static boolean active = false
        static unit    unit

        static method onPeriod takes nothing returns nothing
            // x, y and z are the start coordinates of the missile
            local real x  = GetUnitX(unit)
            local real y  = GetUnitY(unit)
            local real z  = GetUnitFlyHeight(unit) + 60
            // tx, ty and tz are the final coordinates of the missile
            local real tx = GetPlayerMouseX(GetOwningPlayer(unit))
            local real ty = GetPlayerMouseY(GetOwningPlayer(unit))
            local real tz = 60
            // instantiate the missile (onle 1 create method that takes initial x,y,z and final x,y,z)
            // If you set the target member that the final position will automatically adjust itself
            // but you still need to provide a final coordinate
            local Spell missile = Spell.create(x, y, z, tx, ty, tz)

            // Before calling launch you can set what you need
            set missile.source    = unit
            set missile.model     = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
            set missile.speed     = 500
            set missile.damage    = 50
            set missile.collision = 32 // onHit only happens if collision is greater than 0
            set missile.owner     = GetOwningPlayer(unit)
            set missile.arc       = GetRandomReal(0, 35)
            set missile.curve     = GetRandomReal(-25, 25)

            // launches the missile
            call missile.launch()
        endmethod

        static method onCast takes nothing returns nothing
            if active then
                set active = false
                call PauseTimer(timer)
            else
                set unit   = GetTriggerUnit()
                set active = true
                call TimerStart(timer, 0.05, true, function thistype.onPeriod)
            endif
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A000', function thistype.onCast)
            call Cheat("iseedeadpeople")
        endmethod
    endstruct
endscope

I tried to detail every piece of the code so you can understand whats going on. It's also using the version that uses the dummy method. Good Luck!
 

Attachments

  • Example.w3x
    92.3 KB · Views: 70
Last edited:
this is pretty wicked, im new to the whole jass thing, so heres my question, how would i make it so it is doing that damage only to the enemyunits, currently it is damaging everything, my own buildings and units and allies buildings and units, its probably very simple and i am just missing something lol any help would be greatly appreciated :)
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
this is pretty wicked, im new to the whole jass thing, so heres my question, how would i make it so it is doing that damage only to the enemyunits, currently it is damaging everything, my own buildings and units and allies buildings and units, its probably very simple and i am just missing something lol any help would be greatly appreciated :)

Why don't you post your code here so i can guide you through it. To filter enemy units you can use native IsUnitEnemy takes unit whichUnit, player whichPlayer returns boolean inside a onHit event.
 
Why don't you post your code here so i can guide you through it.

well im playing with the missilesW on your map, damaging instead of straight killing, so as it would be in there how would i put it and where exactly lol

updated: hah i figured it out now, i was forgetting to set the owner of dummy:goblin_wtf: lol geez, this missile system is great much fun
 
Last edited:
I made a variation on your rain of meteors spell. It works in game but was wondering if I plugged all the memory leaks and if I made any other mistakes since I'm new to VJASS and timer utils
Code:
scope MeteorRain
    private struct Missile extends Missiles
    real RoX_X = 0.0
    real RoX_Y = 0.0
    real AoE

        method onRemove takes nothing returns nothing
            local group enemies = CreateGroup()
            local unit temp
         
        call GroupEnumUnitsInRange(enemies, RoX_X,RoX_Y, AoE, null)
            loop
            set temp = FirstOfGroup(enemies)
              exitwhen temp == null
            if IsUnitEnemy(temp, GetOwningPlayer(.source)) and not IsUnitType(temp, UNIT_TYPE_MAGIC_IMMUNE) and not BlzIsUnitInvulnerable(temp) and IsUnitAliveBJ(temp) then
                    call UnitDamageTarget(.source, temp, damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
            endif
            call GroupRemoveUnit(enemies, temp)
            endloop
        call DestroyGroup(enemies)
        set enemies = null
        set temp = null
        endmethod
    endstruct

    private struct MeteorRain
        timer   outer_t
        unit    c
        real    fx
        real    fy
        real    tx
        real    ty
    static integer shardcount = 10
        integer count
    integer wavecount = 3
        timer   inner_t
    static integer AId = 'A009'
    real AoE = 500.0
    real damage = 400.0 / wavecount / shardcount

        private static method GetRandomRange takes real radius returns real
            local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
      
            if r > 1 then
                return (2 - r)*radius
            endif
      
            return r*radius
        endmethod


        static method onOuterLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
        //call BJDebugMsg("OuterLoop")
        if .wavecount > 0 then
        set .count = shardcount
                set .inner_t = NewTimerEx(this)
            call TimerStart(.inner_t, 0.05, true, function thistype.onLoop)
            set wavecount = wavecount - 1
        else
        call ReleaseTimer(GetExpiredTimer())
        set .outer_t = null
        endif
    endmethod
        static method onLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real     maxRange
            local real     theta
            local real     toX
            local real     toY
            local real     fromX
            local real     fromY
        local real radius
            local Missile  missile

        //call BJDebugMsg("InnerLoop")
            if count > 0 then
                set count   = count - 1
                set theta   = 2*bj_PI*GetRandomReal(0, 1)
                set radius  = GetRandomRange(.AoE)
                set toX     = tx + radius*Cos(theta)
                set toY     = ty + radius*Sin(theta)
                set theta   = AngleBetweenPointsReal(tx, ty, fx, fy)
                set fromX   = toX + 1500*Cos(theta)
                set fromY   = toY + 1500*Sin(theta)
                set missile = Missile.create(fromX, fromY, 1500, toX, toY, 0)
              
                set missile.source    = c
                set missile.model     = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl"
                set missile.scale     = 1.5
                set missile.duration  = 1.0
                set missile.collision = 100
                set missile.collideZ  = true
        set missile.RoX_X = tx
        set missile.RoX_Y = ty
        set missile.AoE = .AoE
        set missile.damage = .damage

                call missile.launch()
        else
        call ReleaseTimer(GetExpiredTimer())
        set .inner_t = null
            endif
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()

            set outer_t     = NewTimerEx(this)
            set c     = GetTriggerUnit()
            set fx    = GetUnitX(c)
            set fy    = GetUnitY(c)
            set tx    = GetSpellTargetX()
            set ty    = GetSpellTargetY()
        set .count = shardcount
                set .inner_t = NewTimerEx(this)
            call TimerStart(.inner_t, 0.05, true, function thistype.onLoop)
            set wavecount = wavecount - 1
                set .outer_t = NewTimerEx(this)
            call TimerStart(.outer_t, 2.0, true, function thistype.onOuterLoop)
            set c = null
        //call BJDebugMsg("End")
            call deallocate()
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(AId, function thistype.onCast)
        endmethod
    endstruct
endscope
 
Level 20
Joined
May 16, 2012
Messages
635
I made a variation on your rain of meteors spell. It works in game but was wondering if I plugged all the memory leaks and if I made any other mistakes since I'm new to VJASS and timer utils
Code:
scope MeteorRain
    private struct Missile extends Missiles
    real RoX_X = 0.0
    real RoX_Y = 0.0
    real AoE

        method onRemove takes nothing returns nothing
            local group enemies = CreateGroup()
            local unit temp
        
        call GroupEnumUnitsInRange(enemies, RoX_X,RoX_Y, AoE, null)
            loop
            set temp = FirstOfGroup(enemies)
              exitwhen temp == null
            if IsUnitEnemy(temp, GetOwningPlayer(.source)) and not IsUnitType(temp, UNIT_TYPE_MAGIC_IMMUNE) and not BlzIsUnitInvulnerable(temp) and IsUnitAliveBJ(temp) then
                    call UnitDamageTarget(.source, temp, damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
            endif
            call GroupRemoveUnit(enemies, temp)
            endloop
        call DestroyGroup(enemies)
        set enemies = null
        set temp = null
        endmethod
    endstruct

    private struct MeteorRain
        timer   outer_t
        unit    c
        real    fx
        real    fy
        real    tx
        real    ty
    static integer shardcount = 10
        integer count
    integer wavecount = 3
        timer   inner_t
    static integer AId = 'A009'
    real AoE = 500.0
    real damage = 400.0 / wavecount / shardcount

        private static method GetRandomRange takes real radius returns real
            local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
     
            if r > 1 then
                return (2 - r)*radius
            endif
     
            return r*radius
        endmethod


        static method onOuterLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
        //call BJDebugMsg("OuterLoop")
        if .wavecount > 0 then
        set .count = shardcount
                set .inner_t = NewTimerEx(this)
            call TimerStart(.inner_t, 0.05, true, function thistype.onLoop)
            set wavecount = wavecount - 1
        else
        call ReleaseTimer(GetExpiredTimer())
        set .outer_t = null
        endif
    endmethod
        static method onLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real     maxRange
            local real     theta
            local real     toX
            local real     toY
            local real     fromX
            local real     fromY
        local real radius
            local Missile  missile

        //call BJDebugMsg("InnerLoop")
            if count > 0 then
                set count   = count - 1
                set theta   = 2*bj_PI*GetRandomReal(0, 1)
                set radius  = GetRandomRange(.AoE)
                set toX     = tx + radius*Cos(theta)
                set toY     = ty + radius*Sin(theta)
                set theta   = AngleBetweenPointsReal(tx, ty, fx, fy)
                set fromX   = toX + 1500*Cos(theta)
                set fromY   = toY + 1500*Sin(theta)
                set missile = Missile.create(fromX, fromY, 1500, toX, toY, 0)
             
                set missile.source    = c
                set missile.model     = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl"
                set missile.scale     = 1.5
                set missile.duration  = 1.0
                set missile.collision = 100
                set missile.collideZ  = true
        set missile.RoX_X = tx
        set missile.RoX_Y = ty
        set missile.AoE = .AoE
        set missile.damage = .damage

                call missile.launch()
        else
        call ReleaseTimer(GetExpiredTimer())
        set .inner_t = null
            endif
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()

            set outer_t     = NewTimerEx(this)
            set c     = GetTriggerUnit()
            set fx    = GetUnitX(c)
            set fy    = GetUnitY(c)
            set tx    = GetSpellTargetX()
            set ty    = GetSpellTargetY()
        set .count = shardcount
                set .inner_t = NewTimerEx(this)
            call TimerStart(.inner_t, 0.05, true, function thistype.onLoop)
            set wavecount = wavecount - 1
                set .outer_t = NewTimerEx(this)
            call TimerStart(.outer_t, 2.0, true, function thistype.onOuterLoop)
            set c = null
        //call BJDebugMsg("End")
            call deallocate()
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(AId, function thistype.onCast)
        endmethod
    endstruct
endscope

I cant see any leaks in an initial look at your code, but i'm pretty sure there are ways to optimize it. why dont you post what you want your ability to do so i can give you some tips.
 
Basically I'm trying to re-create rain of fire/blizzard but without the need for channeling and with an arbitrary projectile model. Its different then the sample spell in the sense that there is a wave of projectiles and then a short delay, and then another wave and short delay until a certain wave count is reached.
 
Level 20
Joined
May 16, 2012
Messages
635
Basically I'm trying to re-create rain of fire/blizzard but without the need for channeling and with an arbitrary projectile model. Its different then the sample spell in the sense that there is a wave of projectiles and then a short delay, and then another wave and short delay until a certain wave count is reached.

I noticed a few things that your are doing wrong. I'm creating a little snippet to explain how you should do it, just wait until i post it.
 
Level 20
Joined
May 16, 2012
Messages
635
@KitsuneTailsPrower here is the Rain of Fire ability but more optimized. I commented a lot so you can follow along and understand where you messed up a little. For this spell i made it so that missiles would spawn from the same x and y position like in the original rain of fire, but if you want them to be angled like in the example you can use the method you are using to displace the missile spawn position from the impact position.

JASS:
library RainOfFire requires Missiles, SpellEffectEvent, TimerUtils
    private struct Meteor extends Missiles
        real aoe // the damage aoe of each missile

        // Instead of using the onRemove event, use onFinish, it's better.
        method onFinish takes nothing returns boolean
            local group g = CreateGroup()
            local unit  u

            // x and y always represent the current coordinates of the missile
            call GroupEnumUnitsInRange(g, x, y, aoe, null)
            loop
                set u = FirstOfGroup(g)
                exitwhen u == null // Set and use the owner member of the missile system instead of calling GetOwningPlayer(source) every time
                    if UnitAlive(u) and IsUnitEnemy(u, owner) and not IsUnitType(u, UNIT_TYPE_MAGICIMMUNE) then // Use the UnitAlive() native, it's better and safer. ALso no need to check for invulnerability.
                        call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) // I highly recommend not using ATTACK_TYPE_NORMAL and DAMAGE_TYPE_NORMAL together.
                    endif
                call GroupRemoveUnit(g, u)
            endloop
            call DestroyGroup(g)

            set g = null
            return true
        endmethod
    endstruct

    private struct RainOfFire
        static integer ability = 'A009' // Your Ability
        static string  effect  = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl" // The model for the missile
        static real    period  = 1.0 // The period between each wave

        timer   timer
        unit    unit
        player  player
        integer waves
        integer count
        real    damage
        real    aoe
        real    x
        real    y

        static method onPeriod takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local integer  i = 0
            local real     x // Don't confund this x and y with .x and .y one is the local variable and the other is from the strcut instance
            local real     y
            local real     angle
            local real     range
            local Meteor   missile

            // The "." syntax is most of the time optional unless you have a local variable with the same name as a struct member
            // like with the locals x and y and the members .x and .y. For them you should use the "." or only the local will be used.

            if waves > 0 then
                loop // Loop the amount of meteor per wave, so they spawn all at the same time
                    exitwhen i >= count
                        set angle   = 2*bj_PI*GetRandomReal(0, 1)
                        set range   = GetRandomRange(aoe)
                        set x       = .x + range*Cos(angle) // Note the difference between x, y and .x, .y here. .x, .y are the coordinates from the spell cast
                        set y       = .y + range*Sin(angle)
                        set missile = Meteor.create(x, y, 1500, x, y, 0) // fromX and fromY is equal to toX and toY, because rain of fire spawns like that
                 
                        set missile.source   = unit
                        set missile.model    = effect
                        set missile.scale    = 1.5
                        set missile.duration = 1.0
                        set missile.damage   = damage
                        set missile.owner    = player // Instead of calling GetOwningPlayer(.source) in the missile event, just set the owner member and use it
                        set missile.aoe      = 75 // 2 things to notice here. First, if you want the missiles to only apply damage when they hit the ground
                                                   // then you dont need to set up collision. collision is only necessary when you want to implement a
                                                   // onHit, onDestructable, onItem or onMissile event. Lastly the missile.aoe is the area at which units will be damaged
                                                   // so do not confund it with the max spawn aoe
                        call missile.launch()
                    set i = i + 1
                endloop
            else
                call ReleaseTimer(timer) // You dont need to use GetExpiredTimer() again beacause timer = GetExpiredTimer()
                call deallocate() // Don't call deallocate inside the onCast event, it should be here

                set timer  = null // Don't forget to null
                set unit   = null
                set player = null
            endif
            set waves = waves - 1
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.create()

            set timer  = NewTimerEx(this)
            set unit   = GetTriggerUnit()
            set player = GetOwningPlayer(unit)
            set x      = GetSpellTargetX() // The x and y coordinate of the spell target location
            set y      = GetSpellTargetY()
            set waves  = 10 // The number of waves
            set count  = 6 // The number of meteors per wave
            set aoe    = 500 // The max AoE at which a meteor can spawn
            set damage = 400 // The meteor impact damage

            call TimerStart(timer, period, true, function thistype.onPeriod)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ability, function thistype.onCast)
        endmethod
    endstruct
endlibrary

Also, take a look at my Mannoroth Hero Concept, there i implemeted a version of rain of fire as well. It's easy to import and to configure.
 
Sorry for the delay response. Hold your horses for a bit longer, things are a bit chaotic here right now, but as soon as i can i'll do what you asked. As a spoiler, the performance will not be much different, specially if you use the dummy recycler library. If you go to page 2 of this thread and look for a video i posted in response for IcemanBo comment you will be able to see the Barrage spell that has onHit and onDestructible events and still are able to run with thousands of missiles without breaking the game.

Edit: @Dat-C3 Below is a simple spell like you asked. I also attached a test map. What the spell do is when you cast it, from the position of the caster every 0.05 seconds a fire ball will be launched to you current mouse position. The missile has only an onHit declared.

JASS:
scope Example
    // The following Spell struct represents our missile.
    // To make it behave like a Missile just make it extend Missiles.
    // After that you will be able to optionally declare any event implemented.
    private struct Spell extends Missiles
        // the onHit event is one of the events that you can
        // implement. You must declare it like this.
        // It will only happen if your missile has a collision
        // size greater than 0.
        // To see how and what events you can implement and
        // what they do see the Events section in the thread.
        method onHit takes unit hit returns boolean
            // owner, source and damage are all members of the main missile system
            // that you can set when creating it. To see what members you can have access
            // see the Members section of the thread. That's it, now your missile will do
            // damage to any enemy unit within it's collision size
            if IsUnitEnemy(hit, owner) then
                call UnitDamageTarget(source, hit, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
            endif

            // returning true will destroy the missile.
            return false
        endmethod
    endstruct

    private struct Example
        static timer   timer  = CreateTimer()
        static boolean active = false
        static unit    unit

        static method onPeriod takes nothing returns nothing
            // x, y and z are the start coordinates of the missile
            local real x  = GetUnitX(unit)
            local real y  = GetUnitY(unit)
            local real z  = GetUnitFlyHeight(unit) + 60
            // tx, ty and tz are the final coordinates of the missile
            local real tx = GetPlayerMouseX(GetOwningPlayer(unit))
            local real ty = GetPlayerMouseY(GetOwningPlayer(unit))
            local real tz = 60
            // instantiate the missile (onle 1 create method that takes initial x,y,z and final x,y,z)
            // If you set the target member that the final position will automatically adjust itself
            // but you still need to provide a final coordinate
            local Spell missile = Spell.create(x, y, z, tx, ty, tz)

            // Before calling launch you can set what you need
            set missile.source    = unit
            set missile.model     = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
            set missile.speed     = 500
            set missile.damage    = 50
            set missile.collision = 32 // onHit only happens if collision is greater than 0
            set missile.owner     = GetOwningPlayer(unit)
            set missile.arc       = GetRandomReal(0, 35)
            set missile.curve     = GetRandomReal(-25, 25)

            // launches the missile
            call missile.launch()
        endmethod

        static method onCast takes nothing returns nothing
            if active then
                set active = false
                call PauseTimer(timer)
            else
                set unit   = GetTriggerUnit()
                set active = true
                call TimerStart(timer, 0.05, true, function thistype.onPeriod)
            endif
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A000', function thistype.onCast)
            call Cheat("iseedeadpeople")
        endmethod
    endstruct
endscope

I tried to detail every piece of the code so you can understand whats going on. It's also using the version that uses the dummy method. Good Luck!

testmap failed to work b/c I am unable to reforged, however I was able to copy and paste it all. This is pretty cool and a nice limit of 4100 for a 3D system. The jumping/lagging projectiles on the other hand, do they skip over area's?
 
Level 20
Joined
May 16, 2012
Messages
635
testmap failed to work b/c I am unable to reforged, however I was able to copy and paste it all. This is pretty cool and a nice limit of 4100 for a 3D system. The jumping/lagging projectiles on the other hand, do they skip over area's?

The dilation of projectiles movement is proportional to the amount of current active projectiles, so if you want less "jumping/lagging" you can increase the "SWEET_SPOT" amount int he configuration block, but be aware that it may cause more performance impact by doing so, tho i doubt you will be able to create that many projectiles at any given time.
 
The dilation of projectiles movement is proportional to the amount of current active projectiles, so if you want less "jumping/lagging" you can increase the "SWEET_SPOT" amount int he configuration block, but be aware that it may cause more performance impact by doing so, tho i doubt you will be able to create that many projectiles at any given time.
So does that mean the area that was dilated is skipped or is something like a line check going on to double check what it hopped over?
 
Level 12
Joined
May 16, 2020
Messages
660
Hi Chopinski, I'm trying to adjust your W spell to:
  • Have damage per ability level
  • Deal damage to enemy units which are alive / non-structures / non-magic immune
I tried to include what you wrote in a previous answer, but the "MAGICIMMUNE" is leading to errors. Also, for some reason the caster is doing damage on himself, even though I added the "IsUnitEnemy".

Can you please help adjust it?

JASS:
scope MissilesW
    private struct Missile extends Missiles
        method onHit takes unit hit returns boolean
            if UnitAlive(hit) and IsUnitEnemy(hit, owner) and not IsUnitType(hit, UNIT_TYPE_MAGICIMMUNE) then
                call UnitDamageTarget(source, hit, damage, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
            endif

            return false
        endmethod

        method onDestructable takes destructable dest returns boolean
            call KillDestructable(dest)

            return false
        endmethod
    endstruct

    private struct Test
        timer   t
        unit    c
        real    fx
        real    fy
        real    fz
        real    tx
        real    ty
        integer count
        integer i = 1

        private static method GetRandomRange takes real radius returns real
            local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
      
            if r > 1 then
                return (2 - r)*radius
            endif
      
            return r*radius
        endmethod

        static method onLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real     maxRange
            local real     theta
            local real     radius
            local real     toX
            local real     toY
            local Missile  missile

            if count > 0 then
                set i      = -i
                set count  = count - 1
                set theta  = 2*bj_PI*GetRandomReal(0, 1)
                set radius = GetRandomRange(350)
                set toX    = tx + radius*Cos(theta)
                set toY    = ty + radius*Sin(theta)
                set missile = Missile.create(fx, fy, fz, toX, toY, 0)
              
                set missile.source    = c
                set missile.model     = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
                set missile.speed     = 800
                set missile.collision = 75
                set missile.arc       = GetRandomReal(10, 45)
                set missile.curve     = GetRandomReal(5, 20)*i
        set missile.damage    = 50

                call missile.launch()
            else
                call ReleaseTimer(t)
                set t = null
                set c = null
                call deallocate()
            endif
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()

            set t     = NewTimerEx(this)
            set c     = GetTriggerUnit()
            set fx    = GetUnitX(c)
            set fy    = GetUnitY(c)
            set fz    = GetUnitFlyHeight(c) + 50
            set tx    = GetSpellTargetX()
            set ty    = GetSpellTargetY()
            set count = 50

            call TimerStart(.t, 0.05, true, function thistype.onLoop)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A001', function thistype.onCast)
        endmethod
    endstruct
endscope
 
Level 20
Joined
May 16, 2012
Messages
635
Hi Chopinski, I'm trying to adjust your W spell to:
  • Have damage per ability level
  • Deal damage to enemy units which are alive / non-structures / non-magic immune
I tried to include what you wrote in a previous answer, but the "MAGICIMMUNE" is leading to errors. Also, for some reason the caster is doing damage on himself, even though I added the "IsUnitEnemy".

Can you please help adjust it?

JASS:
scope MissilesW
    private struct Missile extends Missiles
        method onHit takes unit hit returns boolean
            if UnitAlive(hit) and IsUnitEnemy(hit, owner) and not IsUnitType(hit, UNIT_TYPE_MAGICIMMUNE) then
                call UnitDamageTarget(source, hit, damage, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
            endif

            return false
        endmethod

        method onDestructable takes destructable dest returns boolean
            call KillDestructable(dest)

            return false
        endmethod
    endstruct

    private struct Test
        timer   t
        unit    c
        real    fx
        real    fy
        real    fz
        real    tx
        real    ty
        integer count
        integer i = 1

        private static method GetRandomRange takes real radius returns real
            local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
   
            if r > 1 then
                return (2 - r)*radius
            endif
   
            return r*radius
        endmethod

        static method onLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real     maxRange
            local real     theta
            local real     radius
            local real     toX
            local real     toY
            local Missile  missile

            if count > 0 then
                set i      = -i
                set count  = count - 1
                set theta  = 2*bj_PI*GetRandomReal(0, 1)
                set radius = GetRandomRange(350)
                set toX    = tx + radius*Cos(theta)
                set toY    = ty + radius*Sin(theta)
                set missile = Missile.create(fx, fy, fz, toX, toY, 0)
           
                set missile.source    = c
                set missile.model     = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
                set missile.speed     = 800
                set missile.collision = 75
                set missile.arc       = GetRandomReal(10, 45)
                set missile.curve     = GetRandomReal(5, 20)*i
        set missile.damage    = 50

                call missile.launch()
            else
                call ReleaseTimer(t)
                set t = null
                set c = null
                call deallocate()
            endif
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()

            set t     = NewTimerEx(this)
            set c     = GetTriggerUnit()
            set fx    = GetUnitX(c)
            set fy    = GetUnitY(c)
            set fz    = GetUnitFlyHeight(c) + 50
            set tx    = GetSpellTargetX()
            set ty    = GetSpellTargetY()
            set count = 50

            call TimerStart(.t, 0.05, true, function thistype.onLoop)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A001', function thistype.onCast)
        endmethod
    endstruct
endscope

It's UNIT_TYPE_MAGIC_IMMUNE and not UNIT_TYPE_MAGICIMMUNE. You are using the owner member but you never set it when creating the missile instance.

Given the info you provided, these are adjustments:

JASS:
scope MissilesW
    private struct Missile extends Missiles
        method onHit takes unit hit returns boolean
            if UnitAlive(hit) and IsUnitEnemy(hit, owner) and not IsUnitType(hit, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) then // UNIT_TYPE_MAGIC_IMMUNE and not UNIT_TYPE_MAGICIMMUNE, my bad on this one
                call UnitDamageTarget(source, hit, damage, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null) // Just a note. if you are setting damage type to DAMAGE_TYPE_MAGIC, you dant need to check for IsUnitType(hit, UNIT_TYPE_MAGIC_IMMUNE)
            endif

            return false
        endmethod

        // onDestructable removed since you dont want to do anything when the missile hits a destructable.
    endstruct

    private struct Test
        timer   timer
        unit    unit // The casting unit
        player  player // The owner of the caster
        real    fx
        real    fy
        real    fz
        real    tx
        real    ty
        integer level // Since you want to deal damage based on level, add the member to access it later
        integer count
        integer i = 1

        private static method GetRandomRange takes real radius returns real
            local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
   
            if r > 1 then
                return (2 - r)*radius
            endif
   
            return r*radius
        endmethod

        static method onLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real     maxRange
            local real     theta
            local real     radius
            local real     toX
            local real     toY
            local Missile  missile

            if count > 0 then
                set i      = -i
                set count  = count - 1
                set theta  = 2*bj_PI*GetRandomReal(0, 1)
                set radius = GetRandomRange(350)
                set toX    = tx + radius*Cos(theta)
                set toY    = ty + radius*Sin(theta)
                set missile = Missile.create(fx, fy, fz, toX, toY, 0)
           
                set missile.source    = unit // Set the source of the missile to the unit saved when the ability was cast
                set missile.owner     = player // You are using the owner memeber in the missile onHit method, but you never set it thats why the unit was doing damage to itself, so set the owner the player saved when the ability was cast
                set missile.model     = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
                set missile.speed     = 800
                set missile.collision = 75
                set missile.arc       = GetRandomReal(10, 45)
                set missile.curve     = GetRandomReal(5, 20)*i
                set missile.damage    = 50 * level // since you want the damage to be based on the level, set the damage to whatever formula you want using the level saved when the ability was being cast.

                call missile.launch()
            else
                call ReleaseTimer(timer)
                set timer = null
                set unit  = null
                call deallocate()
            endif
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()

            set timer  = NewTimerEx(this)
            set unit   = GetTriggerUnit() // Casting unit
            set player = GetOwningPlayer(unit) // Owner of caster
            set level  = GetUnitAbilityLevel(unit, 'A001') // Level of the ability being cast
            set fx     = GetUnitX(unit)
            set fy     = GetUnitY(unit)
            set fz     = GetUnitFlyHeight(unit) + 50
            set tx     = GetSpellTargetX()
            set ty     = GetSpellTargetY()
            set count  = 50

            call TimerStart(timer, 0.05, true, function thistype.onLoop)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A001', function thistype.onCast)
        endmethod
    endstruct
endscope
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Many thanks! Especially for the added comments to better understand what each part does.

I adjusted it a little bit once I understood some things:

JASS:
scope MissilesW
    private struct Missile extends Missiles
        method onHit takes unit hit returns boolean
            if UnitAlive(hit) and IsUnitEnemy(hit, owner) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) then // UNIT_TYPE_MAGIC_IMMUNE and not UNIT_TYPE_MAGIC_IMMUNE, my bad on this one
                call UnitDamageTarget(source, hit, damage, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null) // Just a note. if you are setting damage type to DAMAGE_TYPE_MAGIC, you dant need to check for IsUnitType(hit, UNIT_TYPE_MAGIC_IMMUNE)
   
            endif

        return false
 
        endmethod

        method onDestructable takes destructable dest returns boolean
            call KillDestructable(dest)

            return false
        endmethod
    endstruct

    private struct Test
        timer   timer
        unit    unit // The casting unit
        player  player // The owner of the caster
        real    fx
        real    fy
        real    fz
        real    tx
        real    ty
        integer level // Since you want to deal damage based on level, add the member to access it later
        integer fix   // test
        integer count
        integer i = 1

        private static method GetRandomRange takes real radius returns real
            local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
 
            if r > 1 then
                return (2 - r)*radius
            endif
 
            return r*radius
        endmethod

        static method onLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real     maxRange
            local real     theta
            local real     radius
            local real     toX
            local real     toY
            local Missile  missile

            if count > 0 then
                set i      = -i
                set count  = count - 1
                set theta  = 2*bj_PI*GetRandomReal(0, 1)
                set radius = GetRandomRange(350)
                set toX    = tx + radius*Cos(theta)
                set toY    = ty + radius*Sin(theta)
                set missile = Missile.create(fx, fy, fz, toX, toY, 0)
         
                set missile.source    = unit // Set the source of the missile to the unit saved when the ability was cast
                set missile.owner     = player // You are using the owner memeber in the missile onHit method, but you never set it thats why the unit was doing damage to itself, so set the owner the player saved when the ability was cast
                set missile.model     = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
                set missile.speed     = 800
                set missile.collision = 75
                set missile.arc       = GetRandomReal(10, 45)
                set missile.curve     = GetRandomReal(5, 20)*i
                set missile.damage    = fix + 10 * level // since you want the damage to be based on the level, set the damage to whatever formula you want using the level saved when the ability was being cast.

                call missile.launch()
            else
                call ReleaseTimer(timer)
                set timer = null
                set unit  = null
                call deallocate()
            endif
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()

            set timer  = NewTimerEx(this)
            set unit   = GetTriggerUnit() // Casting unit
            set player = GetOwningPlayer(unit) // Owner of caster
            set level  = GetUnitAbilityLevel(unit, 'A001') // Level of the ability being cast
            set fix    = 150
            set fx     = GetUnitX(unit)
            set fy     = GetUnitY(unit)
            set fz     = GetUnitFlyHeight(unit) + 50
            set tx     = GetSpellTargetX()
            set ty     = GetSpellTargetY()
            set count  = 50

            call TimerStart(.timer, 0.01, true, function thistype.onLoop)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A001', function thistype.onCast)
        endmethod
    endstruct
endscope

But with the added damage I noticed that even low damage is enough to kill a high HP unit if this unit is standing next to the caster (as almost every single fireball will for sure connect).

So sorry some further descriptions:
  • The damage instance should only happen when an enemy unit is within the impact area of a missile, not while the missile is in the air (kinda like cluster rockets, but with a lot more control over the collision area within the AoE selection.)
  • (BONUS if possible) Can this ability be channeled and only continue while the hero keeps channeling? In your Mannoroth Hero concept the Fire Rain spell is cast once and missiles keep coming down. But I was thinking that this W spell makes more sense as channeling ability which can be interrupted (as the the hero can just move around while the missiles keep coming if it takes a long time for all missiles to be fired)

EDIT: Adjusted the text - my bad
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
Many thanks! Especially for the added comments to better understand what each part does.

I adjusted it a little bit once I understood some things:

JASS:
scope MissilesW
    private struct Missile extends Missiles
        method onHit takes unit hit returns boolean
            if UnitAlive(hit) and IsUnitEnemy(hit, owner) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) then // UNIT_TYPE_MAGIC_IMMUNE and not UNIT_TYPE_MAGIC_IMMUNE, my bad on this one
                call UnitDamageTarget(source, hit, damage, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null) // Just a note. if you are setting damage type to DAMAGE_TYPE_MAGIC, you dant need to check for IsUnitType(hit, UNIT_TYPE_MAGIC_IMMUNE)
    
            endif

        return false
  
        endmethod

        method onDestructable takes destructable dest returns boolean
            call KillDestructable(dest)

            return false
        endmethod
    endstruct

    private struct Test
        timer   timer
        unit    unit // The casting unit
        player  player // The owner of the caster
        real    fx
        real    fy
        real    fz
        real    tx
        real    ty
        integer level // Since you want to deal damage based on level, add the member to access it later
        integer fix   // test
        integer count
        integer i = 1

        private static method GetRandomRange takes real radius returns real
            local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
  
            if r > 1 then
                return (2 - r)*radius
            endif
  
            return r*radius
        endmethod

        static method onLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real     maxRange
            local real     theta
            local real     radius
            local real     toX
            local real     toY
            local Missile  missile

            if count > 0 then
                set i      = -i
                set count  = count - 1
                set theta  = 2*bj_PI*GetRandomReal(0, 1)
                set radius = GetRandomRange(350)
                set toX    = tx + radius*Cos(theta)
                set toY    = ty + radius*Sin(theta)
                set missile = Missile.create(fx, fy, fz, toX, toY, 0)
          
                set missile.source    = unit // Set the source of the missile to the unit saved when the ability was cast
                set missile.owner     = player // You are using the owner memeber in the missile onHit method, but you never set it thats why the unit was doing damage to itself, so set the owner the player saved when the ability was cast
                set missile.model     = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
                set missile.speed     = 800
                set missile.collision = 75
                set missile.arc       = GetRandomReal(10, 45)
                set missile.curve     = GetRandomReal(5, 20)*i
                set missile.damage    = fix + 10 * level // since you want the damage to be based on the level, set the damage to whatever formula you want using the level saved when the ability was being cast.

                call missile.launch()
            else
                call ReleaseTimer(timer)
                set timer = null
                set unit  = null
                call deallocate()
            endif
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()

            set timer  = NewTimerEx(this)
            set unit   = GetTriggerUnit() // Casting unit
            set player = GetOwningPlayer(unit) // Owner of caster
            set level  = GetUnitAbilityLevel(unit, 'A001') // Level of the ability being cast
            set fix    = 150
            set fx     = GetUnitX(unit)
            set fy     = GetUnitY(unit)
            set fz     = GetUnitFlyHeight(unit) + 50
            set tx     = GetSpellTargetX()
            set ty     = GetSpellTargetY()
            set count  = 50

            call TimerStart(.timer, 0.01, true, function thistype.onLoop)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A001', function thistype.onCast)
        endmethod
    endstruct
endscope

But with the added damage I noticed that the enemies get damaged on each timer instance instead of once. So basically even 10 damage is enough to kill a 500 HP unit if the fireball goes through the unit. Another thing I noticed is that enemies get damaged outside of the selected ability radius instead of only around the place where the missile impacts. I thought I could fix this via
JASS:
method onHit
return true
...but this would probably only fix that units get damaged several times, not that they get damaged outside of the ability selection radius.

So sorry some further descriptions:
  • The damage should only be dealt 1x to an enemy unit. And not when "touching" it while the missile is in the air, but only if the enemy is within the impact area of the missile. So several units can be hit by 1 missile if they are within the impact area of a missile, but not again after they were damaged by this missile (on impact the missile would be destroyed anyhow)
  • (BONUS if possible) Can this ability be channeled and only continue while the hero keeps channeling? In your Mannoroth Hero concept the Fire Rain spell is cast once and missiles keep coming down. But I was thinking that this W spell makes more sense as channeling ability which can be interrupted (as the the hero can just move around while the missiles keep coming if it takes a long time for all missiles to be fired)

onHit is only called once per unit collision by default, so it's not the system fault. Probably some other trigger is being called (Damage Systems) when you do damage. Each missile only enumerates units within it's collision range, so you eaither have multiple missiles passing through the same unit or there's something else aside from the this piece of code doing it.

Returning true inside a method will cause the missile instance to be destroyed because this is the main form of terminating a missile. Check other triggers, specially those that use on damage events. I suggest you implement this ability i a map alone to guarantee that it is not this piece of code doing it.
 
Level 12
Joined
May 16, 2020
Messages
660
You are right, sorry. Apparently it's because so many fireballs go through a unit that even a unit with high HP dies from all the small damage instances, NOT because a fireball deals damage several times. Adjusted the above post.

Would the above adjustments still be possible though? Otherwise a unit standing right in front of the caster gets destroyed too easily...
 
I have a problem with this system right now. It does not respect the "onFinish" return false, to keep the missile alive.
So the ability I'm working on is the Plasma Grenade from the Halo games.
I throw a grenade, after X seconds, it explodes. If I hit a unit, it "attaches" to the unit.
Note that it might just lay there on the floor

The problem is that this part of the system code is messing with me.
What is the point of this code? Because from my point of view, it's making the documentation lie...
(I'm ofc taling about the line pointed out with "<--")
Does it have strange side-effects if removed?

JASS:
// onFinish event
if .onFinish.exists then
    if allocated and .onFinish() then
        call terminate()
    else
        // deflected onFinish
        if travel > 0 then
            call terminate() //<-- This is happening when attaching to a unit or the projectile stands still... This "else" seems to ruin all "onFinish return false". 
        endif
    endif
else
    call terminate()
endif

JASS:
scope MCPlasmaGrenade
    private struct PlasmaGrenade extends Missiles
        boolean isAttached = false
        boolean isExploded = false
        effect attachedEffect

        method onFinish takes nothing returns boolean
            if not isAttached and speed > 9999 then //TODO fix this
                //TODO Deflect missile "Straight ahead" for "const * speed" distance if enough speed remains
                set speed = speed / 2.0
                call BJDebugMsg("onFinished")
            else 
                set speed = 0
            endif
            return false //model should either bounce or stay still "onFinish"
        endmethod

        method onHit takes unit hit returns boolean
            if not isAttached and hit != .source and UnitAlive(hit) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) then
                call BJDebugMsg("onHit")
                set target = hit
                call BJDebugMsg(GetUnitName(target))
                set isAttached = true
                set attachedEffect = AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl", hit, "overhead")
                set model = "" //Intentionally none for this missile! It should be invisible from now on!
                //Need to keep this instance around to communicate to other struct info about target!
            endif
            return false
        endmethod

        method onPeriod takes nothing returns boolean
            return isExploded
        endmethod

        method onRemove takes nothing returns nothing
            call BJDebugMsg("onRemove")
            if isAttached then
                //Cleanup
                call DestroyEffect(attachedEffect)
                set attachedEffect = null
            endif
        endmethod
    endstruct

    private struct PlasmaGrenadeController
        timer   t
        PlasmaGrenade grenade

        static method onExplosion takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            call BJDebugMsg("onExplosion")
            if grenade.isAttached then
                //Grenade attached to unit
                call BJDebugMsg("isAttached")
                call BJDebugMsg(GetUnitName(grenade.target))
                call doExplosion(GetUnitX(grenade.target), GetUnitY(grenade.target))
            else
                //Grenade on floor
                call BJDebugMsg("notAttached")
                call doExplosion(grenade.x, grenade.y)
            endif
            set grenade.isExploded = true
                           
            call ReleaseTimer(t)
            call deallocate()
            set t = null
        endmethod

        method doExplosion takes real x, real y returns nothing
            local group targetsWithinRadius = CreateGroup()
            local unit u
            call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 325, null)
            loop
                set u = FirstOfGroup(targetsWithinRadius)
                exitwhen u == null
                if UnitAlive(u) and not (IsUnitAlly(u, grenade.owner) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
                    //TODO, deal damage etc. Deal damage to all, except for allied buildings
                endif
                call GroupRemoveUnit(targetsWithinRadius,u)
            endloop
            call DestroyGroup(targetsWithinRadius)
            call DestroyEffect( AddSpecialEffect( "Units\\NightElf\\Wisp\\WispExplode.mdl", x, y) )
            set targetsWithinRadius = null
            set u = null
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()
            local unit     c = GetTriggerUnit()
            local real     x = GetUnitX(c)
            local real     y = GetUnitY(c)
            local real     z = GetUnitFlyHeight(c) + 60
            local real     tx = GetSpellTargetX()
            local real     ty = GetSpellTargetY()
            local real     distance = SquareRoot((x - tx) * (x - tx) + (y - ty) * (y - ty))
            set .t = NewTimerEx(this)
            set .grenade = PlasmaGrenade.create(x, y, z, tx, ty, 10)
            set grenade.source    = c
            set grenade.model     = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
            set grenade.speed     = 400 + distance / 900 * 300
            set grenade.arc       = 30
            set grenade.collision = 16
            set grenade.acceleration = -0.005
            set grenade.owner = GetOwningPlayer(c)
           
            call grenade.launch()

            call TimerStart(t, 2.5, false, function thistype.onExplosion)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A006', function thistype.onCast)
        endmethod
    endstruct
endscope
 
Level 20
Joined
May 16, 2012
Messages
635
I have a problem with this system right now. It does not respect the "onFinish" return false, to keep the missile alive.
So the ability I'm working on is the Plasma Grenade from the Halo games.
I throw a grenade, after X seconds, it explodes. If I hit a unit, it "attaches" to the unit.
Note that it might just lay there on the floor

The problem is that this part of the system code is messing with me.
What is the point of this code? Because from my point of view, it's making the documentation lie...
(I'm ofc taling about the line pointed out with "<--")
Does it have strange side-effects if removed?

JASS:
// onFinish event
if .onFinish.exists then
    if allocated and .onFinish() then
        call terminate()
    else
        // deflected onFinish
        if travel > 0 then
            call terminate() //<-- This is happening when attaching to a unit or the projectile stands still... This "else" seems to ruin all "onFinish return false".
        endif
    endif
else
    call terminate()
endif

JASS:
scope MCPlasmaGrenade
    private struct PlasmaGrenade extends Missiles
        boolean isAttached = false
        boolean isExploded = false
        effect attachedEffect

        method onFinish takes nothing returns boolean
            if not isAttached and speed > 9999 then //TODO fix this
                //TODO Deflect missile "Straight ahead" for "const * speed" distance if enough speed remains
                set speed = speed / 2.0
                call BJDebugMsg("onFinished")
            else
                set speed = 0
            endif
            return false //model should either bounce or stay still "onFinish"
        endmethod

        method onHit takes unit hit returns boolean
            if not isAttached and hit != .source and UnitAlive(hit) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) then
                call BJDebugMsg("onHit")
                set target = hit
                call BJDebugMsg(GetUnitName(target))
                set isAttached = true
                set attachedEffect = AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl", hit, "overhead")
                set model = "" //Intentionally none for this missile! It should be invisible from now on!
                //Need to keep this instance around to communicate to other struct info about target!
            endif
            return false
        endmethod

        method onPeriod takes nothing returns boolean
            return isExploded
        endmethod

        method onRemove takes nothing returns nothing
            call BJDebugMsg("onRemove")
            if isAttached then
                //Cleanup
                call DestroyEffect(attachedEffect)
                set attachedEffect = null
            endif
        endmethod
    endstruct

    private struct PlasmaGrenadeController
        timer   t
        PlasmaGrenade grenade

        static method onExplosion takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            call BJDebugMsg("onExplosion")
            if grenade.isAttached then
                //Grenade attached to unit
                call BJDebugMsg("isAttached")
                call BJDebugMsg(GetUnitName(grenade.target))
                call doExplosion(GetUnitX(grenade.target), GetUnitY(grenade.target))
            else
                //Grenade on floor
                call BJDebugMsg("notAttached")
                call doExplosion(grenade.x, grenade.y)
            endif
            set grenade.isExploded = true
                         
            call ReleaseTimer(t)
            call deallocate()
            set t = null
        endmethod

        method doExplosion takes real x, real y returns nothing
            local group targetsWithinRadius = CreateGroup()
            local unit u
            call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 325, null)
            loop
                set u = FirstOfGroup(targetsWithinRadius)
                exitwhen u == null
                if UnitAlive(u) and not (IsUnitAlly(u, grenade.owner) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
                    //TODO, deal damage etc. Deal damage to all, except for allied buildings
                endif
                call GroupRemoveUnit(targetsWithinRadius,u)
            endloop
            call DestroyGroup(targetsWithinRadius)
            call DestroyEffect( AddSpecialEffect( "Units\\NightElf\\Wisp\\WispExplode.mdl", x, y) )
            set targetsWithinRadius = null
            set u = null
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()
            local unit     c = GetTriggerUnit()
            local real     x = GetUnitX(c)
            local real     y = GetUnitY(c)
            local real     z = GetUnitFlyHeight(c) + 60
            local real     tx = GetSpellTargetX()
            local real     ty = GetSpellTargetY()
            local real     distance = SquareRoot((x - tx) * (x - tx) + (y - ty) * (y - ty))
            set .t = NewTimerEx(this)
            set .grenade = PlasmaGrenade.create(x, y, z, tx, ty, 10)
            set grenade.source    = c
            set grenade.model     = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
            set grenade.speed     = 400 + distance / 900 * 300
            set grenade.arc       = 30
            set grenade.collision = 16
            set grenade.acceleration = -0.005
            set grenade.owner = GetOwningPlayer(c)
         
            call grenade.launch()

            call TimerStart(t, 2.5, false, function thistype.onExplosion)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A006', function thistype.onCast)
        endmethod
    endstruct
endscope

The code you pointed out is there to allow the onFinish event to be able to call the deflect method. You are using the missile instance in a way you should not. Im not home right now but when i get there i will build an example of how should be a better way to do it. Basically you should separate the missile from its explosion component. Launch the missile if it hits an unit call a method from another struct that represents the delayed explosion, or if it reaches its end, then on the onFinish event you call that same method to explode on that location instead

Returning false onFinish does not mean keeping the missile alive, it means that you will delegate to the missiles system to remove the missile in its natural order, on the onRemove event. Returning false on the onFinish will only keep the missile alive if you deflect it, else its end destination do not change and it will end
 
Last edited:
Basically you should separate the missile from its explosion component. Launch the missile if it hits an unit call a method from another struct that represents the delayed explosion, or if it reaches its end, then on the onFinish event you call that same method to explode on that location instead

I'm very new to vJass, but experienced programmer. I have 2 classes atm, one Missile and one Controller (that handles the explotion, damage, etc)
I had issues communicating out from the Missile to the Controller, therefore I let the Missile be the "data holder" for the "main controller". I want the explosion after 2.5 (the ability is WIP, this number is not set in stone!) seconds, regardless of hit or finished, and therefore wanted it to be kept alive until the explosion had occurred.
Note that I want the grenade to "bounce" if it has enough speed left (possibly many times) at "target point" (this is done in "onFinish" I guess) and then stay on the ground (preferably without a new birth animation on the effect!).

I'd appreciate any help :)
I guess that I was doing something wrong, but I also thought that I was doing things according to what was in documentation.

Edit:
I find it a bit confusing that you get Radians when using the "arc" operator, but you have to set it using degrees. Maybe keep the units the same for that operator?
Also: The "get speed" operator gives "speeds per period", but the set speed is "speed per second"
JASS:
set arc = arc * 0.6 // Gives Strange Results
set arc = Rad2Deg(arc * 0.6) //Gives the expected result

set speed = 0.75 * speed  //Gives strange results...
set speed = 0.75 * speed / Missiles_PERIOD //Results in 75% of the current speed.

I also updated my ability somewhat (implemented an initial implementation of bounce, need to test more and see how it feels...). If you were going to use my code as base, here is the updated version:
JASS:
scope MCPlasmaGrenade 

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

    private struct PlasmaGrenade extends Missiles
        boolean isAttached = false
        boolean isExploded = false
        effect attachedEffect

        method onFinish takes nothing returns boolean
            local real distanceFromStartToImpact = distanceBetween(origin.x, origin.y, impact.x, impact.y)
            local real vx
            local real vy
            local real speedInUnits = speed / Missiles_PERIOD
            if not isAttached and distanceFromStartToImpact > 280 then
                //call BJDebugMsg("onFinish - Bounce - speedInUnits: " + I2S(R2I(speedInUnits)))
                set vx = impact.x + ((impact.x - origin.x) / distanceFromStartToImpact) * 0.25 * distanceFromStartToImpact
                set vy = impact.y + ((impact.y - origin.y) / distanceFromStartToImpact) * 0.25 * distanceFromStartToImpact

                //call BJDebugMsg("x=" + I2S(R2I(x)) + ", y=" + I2S(R2I(y)) + " - NEW COORDS: x=" + I2S(R2I(vx)) + ", y=" + I2S(R2I(vy)))
                set speed = speedInUnits * 0.33
                call BJDebugMsg("onFinish - arc before: " + R2S(arc))
                set arc = Rad2Deg(arc * 0.6)
                call BJDebugMsg("onFinish - arc after: " + R2S(arc))
                call deflect(vx, vy)
            else 
                call BJDebugMsg("onFinish - Stay - speedInUnits: " + I2S(R2I(speedInUnits)))
                set speed = 0
            endif
            return false //model should either bounce or stay still "onFinish"
        endmethod

        method onHit takes unit hit returns boolean
            if not isAttached and hit != .source and UnitAlive(hit) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) then
                call BJDebugMsg("onHit")
                set target = hit
                call BJDebugMsg(GetUnitName(target))
                set isAttached = true
                set attachedEffect = AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl", hit, "overhead")
                set model = "" //Intentionally none for this missile! It should be invisible from now on!
                //Need to keep this instance around to communicate to other struct info about target!
            endif
            return false
        endmethod

        method onPeriod takes nothing returns boolean
            return isExploded
        endmethod

        method onRemove takes nothing returns nothing
            call BJDebugMsg("onRemove")
            if isAttached then
                //Cleanup
                call DestroyEffect(attachedEffect)
                set attachedEffect = null
            endif
        endmethod
    endstruct

    private struct PlasmaGrenadeController
        timer   t
        PlasmaGrenade grenade

        static method onExplosion takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            call BJDebugMsg("onExplosion")
            if grenade.isAttached then
                //Grenade attached to unit
                call BJDebugMsg("isAttached")
                call BJDebugMsg(GetUnitName(grenade.target))
                call doExplosion(GetUnitX(grenade.target), GetUnitY(grenade.target))
            else
                //Grenade on floor
                call BJDebugMsg("notAttached")
                call doExplosion(grenade.x, grenade.y)
            endif
            set grenade.isExploded = true
                           
            call ReleaseTimer(t)
            call deallocate()
            set t = null
        endmethod

        method doExplosion takes real x, real y returns nothing
            local group targetsWithinRadius = CreateGroup()
            local unit u
            call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 325, null)
            loop
                set u = FirstOfGroup(targetsWithinRadius)
                exitwhen u == null
                if UnitAlive(u) and not (IsUnitAlly(u, grenade.owner) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
                    //TODO, deal damage etc. Deal damage to all, except for allied buildings
                endif
                call GroupRemoveUnit(targetsWithinRadius,u)
            endloop
            call DestroyGroup(targetsWithinRadius)
            call DestroyEffect( AddSpecialEffect( "Units\\NightElf\\Wisp\\WispExplode.mdl", x, y) )
            set targetsWithinRadius = null
            set u = null
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()
            local unit     c = GetTriggerUnit()
            local real     x = GetUnitX(c)
            local real     y = GetUnitY(c)
            local real     z = GetUnitFlyHeight(c) + 60
            local real     tx = GetSpellTargetX()
            local real     ty = GetSpellTargetY()
            local real     distance = distanceBetween(x, y, tx, ty)
            set .t = NewTimerEx(this)
            set .grenade = PlasmaGrenade.create(x, y, z, tx, ty, 10)
            set grenade.source    = c
            set grenade.model     = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
            set grenade.speed     = 450 + distance / 900 * 200
            set grenade.arc       = 40
            set grenade.collision = 16
            set grenade.owner = GetOwningPlayer(c)
           
            call grenade.launch()

            call TimerStart(t, 2.5, false, function thistype.onExplosion)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A006', function thistype.onCast)
        endmethod
    endstruct
endscope
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
I'm very new to vJass, but experienced programmer. I have 2 classes atm, one Missile and one Controller (that handles the explotion, damage, etc)
I had issues communicating out from the Missile to the Controller, therefore I let the Missile be the "data holder" for the "main controller". I want the explosion after 2.5 (the ability is WIP, this number is not set in stone!) seconds, regardless of hit or finished, and therefore wanted it to be kept alive until the explosion had occurred.
Note that I want the grenade to "bounce" if it has enough speed left (possibly many times) at "target point" (this is done in "onFinish" I guess) and then stay on the ground (preferably without a new birth animation on the effect!).

I'd appreciate any help :)
I guess that I was doing something wrong, but I also thought that I was doing things according to what was in documentation.

Edit:
I find it a bit confusing that you get Radians when using the "arc" operator, but you have to set it using degrees. Maybe keep the units the same for that operator?
Also: The "get speed" operator gives "speeds per period", but the set speed is "speed per second"
JASS:
set arc = arc * 0.6 // Gives Strange Results
set arc = Rad2Deg(arc * 0.6) //Gives the expected result

set speed = 0.75 * speed  //Gives strange results...
set speed = 0.75 * speed / Missiles_PERIOD //Results in 75% of the current speed.

I also updated my ability somewhat (implemented an initial implementation of bounce, need to test more and see how it feels...). If you were going to use my code as base, here is the updated version:
JASS:
scope MCPlasmaGrenade

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

    private struct PlasmaGrenade extends Missiles
        boolean isAttached = false
        boolean isExploded = false
        effect attachedEffect

        method onFinish takes nothing returns boolean
            local real distanceFromStartToImpact = distanceBetween(origin.x, origin.y, impact.x, impact.y)
            local real vx
            local real vy
            local real speedInUnits = speed / Missiles_PERIOD
            if not isAttached and distanceFromStartToImpact > 280 then
                //call BJDebugMsg("onFinish - Bounce - speedInUnits: " + I2S(R2I(speedInUnits)))
                set vx = impact.x + ((impact.x - origin.x) / distanceFromStartToImpact) * 0.25 * distanceFromStartToImpact
                set vy = impact.y + ((impact.y - origin.y) / distanceFromStartToImpact) * 0.25 * distanceFromStartToImpact

                //call BJDebugMsg("x=" + I2S(R2I(x)) + ", y=" + I2S(R2I(y)) + " - NEW COORDS: x=" + I2S(R2I(vx)) + ", y=" + I2S(R2I(vy)))
                set speed = speedInUnits * 0.33
                call BJDebugMsg("onFinish - arc before: " + R2S(arc))
                set arc = Rad2Deg(arc * 0.6)
                call BJDebugMsg("onFinish - arc after: " + R2S(arc))
                call deflect(vx, vy)
            else
                call BJDebugMsg("onFinish - Stay - speedInUnits: " + I2S(R2I(speedInUnits)))
                set speed = 0
            endif
            return false //model should either bounce or stay still "onFinish"
        endmethod

        method onHit takes unit hit returns boolean
            if not isAttached and hit != .source and UnitAlive(hit) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) then
                call BJDebugMsg("onHit")
                set target = hit
                call BJDebugMsg(GetUnitName(target))
                set isAttached = true
                set attachedEffect = AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl", hit, "overhead")
                set model = "" //Intentionally none for this missile! It should be invisible from now on!
                //Need to keep this instance around to communicate to other struct info about target!
            endif
            return false
        endmethod

        method onPeriod takes nothing returns boolean
            return isExploded
        endmethod

        method onRemove takes nothing returns nothing
            call BJDebugMsg("onRemove")
            if isAttached then
                //Cleanup
                call DestroyEffect(attachedEffect)
                set attachedEffect = null
            endif
        endmethod
    endstruct

    private struct PlasmaGrenadeController
        timer   t
        PlasmaGrenade grenade

        static method onExplosion takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            call BJDebugMsg("onExplosion")
            if grenade.isAttached then
                //Grenade attached to unit
                call BJDebugMsg("isAttached")
                call BJDebugMsg(GetUnitName(grenade.target))
                call doExplosion(GetUnitX(grenade.target), GetUnitY(grenade.target))
            else
                //Grenade on floor
                call BJDebugMsg("notAttached")
                call doExplosion(grenade.x, grenade.y)
            endif
            set grenade.isExploded = true
                          
            call ReleaseTimer(t)
            call deallocate()
            set t = null
        endmethod

        method doExplosion takes real x, real y returns nothing
            local group targetsWithinRadius = CreateGroup()
            local unit u
            call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 325, null)
            loop
                set u = FirstOfGroup(targetsWithinRadius)
                exitwhen u == null
                if UnitAlive(u) and not (IsUnitAlly(u, grenade.owner) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
                    //TODO, deal damage etc. Deal damage to all, except for allied buildings
                endif
                call GroupRemoveUnit(targetsWithinRadius,u)
            endloop
            call DestroyGroup(targetsWithinRadius)
            call DestroyEffect( AddSpecialEffect( "Units\\NightElf\\Wisp\\WispExplode.mdl", x, y) )
            set targetsWithinRadius = null
            set u = null
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()
            local unit     c = GetTriggerUnit()
            local real     x = GetUnitX(c)
            local real     y = GetUnitY(c)
            local real     z = GetUnitFlyHeight(c) + 60
            local real     tx = GetSpellTargetX()
            local real     ty = GetSpellTargetY()
            local real     distance = distanceBetween(x, y, tx, ty)
            set .t = NewTimerEx(this)
            set .grenade = PlasmaGrenade.create(x, y, z, tx, ty, 10)
            set grenade.source    = c
            set grenade.model     = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
            set grenade.speed     = 450 + distance / 900 * 200
            set grenade.arc       = 40
            set grenade.collision = 16
            set grenade.owner = GetOwningPlayer(c)
          
            call grenade.launch()

            call TimerStart(t, 2.5, false, function thistype.onExplosion)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A006', function thistype.onCast)
        endmethod
    endstruct
endscope

Tell me exactly what you want your spell to do, like a description.

I'd appreciate any help :)
I guess that I was doing something wrong, but I also thought that I was doing things according to what was in documentation.

Mostly you are, i think you just misuse the onFinish event that's all., that's why i want you to give me a description of what you want your spell to do, so i can create something that is either what you want or similar for you to follow.

Edit:
I find it a bit confusing that you get Radians when using the "arc" operator, but you have to set it using degrees. Maybe keep the units the same for that operator?
Also: The "get speed" operator gives "speeds per period", but the set speed is "speed per second"

Yeah, i agree that it is a bit odd, but that's the way i programmed it in the begging. I might patch it later.
 
Tell me exactly what you want your spell to do, like a description.

Master Chief throws a Plasma Grenade towards target location that will explode after 2.5 seconds. (A fixed timer, regardless of what's happening! The exact time will need to be tested on what looks/feels good)
If the grenade hits a unit during the throw, it will stick to it. The grenade might bounce once at the target location (details here is "however it feels/looks good") traveling slightly further than target location before laying still on the ground (if it doesn't hit any unit) where it will remain until it explodes.
Units within X radius takes double damage, enemies further away than Y radius takes half damage.
Deals A + (B % of <stat>) damage.
Deals friendly fire! (But not to friendly buildings!).

--

Edit:
Honestly, the code I posted last works if I remove the terminate call that I disliked.
I can deflect in "onFinish" without that "terminate" and it gives control over the termination to me (as I like it).
Also veloc is used internally, speed is only used externally, so I'll, in my map, edit the "method operatior speed" to return "veloc / PERIOD". It is safe to do so and it behaves as I'd like it to.

Although I point at things I call flaws, this system is really great! Thanks!
It's really easy to make abilities nice and readable within this system while keeping good performance.
And I don't have to do the math for arcs and curves :)
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
Master Chief throws a Plasma Grenade towards target location that will explode after 2.5 seconds. (A fixed timer, regardless of what's happening! The exact time will need to be tested on what looks/feels good)
If the grenade hits a unit during the throw, it will stick to it. The grenade might bounce once at the target location (details here is "however it feels/looks good") traveling slightly further than target location before laying still on the ground (if it doesn't hit any unit) where it will remain until it explodes.
Units within X radius takes double damage, enemies further away than Y radius takes half damage.
Deals A + (B % of <stat>) damage.
Deals friendly fire! (But not to friendly buildings!).

--

Edit:
Honestly, the code I posted last works if I remove the terminate call that I disliked.
I can deflect in "onFinish" without that "terminate" and it gives control over the termination to me (as I like it).
Also veloc is used internally, speed is only used externally, so I'll, in my map, edit the "method operatior speed" to return "veloc / PERIOD". It is safe to do so and it behaves as I'd like it to.

Although I point at things I call flaws, this system is really great! Thanks!
It's really easy to make abilities nice and readable within this system while keeping good performance.
And I don't have to do the math for arcs and curves :)

That else block is there in case you deflect the missile inside an onFinish event and don't return true in it. You can see the effect in the system test map for the MIssiles - D ability (the phoenix one). If you comment out the else block like you did and cast that ability, the missile will continue to exists and leak, that's why i included it. I will standardize the getting and setting of speed, arc and curve because it makes more sense for the user.
 
That else block is there in case you deflect the missile inside an onFinish event and don't return true in it. You can see the effect in the system test map for the MIssiles - D ability (the phoenix one).
Well, if you'd return "deflected"-variable instead of "false" in the "onFinish" for the Phoenix ability, you'd destroy it, even without that line in the system.
Imho that's a safety-net that hinders abilities that want to control the life of the projectile (something I want to do for my PlasmaGrenade-ability).

---
EDIT: Oops, you cannot just return deflected. You need to do something like:

JASS:
method onFinish takes nothing returns boolean
            if deflected then
                return true
            else
                set deflected = true
                call deflect(GetUnitX(source), GetUnitY(source))
                return false
            endif
        endmethod

But my points is that it's handlable from ability and not needed from the system.
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
Well, if you'd return "deflected"-variable instead of "false" in the "onFinish" for the Phoenix ability, you'd destroy it, even without that line in the system.
Imho that's a safety-net that hinders abilities that want to control the life of the projectile (something I want to do for my PlasmaGrenade-ability).

---
EDIT: Oops, you cannot just return deflected. You need to do something like:

JASS:
method onFinish takes nothing returns boolean
            if deflected then
                return true
            else
                set deflected = true
                call deflect(GetUnitX(source), GetUnitY(source))
                return false
            endif
        endmethod

But my points is that it's handlable from ability and not needed from the system.

That works if the user know exactly what he/she is doing, but for more entry level users that are just learning vJass they might forget it and cause a lot of leaks in their games, which is not good. You're right, it's a safety-net that if it's not there has potential to be very detrimental to the game-play. As a programmer you would understand that some system sacrifice some freedom in exchange for safety, specially if it has a chance to be a system breaking thing.
 
That works if the user know exactly what he/she is doing, but for more entry level users that are just learning vJass they might forget it and cause a lot of leaks in their games, which is not good. You're right, it's a safety-net that if it's not there has potential to be very detrimental to the game-play. As a programmer you would understand that some system sacrifice some freedom in exchange for safety, specially if it has a chance to be a system breaking thing.

Fair point. I've learned a lot about this system and vJass during this and must once again say: great system! :)
 
Level 20
Joined
May 16, 2012
Messages
635
@ThompZon here's a version of your spell i made. It's incomplete with a few basic things that you can add yourself, but the main idea behind what i was talking earlier is there. I also attached a test map.

JASS:
library PlasmaGranade requires Missiles, TimerUtils, SpellEffectEvent, PluginSpellEffect // I'm using my PluginSpellEffect lib to make coding easier but i'm sure you can use you own method or use it if you want
    struct DelayedExplosion
        private timer  timer
        private unit   unit
        private unit   target
        private group  group
        private player player
        private effect effect
        private real   damage
        private real   aoe
        private real   x
        private real   y

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local unit u

            if target != null then
                set x = GetUnitX(target)
                set y = GetUnitY(target)
            endif

            call GroupEnumUnitsInRange(group, x, y, aoe, null)
            loop
                set u = FirstOfGroup(group)
                exitwhen u == null
                    if not (IsUnitAlly(u, player) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
                        call UnitDamageTarget(unit, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
                    endif
                call GroupRemoveUnit(group, u)
            endloop
            call DestroyGroup(group)
            call DestroyEffect(effect)
            call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Demon\\DemonBoltImpact\\DemonBoltImpact.mdl", x, y)) // Explosion model
            call ReleaseTimer(timer)
            call deallocate()

            set timer  = null
            set unit   = null
            set group  = null
            set player = null
            set effect = null
            set target = null
        endmethod

        static method create takes unit source, player owner, unit hit, real tx, real ty, real range, real amount, real timeout returns thistype // You can change this to accomodate you double damage logic
            local thistype this = thistype.allocate()

            set timer  = NewTimerEx(this)
            set unit   = source
            set target = hit
            set player = owner
            set group  = CreateGroup()
            set aoe    = range
            set damage = amount
            set x      = tx
            set y      = ty

            if hit == null then
                set effect = AddSpecialEffect("Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl", x, y)
            else
                set effect = AddSpecialEffectTarget("Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl", hit, "chest")
            endif

            call TimerStart(timer, timeout, false, function thistype.onExpire)
   
            return this
        endmethod
    endstruct
   
    //==============================================================================================================================

    private struct PlasmaGranade extends Missiles
        timer   timer
        real    aoe
        real    fixArc
        integer bounces
        boolean timeout = false
        boolean hasHit  = false

        method onPeriod takes nothing returns boolean
            return timeout
        endmethod

        method onHit takes unit hit returns boolean
            if UnitAlive(hit) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) and hit != source then
                set hasHit = true
                call DelayedExplosion.create(source, owner, hit, x, y, aoe, damage, TimerGetRemaining(timer))
                return true
            endif

            return false
        endmethod

        method onFinish takes nothing returns boolean
            if bounces > 0 then
                set bounces = bounces - 1
                set speed   = speed*0.8
                set fixArc  = fixArc*0.75
                set arc     = fixArc

                // This is a Polar Projection basically, but you can put your formulas here
                call deflect(x + 100*bounces*Cos(effect.yaw), y + 100*bounces*Sin(effect.yaw)) // GetUnitFacing(dummy)*bj_DEGTORAD if you are on the 1.30-. or save it in a member.
            endif

            return false
        endmethod

        method onRemove takes nothing returns nothing
            if not hasHit then
                call DelayedExplosion.create(source, owner, null, x, y, aoe, damage, TimerGetRemaining(timer))
            endif
            call ReleaseTimer(timer)

            set timer = null
        endmethod

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            set timeout = true
        endmethod

        private static method onCast takes nothing returns nothing
            local thistype this = thistype.create(Spell.unitX, Spell.unitY, GetUnitFlyHeight(Spell.unit) + 60, Spell.x, Spell.y, 0)

            set model     = "Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl"
            set source    = Spell.unit
            set owner     = Spell.player
            set speed     = 800
            set fixArc    = 60
            set arc       = fixArc
            set damage    = 100
            set bounces   = 4
            set collision = 75
            set aoe       = 250
            set timer     = NewTimerEx(this)

            call launch()
            call TimerStart(timer, 2.5, false, function thistype.onExpire)
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A000', function thistype.onCast)
        endmethod
    endstruct
endlibrary

sodSTWb.gif
 

Attachments

  • PlasmaGranade.w3x
    59.1 KB · Views: 91
The fact that most of these spell you can spam fire without too much lag is nice. Not much use in normal maps, but I can see DBZ map making use of this many particles at once haha.
Only if it wasn't in vjass, a lot of the epic systems fail to get used b/c of being in a syntax other then jass/gui or lacking an interface for jass/gui.
 
Level 12
Joined
May 16, 2020
Messages
660
It's possible but it has nothing to do with the Missiles system, you would only need to create a trigger that would fire when a unit stops channeling an ability and then stop missiles from being created.

Which part has nothing to do with the Missiles system?
The damage within the missile impact area only (and not while the missile is in the air) is to prevent massive damage when enemies stand near the origin point, as basically 50+ missiles would go through the enemy.
And the channeling part is because I don't know how to make the JASS system "Interact" with a GUI system. I know how to recognize the start and end of a channel in GUI, but not how I can stop the Missile system from firing when an interrupt happens...
 
Level 20
Joined
May 16, 2012
Messages
635
Which part has nothing to do with the Missiles system?
The damage within the missile impact area only (and not while the missile is in the air) is to prevent massive damage when enemies stand near the origin point, as basically 50+ missiles would go through the enemy.
And the channeling part is because I don't know how to make the JASS system "Interact" with a GUI system. I know how to recognize the start and end of a channel in GUI, but not how I can stop the Missile system from firing when an interrupt happens...

To do all that you would need to have a basic understanding of JASS. Basically to not allow a unit to be damaged by all missiles you could use a flag and check the distance between the caster and the hitted unit. To stop missiles from being created when you unit stopped channeling is very basic using events, but again you need to learn JASS. But here is the thing, why would you not want a unit to not be damaged by all missiles if it is to close? If something stay if the face of a barrage deserves to be destroyed.
 
Level 12
Joined
May 16, 2020
Messages
660
But here is the thing, why would you not want a unit to not be damaged by all missiles if it is to close? If something stay if the face of a barrage deserves to be destroyed.

Well xD Good point, but to me it seems unbalanced that a melee unit (which usually is at a disadvantage to range units anyways) is annihilated by 1 click after finally getting close to the nasty range unit. I want this range caster to be very strong at a distance, but WHEN a melee finally gets close, the range caster is more or less screwed.
 
This system has been super useful!

However, I kinda want to be able to make projectiles that travel with a from {x1,y1,70} to {x2,y2,70} with a fixed distance to the ground (similar to basic war3 spells).
My first though was with a linear offset on Z, so {x1,y1,1} to {x2,y2,99} would travel 1 -> 99 on Z linearly with the 1 -> 99 being the offset to LocZ, but I don't really need that atm, just anything with fixed distance to LocZ.
Is it possible?

Currently projectiles clip though terrain, and I want some to behave more like classic War3 projectiles
 
Last edited:
Top