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 > Resources > Code Resources > Systems
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 03-01-2009, 01:35 AM   #1
Opossum
User
 
Opossum's Avatar
 
Join Date: Mar 2008
Posts: 205

Submissions (4)

Opossum will become famous soon enough (59)Opossum will become famous soon enough (59)

Default OppiCam

NOTE: You have been redirected in order for our attachments to be made available to you. This will only last two minutes; these measures where taken to avoid hotlinking and bandwidth theft.
To avoid these restrictions Log in or Register

OppiCam 1.2d

OppiCam is a camera system designed especially for RPGs but can very well be used on any other map. This version currently consists of two main vJass libraries, each containing a proper camera system. The system also contains two optional plugin libraries that add keyboard control to the cameras. For these plugins to work Anitarf's ArrowKeys is required which is also included in this map.
Library ThirdPersonCam:
ThirdPersonCam adds a dynamic third person camera to the game. Its main purpose is to enable the user a highly configurable camera without the usual limitations of third person cameras. Using this camera you can basically use any kind of terrain on your map without caring about the cam falling below the terrain or clipping parts of the terrain.
Expand ThirdPersonCam:

Expand Optional library TPCKeyboardPlugin:



Library FixedCam:
FixedCam adds a camera similar to the one used in games like the Resident Evil series. The camera stays at a fixed point and follows a given unit. This cam is especially useful in small buildings or caves etc. where you are forced to navigate through very narrow passages.
Expand FixedCam:
Expand Optional library FCKeyboardPlugin:



Function and variable lists:
Expand Base libraries:

Expand Plugin libraries:

Change log


1.2d:
  • Fixed the twitchy bug
1.2c:
  • No more public prefixes
1.2b:
  • Added CLIFF_DISTANCE constant
  • Added CappedReal function for the keyboard plugins
1.2:
  • Both keyboard plugins now require Anitarf's ArrowKeys which is included in the map
  • Some minor code changes. No changes in functionality
1.1c:
  • Updated some outdated documentation again. Code still the same
1.1b:
  • Removed some outdated functions and documentation
  • Changed how firstPan works slightly (thanks to Fled)
1.1:
  • Reworked most of the code
  • Fixed some bugs in the interpolation functions
  • Added a "firstPan" value to the Use-functions allowing smoother activation
  • Moved the keyboard functions to seperate plugins
  • Both cameras now feature several public functions and variables that can be accessed by the user at any time
  • Added a more detailed manual
  • Terrain sampling interval is now a constant
  • Angle above terrain is now a constant
  • Interpolation of DISTANCE and OFFSET values is now capped to the given values
1.0b:
  • Added text macros to shorten the code significantly
  • Added a list of functions that should not be redeclared
  • Both libraries now require "OppiCamBase"
  • Basic functionality still the same





Tutorial: How to set up OppiCam if you don't know jack about Jass
For some reason this system is attracting a lot of GUI users who really want to use this system but don't know anything or just very few about (v)Jass. The following tutorial is intended to help these people setting up an OppiCam in their map.
1. Meeting the requirements
In order to use this or any vJass script you need a vJass compiler that converts vJass code into standard Jass so wc3 can read it. The most common vJass compiler out there is JassHelper. However I recommend the tool pack Jass NewGen Pack which includes JassHelper and several other useful mapping tools (like TESH for Jass syntax highlighting). Just extract the Jass NewGen Pack somewhere and run NewGen WE.exe instead of the normal worldeditor.
Keep in mind that from now on you will always have to save your map before you can test it as long as you are using vJass in your map.


2. Copying the libraries
Libraries are chunks of vJass code that add new functionality to your map. Usually libraries are placed in "triggers".
Disclaimer: When speaking of "triggers" here I actually mean what GUI users call a trigger. These are not actual trigger objects as in local trigger t = CreateTrigger().
To do so open your trigger editor and create a new trigger (ctrl + T). Click on this new trigger and convert it into Jass by clicking Edit -> Convert to custom text. Your trigger should now looks like this (names may vary):
Collapse JASS:
function Trig_YourTrigger_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_YourTrigger takes nothing returns nothing
    set gg_trg_YourTrigger = CreateTrigger(  )
    call TriggerAddAction( gg_trg_YourTrigger, function Trig_YourTrigger_Actions )
