System Throw System

Trollvottel

never aging title
Reaction score
262
It's a Throw System, made by me using vJass.

No Screenshots to take, just have a look at it.

JASS:

// Trollvottel's Projectile System v1.4b
// System made by Trollvottel
// Thanks to:
// The maker of vJass, Vexorian and the maker of TT and PUI, Cohadar
// Requirements:
// ++++++++++++++++++++++++
// +*vJass                +
// +*TT                   +
// +*PUI                  +
// ++++++++++++++++++++++++
//
// The system:
// It's a quite realistic system because it uses real physics formulas but the gravitational
// acceleration is a bit high, like the speed, because in WC3 we don't use metres or inches but
// an own system of longimetry, just don't care about it and use these extra-high values
//   PROS:
//      ++ Quite realistic
//      ++ Easy to use
//   CONS:
//      -- a little bit imprecise with lower angles with use of GetSpeedToDis(distance, angle)
//
//-----------------------------------------------------------------------------------
/////////////////////////////////////How to use:////////////////////////////////////////
// Decide if you want it to collide with units and how much damage it will do with which radius.
// the functions:
//
//// call TPS_Throw(UnitToThrow, RawCodeOfProjectile, DirectionToThrow, Speed, AngleToThrow, Damage, CollideWithUnits?, Radius)
// This throws a projectile
// It returns the dummy so if you want you can store it in a variable.
//
//// call TPS_ThrowUnit(UnitToThrow, UnitToThrow, DirectionToThrow, Speed, AngleToThrow, Damage, CollideWithUnits?, Radius)
// This throws a unit
// It returns the unit so if you want you can store it in a variable.
//
//// call TPS_ThrowUnitAndDo(thrower, unittothrow, DirectionToThrow, Speed, AngleToThrow, Collide, Radius, functiontocall)
//// call TPS_ThrowAndDo(thrower, unittothrow, DirectionToThrow, Speed, AngleToThrow, Collide, Radius, functiontocall)
// Throws a unit/projectile. If the unit/projectile collides or reaches its destination the function functiontocall will be runned.
// In this function, the Throwing unit will be THROWER, the Throwed unit will be THROWED. Throw Z-angle is
// THROWZ and the speed is THROWSPEED.
// You get The throw angle with THROWANGLE[INDEX]
// if you want other values in your userfunc create globals with array and use the index GetUnitIndex(unit). for this
// you'll have to use the returned bullet in all this functions. in your userfunc you can refer to the index
// with varname[INDEX]
// It may not contain any waits. This call also returns the throwed unit to store it in a variable.
//
//// TPS_GetSpeedToDis(distance, angle)
// Returns the necessary speed for the given angle to reach the given distance.
// Put it as argument for Speed.
//
//// call TPS_StopIt(whichbullet)
// This stops a projectile or unit.
//
// Look at the example trigger "Cast Test" to look how its done.
//-----------------------------------------------------------------------------------
// Final Words:
// I think GRAVIACCELERATION is best at 300 but try out how you want it to be, remember, the higher
// it is the lower is you parabola.
// It begins here:
library TPS uses TT, PUI

    globals
        private constant real GRAVIACCELERATION = 64*9.8*2 // The higher it is the higher is the gravity
        private constant real NORMHEIGHT = 50 // Starts not from the ground
    endglobals

