the SERVER scripting thread

"eventually EUO will get to the point its (sic) unplayable" - Dudle

Moderator: EUO Moderators

Re: LUA, server API, etc

Postby eggmceye » Mon Nov 05, 2012 5:35 pm

ok der, try it now :fp:
User avatar
eggmceye
hello
 
Posts: 10103
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia

Re: LUA, server API, etc

Postby mud » Mon Nov 05, 2012 9:33 pm

Looks perfect, thanks again.
mud
I once posted in TFIOOC!
 
Posts: 132
Joined: Wed Nov 09, 2005 12:29 pm

Re: LUA, server API, etc

Postby LordMortiferus » Fri Nov 16, 2012 3:37 am

Is there a way to suppress switches from being set back to their original state after a certain time? I hoped making 299 an instance would do the trick but it still is the same problem - you activate a switch and when you get to the barrier several minutes later it is active again. If not I am going to code the switches with LUA.
Otherwise I am nearly done fixing 299.
User avatar
LordMortiferus
MACRO > me
 
Posts: 818
Joined: Tue Dec 01, 2009 10:23 pm

Re: LUA, server API, etc

Postby mud » Sat Aug 17, 2013 10:51 am

Is there a way to import LUA modules into the environment used by auxbox? I was going to do some networking type stuff if possible. I tried just installing Lua in windows and "require" the modules I wanted in an auxbox script, but it didn't seem to work no matter what I did. I also tried dofile and such.
mud
I once posted in TFIOOC!
 
Posts: 132
Joined: Wed Nov 09, 2005 12:29 pm

Re: LUA, server API, etc

Postby eggmceye » Sat Aug 17, 2013 2:54 pm

I've never tried imports via lua ... however ... and this may or may not help
but all the lua files in the clientscripts dir will be loaded - so if there is some custom lua lib you want then it can be pasted in there - but if you want the native lua libs then I'm not so sure.

I just checked server lua source - having a flashback to lua libs - and I did at one point have them in there but are currently commented out. I wonder now if you even need to import anything? I know for sure string and table don't need any imports - I use them both in server lua funcs.

Code: Select all
void start_lua() {
   
   lua_register( pLuaState, "_ALERT", luaError );

   //luaopen_io(pLuaState); // provides io.*
   //luaopen_base(pLuaState);
   //luaopen_table(pLuaState);
   //luaopen_string(pLuaState);
   //luaopen_math(pLuaState);
   //luaopen_loadlib(pLuaState);

   luaL_openlibs(pLuaState);
   
#ifdef MAPED
   tolua_maped_open (pLuaState);
#else
   tolua_eao_open (pLuaState);
#endif
   register_funcs();


   lua_pushstring(pLuaState, "LUA_PATH");
   lua_pushstring(pLuaState, "scripts/?.lua;scripts.maps/?.lua;scripts/includes/?.lua");
   lua_settable ( pLuaState, LUA_GLOBALSINDEX );

   writelog("load scripts rc=%d", load_scripts());
}
User avatar
eggmceye
hello
 
Posts: 10103
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia

Re: LUA, server API, etc

Postby mud » Sat Aug 17, 2013 3:30 pm

Ah, cool, thanks for the info. I'll see if I can get it to work with a bit more fiddling.
mud
I once posted in TFIOOC!
 
Posts: 132
Joined: Wed Nov 09, 2005 12:29 pm

Re: the scripting thread

Postby eggmceye » Tue Sep 24, 2013 3:04 pm

Attaching a script to a spawner
eg to level the mob up or make it heroic
(not that heroic maps use a different system tho the script is the same)

First:
the spawner in maped
note the script field down the bottom - mkEvil
(note also that this screenshot is from future version of maped)
spawner.png


Then here is the attached script (which you put in a lua file)
Code: Select all
function mkEvil(m)

 m:set_align(ALIGN_EVIL)
end



More scripts:
Heroic and Epic: note use of secondary helper funcs

Code: Select all
function spawnerHeroic(m)
   
   scaleCreature(m, 3, 4);
end

function spawnerEpic(m)

   scaleCreature(m, 5, 6);
end

