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:

the SERVER scripting thread

Post by eggmceye »

The euo server (moasv, euo2, eao, apoc mod engine, whatever it's called), as of 2007, uses LUA for expanding the engine functions.

The best thing about LUA is that it's so small and has a simple syntax and is relatively easy to learn. Euo uses LUA 5.1.4 with the C-style hex number notation and bitwise operators patch.

Lua links
http://euotopia.com/manual/index.php/Ru ... g_with_LUA Rusty's LUA scripting thread in the manual
lua on wikipedia (very simple intro)
programming in lua (not bad but hard to read online)
lua users wiki (has lots of info but I get lost :S)
** tute page
lua referance manual (very very dry)


Scripting and EUO
There are two types of lua/server interactions, and they happen in this order:
1) the server calls a lua script, such as when a player hits a mob
2) lua can then call server c++ functions to interact with server objects, such as players, monsters, maps, etc

The basic principle is that the eao server will call certain scripted functions under certain circumstances, and that these functions will manipulate the players, mobs or environment in certain ways.

The post below this documents calls from the server to LUA. The post 2 below documents the 'API' which is the C++ functions provided by the server that LUA can then use.

Noobie traps
:alarm: please declare all your variables that are used inside functions with the word local before using them!

:alarm: Avoid globals attached to monsters ... because if your map becomes instanced then those globals do not become instanced. "variables attached to mobs" is not handled very well: update this post oneday with practical tips and pointers.
Last edited by eggmceye on Thu Apr 12, 2018 10:38 am, edited 9 times in total.
Reason: rewrite
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Post by eggmceye »

This post is about how the EUO server calls LUA.

This list is inexhaustive
if you want to see the complete list of server->lua calls, download this: http://euotopia.com/files/luafuncs.cpp

1) Script functions can be called on certain events (these function names are fixed):
mob_hits() : called when a mob performs a successful attack
hit_mob() : called when a mob is hit
mob_dies() : called when a mob dies
do_chest_trap() : called when a trapped chest is opened

2) scripts can be attached to certain triggers. The script function name is set in the txt field of the trigger. Specifically:
a) the step trigger: when stepped on by a player, the function specified in the text field is called
b) the switch trigger: when activated by a player (with the 'a' key), the function specified in the text field is called

3) scripts can be attached to spawners via the txt field of the spawner. The script is called when the spawner spawns a new mob. The script function name is set in the txt field of the spawner. This is the defacto way of making fancy monsters.

4) certain scripts perform functions that ideally/idealogically should not be hard coded into the engine:

chest_loot() : called by chest spawners to put things in chests
mob_loot() : called when mob dies to work out drops
mob_select_spell() : called when mob decides to cast a spell: this selects the spell to cast
eat_food() : called when a player eats food
get_rand_cave_mobs() : helps decide what mobs to put in buried treasure caves
buried_treasure() : helps decide what goes in buried treasure chests
mob_loot() : loot dropping function

5) heatbeat function for monsters called every second - this function would be attached inside the spawn script mentioned in 3) above

6) questing functions:
6a) functions that are called when kill target is spawned (so that it's parameters can be set in a similar way to spawner scripts). The function name for modifying a quest_target is quest_target_xxx() where xxx is the 0-padded hex id of the quest
6b) a new scripted quest type - where the quest goal is checked in a script. Use this for making complex or non standard quests goals like "get to lvl 60 noob", or to have x ratskulls and y jars of slime in inventory, etc. The function name is quest_goal_xxx() where xxx is the 0-padded hex id of the quest
6c) a function can be optionally attached to any quest to check the pre-requisites for starting the quest (rather than just having an item or another quest token) - useful for checking min player level or skill level. The function name is quest_requirement_xxx() where xxx is the 0-padded hex id of the quest
6d) a function can be optionally attached to any quest to give extra or special rewards, such as stat bonus or skill bonus or teleport the player or activate a switch or whatever. The function name is quest_reward_xxx() where xxx is the 0-padded hex id of the quest

7) when a player uses an item with code XXX (where XXX is a 3 digit hex number) the function use_XXX() is called

8) when a player looks at an item (with the 'l' key) with code XXX (where XXX is a 3 digit hex number) the function look_XXX() is called

9) when a player activates at an item (with the 'a' key) with code XXX (where XXX is a 3 digit hex number) the function activate_XXX() is called

10) when a map is loaded with level number DDD (where DDD is a 3 digit number) the function load_map_DDD() is called

11) a misc function called every_minute(), which is called every 60 sec, is used by the server for odd maintainence jobs, such as spawning werewolves and huge crabs

