the SERVER scripting thread
Moderator: EUO Moderators
Re: LUA, server API, etc
ok der, try it now
Re: LUA, server API, etc
Looks perfect, thanks again.
- LordMortiferus
- MACRO > me
- Posts: 872
- Joined: Tue Dec 01, 2009 10:23 pm
Re: LUA, server API, etc
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.
Otherwise I am nearly done fixing 299.
Re: LUA, server API, etc
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.
Re: LUA, server API, etc
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.
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());
}
Re: LUA, server API, etc
Ah, cool, thanks for the info. I'll see if I can get it to work with a bit more fiddling.
Re: the scripting thread
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) Then here is the attached script (which you put in a lua file)
More scripts:
Heroic and Epic: note use of secondary helper funcs
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) 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.
-
- Girls only want boyfriends who have great skills.
- Posts: 795
- Joined: Thu May 06, 2004 8:45 pm
- Location: Oregon!
- Contact:
Re: the scripting thread
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)
Re: the scripting thread
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 ...
- LordMortiferus
- MACRO > me
- Posts: 872
- Joined: Tue Dec 01, 2009 10:23 pm
Re: the scripting thread
The deathfunction checks if the monster mcode is 0x21a or not.
Hence, it should be enough to use a lightly tinted slime e.g. 0xfff21a. See triple hex code for reference on tinting.
Code: Select all
elseif mob_code==0x21a then
Re: the scripting thread
ha good one
Re: the scripting thread
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 ...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)
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.
- LordMortiferus
- MACRO > me
- Posts: 872
- Joined: Tue Dec 01, 2009 10:23 pm
Re: the scripting thread
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
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.
Re: the scripting thread
Very nice LordM, that looks really cool. A horse mounted/drake mounted special mob would be terrifying.
IGN: 1
- Keighn
- Stop posting already --;
- Posts: 5509
- Joined: Sat Jun 26, 2004 10:13 am
- Location: Hey.... pssttt Back in Orgeon
Re: the scripting thread
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 either a mole or a hippoman.. I don't know but its gray and scary.
Re: the scripting thread
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 :/
- LordMortiferus
- MACRO > me
- Posts: 872
- Joined: Tue Dec 01, 2009 10:23 pm
Re: the scripting thread
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 (don't care about broad side firing though).
Re: the scripting thread
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)).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.
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.
- LordMortiferus
- MACRO > me
- Posts: 872
- Joined: Tue Dec 01, 2009 10:23 pm
Re: the scripting thread
@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.
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.
- LordMortiferus
- MACRO > me
- Posts: 872
- Joined: Tue Dec 01, 2009 10:23 pm
Re: the scripting thread
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.
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.