the SERVER scripting thread
Moderator: EUO Moderators
the SERVER scripting thread
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 )
** 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
please declare all your variables that are used inside functions with the word local before using them!
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.
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 )
** 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
please declare all your variables that are used inside functions with the word local before using them!
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
Reason: rewrite
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.
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
Reason: server->lua
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.
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
Reason: api post
example scripts
mob_dies()
mob_hits()
eat_food()
look_111 (look at fountain)
a script that spawns a wizard in nord that wanders around randomly and where he plagued by bats
illustrates load_map_xxx() & heartbeat
function for using a peergem (item code 446)
this script spawns a guard in nord called Stirling and gives him a simple patrol route around town
edit: removed all refs to unhex and replaced with C style 0x hex humbers
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
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
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
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
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
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
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
Last edited by eggmceye on Sun Sep 10, 2006 7:11 pm, edited 2 times in total.
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?
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.
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.
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]
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!
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.
- LaughingCoyote
- egg has really fucked this game up :(
- Posts: 1090
- Joined: Fri Jan 02, 2004 10:30 pm
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
LC if you need any help with Java PM me. I have a little experience with it
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).
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).
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).
edit: updated, removed all unhex() and replaced with c-style hex numbers
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
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.
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.
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.
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!
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!
i think this is correct ... but you could design around it ...Catherine wrote:One thought I had: currently, teleporting or being moved onto a tile doesn't trigger features, (and thus, I'm guessing, scripts).
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
yeh i'll have to revisit the item code blocks - i've already rearranged them for eao once, but can doso again.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).
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.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.
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.
But for this to work I would a milliseconds timer so I can determine the rate of movement.
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.
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.
example of bonus quest reward: giving 1 pt to tactics and 1 pt to strength. quest id is 2.
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.
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
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
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
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
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"...
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).
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).