//DONT EDIT BELOW:
//-----------------------------------------------------------------------------------
    globals
        private boolean array STOP
        private integer DESTRNUM = 0
        unit THROWER
        unit THROWED
        real THROWSPEED
        real THROWZ
        integer INDEX
        real array THROWANGLE
    endglobals
    
    private struct Projectile
        real startx
        real starty
        real disgone = 0
        unit bullet
        unit caster
        real anglecos
        real anglesin
        real speed
        real anglez
        real time = 0
        real startz = 0
        real radius
        real damage
        integer index
        boolean collide
        boolean uni = false
        boolean anddo = false
        trigger trig = null
        method onDestroy takes nothing returns nothing
            if this.uni == false then
                call KillUnit(this.bullet)
            endif
            call DestroyTrigger(this.trig)
        endmethod
    endstruct
        
    private function alive takes nothing returns boolean
        return GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0
    endfunction
      
    private function GetFlyHeight takes real disgone, real speed, real anglez returns real
        local real number1 = Tan(anglez*bj_DEGTORAD)* disgone 
        local real number2 = (GRAVIACCELERATION / (2*Pow(speed,2)*Pow(Cos(anglez*bj_DEGTORAD),2)))  * Pow(disgone,2)
        local real height = number1-number2
        return height
    endfunction
    
    private function GetTrue takes nothing returns boolean
        return true
    endfunction
       
    public function GetSpeedToDis takes real distance, real angle returns real
        local real number1 = distance * GRAVIACCELERATION
        local real number2 = Sin(2*angle*bj_DEGTORAD)
        return SquareRoot(number1 / number2)
    endfunction
    
    
    private function EnumEm takes nothing returns nothing
        call KillDestructable(GetEnumDestructable())
        set DESTRNUM = DESTRNUM + 1
    endfunction
    
    private function Throw_child takes nothing returns boolean
        local Projectile dat = TT_GetData()
        local real x 
        local real y 
        local unit u
        local group g
        local location loc = GetUnitLoc(dat.bullet)
        local real nZ
        local real atmZ = GetLocationZ(loc)
        local integer count = 0
        call RemoveLocation(loc)
        set loc = null
        set dat.time = dat.time + TT_PERIOD
        set dat.disgone = dat.speed * dat.time * SquareRoot(Pow(Cos(dat.anglez*bj_DEGTORAD),2))
        set x = dat.startx + dat.disgone * dat.anglecos
        set y = dat.starty + dat.disgone * dat.anglesin
        if x > GetRectMinX(bj_mapInitialPlayableArea) and x < GetRectMaxX(bj_mapInitialPlayableArea) and y < GetRectMaxY(bj_mapInitialPlayableArea) and y > GetRectMinY(bj_mapInitialPlayableArea) then
            call SetUnitX(dat.bullet,x)
            call SetUnitY(dat.bullet,y)
        else
            set STOP[dat.index] = true
        endif

        
        call SetUnitFlyHeight(dat.bullet, GetFlyHeight(dat.disgone, dat.speed, dat.anglez)+dat.startz+NORMHEIGHT-atmZ, 0)

        
        if GetUnitFlyHeight(dat.bullet)- atmZ <= dat.radius and dat.collide == true then
            call EnumDestructablesInRect(Rect(GetUnitX(dat.bullet)-dat.radius, GetUnitY(dat.bullet)-dat.radius,GetUnitX(dat.bullet)+dat.radius, GetUnitY(dat.bullet)+dat.radius), Condition(function GetTrue), function EnumEm)
            if DESTRNUM > 0 then
                set STOP[dat.index] = true
                set DESTRNUM = 0
            endif
            if not dat.anddo then
                set g = CreateGroup()
                call GroupEnumUnitsInRange(g, GetUnitX(dat.bullet), GetUnitY(dat.bullet),dat.radius, Condition(function alive))
                loop
                    set u = FirstOfGroup(g)
                    call GroupRemoveUnit(g,u)
                    exitwhen u == null
                    if IsUnitEnemy(u, GetOwningPlayer(dat.caster)) == true then
                        
                        set count = count + 1
                    endif
                endloop
                call DestroyGroup(g)
                set g = null
                if count > 0 then
                    set STOP[dat.index] = true
                endif
            else
                set g = CreateGroup()
                call GroupEnumUnitsInRange(g, GetUnitX(dat.bullet), GetUnitY(dat.bullet),dat.radius, Condition(function alive))
                loop
                    set u = FirstOfGroup(g)
                    call GroupRemoveUnit(g,u)
                    exitwhen u == null
                    if IsUnitEnemy(u, GetOwningPlayer(dat.caster)) == true then
                        set count = count + 1
                        
                    endif
                endloop
                call DestroyGroup(g)
                set g = null
                if count > 0 then
                    set STOP[dat.index] = true
                endif
            endif
        endif
       
        if GetFlyHeight(dat.disgone, dat.speed, dat.anglez)+NORMHEIGHT+dat.startz-atmZ <= 2 or STOP[dat.index] == true then
            if not dat.anddo then
                set g = CreateGroup()
                call GroupEnumUnitsInRange(g, GetUnitX(dat.bullet), GetUnitY(dat.bullet), dat.radius, Condition(function alive))
                loop
                    set u = FirstOfGroup(g)
                    call GroupRemoveUnit(g,u)
                    exitwhen u == null

                    if IsUnitEnemy(u, GetOwningPlayer(dat.caster)) == true then

                        call UnitDamageTarget(dat.caster, u, dat.damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                    endif
                endloop
                call DestroyGroup(g)
                set g = null
            else
                set THROWER = dat.caster
                set THROWED = dat.bullet
                set THROWSPEED = dat.speed
                set THROWZ = dat.anglez
                set INDEX = dat.index
                call TriggerExecute(dat.trig)
            endif
            call dat.destroy()
            return true
        endif
        return false
    endfunction
    

    public function Throw takes unit whichunit, integer tothrow, real angle, real speed, real anglez, real dmg, boolean collide, real radius returns unit
        local Projectile dat = Projectile.create()
        local location loc = GetUnitLoc(whichunit)
        set dat.startz = GetLocationZ(loc)
        call RemoveLocation(loc)
        set loc = null
        set dat.bullet = CreateUnit(GetOwningPlayer(whichunit), tothrow, GetUnitX(whichunit), GetUnitY(whichunit), GetUnitFacing(whichunit))
        set dat.anglecos = Cos(angle * bj_DEGTORAD)
        set dat.anglesin = Sin(angle * bj_DEGTORAD)
        if anglez < 90 then
            set dat.anglez = anglez
        else
            set dat.anglez = 89
        endif
        call UnitAddAbility(dat.bullet,'Arav')
        call UnitRemoveAbility(dat.bullet,'Arav')
        set dat.caster = whichunit
        set dat.startx = GetUnitX(whichunit)
        set dat.starty = GetUnitY(whichunit)
        set dat.radius = radius
        set dat.speed = speed 
        set dat.damage = dmg
        set dat.collide = collide
        set dat.index = GetUnitIndex(dat.bullet)
        set STOP[dat.index] = false
        set THROWANGLE[dat.index] = angle
        call TT_Start(function Throw_child,dat)

        call SetUnitFlyHeight(dat.bullet, NORMHEIGHT,0)
        return dat.bullet
    endfunction

    public function ThrowUnit takes unit whichunit, unit tothrow, real angle, real speed, real anglez, real dmg, boolean collide, real radius returns unit
        local Projectile dat = Projectile.create()
        local location loc = GetUnitLoc(whichunit)
        set dat.startz = GetLocationZ(loc)
        call RemoveLocation(loc)
        set loc = null
        set dat.bullet = tothrow
        set dat.anglecos = Cos(angle * bj_DEGTORAD)
        set dat.anglesin = Sin(angle * bj_DEGTORAD)
        if anglez < 90 then
            set dat.anglez = anglez
        else
            set dat.anglez = 89
        endif
        set dat.caster = whichunit
        set dat.startx = GetUnitX(tothrow)
        set dat.starty = GetUnitY(tothrow)
        set dat.radius = radius
        
        set dat.speed = speed
        set dat.damage = dmg
        set dat.collide = collide
        set dat.uni = true
        call UnitAddAbility(tothrow,'Arav')
        call UnitRemoveAbility(tothrow,'Arav')
        set dat.index = GetUnitIndex(dat.bullet)
        set STOP[dat.index] = false
        set THROWANGLE[dat.index] = angle
        
        call TT_Start(function Throw_child,dat)

        call SetUnitFlyHeight(dat.bullet, NORMHEIGHT,0)
        return dat.bullet
    endfunction

    public function ThrowUnitAndDo takes unit whichunit, unit tothrow, real angle, real speed, real anglez, boolean collide, real radius, code userfunc returns unit
        local Projectile dat = Projectile.create()
        local location loc = GetUnitLoc(whichunit)
        set dat.startz = GetLocationZ(loc)
        call RemoveLocation(loc)
        set loc = null
        set dat.trig = CreateTrigger()
        call TriggerAddAction(dat.trig, userfunc)
        set dat.bullet = tothrow
        set dat.anglecos = Cos(angle * bj_DEGTORAD)
        set dat.anglesin = Sin(angle * bj_DEGTORAD)
        if anglez < 90 then
            set dat.anglez = anglez
        else
            set dat.anglez = 89
        endif
        set dat.caster = whichunit
        set dat.startx = GetUnitX(tothrow)
        set dat.starty = GetUnitY(tothrow)
        set dat.radius = radius
        set dat.anddo = true

        set dat.speed = speed
        set dat.damage = 0
        set dat.collide = collide
        set dat.uni = true
        call UnitAddAbility(tothrow,'Arav')
        call UnitRemoveAbility(tothrow,'Arav')
        set dat.index = GetUnitIndex(dat.bullet)
        set STOP[dat.index] = false
        set THROWANGLE[dat.index] = angle
        
        call TT_Start(function Throw_child,dat)

        call SetUnitFlyHeight(dat.bullet, NORMHEIGHT,0)
        return dat.bullet
    endfunction
    
    public function ThrowAndDo takes unit whichunit, integer tothrow, real angle, real speed, real anglez, boolean collide, real radius, code userfunc returns unit
        local Projectile dat = Projectile.create()
        local location loc = GetUnitLoc(whichunit)
        set dat.startz = GetLocationZ(loc)
        call RemoveLocation(loc)
        set loc = null
        set dat.trig = CreateTrigger()
        call TriggerAddAction(dat.trig,userfunc)
        
        set dat.bullet = CreateUnit(GetOwningPlayer(whichunit), tothrow, GetUnitX(whichunit), GetUnitY(whichunit), GetUnitFacing(whichunit))
        set dat.anglecos = Cos(angle * bj_DEGTORAD)
        set dat.anglesin = Sin(angle * bj_DEGTORAD)
        if anglez < 90 then
            set dat.anglez = anglez
        else
            set dat.anglez = 89
        endif
        call UnitAddAbility(dat.bullet,'Arav')
        call UnitRemoveAbility(dat.bullet,'Arav')
        set dat.caster = whichunit
        set dat.startx = GetUnitX(whichunit)
        set dat.starty = GetUnitY(whichunit)
        set dat.radius = radius
        set dat.anddo = true

        set dat.speed = speed
        set dat.damage = 0
        set dat.collide = collide
        set dat.uni = false
        set dat.index = GetUnitIndex(dat.bullet)
        set STOP[dat.index] = false
        set THROWANGLE[dat.index] = angle
    
        call TT_Start(function Throw_child,dat)

        call SetUnitFlyHeight(dat.bullet, NORMHEIGHT,0)
        return dat.bullet
    endfunction
    
    public function StopIt takes unit projectile returns nothing
        set STOP[GetUnitIndex(projectile)] = true
    endfunction
endlibrary

Have fun <3
If you have some suggestions to make it look nicer or to add new functions, just tell them please.

PS: sry for the horrible terrain :D
 

Attachments

  • Throw.w3x
    36.1 KB · Views: 264

Flare

Stops copies me!
Reaction score
662
Cool, but there's one thing I'm stumped about. When you cast the base spell at it's maximum cast range, the projectile goes to the point exactly, but if you cast it at about 1/2 max cast range, the projectile falls short after travelling 3/4 of the distance ^^

JASS:
//   CONS:
//      - You can&#039;t throw it a specific distance


Do you plan on changing that in the future, or is that just a limitation that you can't get past?
 

Steel

Software Engineer
Reaction score
109
Cool, but there's one thing I'm stumped about. When you cast the base spell at it's maximum cast range, the projectile goes to the point exactly, but if you cast it at about 1/2 max cast range, the projectile falls short after travelling 3/4 of the distance ^^

JASS:
//   CONS:
//      - You can&#039;t throw it a specific distance


Do you plan on changing that in the future, or is that just a limitation that you can't get past?

To add to this: You can calculate distance using the missile speed of a projectile. So then you can calculate the exact time you need to remove the projectile.

Example:

Max Cast Distance is 1000
You cast it at a distance of 500
You can calculate the time it will take to reach the target location. You need to calculate the distance of the target and the point you're casting it at, then you need to calculate the missile speed, I think every 1000 is 1 second, but I can't remember it off the top of my head.
 

Cohadar

master of fugue
Reaction score
209
Ok I am going to critic you now.
Don't get it personal it is so you make it better ok :)

First of all you should change the tThrow name.
Always start libraries with uppercase.
Mixing the cases in the way you did is absolutely horrific.

Documentation needs a more emphasized headers.

BAD:
JASS:

set dat.time = dat.time + 0.04


GOOD:
JASS:

set dat.time = dat.time + TT_PERIOD


TT_PERIOD is actually 0.03125 by default, not 0.04 so that was not only bad but an error.

BAD:
JASS:

        set x = dat.startx + dat.disgone * Cos(dat.angle * bj_DEGTORAD)
        set y = dat.starty + dat.disgone * Sin(dat.angle * bj_DEGTORAD)


Angle is fixed during the whole time so there is no need to recalculates Sin and Cos every period, use dx and dy instead
(for further details see any of my demo maps that has slide example)

This is actually a projectile not a throw system, so I suggest you name it that way.
 

cr4xzZz

Also known as azwraith_ftL.
Reaction score
51
> - You can't throw it a specific distance

Really bad. I suggest you to make it configureable :) Anyway gj on the system
 