The script functions themselves call upon a large selection of exported functions, enums and global objects to interact with the server. These include stand alone functions, objects such as wl (stands for weapon list) which holds the item info database, and actual sentient, player & monster objects which may be manipulated to certain degrees.
Last edited by eggmceye on Thu Apr 12, 2018 10:25 am, edited 3 times in total.
Reason: server->lua
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Post by eggmceye »

This post is about what functions LUA can use that are provided by the server. This is loosely called the API

Current EUO/moasv api is here: (updated as of at least April 2018)

http://euotopia.com/files/eao.pkg.cpp

for those that are interested, this file is a manually edited, cut paste of real class defs and funciton defs from the euo server source. I then use tolua++ that uses this file and generates all the c++ the server needs to use LUA. This eao.pkg.cpp file also serves as handy reference as to what is available to the LUA scripting engine in euo.
Last edited by eggmceye on Thu Apr 12, 2018 10:24 am, edited 1 time in total.
Reason: api post
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Post by eggmceye »

example scripts

mob_dies()

Code: Select all

function mob_dies(mob_id, killer_id)

	m=get_monster_ptr(mob_id);
	mob_code=m.code;	
	
	-- gremlin
	if mob_code==0x202 then
	
		send_message(killer_id, string.format("The gremlin (code %x) shits green shit all over you (id %d)!",mob_code,killer_id) )
		
	-- slime
	elseif mob_code==0x21a then
	
		if math.random(100)<30 then
			spawn_near_id(mob_code, killer_id)
			spawn_near_id(mob_code, killer_id)
			send_message(killer_id,"The slime splits in two!")
		end
		
	end
	
	--xp=monsters:get_exp_by_id(mob_id)
	--print("boss="..tostring(m.boss)..", xp="..xp)
end
mob_hits()

Code: Select all

function mob_hits(mob_id, target_id)

	m=get_monster_ptr(mob_id);
	mob_code=m.code;
	
	-- rat
	if (mob_code==0x211 or mob_code==0x21b) and is_player(target_id) then
	
		send_message(target_id, "The rat gives you AIDS and u go blind!" )
		-- set_health(target_id,HN_BLIND,3) -- 2==blind
		make_blind(target_id,3)
	

	elseif mob_code==0x202 then -- gremlin stealing gold

		if math.random(100)<50>0 then
				simple_send_event(target_id, EVENT_STEAL)
				send_message(target_id, string.format("A gremlin stole %d gold!", qty) )
			end
		end
	end
		
end
eat_food()

Code: Select all

function eat_food(id, itm, bcode)



	if(bcode==0x4fb) then
	
		set_health(id, HN_BAD_BREATH,10)
		send_message(id, "The boiled egg gives you bad breath!")
		   
	elseif bcode==0x4fa and get_health(id, HN_BLIND)~=0 then
	
		set_health(id, HN_BLIND, 0)
		send_message(id, "You can see again!")
		
	end
	
end
look_111 (look at fountain)

Code: Select all

function look_111   (id, itm, bcode, x, y, z) -- fountain


	-- simplified fountain
	-- empties, and heals a little
	-- small chance of summoning a water demon or snakes
	
	if(math.random(10)==1 or is_cursed(itm)) then
	
		-- snakes
		for i=1,7 do
			spawn_near_id(0x210,id)
		end
		-- doesn't have nethack message variation when blind, halluc
		send_message(id,"An endless stream of snakes pour forth!")
		
	elseif(math.random(20)==1) then
	
		-- water demon
		m=spawn_near_id(0x241,id)
		if(m~=nil) then
			send_message(id,"You summon a water demon!")
			if(math.random(3)==1) then
				m:set_align(ALIGN_GOOD)
				m.time_alive=60           
				m.owner_id=id   
				send_message(id,"He seems relatively pleased to see you!");
			end
		else
			-- this is very unlikely - the spawn failed
			send_message(id,"Water squirts in your eye!");
		end
		
	else
	    hp,hpmax,mana,manamax = get_hp_and_mana(id)
		h=heal(id,math.random(20,30)*hpmax/100)
		if(h==0) then
			send_message(id,"The tepid water is tasteless!")
		else
			send_message(id, "Refreshing!")
		end
	end
	map_set_item_code(x,y,z,0x133) -- makes the fountain dry
	update_fov(id)
end
a script that spawns a wizard in nord that wanders around randomly and where he plagued by bats

illustrates load_map_xxx() & heartbeat

Code: Select all