-- m is the monster pointer
-- c is the scale (or Constant) (so heroic is 3x HP etc, epic is 5x)
-- qbump will add on quality to the loot
function scaleCreature(m, c, qBump)

   m.hp=m.hp*c
   m:set_hp_max(m.hp)
   m.xp_kill=m.xp_kill*c
   m:set_thaco((m:get_thaco()-10)*c+10)
   m:set_base_ac(m:get_ac()+10*(c-1))
   m:set_intel(m:get_intel()*c)
   m:set_magic_resistance(m:get_magic_resistance()*c)
   m:set_dmg_dice(m:get_dmg_dice()*c/2)
   
   q=m.treasure/0x10000
   q=q+qBump
   if q>15 then q=15 end
   m.treasure=m.treasure&0xffff+q*0x10000
   
   m.tamable=false

end
You do not have the required permissions to view the files attached to this post.
User avatar
eggmceye
hello
 
Posts: 10103
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia

Re: the scripting thread

Postby Bugbo » Tue Oct 15, 2013 5:11 am

Would it be possible to script a slime that wouldn't have the ability to divide? (in case you wanted to make a room filled with 20 of them for example)
Bugbo
Girls only want boyfriends who have great skills.
 
Posts: 790
Joined: Thu May 06, 2004 8:45 pm
Location: Oregon!

Re: the scripting thread

Postby eggmceye » Tue Oct 15, 2013 9:11 am

hmm - the split code is done in a common monster death func - so somehow you would have to flag the slime via a spawn script such that the death func then knows not to split it when it dies ...
User avatar
eggmceye
hello
 
Posts: 10103
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia

Re: the scripting thread

Postby LordMortiferus » Tue Oct 15, 2013 4:37 pm

The deathfunction checks if the monster mcode is 0x21a or not.
Code: Select all
elseif mob_code==0x21a then

Hence, it should be enough to use a lightly tinted slime e.g. 0xfff21a. See triple hex code for reference on tinting.
User avatar
LordMortiferus
MACRO > me
 
Posts: 818
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Postby eggmceye » Tue Oct 15, 2013 7:58 pm

ha good one
User avatar
eggmceye
hello
 
Posts: 10103
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia

Re: the scripting thread

Postby Celerion » Mon Nov 25, 2013 10:23 am

eggmceye wrote:Well you can't really develop scripts because you can't test them because you don't have a dev server! and I'm not giving away dev servs unless I do a stripped down version everyone can have (in far future)

Ok, so map-development with Lua-scripts still seems to be stuck a bit, right? Or is some kind of test-server, that only runs on the local machine (without source-code, of course) already available? Just curious ...

Edit: Maybe it would be a good idea to give up the client/server-architecture for test-playing at all and put it all into just one local application. Or even better to integrate it all into MapEd.
To me, "Play the map (including scripts)" seems to be an essential function to MapEd. Although people seem to have been able to write maps without it in the past. Hard to believe. ;)
Celerion
I'll be going back to Tibia next week.
 
Posts: 49
Joined: Sat Dec 15, 2012 6:29 am

Re: the scripting thread

Postby LordMortiferus » Wed Feb 19, 2014 2:34 am

Simple Lua script for using mount sprites or Stone Guardian tiles as a mob avatar that faces in both directions depending on its target.
Code: Select all
-- this is an example function added to the spawner!
function your_function_here(m)
   m.code = 0xdf0043d -- e.g. stone guardian
   flip(m) -- calls the function below
end

-- this is the actual function that will define which sprite is used for facing left/right
function flip(m)
   -- var to track movement
   move_x = m.x
   
   -- for stone guardian
   if m.code == 0xdf0043d or m.code == 0xe00043d then
    flip_left = 0xe00043d
    flip_right = 0xdf0043d
   
   -- for mounts
   else
     local dir_array = {
         {"447","446"}, -- white horse
         {"44a","449"}, -- raft
         {"44d","44c"}, -- cow
         {"53e","53d"}, -- browne horse
         {"540","53f"}, -- bunny
         {"542","541"}, -- drake
         {"544","543"}  -- turkey
         -- dyable drake is missing
         }
     -- m.code is formated into a string and then splitted into 2 parts for sprite and attributes
    local string_code = string.format("%x",tostring(m.code))
    local string_len = string.len(string_code)
    local sprite_cache = string.sub(string_code, string_len-2, string_len)
    local attrib_cache = string.sub(string_code, 1, string_len-3)

   -- compare the m.code with the codes within the array
      for i,v in ipairs(dir_array) do
         for k,w in ipairs(v) do
            if sprite_cache == w then
             -- set code for left and right mob including the attributes and convert it back to hex number
               flip_left  = tonumber(string.format("%s%s",attrib_cache,v[1]),16)
             flip_right = tonumber(string.format("%s%s",attrib_cache,v[2]),16)
            end
         end
      end
   end
   
   -- if left and right is set a heartbeat function is called
   if flip_left ~= nil and flip_right ~= nil then
    m.heartbeat_func="flip_actions"
   end