Trollvottel

never aging title
Reaction score
262
ok i will fix everything and will calculate the distance with
(v²*sin(2*alpha)) / g
so i should be able to make it so that it automaticly chooses the right speed.
thank you for your replies.
 

Hatebreeder

So many apples
Reaction score
380
Why not make screenshots?
Zzzzmmmm... Also, is this MUI, if I call this with Global Arguments?
 

Trollvottel

never aging title
Reaction score
262
sure it is MUI and SUI und Multi-everything I can think of. The global arguments are passed into the struct and are saved there.
and there is no screenshot because you should take a look at it yourself and see how it looks to make your opinion. a ball is no very spectacular screenshot, is it?
 

Hatebreeder

So many apples
Reaction score
380
sure it is MUI and SUI und Multi-everything I can think of. The global arguments are passed into the struct and are saved there.
and there is no screenshot because you should take a look at it yourself and see how it looks to make your opinion. a ball is no very spectacular screenshot, is it?

pfff...
A ball can be realy spectacular, take a look at all those Toss Spells in the Spells Section.
Also, I think you need a function that detects if the Units lands, because I can think of people wanting the Caster to Damage other units apon impact ( If I throw a rock on you, then it should hurt, if I hit, right? :p ).
Anyways, other than that, this is a very sexy system ^^ I might use this, or make similar throw/jump spells using this as a Template :)
 