function load_map_033(lvl)

	nordWizard()

end

function nordWizard()

	m=spawn_at(0x293,15,15,33)
	
	m:set_name('Estorrath')
	m:set_heartbeat_function('wizHeartbeat')
	m:learn_spell("zap")
	m:set_mana_max(100);
	m:set_hp_max(1000);
	m.talk_line="Hello stranger!";
end

function wizHeartbeat(m)

	wp=m:get_next_waypoint()
	if(wp.x==m.x and wp.y==m.y) then
	
		m:clear_waypoints()
		map=get_map_ptr(m.map_level)
		x=0
		y=0
		repeat
	     x=math.random(16,28)
		 y=math.random(19,36)
		until  map:not_blocked(x,y)
		
		m:add_waypoint(x, y)
		
	end
	
	if(math.random(60)==1 and m.target_id<0) then
		
		-- spawn random bats to zap
		for i=1,5 do
			spawn_near_id(0x213,m.id)
		end
	end
	m:set_hp_max(1000); -- keeps him at full health
end
function for using a peergem (item code 446)

Code: Select all

function use_446 (id,tgt_id,alt,itm,bcode,x,y,z)
	if is_cursed(itm) then
		send_message(id,"Death vision!")
		hurt(id,xdy(4,4),"a cursed peergem")
	else
		make_peering(id)
	end
	remove_item(id, itm, 1, true) -- no item removed msg
end
this script spawns a guard in nord called Stirling and gives him a simple patrol route around town

Code: Select all

function nordGuard()

	m=spawn_at(0x290,22,28,33)
	m:set_name('Stirling')
	m:add_waypoint(27,36)
	m:add_waypoint(27,20)
	m:add_waypoint(17,20)
	m:add_waypoint(17,36)
		
end
edit: removed all refs to unhex and replaced with C style 0x hex humbers
Last edited by eggmceye on Sun Sep 10, 2006 7:11 pm, edited 2 times in total.
Ulric
buying vamp LS of any kind +5
Posts: 95
Joined: Wed May 31, 2006 7:17 am

Post by Ulric »

Great work Egg. I did a quick scan of the methods to see if it is possible to change a tile when a char steps on it. I would like to put some puzzles in to the maps. For example you enter a door way and there is a pattern puzzle to solve. So when they step on a tile turns either red or green. Green meaning that it is correct or red meaning that it is wrong. If they step on one that turns red they are tp back to the start with all the green tiles turning back to normal. Is this type of thing possible?
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Post by eggmceye »

ah hah, there is a func (not exported) that changes tiles, the one that is called when you use a tile scroll to change the floor of your house, or when I do a /settile to terraform ingame.

only thing is that it permanently changes the tiles. so make sure there is a resetting trigger somewhere (i made it the first step before the puzzle tiles)

edit: I'm doing a little test map now

edit: here's the scripts, and screenshots attached

the green tiles have step triggers that call func lvl001green, and the wrong tiles have step triggers that call func lvl001wrong. the final green square (at the top) calls lvl001final (which itself calls lvl001green to make it green), and the square between the bottom ladder and the first green tile calls lvl001reset to reset the puzzle.

Code: Select all

function lvl001reset(id, px, py, z)

	for x=6,8 do
		for y=4,10 do
			set_and_send_new_tile(x,y,z,95) -- 95 is the red tile
		end
	end
	reset_trigger(7,3,z,1) 

end

function lvl001wrong(id, x, y, z)

	-- reset all tiles
	-- lvl001reset(id,x,y,z)
	
	-- send plyr back to start
	send_player_to_xy(id,7,12,true) -- don't check trigs (may cause loop)
end

function lvl001green(id, x, y, z)

	set_and_send_new_tile(x,y,z,222) -- 222 is the green tile
end

function lvl001final(id, x, y, z)

	lvl001green(id,x,y,z)
	reset_trigger(7,3,z,0) -- disables the barrier
	
end

You do not have the required permissions to view the files attached to this post.
User avatar
Catherine
English correct bastard.
Posts: 1254
Joined: Mon Sep 08, 2003 2:40 am

Post by Catherine »

Ka-wow... I've dreamed of that function since opening up MapEd! (Ultima5 did have it, even if it was only [if square hit > new tile] :smoke:


Definately going to look at LUA.

Couple of questions:

1) Attaching the script to a trigger means they'll be a /EUA/scripts folder? If so, would there be generic templates in the MapEd for the simple ones? e.g. If [monster X] dies then [spawn X] etc

