wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Warcraft III Modding > Developer's Corner > Triggers & Scripts
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 09-13-2011, 07:08 PM   #1
Yrth
User
 
Yrth's Avatar
 
Join Date: Jul 2010
Posts: 76

Yrth has little to show at this moment (4)

Default [vJASS] damn you structs

this ones had me stumped for a few days and now that my brain hurts, I'm coming here
i tried to do it with lists like Anitarf showed me with wisp wheel but it doesn't quite work yet

Collapse Easy To Read Version:
globals
    timer LevelTimer = CreateTimer()
    group LevelGroup = CreateGroup()
endglobals

library LevelStructure requires ListModule
    struct Level
        //Member variables
        
        implement List
                
        method LevelWait takes real r returns nothing
            call TriggerSleepAction(r)
        endmethod
        
        //unit has reached a checkpoint
        method LevelRegionEntry takes unit u returns nothing

        endmethod
        
        private method LevelGroupHook takes nothing returns nothing
            call LevelRegionEntry(GetEnumUnit())
        endmethod
        
        private static method LevelPeriodic takes nothing returns nothing
            local Level l = .first
        
            loop
            exitwhen l == 0
                call GroupEnumUnitsInRect(LevelGroup, l.StartCPRect, PlayerOwned)
                call ForGroup(LevelGroup, function Level.LevelGroupHook)
                
                set l = l.next
            endloop
        endmethod
        
        private static method onInit takes nothing returns nothing
            call TimerStart(LevelTimer, .1, true, function Level.LevelPeriodic)
        endmethod
        
        //creates a level struct
        static method create takes integer number, string name, integer diff, trigger start, trigger stop, trigger nextpre, trigger nextun, boolean hasnext, rect startrect, rect vision returns Level            
            call new.listAdd()
            
            return new
        endmethod
    endstruct
endlibrary

so basically what I want to happen is
the timer periodically checks all the Level structs in the list to see if a PlayerOwned unit is in the matching rect for that instance of the Level struct
if there are such units, then for all units in that rect call LevelRegionEntry

LevelRegionEntry is pretty complex so it'd be really really helpful if I could do this like I'm trying to

