1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The poll for our 11th Music Contest is up! Help us choose the most awesome cinematic tracks by casting a vote!
    Dismiss Notice
  3. Melee Mapping contest #3 - Poll is up! Vote for the best 4v4 melee maps!
    Dismiss Notice
  4. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  5. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice

PurplePoot - Prismatic Spray

Submitted by PurplePoot
This bundle is marked as approved. It works and satisfies the submission rules.
My entry for Zephyr Contest #15 - Model-based. Uses Direfury's Gnome Arcanist and Vexorian's dummy.mdx and TimerUtils.

To import this, simply copy+paste the object data into your map and then change the appropriate configuration variables (every four-digit code between single quotes, such as 'A000'). You may also wish to read the more general spell importing instructions.


  • Code (vJASS):

    scope PrismaticSpray initializer init
    globals
        private constant hashtable timers = InitHashtable()
    endglobals

    private function CastSpell takes unit caster, unit target, integer level, integer spell, integer order returns nothing
        local unit dumm = CreateUnit(GetOwningPlayer(caster),PrismaticSprayConfig_DUMMY,GetUnitX(caster),GetUnitY(caster),Atan2(GetUnitY(target)-GetUnitY(caster),GetUnitX(target)-GetUnitX(caster))*bj_RADTODEG)
        call UnitApplyTimedLife(dumm,'BTLF',5.)
        call SetUnitExploded(dumm,true)
        call UnitAddAbility(dumm,spell)
        call SetUnitAbilityLevel(dumm,spell,level)
        call IssueTargetOrderById(dumm,order,target)
        set dumm = null
    endfunction

    private function RayOfFrostInitial takes unit source, unit target, integer level returns nothing
        if IsPlayerEnemy(GetOwningPlayer(target),GetOwningPlayer(source)) then
            call CastSpell(source,target,level,PrismaticSprayConfig_RAY_OF_FROST_SLOW_ID,PrismaticSprayConfig_RAY_OF_FROST_SLOW_ORDER_ID)
        endif
    endfunction
    private function RayOfFrostOngoing takes unit source, unit target, integer level returns nothing
        if IsPlayerEnemy(GetOwningPlayer(target),GetOwningPlayer(source)) then
            call UnitDamageTarget(source,target,PrismaticSprayConfig_RayOfFrostDamageTick(level),false,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
        endif
    endfunction

    private function RayOfFireInitial takes unit source, unit target, integer level returns nothing
        if IsPlayerEnemy(GetOwningPlayer(target),GetOwningPlayer(source)) then
            call CastSpell(source,target,level,PrismaticSprayConfig_RAY_OF_FIRE_BURN_ID,PrismaticSprayConfig_RAY_OF_FIRE_BURN_ORDER_ID)
        endif
    endfunction
    private function RayOfFireOngoing takes unit source, unit target, integer level returns nothing
        if IsPlayerEnemy(GetOwningPlayer(target),GetOwningPlayer(source)) then
            call UnitDamageTarget(source,target,PrismaticSprayConfig_RayOfFireDamageTick(level),false,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
        endif
    endfunction

    private function HolyRayOngoing takes unit source, unit target, integer level returns nothing
        if IsPlayerAlly(GetOwningPlayer(target),GetOwningPlayer(source)) then
            call SetWidgetLife(target,GetWidgetLife(target) + PrismaticSprayConfig_HolyRayHealingTick(level))
        endif
    endfunction

    private function DarkRayInitial takes unit source, unit target, integer level returns nothing
        if IsPlayerEnemy(GetOwningPlayer(target),GetOwningPlayer(source)) then
            if GetWidgetLife(target) < PrismaticSprayConfig_DARK_RAY_KILL_THRESHOLD then
                call UnitDamageTarget(source,target,99999999.,false,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
            else
                call CastSpell(source,target,level,PrismaticSprayConfig_DARK_RAY_STUN_ID,PrismaticSprayConfig_DARK_RAY_STUN_ORDER_ID)
            endif
        endif
    endfunction

    private function ArcaneRayInitial takes unit source, unit target, integer level returns nothing
        if IsPlayerEnemy(GetOwningPlayer(target),GetOwningPlayer(source)) then
            call CastSpell(source,target,level,PrismaticSprayConfig_ARCANE_RAY_SLOW_ID,PrismaticSprayConfig_ARCANE_RAY_SLOW_ORDER_ID)
        elseif IsPlayerAlly(GetOwningPlayer(target),GetOwningPlayer(source)) then
            call CastSpell(source,target,level,PrismaticSprayConfig_ARCANE_RAY_HASTE_ID,PrismaticSprayConfig_ARCANE_RAY_HASTE_ORDER_ID)
        endif
    endfunction

    private struct Data
        unit caster
        integer level
        real angle
        boolean roundEnd
        method onDestroy takes nothing returns nothing
            set this.caster = null
        endmethod
    endstruct

    private function ShootRays takes Data dat returns nothing
        local real sourcex = GetUnitX(dat.caster) + PrismaticSprayConfig_EFFECT_POINT_XY_FORWARD * Cos(dat.angle) + PrismaticSprayConfig_EFFECT_POINT_XY_PERP * Cos(dat.angle + bj_PI/2.)
        local real sourcey = GetUnitY(dat.caster) + PrismaticSprayConfig_EFFECT_POINT_XY_FORWARD * Sin(dat.angle) + PrismaticSprayConfig_EFFECT_POINT_XY_PERP * Sin(dat.angle + bj_PI/2.)
        local integer numRays = PrismaticSprayConfig_RaysPerLevel(dat.level)
        local real angle = dat.angle - PrismaticSprayConfig_RAY_EFFECT_ARC/2.
        local real angleIncrement = PrismaticSprayConfig_RAY_EFFECT_ARC/I2R(numRays - 1)
        local integer rayType
        local PrismaticSprayRays_RayEffect initialEffect
        local PrismaticSprayRays_RayEffect ongoingEffect
        local string sfx
        loop
            exitwhen numRays == 0
            set rayType = GetRandomInt(0,4)
            if rayType == 0 then //Fire
                set initialEffect = RayOfFireInitial
                set ongoingEffect = RayOfFireOngoing
                set sfx = PrismaticSprayConfig_RAY_OF_FIRE_EFFECT
            elseif rayType == 1 then //Frost
                set initialEffect = RayOfFrostInitial
                set ongoingEffect = RayOfFireOngoing
                set sfx = PrismaticSprayConfig_RAY_OF_FROST_EFFECT
            elseif rayType == 2 then //Holy
                set initialEffect = PrismaticSprayRays_NoInitialEffect
                set ongoingEffect = HolyRayOngoing
                set sfx = PrismaticSprayConfig_HOLY_RAY_EFFECT
            elseif rayType == 3 then //Dark
                set initialEffect = DarkRayInitial
                set ongoingEffect = PrismaticSprayRays_NoOngoingEffect
                set sfx = PrismaticSprayConfig_DARK_RAY_EFFECT
            elseif rayType == 4 then //Arcane
                set initialEffect = ArcaneRayInitial
                set ongoingEffect = PrismaticSprayRays_NoOngoingEffect
                set sfx = PrismaticSprayConfig_ARCANE_RAY_EFFECT
            endif
            call FireRay(dat.caster,sfx,sourcex,sourcey,angle,PrismaticSprayConfig_EFFECT_POINT_Z,dat.level,initialEffect,ongoingEffect)
            set numRays = numRays - 1
            set angle = angle + angleIncrement
        endloop
    endfunction

    private function TimerExpires takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local Data dat = GetTimerData(t)
        //Create rays if appropriate, then go to next iteration
        if dat.roundEnd then
            call TimerStart(t,PrismaticSprayConfig_EFFECT_POINT_TIME,false,function TimerExpires)
        else
            call ShootRays(dat)
            call TimerStart(t,PrismaticSprayConfig_EFFECT_END_TIME - PrismaticSprayConfig_EFFECT_POINT_TIME,false,function TimerExpires)
        endif
        set dat.roundEnd = not dat.roundEnd
        set t = null
    endfunction

    private function Actions takes nothing returns nothing
        local Data dat = Data.create()
        local timer t = NewTimer()
        local real x = GetUnitX(GetTriggerUnit())
        local real y = GetUnitY(GetTriggerUnit())
        set dat.caster = GetTriggerUnit()
        set dat.level = GetUnitAbilityLevel(dat.caster,GetSpellAbilityId())
        set dat.angle = Atan2(GetSpellTargetY() - y,GetSpellTargetX() - x)
        set dat.roundEnd = false
        call SaveTimerHandle(timers,GetHandleId(dat.caster),0,t)
        call SetTimerData(t,dat)
        call TimerStart(t,PrismaticSprayConfig_EFFECT_POINT_TIME,false,function TimerExpires)
        set t = null
    endfunction

    private function EndActions takes nothing returns nothing
        local timer t = LoadTimerHandle(timers,GetHandleId(GetTriggerUnit()),0)
        local Data dat = GetTimerData(t)
        call FlushChildHashtable(timers,GetHandleId(GetTriggerUnit()))
        call dat.destroy()
        call PauseTimer(t)
        call ReleaseTimer(t)
        set t = null
    endfunction

    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == PrismaticSprayConfig_ABILITY_ID
    endfunction

    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function Actions)
     
        set t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_FINISH)
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function EndActions)
    endfunction
    endscope
     


  • Code (vJASS):

    library PrismaticSprayConfig
    //===========================================================================================
    //=                                      Configuration                                      =
    //===========================================================================================
    globals
        public constant integer ABILITY_ID = 'A000'
        public constant integer DUMMY = 'e000'
     
        //The point after the beginning of the animation at which the rays should fire (animation-based)
        public constant real EFFECT_POINT_TIME = 0.45
        //The point after which each "round" of the spell ends
        //The spell duration should be a multiple of this
        public constant real EFFECT_END_TIME = 1.208
        //The offset towards the target from the centre of the hero where the effect should begin (model-based)
        public constant real EFFECT_POINT_XY_FORWARD = 75.
        //The offset perpendicular to the target from the centre of the model where the effect should begin (model-based)
        public constant real EFFECT_POINT_XY_PERP = 0.
        //The height at which the effect should begin (model-based)
        public constant real EFFECT_POINT_Z = 30.
     
        //The number of SFX displayed in a line per ray
        public constant integer RAY_EFFECT_COUNT = 15
        //The length of each ray
        public constant real RAY_EFFECT_LENGTH = 500.
        //The width of each ray's effect
        public constant real RAY_EFFECT_WIDTH = 100.
        //The elevation of each ray's special effects
        public constant real RAY_EFFECT_HEIGHT = 30.
        //The length of time that each ray applies its effect
        //.9s is appropriate for most sfx.
        public constant real RAY_EFFECT_DURATION = .9
        //Frequency at which ray effects are checked
        public constant real RAY_TIMER_TICK = .03
     
        //The size of the arc (in radians) in which rays are emitted
        public constant real RAY_EFFECT_ARC = bj_PI/2.
     
        //Enemies below this much health die instantly when hit by a dark ray.
        public constant real DARK_RAY_KILL_THRESHOLD = 200.
     
        //Special effect models
        public constant string RAY_OF_FIRE_EFFECT = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
        public constant string RAY_OF_FROST_EFFECT = "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
        public constant string HOLY_RAY_EFFECT = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl"
        public constant string DARK_RAY_EFFECT = "Abilities\\Weapons\\BansheeMissile\\BansheeMissile.mdl"
        public constant string ARCANE_RAY_EFFECT = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
     
        //Spell IDs and OrderIDs
        public constant integer ARCANE_RAY_HASTE_ID  = 'A001'
        public constant integer ARCANE_RAY_SLOW_ID   = 'A002'
        public constant integer RAY_OF_FROST_SLOW_ID = 'A003'
        public constant integer RAY_OF_FIRE_BURN_ID  = 'A004'
        public constant integer DARK_RAY_STUN_ID     = 'A005'
     
        public constant integer ARCANE_RAY_HASTE_ORDER_ID  = 852101
        public constant integer ARCANE_RAY_SLOW_ORDER_ID   = 852075
        public constant integer RAY_OF_FROST_SLOW_ORDER_ID = 852226
        public constant integer RAY_OF_FIRE_BURN_ORDER_ID  = 852662
        public constant integer DARK_RAY_STUN_ORDER_ID     = 852095
    endglobals

    //Level-based effects
    public function RayOfFrostDamageTick takes integer level returns real
        return 2. + I2R(level)
    endfunction
    public function RayOfFireDamageTick takes integer level returns real
        return 3. + 2.*I2R(level)
    endfunction
    public function HolyRayHealingTick takes integer level returns real
        return 5. + 3.*I2R(level)
    endfunction

    //You can use GetRandomInt to randomize the number of rays somewhat
    public function RaysPerLevel takes integer level returns integer
        return GetRandomInt(3,5) + level
    endfunction

    public function TargetFilter takes nothing returns boolean
        return GetWidgetLife(GetFilterUnit()) > 0. and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_FLYING) or IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE))
    endfunction
    endlibrary
     


  • Code (vJASS):

    library PrismaticSprayRays requires PrismaticSprayGeometry, PrismaticSprayConfig
    globals
        private constant hashtable rays     = InitHashtable()
        private constant timer     T        = CreateTimer()
        private constant group     G        = CreateGroup()
        private          integer   numRays  = 0
     
        private constant real      RADIUS   = 2.*RMaxBJ(PrismaticSprayConfig_RAY_EFFECT_LENGTH,PrismaticSprayConfig_RAY_EFFECT_WIDTH)
    endglobals

    public function interface RayEffect takes unit source, unit target, integer level returns nothing

    public function NoInitialEffect takes unit source, unit target, integer level returns nothing
    endfunction
    public function NoOngoingEffect takes unit source, unit target, integer level returns nothing
    endfunction

    private struct RayStruct
        Rectangle rayRect
        unit owner
        real lifespan
        group targetedUnits
        integer level
        RayEffect callbackInitial
        RayEffect callbackOngoing
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set this.targetedUnits = CreateGroup()
            return this
        endmethod
        method onDestroy takes nothing returns nothing
            call GroupClear(this.targetedUnits)
            call DestroyGroup(this.targetedUnits)
            set this.targetedUnits = null
            set this.owner = null
            call this.rayRect.destroy()
        endmethod
    endstruct

    private function RayTimerLoop takes nothing returns nothing
        local RayStruct current
        local integer i = 0
        local unit u
        loop
            exitwhen i == numRays
            set current = LoadInteger(rays,i,0)
            call GroupEnumUnitsInRange(G, current.rayRect.p1x, current.rayRect.p1y, RADIUS, Filter(function PrismaticSprayConfig_TargetFilter))
            loop
                set u = FirstOfGroup(G)
                exitwhen u == null
                if current.rayRect.containsPoint(GetUnitX(u),GetUnitY(u)) then
                    if not IsUnitInGroup(u, current.targetedUnits) then
                        call current.callbackInitial.evaluate(current.owner,u,current.level)
                        call GroupAddUnit(current.targetedUnits, u)
                    endif
                    call current.callbackOngoing.evaluate(current.owner,u,current.level)
                endif
                call GroupRemoveUnit(G,u)
            endloop
            set current.lifespan = current.lifespan - PrismaticSprayConfig_RAY_TIMER_TICK
            if current.lifespan <= 0. then
                call current.destroy()
                call SaveInteger(rays,i,0,LoadInteger(rays,0,numRays - 1))
                set numRays = numRays - 1
                call FlushChildHashtable(rays,numRays)
            else
                set i = i + 1
            endif
        endloop
        if numRays == 0 then
            call PauseTimer(T)
        endif
    endfunction

    function FireRay takes unit owner, string sfx, real sourcex, real sourcey, real angle, real height, integer level, RayEffect initialEffect, RayEffect ongoingEffect returns nothing
        local RayStruct ray = RayStruct.create()
        set ray.rayRect = Rectangle.createFacing(sourcex,sourcey,PrismaticSprayConfig_RAY_EFFECT_WIDTH,PrismaticSprayConfig_RAY_EFFECT_LENGTH,angle)
        set ray.owner = owner
        set ray.callbackInitial = initialEffect
        set ray.callbackOngoing = ongoingEffect
        set ray.lifespan = PrismaticSprayConfig_RAY_EFFECT_DURATION
        set ray.level = level
        call DisplayEffectInLine(sfx,sourcex,sourcey,PrismaticSprayConfig_RAY_EFFECT_LENGTH,angle,PrismaticSprayConfig_RAY_EFFECT_HEIGHT,PrismaticSprayConfig_RAY_EFFECT_COUNT)
     
        call SaveInteger(rays,numRays,0,ray)
        set numRays = numRays + 1
        if numRays == 1 then
            call TimerStart(T,PrismaticSprayConfig_RAY_TIMER_TICK,true,function RayTimerLoop)
        endif
    endfunction
    endlibrary
     


  • Code (vJASS):

    library PrismaticSprayGeometry requires PrismaticSprayConfig

    struct Rectangle
        real p1x
        real p2x
        real p3x
        real p4x
        real p1y
        real p2y
        real p3y
        real p4y
     
        real x12diff
        real x23diff
        real x34diff
        real x41diff
        real y12diff
        real y23diff
        real y34diff
        real y41diff
     
        //1 and 3 (and also 2 and 4) are opposite corners
        static method create takes real x1, real y1, real x2, real y2, real x3, real y3, real x4, real y4 returns thistype
            local thistype this = thistype.allocate()
            set this.p1x = x1
            set this.p2x = x2
            set this.p3x = x3
            set this.p4x = x4
            set this.p1y = y1
            set this.p2y = y2
            set this.p3y = y3
            set this.p4y = y4
            set this.x12diff = x2 - x1
            set this.x23diff = x3 - x2
            set this.x34diff = x4 - x3
            set this.x41diff = x1 - x4
            set this.y12diff = y2 - y1
            set this.y23diff = y3 - y2
            set this.y34diff = y4 - y3
            set this.y41diff = y1 - y4
            return this
        endmethod
     
        static method createFacing takes real px, real py, real width, real length, real angle returns thistype
            local real perpAngle = angle + bj_PI/2.
            local real offsetCosP = width/2. * Cos(perpAngle)
            local real offsetSinP = width/2. * Sin(perpAngle)
            local real lenCos = length * Cos(angle)
            local real lenSin = length * Sin(angle)
            local real p1x = px - offsetCosP
            local real p1y = py - offsetSinP
            local real p2x = px + offsetCosP
            local real p2y = py + offsetSinP
            return thistype.create(p1x, p1y, p2x, p2y, p2x + lenCos, p2y + lenSin, p1x + lenCos, p1y + lenSin)
        endmethod
     
        method containsPoint takes real px, real py returns boolean
          return (py - this.p1y)*this.x12diff <= (px - this.p1x)*this.y12diff and (py - this.p2y)*this.x23diff <= (px - this.p2x)*this.y23diff and (py - this.p3y)*this.x34diff <= (px - this.p3x)*this.y34diff and (py - this.p4y)*this.x41diff <= (px - this.p4x)*this.y41diff
        endmethod
    endstruct

    //Requires Count >= 2
    function DisplayEffectInLine takes string model, real px, real py, real distance, real angle, real height, integer count returns nothing
        local real dx = px + distance * Cos(angle)
        local real dy = py + distance * Sin(angle)
        local real dist = SquareRoot((dx - px)*(dx - px) + (dy - py)*(dy - py))
        local real vx = distance/I2R(count - 1) * Cos(angle)
        local real vy = distance/I2R(count - 1) * Sin(angle)
        local unit u
        loop
            set count = count - 1
            exitwhen count <= 0
            set u = CreateUnit(Player(15),PrismaticSprayConfig_DUMMY,px,py,270.)
            call UnitApplyTimedLife(u,'BTLF',2.)
            call SetUnitFlyHeight(u,height,0.)
            call SetUnitExploded(u,true)
            call DestroyEffect(AddSpecialEffectTarget(model,u,"origin"))
            set px = px + vx
            set py = py + vy
        endloop
        set u = null
    endfunction
    endlibrary
     