2) In the example above, does each tile have a separate script attached? i.e. each red tile has the script.txt level..wrong attached to the trigger and a green one level..green? If so that makes it a lot easier, although it means you have to write the scripts first, then keep track of them to insert the correct one into MapEd. This will lead to a lot more potential for map breaking errors, and required testing - not something MapEd allows you to do. Given the current status of MapEd, having it able to run in a window would make switching between the two (scripts and maped) a lot easier. Suggestion (not sure how workable) - give *trusted* map creators a psuedo- server they can upload maps to and test them on single-player with a DM like char, to be able to work through and bug test a map as its being made? Otherwise, its going to be a lot of work for you to play-test each one.

3) Thinking back to when MapEd first appeared, when you had to hand-type in a hex code to the spawner - the ScriptEd will probably need documentation listing the hex code for each tile, unless you can get it to display the hex for each (which I think it does for the monsters still, at least). Which brings us back to 2) first part.

4) I always got confused in the quest maker to which hex tiles were free to use for making quest tokens / items, partly why I didn't get more into it. Would it be possible to have a "unique hex creator" that generated labels on such items, in contact with the server to prevent conflicts? This would make quest making via the new scripts a lot easier - e.g. Kill X then spawn [quest token > create unique hex code, auto-insert].

5) Looking at the Mob hits script, that looks great - its possible to create unique ones for the "Diablo" bosses. From your fire breathing cows example, it looks like every spawner can be customised? Will having a 100x100 map with 50 unique scripted spawners totally bugger the server up? Also, in MapEd it'd be a *good* idea to make a spawner that has a script attached to it have a changed colour, so that when creating maps you can quickly differentiate between vanilla spawns and altered ones, for testing & fixing. e.g. altered spawners change to red txt on MapEd.

6) Given the flexibility of the LUA thingies, is this going to make parts of the old features redundant, or conflict? Just a thought - if someone puts a tele feature with a script to change a tile on it, and you 'port into a blocker tile, you could easily get the player stuck etc. (Silly example, but I hope the concept makes sense).

7) Is there a limit on number of scripts / feature? (Apart from the obvious maxim, don't go insane with the scripts)


These questions are probably showing my unfamiliarity with coding, so feel free to just say "doesn't apply". However, very excited at the possibilities! :)
Last edited by Catherine on Sat Sep 02, 2006 1:27 am, edited 1 time in total.
User avatar
LaughingCoyote
egg has really fucked this game up :(
Posts: 1089
Joined: Fri Jan 02, 2004 10:30 pm

Post by LaughingCoyote »

Not too hard... although I only just scraped through passing a first year subject introducing java.

:oops:
Ulric
buying vamp LS of any kind +5
Posts: 95
Joined: Wed May 31, 2006 7:17 am

Post by Ulric »

What about the time spent on a tile? For example if I wanted something to happen if someone stayed on a tile for 5 seconds it would spawn a mob. This could give a random effect to a dungeon and give the effect of mobs patrolling a location :) . Something else that would be fun for those annoying high level chars that run through dungeons as fast as possible would be if they spent to little time on a tile it could spawn a bat in front of them every step they take too quickly or maybe a bally every step :). This could be thought of as running down a hallway and every mob in the dungeon hearing you.

LC if you need any help with Java PM me. I have a little experience with it :)
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Post by eggmceye »

cat:

1) yes there is a scripts folder, wasn't planning on having maped used as a script editor (you can use notepad2, or luaedit which both have syntax highliting). as for templates, i guess there would be examples floatng around.

2) there's only 1 script, and 2 main funcs that are bound to tiles (lvl2wrong and lvl2green).

re development/testing server: yeh i think it will definitely be time to give out dev servers, there is no way to test any of this shit w/o one

3) maped now displays the codes for tiles, that's where i got them from for this script

4) yeh i found that to be a prob with kyb who was into making new items: he'd have to ask me to reserve say 10 item numbers for him to use in quests he was making - i can't see a way around it

5) good idea with the colour indicator. i don't know how much load the scripting is going to put on the server, but would you really have 50 scripted spawners in one map? (i guess you could...) - i dunno, i'm not worried about it. worst case scenario: optimise server code and/or upgrade server hardware ;)

6) yeh euo2 is a huge retro fit and there is huge room for redundancy, but i wouldn't remommend anyone script anything that could be achieved by other (even if old fashioned, since that is probably more efficient) means (eg make ALL cows firebreathing with many spawners scripts rather than just editing monsters.txt to give cows the vf spell). scripting is for enhancing the server, not for reinventing the wheel. also when you're developing the maps now, you'll have to test the scripted bits and try and think of all the possibilites of why it could go wrong, which is very much easier said than done