also Anitarf (since I'm like 99% sure its gonna b you answering) you said that you like to periodically go through all your regions instead of using the region entry event built into the game engine. what do you use for a periodic time?

and last question:
I'm gonna need to use waits in some of my triggers coming up, so this is important.
if I put a triggersleepaction in a function but that function gets called again, it glitches up and skips the rest of the actions (in the original function call after the wait call)
so if I make a function
Collapse JASS:
myWait(real x)
that just calls TriggerSleepAction(x) will it be better?
ie
mainFunction calls myWait
myWait calls TriggerSleepAction()
in the middle of that, mainFunction happens to be called again
does it skip just the myWait call or the entire rest of the first mainFunction?

Full Version

Collapse JASS:
globals
    Level array     Levels[100]     //an array containing all the levels. the index of the array should match its elements levelnumber
    timer LevelTimer = CreateTimer()
    group LevelGroup = CreateGroup()
endglobals

library LevelStructure requires ListModule
    struct Level
        integer     LevelNumber     //the number of a level
        string      Name            //a levels name, only used in the multiboard
        integer     Difficulty      //refers to the difficulty of a level
        trigger     Start           //this trigger turns a level "on"
        trigger     Stop            //this turns a level "off"
        trigger     NextPreload     //if the next level requires time to be setup, use this to do it
        trigger     NextUnload      //if the mazer dies without continues before reaching the next level, use this to unload it
        boolean     HasPreload      //some levels have preloads, some don't. this is more for safety than anything else.
        rect        StartRect       //marks the position to move the revive rect
        rect        Vision          //the bounds placed on a player's vision
        rect        StartCPRect     //the checkpoint that triggers this level
        Level       NextLevel       //pointer to the next Level struct
        Level       PrevLevel       //pointer to the prev Level struct        
        
        implement List
        
        //counts the number of mazers currently on this level
        private method NumberOnLevel takes nothing returns integer
            local integer NumMazers = 0
            local integer i = 0
            
            loop
            exitwhen i >= NumberPlayers
                //if MazerOnLevel[i] == LevelNumber then
                if AllPlayers[i].OnLevel == LevelNumber then
                    set NumMazers = NumMazers + 1
                endif
                
                set i = i + 1
            endloop
            
            return NumMazers
        endmethod
        
        //starts this level unless someone else is on it
        method StartLevel takes nothing returns nothing
            if NumberOnLevel() == 0 then
                call TriggerExecute(Start)
                
                if HasPreload and NextLevel.NumberOnLevel() == 0 then //preload next level unless it doesnt have a preload or someone's already there
                    call TriggerExecute(NextPreload)
                endif
            endif
        endmethod
        
        //stops this level unless someone else is on it
        method StopLevel takes nothing returns nothing
            if NumberOnLevel() == 0 and PrevLevel.NumberOnLevel() == 0 then
                call TriggerExecute(Stop)
            endif
        endmethod
        
        method LevelWait takes real r returns nothing
            call TriggerSleepAction(r)
        endmethod
        
        //unit has reached a checkpoint
        method LevelRegionEntry takes unit u returns nothing
            local integer i = GetPlayerId(GetOwningPlayer(u))
            
            if AllPlayers[i].OnLevel != LevelNumber then //no point
                if TeamMode == 0 then //solo mazing
                    //automatically checks for people already on the level
                    call NextLevel.StartLevel() 
            
                    //move the mazer and the revive rect
                    call MoveRectTo(AllPlayers[i].Revive, GetRectCenterX(NextLevel.StartRect), GetRectCenterY(NextLevel.StartRect))
                    call SetUnitPosition(u, GetRectCenterX(NextLevel.StartRect), GetRectCenterY(NextLevel.StartRect))
                    set AllPlayers[i].OnLevel = NextLevel.LevelNumber
                    if (GetLocalPlayer() == Player(i)) then
                        call PanCameraToTimed(GetUnitX(u), GetUnitY(u), 0)
                    endif
            
                    call StopLevel()
                
                    //update the multiboard with the player's new level string and flash green twice
                    call MultiboardSetItemValue(MultiboardGetItem(PlayerStats, i + 1, 1), NextLevel.Name)
                    call MultiboardSetItemValueColor(MultiboardGetItem(PlayerStats, i + 1, 1), 0, 255, 0, 100)
                    call LevelWait(.5)
                    call MultiboardSetItemValueColor(MultiboardGetItem(PlayerStats, i + 1, 1), 255, 255, 255, 100)
                    call LevelWait(.5)
                    call MultiboardSetItemValueColor(MultiboardGetItem(PlayerStats, i + 1, 1), 0, 255, 0, 100)
                    call LevelWait(.5)
                    call MultiboardSetItemValueColor(MultiboardGetItem(PlayerStats, i + 1, 1), 255, 255, 255, 100)
                    call MultiboardReleaseItem(MultiboardGetItem(PlayerStats, i + 1, 1))
            
                elseif TeamMode == 1 then //all on 1 team
        
        
                elseif TeamMode == 2 then //teams of 2
        
        
                elseif TeamMode == 3 then //2 even-sized teams
        
                else
                    call DisplayTextToForce(bj_FORCE_PLAYER[i], "Invalid Game Mode: 'Team Mode'")
                endif
            endif
        endmethod
        
        private method LevelGroupHook takes nothing returns nothing
            call LevelRegionEntry(GetEnumUnit())
        endmethod
        
        private static method LevelPeriodic takes nothing returns nothing
            local Level l = .first
        
            loop
            exitwhen l == 0
                call GroupEnumUnitsInRect(LevelGroup, l.StartCPRect, PlayerOwned)
                call ForGroup(LevelGroup, function Level.LevelGroupHook)
                
                set l = l.next
            endloop
        endmethod
        
        private static method onInit takes nothing returns nothing
            call TimerStart(LevelTimer, .1, true, function Level.LevelPeriodic)
        endmethod
        
        //creates a level struct
        static method create takes integer number, string name, integer diff, trigger start, trigger stop, trigger nextpre, trigger nextun, boolean hasnext, rect startrect, rect vision returns Level
            local Level new = Level.allocate()
            
            set new.LevelNumber = number
            set new.Name = name
            set new.Difficulty = diff
            set new.Start = start
            set new.Stop = stop
            set new.NextPreload = nextpre
            set new.NextUnload = nextun
            set new.HasPreload = hasnext
            set new.StartRect = startrect
            set new.Vision = vision
            
            call new.listAdd()
            
            return new
        endmethod
    endstruct
endlibrary



many thanks to those who help/try to help =)