end

-- this heartbeat function is called every 1s to change the sprite if needed
-- cause it is a heartbeat func there is a slight delay between mob movement and sprite change
function flip_actions(m)
   
   local tgt
   
   -- if the mob has a target it will face this target
   if m.target_id >= 0 then
      tgt=get_sent_ptr(m.target_id)
      if tgt.x < m.x and m.code ~= flip_left then
       m.code = flip_left
       update_everyones_fov(m)
       elseif tgt.x > m.x and m.code ~= flip_right then
       m.code = flip_right
       update_everyones_fov(m)
         elseif tgt.x == m.x and tgt.y < m.y and m.code ~= flip_right then
       m.code = flip_right
       update_everyones_fov(m)
       elseif tgt.x == m.x and tgt.y > m.y and m.code ~= flip_left then
       m.code = flip_left
       update_everyones_fov(m)
      end
   
   -- if the mob moves without a target it faces the direction of movement
   elseif m.target_id < 0 and m.x ~= move_x then
      if m.x < move_x and m.code ~= flip_left then
       m.code = flip_left
       update_everyones_fov(m)
       elseif m.x > move_x and m.code ~= flip_right then
       m.code = flip_right
       update_everyones_fov(m)
      end
    move_x = m.x
   end
end

Also works with mounts.
Updated the script to make it more versatile.
You do not have the required permissions to view the files attached to this post.
Last edited by LordMortiferus on Mon Feb 24, 2014 1:56 am, edited 1 time in total.
User avatar
LordMortiferus
MACRO > me
 
Posts: 818
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Postby 1[WoWz] » Wed Feb 19, 2014 2:57 am

Very nice LordM, that looks really cool. A horse mounted/drake mounted special mob would be terrifying.
IGN: 1
User avatar
1[WoWz]
MACRO > me
 
Posts: 828
Joined: Wed Apr 21, 2004 6:48 pm
Location: Southern California, USA

Re: the scripting thread

Postby Keighn » Wed Feb 19, 2014 6:21 am

Can you make my house do this?
Image

That's either a mole or a hippoman.. I don't know but its gray and scary.
User avatar
Keighn
Stop posting already --;
 
Posts: 4965
Joined: Sat Jun 26, 2004 10:13 am
Location: Exiled fucking again.... Georgia ATM!!!

Re: the scripting thread

Postby CinisterD » Wed Feb 19, 2014 9:09 am

1[WoWz] wrote:Very nice LordM, that looks really cool. A horse mounted/drake mounted special mob would be terrifying.



I've had an idea for this some time now. I just need to team up with someone that can do the scripting.

I can do Map'd easy, but what I want to do is a bit beyond my talents. Unfortunately Shrike is not around so I'm stuck :/
User avatar
CinisterD
Here for the lesbians.
 
Posts: 428
Joined: Mon Jul 06, 2009 6:26 pm

Re: the scripting thread

Postby LordMortiferus » Mon Feb 24, 2014 2:03 am

Updated the script above to make it more versatile. Hope that will be helpful for Cin's project. I will try to include ships/chairs(?!) in a future update - already got a working concept on deciding how to determine if the mob has to face north, south, east or west by rotating a fictional Cartesian coordination system around the mob :lesson: (don't care about broad side firing though).
User avatar
LordMortiferus
MACRO > me
 
Posts: 818
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Postby Celerion » Mon Mar 03, 2014 3:35 am