7) there's no limits

ulric:
depends if you just meant one tile in a whole map that a player stayed put on (like a trap), or, just seeing if the player is not moving within the whole map (or a section of it).

either way, both conceptually make me think of a timer. the only timer atm is the heatbeat of a mob (which is a func that can bound to a mob that is called every 1 second). so you could i guess add a hidden dungeonmaster in a map who keeps track of player movements. actually, there is a player variable plyr[].move_timer: which is the number of seconds the player hasn't moved. So the DMNPC could just measure the move_timers of all players and if it catches any over X seconds, spawn mobs around them. This could be tested for one square, a whole map, or just an area. I'll make move_timer a readonly var available to lua.

The idea of the npc-dm i came up when i was making a scripted minigame (which I'll post below). The mini game needed a referee and controller, so I spawned an NPC and gave his heartbeat function all the ai that runs the minigame. You could use this plot-device (for want of a better word) to add all sorts of weird AI to maps. As long as the npc can't be killed (make him good aligned).
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Post by eggmceye »

cow hole - a scripted minigame

the best way to test and improve and expand the scripting interface is to actualy implement something. So I wanted to re-create a nintendo game and watch within a euo map. I picked manhole, as it's one of my favs + it's doesn't really require collision detection (which say donkey kong might).

the concept of manhole is a bit like lemmings: random guys spawn and you have to stop them falling into the sewer when they step on manholes. there are 4 manholes but only one sewer lid which you hold and travel between the 4 holes to keep the random guys out. as your score gets higher, more guys spawn and it gets faster and harder, etc.

ok so cow hole is the same thing. cows spawn but you have to stop them stepping in the lava which kills them. you magically provide a bride by stepping in the 4 squares in the middle of the map. the topleft square provids a bridge in the topleft lavahole, etc . see pic.

cowmaster henry does most of the AI work: his heartbeat spawns cows, counts dead cows, etc. a spawned cow gets a single way point which it walks to, and if it gets there, you get a point. if it dies, you lose a life. three cow deaths and the game is over.

to start the game you pull the lever near the ladder. this resets the map, spawns henry. when the game is over, henry goes home. you can of course pull the lever again to play again.

this demonstrates npc-dm ai and the ever expanding awesomeness of the eao engine. it's not so awesome due to me: but lua being so powerful and thanks to tolua++ (a utility that exports euo server funcs to lua).

Code: Select all

require("funcs")

-- global vars
-- declared globally so they are persistant
-- must be unique within lua so are allprefixed with gCH
-- (g for global, CH for cow hole)
gCHscore=0
gCHdeaths=0
gCHcows=0
gCHgameover=false

function cowHoleReset(id,x,y,z)

	cowHoleClearBridges(z)
	cowHoleSpawnController(z)
	gCHscore=0
	gCHdeaths=0
	gCHcows=0
	gCHgameover=false
	
	server_msg("Game starting!",0,z)
	
	-- get the item code of the switch
	-- then toggle it
	map=get_map_ptr(z)
	c=map:get_item_code(x,y)

	if(c==BCODE_SWITCH_ON) then 
	 c=BCODE_SWITCH_OFF
	else 
	 c=BCODE_SWITCH_ON 
	end
	map:set_item_code(x,y,c)

	update_fov(id)
end

function cowHoleStep(id,x,y,z)

	cowHoleClearBridges(z)
	
	if(x==7 and y==6) then
		set_and_send_new_tile(6,4,z,61)
	elseif(x==8 and y==6) then
		set_and_send_new_tile(9,4,z,61)
	elseif(x==7 and y==7) then
		set_and_send_new_tile(6,9,z,61)
	elseif(x==8 and y==7) then
		set_and_send_new_tile(9,9,z,61)
	end
	
end

function cowHoleClearBridges(z)

	set_and_send_new_tile(6,4,z,220)
	set_and_send_new_tile(9,4,z,220)
	set_and_send_new_tile(6,9,z,220)
	set_and_send_new_tile(9,9,z,220)
end

function cowHoleSpawnController(z)

	-- see if another controller is already there!
	map=get_map_ptr(z)
	haveController=false;
	for x=4,5 do
		for y=6,7 do
		
			c=map:get_mon_ptr(x,y)
			if(c~=nil and c.code==0x293) then haveController=true; end
		end
	end	

	if(not haveController) then
		m=spawn_at(0x293,5,6,z)
		m:set_heartbeat_function("cowHoleControllerHeartbeat")
		m:learn_spell("og2")
		m:set_mana_max(100);
		m:set_hp_max(1000);
		m:set_name("Cow-master Henry!")
	end
end


function cowHoleControllerHeartbeat(me)

	z=me.map_level
	-- see if there are 3 deaths! if so, kill all cows and kill me
	map=get_map_ptr(z)

	if(gCHdeaths>=3 and not gCHgameover) then
	
		server_msg("GAME OVER!",0,z) -- send only to this level
		server_msg("Final score: "..gCHscore,0,z) -- send only to this level
		
		-- make all cows evil so controller will og them
		-- also kill off controller
		for x=0,map:get_max_x()-1 do
			for y=0,map:get_max_y()-1 do
			
				cow=map:get_mon_ptr(x,y)
				if(cow~=nil and cow.code==0x297) then 
					cow:set_align(ALIGN_EVIL) 
					cow.time_alive=4
				elseif(cow~=nil) then
					cow.time_alive=5 -- set's controllers suicide time
				end
			end
		end
		
		-- set game over
		gCHgameover=true		
		
	elseif(not gCHgameover) then 
	
		-- game is in progress
		
		-- count onscreen cows
		cows=0
		for x=0,map:get_max_x()-1 do
			for y=0,map:get_max_y()-1 do
			
				cow=map:get_mon_ptr(x,y)
				if(cow~=nil and cow.code==0x297) then 
					cows=cows+1
				end
			end
		end		
		
		r=30; -- make this smaller as the score gets higher

		if(gCHscore>=50) then r=5
		elseif(gCHscore>=40) then r=10
		elseif(gCHscore>=30) then r=15
		elseif(gCHscore>=20) then r=20
		elseif(gCHscore>=10) then r=25
		end
		
		if(cows==0 or math.random(r)==1) then
		
			r=math.random(2)
			
			if    (r==1) then
			
				cow=spawn_at(0x297,3,4,z)
				if(cow~=nil) then cow:add_waypoint(13,4) end	
				
			elseif(r==2) then
			
				cow=spawn_at(0x297,12,9,z)
				if(cow~=nil) then cow:add_waypoint(2,9) end
				
			end
			
			if(cow~=nil) then 
			
				cow:set_heartbeat_function("cowHoleCowHeartbeat") 
				cow.flying=true -- this is a cheese so that they will walk on the lava!
				gCHcows=gCHcows+1 -- cows spawned total
				cow:set_name("Cow #"..gCHcows)
				
			end
		end
	end
end

function cowHoleCowHeartbeat(me)

	-- check to see if reached destination, if so add to score
	if(me.x==13 and me.y==4 or me.x==2 and me.y==4 or me.x==13 and me.y==9 or me.x==2 and me.y==9) then
		me.time_alive=0
		gCHscore=gCHscore+1
		map=get_map_ptr(me.map_level)
		server_msg("A cow survived! Cows saved: "..gCHscore,0,me.map_level) 
	end
	
	-- check to see if on lava, if so die
	map=get_map_ptr(me.map_level)
	if(map:get_tile(me.x,me.y)==220 and me:get_align()~=ALIGN_EVIL) then -- dont do for evil cows
		me.time_alive=0
		gCHdeaths=gCHdeaths+1
		simple_send_event(me.id,EVENT_HIT,0)
		server_msg("A cow died! Cow deaths: "..gCHdeaths,0,me.map_level) 
	end
end

edit: updated, removed all unhex() and replaced with c-style hex numbers
You do not have the required permissions to view the files attached to this post.
Last edited by eggmceye on Sat Sep 09, 2006 9:39 am, edited 1 time in total.
User avatar
Catherine
English correct bastard.
Posts: 1254
Joined: Mon Sep 08, 2003 2:40 am

Post by Catherine »

Scripted mini-games available in dungeons?


EUA>everything. :dancecow:
User avatar
GenoStar
Posting from my ass computer.
Posts: 612
Joined: Fri Dec 17, 2004 8:50 am

Post by GenoStar »

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

Post by eggmceye »

here's a script that turns a ring of 8 braizers on and summons sea serpents at the toggle of a switch. turning the switch off again turns the lights out and removes the seaserpents

update: removed unhex. also, adds the items (ie the braziers and the switch) in the load_map hook rather than the items being required to be placed via maped.

Code: Select all

require("funcs")

map003braziers={{7,3},{10,4},{11,7},{10,10},{7,11},{4,10},{3,7},{4,4}}

function load_map_003()

	for b=1,8 do
	
		x=map003braziers[b][1]
		y=map003braziers[b][2]
		c=BCODE_BRAZIER_OFF
		map_add_item(x,y,3,c,1)
	end
	map_add_item(7,6,3,BCODE_SWITCH_OFF,1)
end

function doBraziers(map, newState)

	
	
	for b=1,8 do
	
		x=map003braziers[b][1]
		y=map003braziers[b][2]
		c=BCODE_BRAZIER_OFF
		if(newState) then c=BCODE_BRAZIER end

		map:set_item_code(x,y,c)
		-- server_msg("x="..x.." y="..y.." c="..c)
	end
		
end

function lvl003switch(id,x,y,z)

	-- get the item code of the switch
	-- then toggle it
	map=get_map_ptr(z)
	c=map:get_item_code(x,y)

	if(c==BCODE_SWITCH_ON) then 
	 c=BCODE_SWITCH_OFF
	else 
	 c=BCODE_SWITCH_ON 
	end
	map:set_item_code(x,y,c)


	-- toggle all of the braziers
	-- use the one at 7,3 as the master
	b=false -- b is the current state, false means off
	if(map:get_item_code(7,3)==BCODE_BRAZIER) then b=true end

	doBraziers(map, not b) -- toggle braziers
	if(b) then
		-- kill all mobs
		for x=0,map:get_max_x()-1 do
			for y=0,map:get_max_y()-1 do
				m=map:get_mon_ptr(x,y)
				if(m~=nil) then hurt(m.id,10000,"") end
			end
		end
	else
		-- spawn sea monsters
		spawn_at(0x21d,3,3,z)
		spawn_at(0x21d,11,3,z)
		spawn_at(0x21d,3,11,z)
		spawn_at(0x21d,11,11,z)
	end
	update_fov(id)
end

You do not have the required permissions to view the files attached to this post.
Last edited by eggmceye on Sat Sep 09, 2006 9:45 am, edited 1 time in total.
User avatar
Catherine
English correct bastard.
Posts: 1254
Joined: Mon Sep 08, 2003 2:40 am

Post by Catherine »

One thought I had: currently, teleporting or being moved onto a tile doesn't trigger features, (and thus, I'm guessing, scripts).