edit 1 more question:
which one of these is preferable?
Collapse JASS:
globals
    constant integer x = 10
    
    real array      a[x]
    string array    b[x]
    integer array   c[x]
    integer array   d[x]
    boolean array   e[x]
endglobals

versus

Collapse JASS:
globals
    example array   ex[x]
endglobals

struct example
    real        a
    string      b
    integer     c
    integer     d
    boolean     e
endstruct

ie.
hows the lookup on ex[i].a compare to a[i]?
is it around the same amount of space to store everything in 1 struct versus lots of different arrays?

so far all my triggers have been using the first method (so it'll suck to switch to the second one). so if the second one is preferable, how preferable exactly?

Last edited by Yrth : 09-13-2011 at 09:55 PM.
Yrth is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 09-17-2011, 06:24 AM   #2
PurgeandFire111
User
 
PurgeandFire111's Avatar
 
Join Date: Dec 2006
Posts: 253

PurgeandFire111 will become famous soon enough (58)PurgeandFire111 will become famous soon enough (58)

Default

Quote:
that just calls TriggerSleepAction(x) will it be better?

You will probably want to refrain from using TriggerSleepAction. In periodics especially, it can lead to very odd wait times. Generally, it may do things you just don't want it to do. I recommend that you opt for a timer method, it is harder to implement but the results will be a lot less varying.

Quote:
which one of these is preferable?

Well, I am not sure exactly what you mean. Do you mean a global array vs. a struct array? They are pretty much different things. The first one is just a bunch of arrays, so that will be very fast. The second one is an array variable of a struct, aka an integer array. So when reading the globals, it would just be a[x] while reading it from the struct will be ex[x].a. The second method will be slower because it is actually doing a[ex[x]], which is two array reads compared to a simple a[x].

... but there is always the high chance that I misunderstood your question. :P But if you are curious about strictly speed, the global block will be faster. It really depends on what you are doing as to which one you will use. If you are questioning structs vs using globals and doing all the work yourself, then I say you should just opt for structs because they were made for a reason. =) However, if you don't need a struct, then don't use one.

Last edited by PurgeandFire111 : 09-17-2011 at 06:24 AM.
PurgeandFire111 is offline   Reply With Quote
Old 09-17-2011, 09:23 AM   #3
Anitarf
Procrastination Incarnate


Development Director
 
Join Date: Feb 2004
Posts: 8,190

Submissions (19)

Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)

2008 Spell olympics - Fire - SilverApproved Map: Old School Alliance TacticsHero Contest #2 - 3rd PlaceSpell making session 2 winner

Default

Quote:
Originally Posted by Yrth
so basically what I want to happen is
the timer periodically checks all the Level structs in the list to see if a PlayerOwned unit is in the matching rect for that instance of the Level struct
if there are such units, then for all units in that rect call LevelRegionEntry
The code you posted should already do that. I don't see where the problem is.