endfunction

Delete all of it so you have an empty trigger in custom script format. Now copy the script you want to use from above and paste it into this trigger. Make sure you copy everything from and including library to endlibrary.
You can now call functions and variables that are provided by the copied library. Rinse and repeat for every library you want to copy.
Hint: If you downloaded the demo map you can just directly copy its libraries to your map by copying the according triggers.
For the main functionality of the third person cam you only need the library ThirdPersonCam. If you also want keyboard control for the camera you also need TPCKeyboardPlugin and Anitarf's ArrowKeys.
For the fixed cam it's FixedCam and FCKeyboardPlugin + ArrowKeys respectively.
You can also copy the function and variable lists if you need them. If you do, make sure you don't have those activated as they're just for documentation.


3. Activating the camera
After only copying the libraries you won't notice any difference ingame as this system doesn't activate itself. For the camera to be activated you have to set up a trigger that determines when the camera should be used.
For this you need custom script i.e., a line of Jass in your GUI trigger. The functions you need for activation are call EnableThirdPersonCam(Player, Unit, PanDuration) for the third person camera and call EnableFixedCam(Player, Unit, X, Y, Z, PanDuration) for the fixed camera respectively (X and Y are absolute coordinates for the camera whereas Z determines the camera's offset above the terrain).
Example:
In this example I set up a trigger that activates a third person cam as soon as a hero enters a region, attaching the camera to that hero.
Trigger:
ActivateThirdPersonCam
Collapse Events
Unit - A unit enters YourRegion <gen>
Collapse Conditions
((Entering unit) is A Hero) Equal to True
Collapse Actions
Set CameraUnit = (Entering unit)
Set CameraPlayer = (Owner of CameraUnit)
Custom script: call EnableThirdPersonCam(udg_CameraPlayer, udg_CameraUnit, 0)
Instead of creating two new variables you can also directly use the Jass functions GetOwningPlayer(GetEnteringUnit()) and GetEnteringUnit() if you know how to use them. In the custom script part you can use a different value for 0 if you don't want the camera to be activated instantly. A value of 2 for example will let the camera slowly pan to the unit over 2 seconds.
Naturally you can use any type of event, conditions and actions here.

4. Deactivating the camera
You may want to deactivate the camera at a certain point in the game. If not, just skip this step.
To deactivate either of the cameras for a player you just have to pass a null unit to the enabling function i.e., call EnableThirdPersonCam(Player, null, 0) for the third person camera and call EnableFixedCam(Player, null, 0, 0, 0, 0) for the fixed camera.
Example:
In this example I deactivate the camera as soon as a player's (previously in a variable stored) hero dies.

Trigger:
DeactivateThirdPersonCam
Collapse Events
Unit - A unit dies
Collapse Conditions
(Dying unit) Equal to Hero[(Player number of (Owner of (Dying unit)))]
Collapse Actions
Set CameraPlayer = (Owner of (Dying unit))
Custom script: call EnableThirdPersonCam(udg_CameraPlayer, null, 0)

5. Calibrating the camera
There are several constants that change the camera's look and feel. You find a full list and descriptions in the function and variable list above under "private constants".
To change them, just replace their value in the according script.
Example:
In this example I feel like the third person camera is too low and should be higher. So I go to the ThirdPersonCam library's calibration section (labeled "CONSTANTS") and look for a constant that might change this:
Collapse JASS:
        //*****************************************************************************
        // CONSTANTS:
        private constant real DISTANCE_AOA_MIN      = -15
        private constant real DISTANCE_DISTANCE_MIN = 300
        private constant real DISTANCE_AOA_MAX      = -65
        private constant real DISTANCE_DISTANCE_MAX = 500
        private constant real OFFSET_AOA_MIN        = -35
        private constant real OFFSET_OFFSET_MIN     = 0
        private constant real OFFSET_AOA_MAX        = -70
        private constant real OFFSET_OFFSET_MAX     = 150
        
        private constant real Z_OFFSET            = 100
        private constant real TIMEOUT             = 0.1
        private constant real TERRAIN_SAMPLING    = 32
        private constant real PAN_DURATION        = 0.5
        private constant real ANGLE_ABOVE_TERRAIN = 15
        private constant real DEFAULT_AOA         = -20
        private constant real DEFAULT_ROT         = 0
        private constant real FIELD_OF_VIEW       = 120
        private constant real FAR_Z               = 5000
        private constant real CLIFF_DISTANCE      = 500
        //
        //*****************************************************************************
In this case the constant I'm looking for is Z_OFFSET, so I just increase it:
Collapse JASS:
        private constant real Z_OFFSET            = 200

6. Learn Jass!
Ok, not quite necessary but still recommended.

7. Profit!
You are now (hopefully) able to set up an OppiCam in your map and enjoy the intense pleasures of this wondrous system. And if you're kind, you even give credits.
Attached Images
File Type: jpg OppiCamThumb.jpg (124.8 KB, 1600 views)
Attached Files
File Type: w3x OppiCam1.2d.w3x (46.7 KB, 787 views)

Last edited by Opossum : 07-19-2010 at 03:10 PM.
Opossum is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 03-01-2009, 02:27 AM   #2
Joker
User
 
Joker's Avatar
 
Join Date: Sep 2006
Posts: 687

Joker will become famous soon enough (32)Joker will become famous soon enough (32)

Default

Post the trigger.
Joker is offline   Reply With Quote
Old 03-01-2009, 02:39 AM   #3
Veev
User
 
Join Date: Nov 2006
Posts: 199

Veev will become famous soon enough (63)Veev will become famous soon enough (63)Veev will become famous soon enough (63)

Default

Hmm. Very nice. I really liked this camera, especially being able to change the angle of attack so fluidly. The fixed camera is also pretty cool, it really did remind me of RE. Nice work.

It behaves a little strangely near cliffs. I was able to get the camera to continuously try to give me view of my footman. The white box on the minimap was constantly resizing and it caused my camera to sort of rotate around until I moved him away from the cliff. Granted, I had to really try to get the camera to bug out a little.
Veev is offline   Reply With Quote
Old 03-01-2009, 06:33 AM   #4
Anopob
Yay!
 
Anopob's Avatar
 
Join Date: May 2006
Posts: 870

Anopob has a spectacular aura about (120)

Default

This is incredible. I just tried both cameras recently and I find it very fluid and non-chunky like. Even tho as Veev said it is buggable (to an extent), it's still very rare in a normal game. +rep for awesome system
__________________
Give +REP to people who help you with things or make good resources.
Better Ways of Requesting For Help - Race Contest #2 WIP Thread (Bone Race)
As of December 1st I have my blue rep. Yay :D
Anopob is offline   Reply With Quote
Old 03-01-2009, 07:28 AM   #5
MaD[Lion]
MaD Da ViNci
 
MaD[Lion]'s Avatar


Respected User
 
Join Date: Apr 2003
Posts: 1,699

Submissions (10)

MaD[Lion] is a jewel in the rough (225)MaD[Lion] is a jewel in the rough (225)MaD[Lion] is a jewel in the rough (225)MaD[Lion] is a jewel in the rough (225)

Default

very nice system, havent tried it but is it possible to make it rotate to the footman's facing angle slower? tat would be awesome. (or not rotate at all)
__________________
Current Projects:
MaDOS (outdated)System for object movements & effects - NEW VERSION IS UNDER W.I.P
Cinematic SystemSystem for making better cinematics and with fancy effects
Timing SystemTiming system that simulates the usage of PolledWait just with 0.01 accuracy
MaD[Lion] is offline   Reply With Quote
Old 03-01-2009, 08:43 AM   #6
Fledermaus
default string
 
Fledermaus's Avatar
 
Join Date: May 2006
Posts: 702

Submissions (1)

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

Send a message via MSN to Fledermaus
Default

Quote:
Originally Posted by MaD[Lion]
very nice system, havent tried it but is it possible to make it rotate to the footman's facing angle slower? tat would be awesome. (or not rotate at all)
PAN_DURATION: Duration over which camera fields are set periodically. Higher values make the camera movements smoother but also slower.

If you want to make it not rotate at all you'll have to modify the system yourself (as I have done) because Oppi hates it not rotating/panning so he didn't include that option into the system. Although I think he should.
Either comment out the lines call SetCameraField(CAMERA_FIELD_ROTATION, GetUnitFacing(Unit[p]) + ControlableRot[p], PAN_DURATION) for rotation and call PanCameraToTimed(panx, pany, PAN_DURATION) for panning.
The other option is to add a boolean array check for each player so that they can turn it on/off whenever they want to (which is what I do).

Last edited by Fledermaus : 03-01-2009 at 08:51 AM.
Fledermaus is offline   Reply With Quote
Old 03-01-2009, 12:29 PM   #7
Opossum
User
 
Opossum's Avatar
 
Join Date: Mar 2008
Posts: 205

Submissions (4)

Opossum will become famous soon enough (59)Opossum will become famous soon enough (59)

Default

Weee first: thanks for the positive feedback :)