Keighn wrote:Can you make my house do this?-
That's either a mole or a hippoman.. I don't know but its gray and scary.

:) That's Monty, a mole I think. (Looks a lot like the great "Jet Set Willy", if you ask me; there's also a PC-remake of it (JSW)).
Anyway, unfortunately this can't be done to houses, because of the perspective: You look at these Monty-platforms from the side, while you look at EUO from above (kind of). Though in real top-view you would always see the top of the heads, like in Alien Breed. That's not optimal either.
Now I feel dizzy.
Celerion
I'll be going back to Tibia next week.
 
Posts: 49
Joined: Sat Dec 15, 2012 6:29 am

Re: the scripting thread

Postby LordMortiferus » Mon Mar 03, 2014 4:25 am

@cel:
As a start you could calculate the server time using os.date("!*t") to get UTC/GMT and then add 11 hours or so for Sydney time. Also refer to lua-users/timezones and lua manual on os.date and lua-users/os_lib_tutorial.

Edit: Would actually be nice to have an auxbox line that just tells you the time in down-under for scavenger/statue hunt.
User avatar
LordMortiferus
MACRO > me
 
Posts: 818
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Postby LordMortiferus » Sun Mar 09, 2014 7:40 am

Below is a code that can be used to copy a rectangular part of a map and paste it somewhere else. The code is executed by Maped when the appropriate level is loaded. You will find instructions for the script here

Big thanks to the community of lua-users.org for creating a function to export and import tables, as well as explode. Thanks to ched for helping me out with this and to egg for his feedback.

Either copy the script below into an empty text file and save it with the .lua extension or grab it straight from my dorpbox.

Code: Select all
-- 2014/09/03 LordMortiferus & Chedich
-- Copy and Paste function for Maped