Quote:
also Anitarf (since I'm like 99% sure its gonna b you answering) you said that you like to periodically go through all your regions instead of using the region entry event built into the game engine. what do you use for a periodic time?
0.1 is fine.

As for your wait question, I think you can't use TriggerSleepAction in timer callbacks at all. Use a timer instead.
__________________
Anitarf is offline   Reply With Quote
Old 09-17-2011, 07:13 PM   #4
Yrth
User
 
Yrth's Avatar
 
Join Date: Jul 2010
Posts: 76

Yrth has little to show at this moment (4)

Default

Quote:
... but there is always the high chance that I misunderstood your question. :P But if you are curious about strictly speed, the global block will be faster. It really depends on what you are doing as to which one you will use. If you are questioning structs vs using globals and doing all the work yourself, then I say you should just opt for structs because they were made for a reason. =) However, if you don't need a struct, then don't use one.
nope i think u got what i meant. ive got like 16 different global arrays that could all be put inside one struct. it just seemed like it might be much easier/more organized
i still might since 2 array lookups isnt really worth caring about
especially since doing it that way would make object oriented jesus cry :(
thank you very much for ze info's


Quote:
The code you posted should already do that. I don't see where the problem is.

it doesn't like me calling LevelGroupHook from the ForGroup call
Collapse JASS:
call ForGroup(LevelGroup, function Level.LevelGroupHook)
Collapse JASS:
private method LevelGroupHook takes nothing returns nothing
            call LevelRegionEntry(GetEnumUnit())
        endmethod
i could call LevelRegionEntry() and make a local to get the enum unit, but that would have the same problem. i need to retain the specific information of the struct.

the error is LevelGroupHook is not an static method of Level that takes nothing.

Quote:
As for your wait question, I think you can't use TriggerSleepAction in timer callbacks at all. Use a timer instead.
ok well if i do that i should probably really start using timer utils
so if someone would do a crash course with me that'd be awesome

if i have timerutils should i always create timers through it?
can i release timers that weren't made with timerutils?
whats the easiest way to get the integer value of a handle? i'm assuming thats the main point of the data attachment feature, pls correct me if i'm wrong.
or wait, is it possible to flat out attach a struct to a timer?
Yrth is offline   Reply With Quote
Old 09-17-2011, 11:42 PM   #5
Anitarf
Procrastination Incarnate


Development Director
 
Join Date: Feb 2004
Posts: 8,190

Submissions (19)

Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)

2008 Spell olympics - Fire - SilverApproved Map: Old School Alliance TacticsHero Contest #2 - 3rd PlaceSpell making session 2 winner

Default

Quote:
Originally Posted by Yrth
i could call LevelRegionEntry() and make a local to get the enum unit, but that would have the same problem. i need to retain the specific information of the struct.

the error is LevelGroupHook is not an static method of Level that takes nothing.
Yeah, it has to be a static method, I didn't notice you were using a regular method for this when I first looked over this. If you want to retain the struct instance, you need to store it in a global variable for the ForGroup call (or the GroupEnum call, it's more efficient to just do everything in the enum callback and never add the units to the group in the first place), or you could use a FirstOfGroup loop instead of ForGroup.

Quote:
ok well if i do that i should probably really start using timer utils
so if someone would do a crash course with me that'd be awesome

if i have timerutils should i always create timers through it?
can i release timers that weren't made with timerutils?
You can, probably, depending on the flavour of TimerUtils used, but why would you? If you know you're going to stop using a timer at some point, create it with NewTimer so you can recycle it afterwards. If you have a static timer that you won't ever recycle, then you probably also don't need to attach data to it so in that case you can use NewTimer or CreateTimer, it won't make any difference.

Quote:
whats the easiest way to get the integer value of a handle? i'm assuming thats the main point of the data attachment feature, pls correct me if i'm wrong.
or wait, is it possible to flat out attach a struct to a timer?
GetHandleId() gives you a unique integer for each handle, but since there's no way to convert that integer back to said handle there's little use to attaching that value to anything (it can be useful for attaching other values to that handle, though). As you guessed, attaching structs to timers is the way to go.
__________________
Anitarf is offline   Reply With Quote
Old 09-21-2011, 02:52 AM   #6
Yrth
User
 