Contents

PurplePoot - Prismatic Spray (Map)

Reviews
KILLCIDE
I took a look through IcemanBo's judge review for your spell and did not notice anything game breaking, so I have no reason not to approve this resource. It would be great if you can fix the potential thread crash he mentioned though. Needs Fixed...
IcemanBo
^These two things would be good to be fixed.
  1. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    I know you were a Mod before, but you probably forgot that the script/code must be in the post. Also, I think you should not be posting entries to an unfinished contest. Usually, people wait for the result of the contest before submitting it here.
     
  2. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    5,846
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    It's pretty new, but now it's in rules in new contests having to submit the submission in the resource section, rather then only having it in the contets thread.
    The major reason was that with that hive can directly ensure to have it in the resource data base, as it seems many people didn't submit their contest submissions by them selves here.
     
  3. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Ahh ok. I support this because some good contest entries (model, icon, spell, etc) are really good but not in the resource section so when I searched for something, it won't show because it's not in the resource section so I end up missing that good resource.

    Maybe they did not want to? Maybe they did that for the purpose of the contest only?
     
  4. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    5,846
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Locked until contest ends.
     
  5. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,440
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    I took a look through IcemanBo's judge review for your spell and did not notice anything game breaking, so I have no reason not to approve this resource. It would be great if you can fix the potential thread crash he mentioned though.

    Needs Fixed

    • Nothing

    Suggestions

    • Fix any of the suggestions made by IcemanBo in his judge review

    Status


    Approved
     
  6. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    5,846
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    ^These two things would be good to be fixed.
     
  7. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,440
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    Oh! Totally missed that first one.