function load_map_666(lvl, load_on_the_fly) -- change map number if needed

   local func = "copy" -- copy or paste
   local xy_start = {0,0} -- {x,y} -- needs to be set for copy or paste
   local xy_end = {30,30} -- {x,y} -- only essential for copy

   local cp_tiles = true -- set to false if you do not want to copy&paste tiles
   local cp_items = true -- set to false if you do not want to copy&paste items
   local cp_feat = true -- set to false if you do not want to copy&paste features
   local cp_spawner = true -- set to false if you do not want to copy&paste spawners

   
   local table_index = 1
   
   map=get_map_ptr(lvl)

   -- copy function
   if func == "copy" then
   
      -- egg mod: dont crap out if the end coords are bigger than map size: instead, cap them and continue
      if xy_end[1]>=map:get_max_x() then xy_end[1]=map:get_max_x()-1; end
      if xy_end[2]>=map:get_max_y() then xy_end[2]=map:get_max_y()-1; end
      
      if xy_start[1] >= 0 and xy_end[1] > xy_start[1] and xy_end[1] < map:get_max_x() and
         xy_start[2] >= 0 and xy_end[2] > xy_start[2] and xy_end[2] < map:get_max_y() then
      
       local chunk_size = {}
       table.insert (chunk_size, xy_end[1]-xy_start[1])
       table.insert (chunk_size, xy_end[2]-xy_start[2])
       table.save(chunk_size, "chunk_size.lua")
      
          
          local tile_cache = {}
          local item_cache = {}
          local feat_cache = {}
          local spawner_cache = {}
         
         for yy = xy_start[2] , xy_end[2] do
            for xx = xy_start[1] , xy_end[1] do
               
               if cp_tiles == true then
                  table.insert (tile_cache,map:get_tile(xx,yy))
               end
               
               if cp_items == true then
                  if map:get_item_qty(xx,yy) == 0 then
                    table.insert (item_cache,0)
                   else
                   table.insert (item_cache,map:get_item_code(xx, yy):intVal())
                  end
               end
               
               if cp_feat == true then
                  if map:get_feat_typ(xx, yy) >= 0 and map:get_feat_typ(xx, yy) ~= 4 then
                   table.insert (feat_cache,string.format("%d$%d$%d$%d$%d$%s",map:get_feat_typ(xx,yy)
                                                               ,map:get_feat_val(xx,yy)
                                                               ,map:get_feat_dest_x(xx,yy)
                                                               ,map:get_feat_dest_y(xx,yy)
                                                               ,map:get_feat_itm(xx,yy):intVal()
                                                               ,map:get_feat_text(xx,yy)))
                    else
                   table.insert (feat_cache,"nil")
                  end
               end
               
               if cp_spawner == true then
                  if map:get_feat_typ(xx, yy) == 4 then
                     local cp_spawner = map:get_spawner(xx, yy)
                     table.insert(spawner_cache,string.format("4$%d$%d$%d$%d$%d$%d$%s",cp_spawner.sp_type,
                                                                          cp_spawner.sp_code,
                                                                       cp_spawner.sp_rate,
                                                                       cp_spawner.sp_max,
                                                                       cp_spawner.sp_area,
                                                                       cp_spawner.quest_id,
                                                                       cp_spawner.script))
                  else
                   table.insert (spawner_cache,"nil")
                  end
               end
            end
         end
      
         if cp_tiles == true then
          table.save(tile_cache, "tile_cache.lua")
         end
         if cp_items == true then
          table.save(item_cache, "item_cache.lua")
         end
         if cp_feat == true then
          table.save(feat_cache, "feat_cache.lua")
         end
         if cp_spawner == true then
          table.save(spawner_cache, "spawner_cache.lua")
         end
      end
      
   -- paste function
   elseif func == "paste" then
   
     tile_cache = table.load("tile_cache.lua")
     item_cache = table.load("item_cache.lua")
     feat_cache = table.load("feat_cache.lua")
     spawner_cache = table.load("spawner_cache.lua")
     chunk_size = table.load("chunk_size.lua")
      
      if chunk_size ~= nil and
         xy_start[1] >= 0 and xy_start[1]+chunk_size[1] < map:get_max_x() and
         xy_start[2] >= 0 and xy_start[2]+chunk_size[2] < map:get_max_y() then
         for yy = xy_start[2], xy_start[2]+chunk_size[2] do
            for xx = xy_start[1], xy_start[1]+chunk_size[1] do
               
               if cp_tiles == true and tile_cache ~= nil then
                map:set_tile(xx,yy,tile_cache[table_index])
               end
               
               if cp_items == true and item_cache ~= nil then
                map:destroy_pile(xx,yy)
                  if item_cache[table_index] ~= 0 then
                   map:add_item(xx, yy, 1, Item(item_cache[table_index]))
                   local i = item_cache[table_index]
                     if i ~= 0x51b and i ~= 0x51c and i ~= 0x111 then
                        map:set_as_furniture(xx,yy)
                     end
                  end
               end
                           
               if cp_feat == true or cp_spawner == true then
                map:del_feat(xx,yy)
               end
               
               if cp_feat == true and feat_cache ~= nil then
                local feat_data = explode("$",feat_cache[table_index])
                  if feat_data[1] ~= "nil" then
                  map:add_feat(xx, yy, tonumber(feat_data[1]), -- feat type
                                  tonumber(feat_data[2]), -- feat value
                                  tonumber(feat_data[3]), -- feat dest x
                                  tonumber(feat_data[4]), -- feat dest y
                                  Item(tonumber(feat_data[5])), -- feat item
                                  feat_data[6]) -- feat text
                  end
               end
               
               if cp_spawner == true and spawner_cache ~= nil then
                local spawner_data = explode("$",spawner_cache[table_index])
                  if spawner_data[1] ~= "nil" then
                  map.spawners:add(tonumber(spawner_data[2]), -- type
                               tonumber(spawner_data[3]), -- code
                               tonumber(spawner_data[4]), -- rate
                               lvl, -- level
                               tonumber(spawner_data[5]), -- max
                               0, -- currtime ?
                               xx, yy,
                               tonumber(spawner_data[6]), -- area
                               tonumber(spawner_data[7]), -- quest id
                               spawner_data[8]) -- script
                  map:add_feat(xx,yy,SPAWNER)
                  end
               end
             table_index = table_index + 1
            end
         end
      end
   end
end   