Yrth's Avatar
 
Join Date: Jul 2010
Posts: 76

Yrth has little to show at this moment (4)

Default

Quote:
or the GroupEnum call, it's more efficient to just do everything in the enum callback and never add the units to the group in the first place
could you please show me what you mean?
not like something elaborate, just a short example... with a comment or two

Quote:
As you guessed, attaching structs to timers is the way to go.
so to attach a struct I literally just do
Collapse JASS:
call SetTimerData(t, myStruct)
and then to call a method for that struct all I'd need to do is
Collapse JASS:
call GetTimerData(t).methodName

Quote:
or you could use a FirstOfGroup loop instead of ForGroup
I'm guessing to do this I'd enumerate all the units into the group and then do a loop calling the function for the first of group and then removing the first from the group.
Could I do:
Collapse JASS:
exitwhen FirstOfGroup(g) == null
or would I have to use:
Collapse JASS:
exitwhen IsUnitGroupEmpty(g)
Yrth is offline   Reply With Quote
Old 09-21-2011, 11:50 AM   #7
BBQ
User
 
Join Date: May 2011
Posts: 85

Submissions (2)

BBQ will become famous soon enough (30)BBQ will become famous soon enough (30)

Default

Quote:
Originally Posted by Yrth
could you please show me what you mean?
not like something elaborate, just a short example... with a comment or two
Instead of doing this:
Collapse JASS:
globals
    group someGroup = CreateGroup()
endglobals

function unitCheck takes nothing returns boolean
    return (not IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) and GetUnitTypeId(GetFilterUnit) == 'hfoo') // Enumerate only living footmen.
endfunction