Will scripts trigger independently of a feature, or not? Its a minor one, but might impact play.

A lot of the examples look fairly straightforward, once you get your head around the language. Not as scary as I was expecting, but not a cake-walk either. Do you mind if people post attempts at doing things, then point out the problems? An online work-through of useful scripts! :)

A simple copy / paste of your example to put in the U5 gazer death sequence. (with errors, no doubt)

-- Gazer
elseif mob_code==unhex("299") then

if math.random(100)<90 then
spawn_near_id(298, killer_id)
send_message(killer_id,"Insects buzz around the body!")
end

Reading through now... I'm so right hand side its not funny though.

p.s. Idea for making quest items etc a lot easier - is it possible to create a block of reserved "blank" codes that would be used for such items, and give a new map a block of them? e.g. a new map is assigned a block of say 25 such hex codes, which then either are used or not. All quests within that map would use those blanks - if they need more, then they'd get another block. I was thinking about maps such as forest, where there a lot of uncoded quests as I never got around to doing them (although I think NPC dialogue exists for them).


[edit]

Just looking at the two you've created (step and switch) I can see that multi-player puzzles are really easy to make. E.g. Player A & B are in front of a chasm they can't cross. Player A steps on a pressure pad that opens a way through a near-by wall, but only whilst there's a player standing on it, so player B tootles off all the way around and then pulls a lever on the other side making a bridge across for player A. Its actually a lot simpler than your examples as well! :)
User avatar
Balboa
I once posted in TFIOOC!
Posts: 123
Joined: Sun May 22, 2005 5:01 am
Location: Finland