--Taken from http://lua-users.org/wiki/SaveTableToFile
--[[
   Save Table to File
   Load Table from File
   v 1.0
   
   Lua 5.2 compatible
   
   Only Saves Tables, Numbers and Strings
   Insides Table References are saved
   Does not save Userdata, Metatables, Functions and indices of these
   ----------------------------------------------------
   table.save( table , filename )
   
   on failure: returns an error msg
   
   ----------------------------------------------------
   table.load( filename or stringtable )
   
   Loads a table that has been saved via the table.save function
   
   on success: returns a previously saved table
   on failure: returns as second argument an error msg
   ----------------------------------------------------
   
   Licensed under the same terms as Lua itself.
]]--

do
   -- declare local variables
   --// exportstring( string )
   --// returns a "Lua" portable version of the string
   local function exportstring( s )
      return string.format("%q", s)
   end

   --// The Save Function
   function table.save(  tbl,filename )
      local charS,charE = "   ","\n"
      local file,err = io.open( filename, "wb" )
      if err then return err end

      -- initiate variables for save procedure
      local tables,lookup = { tbl },{ [tbl] = 1 }
      file:write( "return {"..charE )

      for idx,t in ipairs( tables ) do
         file:write( "-- Table: {"..idx.."}"..charE )
         file:write( "{"..charE )
         local thandled = {}

         for i,v in ipairs( t ) do
            thandled[i] = true
            local stype = type( v )
            -- only handle value
            if stype == "table" then
               if not lookup[v] then
                  table.insert( tables, v )
                  lookup[v] = #tables
               end
               file:write( charS.."{"..lookup[v].."},"..charE )
            elseif stype == "string" then
               file:write(  charS..exportstring( v )..","..charE )
            elseif stype == "number" then
               file:write(  charS..tostring( v )..","..charE )
            end
         end

         for i,v in pairs( t ) do
            -- escape handled values
            if (not thandled[i]) then
           
               local str = ""
               local stype = type( i )
               -- handle index
               if stype == "table" then
                  if not lookup[i] then
                     table.insert( tables,i )
                     lookup[i] = #tables
                  end
                  str = charS.."[{"..lookup[i].."}]="
               elseif stype == "string" then
                  str = charS.."["..exportstring( i ).."]="
               elseif stype == "number" then
                  str = charS.."["..tostring( i ).."]="
               end
           
               if str ~= "" then
                  stype = type( v )
                  -- handle value
                  if stype == "table" then
                     if not lookup[v] then
                        table.insert( tables,v )
                        lookup[v] = #tables
                     end
                     file:write( str.."{"..lookup[v].."},"..charE )
                  elseif stype == "string" then
                     file:write( str..exportstring( v )..","..charE )
                  elseif stype == "number" then
                     file:write( str..tostring( v )..","..charE )
                  end
               end
            end
         end
         file:write( "},"..charE )
      end
      file:write( "}" )
      file:close()
   end
   
   --// The Load Function
   function table.load( sfile )
      local ftables,err = loadfile( sfile )
      if err then return _,err end
      local tables = ftables()
      if tables == nil then return nil end
      for idx = 1,#tables do
         local tolinki = {}
         for i,v in pairs( tables[idx] ) do
            if type( v ) == "table" then
               tables[idx][i] = tables[v[1]]
            end
            if type( i ) == "table" and tables[i[1]] then
               table.insert( tolinki,{ i,tables[i[1]] } )
            end
         end
         -- link indices
         for _,v in ipairs( tolinki ) do
            tables[idx][v[2]],tables[idx][v[1]] =  tables[idx][v[1]],nil
         end
      end
      return tables[1]
   end
-- close do
end

function explode(div,str) -- credit: http://richard.warburton.it
  if (div=='') then return false end
  local pos,arr = 0,{}
  -- for each divider found
  for st,sp in function() return string.find(str,div,pos,true) end do
    table.insert(arr,string.sub(str,pos,st-1)) -- Attach chars left of current divider
    pos = sp + 1 -- Jump past current divider
  end
  table.insert(arr,string.sub(str,pos)) -- Attach chars right of last divider
  return arr
end
Last edited by LordMortiferus on Thu Sep 04, 2014 2:32 am, edited 4 times in total.
User avatar
LordMortiferus
MACRO > me
 
Posts: 818
Joined: Tue Dec 01, 2009 10:23 pm

PreviousNext

Return to EUO Development

Who is online

Users browsing this forum: No registered users and 2 guests