function healthDisplay takes nothing returns nothing
    call BJDebugMsg(R2S(GetWidgetLife(GetEnumUnit())) // Display the health of all enumerated units (footmen).
endfunction

// Pretend that the following code is somewhere within a function.
call GroupEnumUnitsInRange(someGroup, 0.0, 0.0, 500.0, Filter(function unitCheck))
call ForGroup(someGroup, function healthDisplay)
call GroupClear(someGroup)

you should do this:
Collapse JASS:
globals
    unit filterUnit = null // We'll use this only to improve the efficiency.
    group someGroup = CreateGroup()
endglobals

function unitCheckAndHealthDisplay takes nothing returns boolean
    set filterUnit = GetFilterUnit() // We can't use a local, 'cos we won't get the chance to null it and prevent it from leaking.
    if (not IsUnitType(filterUnit, UNIT_TYPE_DEAD) and GetUnitTypeId(filterUnit) == 'hfoo') then // Check if the unit is a footman.
        call BJDebugMsg(R2S(GetWidgetLife(filterUnit)) // Display its health.
    endif
    return false // This prevents any unit from passing the filter, so not a single units gets added to the group. 
endfunction      // But there's no need to - as we've already done all actions we needed.

// Pretend that the following code is somewhere within a function.
call GroupEnumUnitsInRange(someGroup, 0.0, 0.0, 500.0, Filter(function unitCheckAndHealthDisplay))
// We no longer need to use ForGroup(), or even clear the group, because no units get added to it anyway.

Quote:
Originally Posted by Yrth
so to attach a struct I literally just do
Collapse JASS:
call SetTimerData(t, myStruct)

and then to call a method for that struct all I'd need to do is
Collapse JASS:
call GetTimerData(t).methodName
Yes, just don't forget to store the value of GetTimerData(someTimer) inside of a variable if you're using it multiple times.

Quote:
Originally Posted by Yrth
I'm guessing to do this I'd enumerate all the units into the group and then do a loop calling the function for the first of group and then removing the first from the group.
Could I do:
Collapse JASS:
exitwhen FirstOfGroup(g) == null

or would I have to use:
Collapse JASS:
exitwhen IsUnitGroupEmpty(g)
Here's what you should do:
Collapse JASS:
// Pretend that the following code is somewhere within a function.
local unit temporaryUnit

// Enumerate units and everything else...

loop
    set temporaryUnit = FirstOfGroup(someGroup) // Pretend that the group is filled with units.
    exitwhen (temporaryUnit == null)
    // Do stuff with the "temporaryUnit" unit.
endloop
BBQ is offline   Reply With Quote
Old 09-21-2011, 01:54 PM   #8
Fledermaus
default string
 
Fledermaus's Avatar
 
Join Date: May 2006
Posts: 705

Submissions (1)

Fledermaus is a jewel in the rough (194)Fledermaus is a jewel in the rough (194)Fledermaus is a jewel in the rough (194)

Send a message via MSN to Fledermaus
Default

Quote:
Originally Posted by BBQ
Collapse JASS:
// Pretend that the following code is somewhere within a function.
local unit temporaryUnit

// Enumerate units and everything else...

loop
    set temporaryUnit = FirstOfGroup(someGroup) // Pretend that the group is filled with units.
    exitwhen (temporaryUnit == null)
    // Do stuff with the "temporaryUnit" unit.
    call GroupRemoveUnit(someGroup, temporaryUnit) //You forgot this bit
endloop
Fledermaus is offline   Reply With Quote
Old 09-21-2011, 03:10 PM   #9
Anitarf
Procrastination Incarnate


Development Director
 
Join Date: Feb 2004
Posts: 8,190

Submissions (19)

Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)

2008 Spell olympics - Fire - SilverApproved Map: Old School Alliance TacticsHero Contest #2 - 3rd PlaceSpell making session 2 winner

Default

Quote:
Originally Posted by Yrth
so to attach a struct I literally just do
Collapse JASS:
call SetTimerData(t, myStruct)
and then to call a method for that struct all I'd need to do is
Collapse JASS:
call GetTimerData(t).methodName
Almost. Since the data attached to the timer is just an integer, you need to typecast it back to a struct before you can call methods on it, like this: call myStructType(GetTimerData(t)).methodName() or first store it in a local variable like BBQ suggested if you plan on doing multiple operations on it. Strictly speaking, when attaching the struct, it should also first be typecast to integer like this: call SetTimerData(t, integer(myStruct)), but if you don't do this JassHelper will do it automatically.
__________________
Anitarf is offline   Reply With Quote
Old 09-23-2011, 08:55 PM   #10
Yrth
User
 
Yrth's Avatar
 
Join Date: Jul 2010
Posts: 76

Yrth has little to show at this moment (4)

Default

Quote:
Almost. Since the data attached to the timer is just an integer, you need to typecast it back to a struct before you can call methods on it, like this: call myStructType(GetTimerData(t)).methodName() or first store it in a local variable like BBQ suggested if you plan on doing multiple operations on it. Strictly speaking, when attaching the struct, it should also first be typecast to integer like this: call SetTimerData(t, integer(myStruct)), but if you don't do this JassHelper will do it automatically.
that's so cool
are the integer values given by GetHandleId(h) pointers to the actual handle or a kinda hashed version of the handle?

thanks everybody for the help, you guys are da best
Yrth is offline   Reply With Quote
Old 09-23-2011, 09:44 PM   #11
Anitarf
Procrastination Incarnate


Development Director
 
Join Date: Feb 2004
Posts: 8,190

Submissions (19)

Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)

2008 Spell olympics - Fire - SilverApproved Map: Old School Alliance TacticsHero Contest #2 - 3rd PlaceSpell making session 2 winner

Default

Quote:
Originally Posted by Yrth
are the integer values given by GetHandleId(h) pointers to the actual handle or a kinda hashed version of the handle?
The values returned by GetHandleId are as far as I know the same as the values we used to get with the return bug.
__________________
Anitarf is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 07:45 AM.


Affiliates
The Hubb The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2019, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services