Post by Balboa »

I still dont understand those scriptings 8D
Eye of the banjo
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Post by eggmceye »

Catherine wrote:One thought I had: currently, teleporting or being moved onto a tile doesn't trigger features, (and thus, I'm guessing, scripts).
i think this is correct ... but you could design around it ...

Code: Select all

-- Gazer
   elseif mob_code==unhex("299") then
   
      if math.random(100)<90 then
         spawn_near_id(unhex(298), killer_id)
         send_message(killer_id,"Insects buzz around the body!")
      end 
  endif
that's about right - just need the unhex around all item and mob codes, since they are in hexadecimal (base 16) - lua doesn't have inbuilt conversion from hex like C does (eg 0x298) but there might be a patch for lua to add this is in, something to think about.
p.s. Idea for making quest items etc a lot easier - is it possible to create a block of reserved "blank" codes that would be used for such items, and give a new map a block of them? e.g. a new map is assigned a block of say 25 such hex codes, which then either are used or not. All quests within that map would use those blanks - if they need more, then they'd get another block. I was thinking about maps such as forest, where there a lot of uncoded quests as I never got around to doing them (although I think NPC dialogue exists for them).
yeh i'll have to revisit the item code blocks - i've already rearranged them for eao once, but can doso again.

Just looking at the two you've created (step and switch) I can see that multi-player puzzles are really easy to make. E.g. Player A & B are in front of a chasm they can't cross. Player A steps on a pressure pad that opens a way through a near-by wall, but only whilst there's a player standing on it, so player B tootles off all the way around and then pulls a lever on the other side making a bridge across for player A.
only thing wrong with that is there is no hook for stepping off a script, tho you could make all of the tiles around the step trigger hooks to reset the bridge. Or I could just see if I can add a step-off hook function which would do what you want. THat would probably be worthwhile as it does then create pressure pads.
Ulric
buying vamp LS of any kind +5
Posts: 95
Joined: Wed May 31, 2006 7:17 am