Quote:
Originally Posted by Joker
Post the trigger.
Tried to but when trying to edit the code in and saving the edit the post just shows up the same as before... Also when I now try to edit the post again the edit box shows up empty as if I've written nothing. Bug on the forums maybe?
Edit: Added the ThirdPersonCam code. Strangely I can only post one of the libraries. Is there any character limit for posts?

Quote:
Originally Posted by Veev
It behaves a little strangely near cliffs. I was able to get the camera to continuously try to give me view of my footman. The white box on the minimap was constantly resizing and it caused my camera to sort of rotate around until I moved him away from the cliff. Granted, I had to really try to get the camera to bug out a little.
What do you mean by "sort of rotate around"? You mean vertically? The anti-clipping part of the camera isn't perfect because it would just be too performance-heavy if I was making it 100% safe. But I think it works as it is as long as you don't really try to bug the cam :).

Quote:
Originally Posted by MaD[Lion]
very nice system, havent tried it but is it possible to make it rotate to the footman's facing angle slower? tat would be awesome. (or not rotate at all)
Fleddy is right. Try PAN_DURATION.

Quote:
Originally Posted by Fledermaus
If you want to make it not rotate at all you'll have to modify the system yourself (as I have done) because Oppi hates it not rotating/panning so he didn't include that option into the system. Although I think he should.
I hate it because it cripples the system. If you don't want the camera to rotate or even pan to the unit you wouldn't need a system like that. And I will still try to track down and kill everyone that tries to "unlock" that cam (same still applies to you Fled).