waaaks!

Zinctified
Reaction score
255
this will be useful for people that likes jump spell, like me...:D
can u add a function like, ThrowImpact, the function will call another function after the throwed unit is done jumping? and will use a generic unit as the throwed unit, like GetEnumUnit() or GetTriggerUnit(), or anything that you like

for example:
JASS:
function Damage takes nothing returns nothing
 call UnitDamageTarget(GetTriggerUnit(), GetEnumUnit(), 500, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
 // GetEnumUnit() refers to the throwed unit, ang GetTriggerUnit() refers to the unit used in the event
endfunction

function Actions takes nothing returns nothing
 call ThrowImpact(..., ..., ..., ..., function Damage)
endfunction
 

Trollvottel

never aging title
Reaction score
262
good idea, i will add that, and i will also add a function which will throw an existing unit to a target point.

// edit: i dont get this part:

JASS:
 // GetEnumUnit() refers to the throwed unit, ang GetTriggerUnit() refers to the unit used in the event
 

cr4xzZz

Also known as azwraith_ftL.
Reaction score
51
> // edit: i dont get this part:


He means GetEnumUnit() = GetImpactUnit() or something like that. A function that would return the unit that impacted from the jump system. That way he could call other functions with that unit (e.g. damage, knockback, etc.).


Or sth like that... That's how I understand it xD
 

Trollvottel

never aging title
Reaction score
262
new version out. it allows to throw the projectile a certain distance.


next version will bring this
call ThrowUnitAndDo(args, function userfunc)

if its possible.
 

waaaks!

Zinctified
Reaction score
255
yup thats what i meant, now ill only download it after releasing the full version
 

cr4xzZz

Also known as azwraith_ftL.
Reaction score
51
> ok i updated it, now you can throw units and call a function for them after.
Sweet. Now it's really nice :)
 

GooS

Azrael
Reaction score
154
I tried this out and it works great, however If the targeted unit is moving the "ball" will not hit neither the place the place the target was standing at when spell was casted nor the current position of the targeted unit, it will hit like , hmm well I dont really know but it feels like the same distance but the current new angle of the targeted unit seen from the caster.
 

Trollvottel

never aging title
Reaction score
262
i dont think that is possible. the angle is locked at the beginning of throwing so it wont change.
 

Trollvottel

never aging title
Reaction score
262
that would be shit. i had always to recalculate everything. if you want that, use abilitys like stormbolt, my system is for realistic throw effects and jumps
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top