Post by Ulric »

The step off callback would be very useful for determining how fast a player was moving though a dungeon. I am envisioning a kind of "quick sand" type of room where the faster you move the more monsters are spawned. This room would be a great for slowing down high lvl chars who try to speed through a dungeon to get to the big prize at the end.

But for this to work I would a milliseconds timer so I can determine the rate of movement.
User avatar
eggmceye
hello
Posts: 10577
Joined: Mon Mar 11, 2002 3:55 pm
Location: Sydney, Australia
Contact:

Post by eggmceye »

scripted questing:

example of enhancing a quest target - this sets the quest target a couple of waypoints to patrol bumps the hp to 20 and sets him as a boss. The quest id is 1.

Code: Select all

function quest_target_001(m)

	m:add_waypoint(13,4)
	m:add_waypoint(11,4)
	m:set_hp_max(20)
	m:mk_boss()

end

example of addition pre-req checks. player must be lvl 2 and have 5 pts in tactics to activate the quest. quest id is 2.

Code: Select all


-- must return 0 if req ok
-- can't use send_npc_reply as this script can be called multiple times

function quest_requirement_002(qid, plyr_id, m) 


	p=get_player_ptr(plyr_id)
	if(p.lvl<2) then
	
		return 1
	
	elseif(p:get_raw_skill(SK_TACTICS)<5) then
	
		return 1
			
	end

	return 0
end
example of bonus quest reward: giving 1 pt to tactics and 1 pt to strength. quest id is 2.

Code: Select all

function quest_reward_002(qid,plyr_id)

	adj_skill(plyr_id, SK_TACTICS, 1, true)
	adj_stat(plyr_id,STAT_STR,1);

end
scripted quest type: where the engine uses lua to check to see if the goal is met: here, the goal is for the player to get to 10 tactics. quest id is 3.

Code: Select all

-- return 0 if goal met
-- 1 otherwise
function quest_goal_003(qid,plyr_id)

	p=get_player_ptr(plyr_id)
	if(p:get_raw_skill(SK_TACTICS)==10) then return 0 end
	return 1
	
end
note in this last func i did get_raw_skill(SK_TACTICS)==10 : this should be GREATER THAN OR EQUAL TO 10 but the bb code/html code parser didn't like it
User avatar
Catherine
English correct bastard.
Posts: 1254
Joined: Mon Sep 08, 2003 2:40 am

Post by Catherine »

Laptop blew up - 15 yr old cousin visiting + latest MSN + half the internet as a contacts list = too many viruses to count, "boot drive unmountable". (Although I'd count the latest MSN as virtually a virus in itself the amount of auto-installing of unwanted features it does, even when told not to). So online time is limited to cafes / free "learning centres"... :cry:

The step off hook would be a plus - I'd figured on just surrounding the tile with resets, but that leads to a lot of problems. i.e. Players KOPing (or future Apocalypse equivilent) or IPing. In general, map creators are going to have to be very savy about the effects of players not triggering, or leaving triggered, scripts. Perhaps a universal (map wide) timed "reset to start" might be a good idea? e.g. If Player=0 for X mins, reset all, at the same time as the monster respawn timer.

Quest parsing looks great though :)


Just a small one: will monsters trigger scripts? I'd imagine not, but it might be worth considering. (Or might just prove a massive headache).
Post Reply