Last edited by Opossum : 03-01-2009 at 04:27 PM.
Opossum is offline   Reply With Quote
Old 03-01-2009, 04:52 PM   #8
Veev
User
 
Join Date: Nov 2006
Posts: 199

Veev will become famous soon enough (63)Veev will become famous soon enough (63)Veev will become famous soon enough (63)

Default

Yes, by "sort of rotate around" it was moving around my hero in a circular fashion, trying to find him. But, like I said, the camera was only doing that because I was trying pretty hard to bug it out.
Veev is offline   Reply With Quote
Old 03-02-2009, 12:50 AM   #9
fX_
User
 
fX_'s Avatar
 
Join Date: Jan 2007
Posts: 528

Submissions (2)

fX_ will become famous soon enough (38)fX_ will become famous soon enough (38)

Default

It's nice but it doesn't work for flying units about a cliff (I changed the subject to an Orc Wind Rider).
fX_ is offline   Reply With Quote
Old 03-02-2009, 05:29 PM   #10
Opossum
User
 
Opossum's Avatar
 
Join Date: Mar 2008
Posts: 205

Submissions (4)

Opossum will become famous soon enough (59)Opossum will become famous soon enough (59)

Default

Quote:
Originally Posted by fX_
It's nice but it doesn't work for flying units about a cliff (I changed the subject to an Orc Wind Rider).
Could you elaborate "doesn't work" a bit?
I just tried a gryphon rider and it worked fine. Ok Blizzard's flying height smoothing causes GetUnitFlyHeight to not always return the correct flying height but that can easily be solved by adding 'Arav' to the unit. Although adding that to the system would limitate the user quite a bit imo so I left it out.
Opossum is offline   Reply With Quote
Old 03-03-2009, 12:25 AM   #11
fX_
User
 
