the SERVER scripting thread

Moderator: EUO Moderators

Post Reply
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Re: LUA, server API, etc

Post by eggmceye »

ok der, try it now :fp:
mud
I once posted in TFIOOC!
Posts: 134
Joined: Wed Nov 09, 2005 12:29 pm

Re: LUA, server API, etc

Post by mud »

Looks perfect, thanks again.
User avatar
LordMortiferus
MACRO > me
Posts: 872
Joined: Tue Dec 01, 2009 10:23 pm

Re: LUA, server API, etc

Post by LordMortiferus »

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.
mud
I once posted in TFIOOC!
Posts: 134
Joined: Wed Nov 09, 2005 12:29 pm

Re: LUA, server API, etc

Post by mud »

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.
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Re: LUA, server API, etc

Post by eggmceye »

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());
}
mud
I once posted in TFIOOC!
Posts: 134
Joined: Wed Nov 09, 2005 12:29 pm

Re: LUA, server API, etc

Post by mud »

Ah, cool, thanks for the info. I'll see if I can get it to work with a bit more fiddling.
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Re: the scripting thread

Post by eggmceye »

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.
Bugbo
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

Post by Bugbo »

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)
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Re: the scripting thread

Post by eggmceye »

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
LordMortiferus
MACRO > me
Posts: 872
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Post by LordMortiferus »

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
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Re: the scripting thread

Post by eggmceye »

ha good one
Celerion
LAUGHING OUT LOUD LIKE A MORON
Posts: 64
Joined: Sat Dec 15, 2012 6:29 am

Re: the scripting thread

Post by Celerion »

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. ;)
User avatar
LordMortiferus
MACRO > me
Posts: 872
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Post by LordMortiferus »

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
1[WoWz]
MACRO > me
Posts: 841
Joined: Wed Apr 21, 2004 6:48 pm
Location: Southern California, USA

Re: the scripting thread

Post by 1[WoWz] »

Very nice LordM, that looks really cool. A horse mounted/drake mounted special mob would be terrifying.
IGN: 1
User avatar
Keighn
Stop posting already --;
Posts: 5509
Joined: Sat Jun 26, 2004 10:13 am
Location: Hey.... pssttt Back in Orgeon

Re: the scripting thread

Post by Keighn »

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
CinisterD
Tune in next time & see how they do it.
Posts: 504
Joined: Mon Jul 06, 2009 6:26 pm

Re: the scripting thread

Post by CinisterD »

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
LordMortiferus
MACRO > me
Posts: 872
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Post by LordMortiferus »

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).
Celerion
LAUGHING OUT LOUD LIKE A MORON
Posts: 64
Joined: Sat Dec 15, 2012 6:29 am

Re: the scripting thread

Post by Celerion »

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.
User avatar
LordMortiferus
MACRO > me
Posts: 872
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Post by LordMortiferus »

@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: 872
Joined: Tue Dec 01, 2009 10:23 pm

Re: the scripting thread

Post by LordMortiferus »

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.
Post Reply