fX_'s Avatar
 
Join Date: Jan 2007
Posts: 528

Submissions (2)

fX_ will become famous soon enough (38)fX_ will become famous soon enough (38)

Default

ya the smoothing

just thought id inform you so you can ameliorate it if you wanted
fX_ is offline   Reply With Quote
Old 03-03-2009, 03:49 PM   #12
Opossum
User
 
Opossum's Avatar
 
Join Date: Mar 2008
Posts: 205

Submissions (4)

Opossum will become famous soon enough (59)Opossum will become famous soon enough (59)

Default

Updated the map and main post.
Somebody should've told me about text macros a long time ago. <3 them :)
Opossum is offline   Reply With Quote
Old 03-03-2009, 04:07 PM   #13
Anitarf
Procrastination Incarnate


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

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

I have only looked at the third person camera code so far. My thoughts:

You have a lot of unneeded function calls, functions which you only call once and could easily inline, getting rid of code bloat such as the loc struct. You could argue that the functions are there to help organize the code and make it more readable but since your code is entirely uncommented you can hardly use readability as an argument.

GetHighestPointInLine isn't really what you need here. You need the highest angle of attack, a lower point that's closer to the unit could get more in the way of the camera than a slightly higher point that's further away.

I disagree with the hardcoded reset command, especially because it uses the clumsy and ugly chat messages. Instead you should provide a public function for resetting the orientation and let the user decide how and when to run it with external code. In fact, you could do the same for all camera controls, moving all the arrow keys code to an optional module.

I think it would be better if you only interpolated values between OFFSET_AOA_1 and 2, while if the angle is greater than OFFSET_AOA_1 the offset would simply get capped at OFFSET_OFFSET_1. That way you would also no longer need the OFFSET_LIMIT constant.

The code isn't quite streamlined yet, I already mentioned the unneeded function calls, then there's some unneeded local variables etc. You could further reduce the code bloat by using some external libraries such as ArrowKeys.

Edit: I was reminded of a function for getting a precise camera z offset that Toadcop posted in this thread. I see you use a different method, I haven't been able to test how it works yet so I don't know if either of the two methods is better than the other, I just thought I'd link to it for your consideration. I notice you smooth out your camera movement anyway while that function was designed to get an instant camera lock so it might not apply.
__________________
Anitarf is offline   Reply With Quote
Old 03-03-2009, 04:59 PM   #14
Opossum
User
 
Opossum's Avatar
 
Join Date: Mar 2008
Posts: 205

Submissions (4)

Opossum will become famous soon enough (59)Opossum will become famous soon enough (59)

Default

Quote:
Originally Posted by Anitarf
Edit: I was reminded of a function for getting a precise camera z offset that Toadcop posted in this thread. I see you use a different method, I haven't been able to test how it works yet so I don't know if either of the two methods is better than the other, I just thought I'd link to it for your consideration. I notice you smooth out your camera movement anyway while that function was designed to get an instant camera lock so it might not apply.
That link doesn't work for me but what I am doing to get a precise camera z offset is pretty simple once you figured out the code:
GetCameraField(CAMERA_FIELD_ZOFFSET) - ((GetCameraTargetPositionZ() - GetLocationZ(Loc)) - (Z_OFFSET + GetUnitFlyHeight(Unit[p])))
First I get (GetCameraTargetPositionZ() - GetLocationZ(Loc) This returns the actual difference between the camera target's z (that includes the camera's smoothened z-offset) and the terrain height of the target location. So basically what this returns is the camera's real z offset. Let's call this RealZ.
Then I get the difference between RealZ and the actual z offset I want:
RealZ - (Z_OFFSET + GetUnitFlyHeight(Unit[p])) (let's set the flying height to 0 to simplify it a bit). Let's call this value "TooHigh"
If this value is positive then the camera is actually too high meaning I have to reduce the current "non-real" but controlable z offset by this difference.
GetCameraField(CAMERA_FIELD_ZOFFSET) - TooHigh
That's it... not that complicated imo.

Quote:
Originally Posted by Anitarf
You have a lot of unneeded function calls, functions which you only call once and could easily inline, getting rid of code bloat such as the loc struct. You could argue that the functions are there to help organize the code and make it more readable but since your code is entirely uncommented you can hardly use readability as an argument.

GetHighestPointInLine isn't really what you need here. You need the highest angle of attack, a lower point that's closer to the unit could get more in the way of the camera than a slightly higher point that's further away.
True and that's exactly what the function does. Its name is just a bit outdated. Sorry about that. Guess I'll just inline that function into the camera function anyway. That will also get rid of the struct as you've mentioned.

Quote:
Originally Posted by Anitarf
I disagree with the hardcoded reset command, especially because it uses the clumsy and ugly chat messages. Instead you should provide a public function for resetting the orientation and let the user decide how and when to run it with external code. In fact, you could do the same for all camera controls, moving all the arrow keys code to an optional module.
That's actually a pretty good idea. I'll give it a try.

Quote:
Originally Posted by Anitarf
I think it would be better if you only interpolated values between OFFSET_AOA_1 and 2, while if the angle is greater than OFFSET_AOA_1 the offset would simply get capped at OFFSET_OFFSET_1. That way you would also no longer need the OFFSET_LIMIT constant.
This might be another good idea. I guess I'll keep my original method for the DISTANCE values though as it's easier for the user to adjust the camera settings then.

Quote:
Originally Posted by Anitarf
The code isn't quite streamlined yet, I already mentioned the unneeded function calls, then there's some unneeded local variables etc. You could further reduce the code bloat by using some external libraries such as ArrowKeys.
I don't think I need such a complex library featuring key sequences and stuff. And many local variables that seem unneeded at first (like angleofattack in ApplyCam) are just used to reduce function calls. Although I'm not entirely sure if that's always the fastest way.
Edit: Ok I just noticed unitx, unity, panx, pany are actually not absolutely necessary. I'll watch out more for stuff like that.

Anyway: thanks for having a look at it :).

Last edited by Opossum : 03-03-2009 at 05:18 PM.
Opossum is offline   Reply With Quote
Old 03-03-2009, 08:08 PM   #15
Viikuna-
User
 
Viikuna-'s Avatar
 
Join Date: Feb 2009
Posts: 203

Viikuna- will become famous soon enough (44)Viikuna- will become famous soon enough (44)

Default

Toadcops CamZ function just brakes Blizzards stupid camera smoothning, so you can get more accurate Z heigths.
It has caused some weird bugs to me, but you should maybe test it and see if it is better for you.

Collapse JASS:
public function CamZ takes real z returns nothing
  set z=GetCameraField(CAMERA_FIELD_ZOFFSET)+z-GetCameraTargetPositionZ()
  call SetCameraField(CAMERA_FIELD_ZOFFSET,z,-0.01)
  call SetCameraField(CAMERA_FIELD_ZOFFSET,z,0.01)
endfunction

Anyways, this system is awesome. Nice work.
__________________
No Marlo, no game.

Last edited by Viikuna- : 03-03-2009 at 08:13 PM.
Viikuna- 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 01:41 AM.


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

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