diff --git a/exec/ircbots/rpgbot/items.xml b/exec/ircbots/rpgbot/items.xml new file mode 100644 index 0000000000000000000000000000000000000000..71b91f5408164e40de6e6d06b2353d8f6a06f600 --- /dev/null +++ b/exec/ircbots/rpgbot/items.xml @@ -0,0 +1,38 @@ +<items> + <item id="0"> + <type>food</type> + <title>a loaf of bread</title> + <keywords>loaf of bread</keywords> + <description> + It is hard, stale and disgusting. + </description> + <appearance>a moldy loaf of bread is lying here.</appearance> + <weight>1</weight> + </item> + <item id="1"> + <equip id="wielded"/> + <title>a dull practice sword</title> + <keywords>dull practice sword</keywords> + <description>The sword is so tarnished and worn that it is barely shiny.</description> + <appearance>a battered-looking sword lies at your feet.</appearance> + <dice qty="2" sides="4"/> + <weight>5</weight> + </item> + <item id="2"> + <equip id="body"/> + <title>a tattered cotton shirt</title> + <keywords>tattered cotton shirt</keywords> + <description>The shirt is filthy and offers little protection from the elements.</description> + <appearance>a crumpled pile of cloth lies here.</appearance> + <armor>10</armor> + <weight>1</weight> + </item> + <item id="3"> + <type>container</type> + <title>a small bag</title> + <keywords>small bag</keywords> + <description>The bag is small and lightweight.</description> + <appearance>a small bag has been discarded here.</appearance> + <weight>1</weight> + </item> +</items> diff --git a/exec/ircbots/rpgbot/rpg_commands.js b/exec/ircbots/rpgbot/rpg_commands.js index 979585a092d59ff6d3ca534ba044f03fd90815a7..e0f09b6ae4932287f8e73d7d498dfe00542e5b9f 100644 --- a/exec/ircbots/rpgbot/rpg_commands.js +++ b/exec/ircbots/rpgbot/rpg_commands.js @@ -54,7 +54,7 @@ Bot_Commands["ZONES"].help = Bot_Commands["ZONES"].command = function (target,onick,ouh,srv,lvl,cmd) { var list=[]; for(var z in zones) { - list.push(zones[z].name); + list.push(zones[z].@name); } srv.o(target,"Zones: " + list.join(", ")); } @@ -83,8 +83,8 @@ Bot_Commands["LOGIN"].command = function (target,onick,ouh,srv,lvl,cmd) { Bot_Commands["LOGOUT"] = new Bot_Command(0,false,false); Bot_Commands["LOGOUT"].command = function (target,onick,ouh,srv,lvl,cmd) { - var player=players[onick.toUpperCase()]; - if(!player) { + var player=players.player.(@name == onick); + if(!player.length() > 0) { srv.o(target,"You have not created a character."); return; } @@ -99,6 +99,200 @@ Bot_Commands["LOGOUT"].command = function (target,onick,ouh,srv,lvl,cmd) { /* Game commands */ var RPG_Commands=[]; +RPG_Commands["NEW"] = function (srv,target,zone,room,cmd,player) { + if(player.@editing != 1) { + srv.o(target,"Editor is OFF. Type 'Editor ON' to enable editing."); + return; + } + + cmd.shift(); + var editor_cmd=cmd.shift(); + + switch(editor_cmd.toUpperCase()) { + case "ZONE": + var zone_level=cmd.shift(); + var zone_title=cmd.join(" "); + + if(!zone_title || !zone_level) { + srv.o(target,"usage: NEW ZONE <level> <title>"); + return; + } + var zone_id=get_new_zone_id(); + zones[zone_id]= + <zone name={zone_title} id={zone_id} level={zone_level}> + <room id="0"> + <title>{settings.title}</title> + <description>{settings.description}</description> + </room> + </zone>; + srv.o(target,"Zone '" + zone_title + "' created."); + player.@room=0; + player.@zone=zone_id; + display_room(srv,player,zone_id,0); + break; + case "ITEM": + srv.o(target,"TODO: Allow dynamic item creation"); + break; + case "MOB": + srv.o(target,"TODO: Allow dynamic mob creation"); + break; + case "DOOR": + srv.o(target,"TODO: Allow dynamic door creation"); + break; + } +} + +RPG_Commands["EDITOR"] = function (srv,target,zone,room,cmd,player) { + cmd.shift(); + var editor_cmd=cmd.shift(); + + switch(editor_cmd.toUpperCase()) { + case "ON": + if(player.@editing != 1) { + player.@editing = 1; + srv.o(target,"Zone editor ON"); + } + return; + case "OFF": + if(player.@editing == 1) { + player.@editing = 0; + srv.o(target,"Zone editor OFF"); + } + return; + } + + if(player.@editing != 1) { + srv.o(target,"Editor is OFF. Type 'Editor ON' to enable editing."); + return; + } + + switch(editor_cmd.toUpperCase()) { + case "SAVE": + mkdir(z_dir + zone.@name.toLowerCase()); + var z_file=new File(z_dir+zone.@name.toLowerCase()+"/map.xml"); + if(!z_file.open("w+")) return; + z_file.write(zone); + srv.o(target,"Zone saved"); + break; + case "RESTORE": + var zone_dir=z_dir+zone.@name.toLowerCase(); + var zone=load_zone(zone_dir); + if(!zone) { + srv.o(target,"Error loading zone"); + break; + } + zones[zone.@id]=zone; + srv.o(target,"Reverted current zone to saved data"); + break; + } + + return; +} + +RPG_Commands["SET"] = function (srv,target,zone,room,cmd,player) { + if(player.@editing == 0) { + srv.o(target,"You aren't currently editing."); + return; + } + + cmd.shift(); + var set=cmd.shift(); + if(!set) { + srv.o(target,"Set what?"); + return; + } + + switch(set.toUpperCase()) { + case "NEW": + switch(cmd.shift().toUpperCase()) { + case "TITLE": + settings.title=cmd.join(" "); + srv.o(target,"New room title set to: " + settings.title); + break; + case "DESCRIPTION": + settings.description=cmd.join(" "); + srv.o(target,"New room description set to: " + settings.description); + break; + default: + srv.o(target,"usage: set new <property> <value>"); + break; + } + break; + case "CURRENT": + switch(cmd.shift().toUpperCase()) { + case "TITLE": + room.title=cmd.join(" "); + srv.o(target,"Current room title set to: " + room.title); + break; + case "DESCRIPTION": + room.description=cmd.join(" "); + srv.o(target,"Current room description set to: " + room.description); + break; + default: + srv.o(target,"usage: set current <property> <value>"); + break; + } + break; + case "AUTOLINK": + if(settings.autolink) { + settings.autolink=false; + srv.o(target,"Auto-link disabled."); + } else { + settings.autolink=true; + srv.o(target,"Auto-link enabled."); + } + break; + case "LINK": + var dir=cmd.shift().toLowerCase() + switch(dir) { + case "north": + case "south": + case "east": + case "west": + case "up": + case "down": + var room_id=cmd.shift(); + + if(!room_id) { + target_id=room.exit.(@name == dir).@target; + target_room=zone.room.(@id == target_id); + + /* clear link if no target specified */ + room.exit.(@name == dir).(@target=""); + srv.o(target,"Room exit deleted"); + + /* clear corresponding link in original target room */ + if(settings.autolink) target_room.exit.(@name == reverse_dir(dir)).(@target=""); + } else { + var target_room=zone.room.(@id == room_id); + var exit=room.exit.(@name == dir); + + /* set exit target for current room */ + if(exit.@target.toString()=="") { + room.exit += <exit name={dir} target={room_id}/>; + srv.o(target,"Room exit added"); + + /* change exit target for current room */ + } else { + exit.@target=room_id; + srv.o(target,"Room target changed"); + } + + /* change corresponding exit for target room */ + if(settings.autolink) target_room.exit.(@name == reverse_dir(dir)).(@target = room.@id); + } + break; + default: + srv.o(target,"usage: set <direction> <target>"); + break; + } + break; + deftault: + srv.o(target,"Set what?"); + break; + } +} + RPG_Commands["FLEE"] = function (srv,target,zone,room,cmd,player) { var chance=random(100); if(chance<25) { @@ -170,24 +364,30 @@ RPG_Commands["STATUS"] = function (srv,target,zone,room,cmd,player) { RPG_Commands["MOVE"] = function (srv,target,zone,room,cmd,player) { var exit=room.exit.(@name == cmd[1].toLowerCase()); if(exit.@target.toString()=="") { - srv.o(target,"You cannot go that way."); - return false; + if(player.@editing == 1) { + create_new_room(srv,target,zone,room,player,cmd[1].toLowerCase()); + return true; + } else { + srv.o(target,"You cannot go that way."); + return false; + } } if(exit.door) { var door=zone.door.(@id == exit.door); - if(door.open == false) { + if(door.open == false && player.@editing != 1) { srv.o(target,"The " + door.@name + " is closed."); return false; } } + if(zones[exit.@zone]) player.@zone=<zone>{exit.@zone}</zone>; player.@room=<room>{exit.@target}</room>; zone=zones[player.@zone]; room=zone.room.(@id == player.@room); - srv.o(target,"You " + player.travel + " " + exit.@name + " to " + room.title + ". " + get_exit_string(zone,room)); + srv.o(target,"You " + player.travel + " " + exit.@name + " to " + room.title + ". " + get_exit_string(zone,room,player)); } RPG_Commands["UNLOCK"] = function (srv,target,zone,room,cmd,player) { diff --git a/exec/ircbots/rpgbot/rpg_functions.js b/exec/ircbots/rpgbot/rpg_functions.js index ec6b1ba832d0f9db9222f0e34d6fb0048e49d100..5189ed673b443dee9ddd92e1676aa795c2b54626 100644 --- a/exec/ircbots/rpgbot/rpg_functions.js +++ b/exec/ircbots/rpgbot/rpg_functions.js @@ -1,6 +1,7 @@ function Server_command(srv,cmdline,onick,ouh) { var cmd=IRC_parsecommand(cmdline); var player=players.player.(@name == onick); + switch (cmd[0]) { case "PART": case "QUIT": @@ -116,36 +117,41 @@ function handle_command(srv,cmd,player,target) { /* game activity functions */ function display_room(srv,player,z,r) { - log("displaying zone: " + z + " room: " + r); - var zone=zones[z]; var room=zone.room.(@id == r); - show_room_title(srv,player.@channel,zone,room); - show_room_description(srv,player.@channel,zone,room); - show_room_exits(srv,player.@channel,zone,room); - show_room_items(srv,player.@channel,zone,room); - show_room_mobs(srv,player.@channel,zone,room); + if(player.@editing == 1) { + srv.o(player.@channel,"Editing '" + zone.@name + "' [Z: " + z + " R: " + r + "]"); + } + show_room_title(srv,player.@channel,zone,room,player); + show_room_description(srv,player.@channel,zone,room,player); + show_room_exits(srv,player.@channel,zone,room,player); + show_room_items(srv,player.@channel,zone,room,player); + show_room_mobs(srv,player.@channel,zone,room,player); show_room_players(srv,player,zone,room); } -function show_room_description(srv,target,zone,room) { - srv.o(target,strip_ctrl(room.description)); +function show_room_description(srv,target,zone,room,player) { + if(player.@editing == 1) srv.o(target,"[DESC] " + strip_ctrl(room.description)); + else srv.o(target,strip_ctrl(room.description)); } -function show_room_title(srv,target,zone,room) { - srv.o(target,room.title); +function show_room_title(srv,target,zone,room,player) { + if(player.@editing == 1) srv.o(target,"[TITLE] " + room.title); + else srv.o(target,room.title); } -function show_room_items(srv,target,zone,room) { +function show_room_items(srv,target,zone,room,player) { for each(var i in room.item) { - srv.o(target,i.appearance); + if(player.@editing == 1) srv.o(target,"[ITEM: " + i.@id + "] " + i.appearance); + else srv.o(target,i.appearance); } } -function show_room_mobs(srv,target,zone,room) { +function show_room_mobs(srv,target,zone,room,player) { for each(var m in room.mob) { - srv.o(target,m.appearance); + if(player.@editing == 1) srv.o(target,"[MOB: " + m.@id + "] " + m.appearance); + else srv.o(target,m.appearance); } } @@ -159,20 +165,22 @@ function show_room_players(srv,player,zone,room) { } } -function show_room_exits(srv,target,zone,room) { - srv.o(target,get_exit_string(zone,room)); +function show_room_exits(srv,target,zone,room,player) { + srv.o(target,get_exit_string(zone,room,player)); } -function get_exit_string(zone,room) { +function get_exit_string(zone,room,player) { var exit_str=""; - for(var e in room.exit) { + var exits=room.exit.(@target != ""); + for(var e in exits) { var prefix=""; - if(room.exit[e].door) { - var door=zone.door.(@id == room.exit[e].door); + if(exits[e].door) { + var door=zone.door.(@id == exits[e].door); if(door.locked==true) prefix="@"; else if(door.open==false) prefix="#"; } - exit_str+=", "+prefix+room.exit[e].@name; + exit_str+=", "+prefix+exits[e].@name.substr(0,1); + if(player.@editing == 1) exit_str+="[ID:"+exits[e].@target+"]"; } return ("exits: " + exit_str.substr(2)); } @@ -372,7 +380,7 @@ function load_zone(dir) { log("loading zone file: " + z_file.name); if(!z_file.open("r+")) return false; - var zone=new XML(z_file.readAll().join()); + var zone=new XML(z_file.read()); z_file.close(); zone.directory=<directory>{dir}</directory>; @@ -397,14 +405,14 @@ function load_zone(dir) { function load_items(file) { if(!file.open("r+")) return false; - var items=new XML(file.readAll().join()); + var items=new XML(file.read()); file.close(); return items; } function load_mobs(file) { if(!file.open("r+")) return false; - var mobs=new XML(file.readAll().join()); + var mobs=new XML(file.read()); file.close(); return mobs; } @@ -416,9 +424,14 @@ function load_players() { var p_file=new File(p_list[f]); if(!p_file.open("r+")) return false; log("loading player data: " + p_file.name); - var player=new XML(p_file.readAll().join()); + var player=new XML(p_file.read()); p_file.close(); player.@active=0; + player.@editing=0; + if(!zones[player.@zone]) { + player.@zone=0; + player.@room=1; + } players.appendChild(player); } } @@ -435,13 +448,199 @@ function load_zones() { function load_classes() { if(!c_file.open("r+")) return false; writeln("loading class data: " + c_file.name); - classes=new XML(c_file.readAll().join()); + classes=new XML(c_file.read()); c_file.close(); } function load_races() { if(!r_file.open("r+")) return false; writeln("loading race data: " + r_file.name); - races=new XML(r_file.readAll().join()); + races=new XML(r_file.read()); r_file.close(); -} \ No newline at end of file +} + +/* game edit functions */ +function find_link(zone,room,start_coords,finish_coords,checked) { + + + /* cumulative array for tracking scanned rooms (by room.@id) */ + checked=checked || []; + if(checked[room.@id]) return -1; + checked[room.@id] = true; + + + if(!start_coords) start_coords=new Coords(0,0,0); + else { + /* if the passed starting coordinates match our query, return the current room id */ + if(start_coords.x == finish_coords.x && + start_coords.y == finish_coords.y && + start_coords.z == finish_coords.z) { + return room.@id; + } + } + /* store all room exits in an array */ + var dirs=[]; + for(var e=0;e<room.exit.length();e++) { + if(room.exit[e].@target == "" || zones[room.exit[e].@zone]) continue; + + var dir=room.exit[e].@name; + var target_id=room.exit[e].@target; + var new_coords=get_exit_coords(start_coords,dir); + var target=zone.room.(@id == target_id); + + /* do recursive scan on all rooms */ + var result=find_link(zone,target,new_coords,finish_coords,checked); + if(result >= 0) return result; + } + return -1; +} + +function get_exit_coords(coords,dir) { + var new_coords=new Coords(coords.x,coords.y,coords.z); + switch(dir.toString()) { + case "north": + new_coords.y++; + break; + case "south": + new_coords.y--; + break; + case "east": + new_coords.x++; + break; + case "west": + new_coords.x--; + break; + case "up": + new_coords.z++; + break; + case "down": + new_coords.z--; + break; + default: + log("unknown dir: " + dir); + break; + } + return new_coords; +} + +function create_player(name,player_race,player_class) { + var player= + <player name={name} active="0" zone="0" room="0"> + <experience>0</experience> + <hitpoints>{base_hitpoints}</hitpoints> + <movement>{base_movement}</movement> + <mana>{base_mana}</mana> + <gold>0</gold> + <strength>{base_stats}</strength> + <dexterity>{base_stats}</dexterity> + <wisdom>{base_stats}</wisdom> + <intelligence>{base_stats}</intelligence> + <constitution>{base_stats}</constitution> + <level>1</level> + <travel>walk</travel> + <player_class>{player_class}</player_class> + <player_race>{player_race}</player_race> + <inventory> + {global_items.item.(@id == "1").copy()} + {global_items.item.(@id == "2").copy()} + {global_items.item.(@id == "3").copy()} + {global_items.item.(@id == "4").copy()} + </inventory> + <equipment> + <slot id="head" qty="1"/> + <slot id="neck" qty="1"/> + <slot id="about body" qty="1"/> + <slot id="body" qty="1"/> + <slot id="waist" qty="1"/> + <slot id="wrist" qty="2"/> + <slot id="hands" qty="1"/> + <slot id="finger" qty="2"/> + <slot id="wielded" qty="1"/> + <slot id="legs" qty="1"/> + <slot id="feet" qty="1"/> + </equipment> + </player>; + return player; +} + +function create_new_room(srv,target,zone,room,player,dir) { + var new_id=get_new_room_id(zone); + var old_id=room.@id; + /* add this room to previous room's exit list */ + room.exit +=<exit name={dir} target={new_id}/>; + + /* add new room data to zone */ + zone.room += + <room id={new_id}> + <title>{settings.title}</title> + <description>{settings.description}</description> + </room>; + + /* get direction we came from and add that exit to new room */ + dir=reverse_dir(dir); + var new_room=zone.room.(@id == new_id); + new_room.exit += <exit name={dir} target={old_id}/>; + + if(settings.autolink) { + var dirs=get_dir_coords(dir); + for(var d in dirs) { + if(!dirs[d]) continue; + var result=find_link(zone,new_room,undefined,dirs[d],undefined); + if(result >= 0) { + /* add link to found exit */ + new_room.exit += <exit name={d} target={result}/>; + /* add corresponding exit for target room */ + var target_room=zone.room.(@id == result); + target_room.exit += <exit name={reverse_dir(d)} target = {new_id}/>; + } + } + } + + /* move player to new room */ + player.@room=new_id; + srv.o(target,"room " + new_id + " created"); + display_room(srv,player,zone.@id,new_id); +} + +function get_dir_coords(exclude) { + var dirs={ + north:new Coords(0,1,0), + south:new Coords(0,-1,0), + east:new Coords(1,0,0), + west:new Coords(-1,0,0), + up:new Coords(0,0,1), + down:new Coords(0,0,-1), + } + delete dirs[exclude]; + return dirs; +} + +function reverse_dir(dir) { + if(dir=="north") dir="south"; + else if(dir=="south") dir="north"; + else if(dir=="east") dir="west"; + else if(dir=="west") dir="east"; + else if(dir=="up") dir="down"; + else if(dir=="down") dir="up"; + return dir; +} + +function get_new_room_id(zone) { + var id = 0; + var r = zone.room.(@id == id); + while(r != undefined) { + id++; + r=zone.room.(@id == id); + } + return id; +} + +function get_new_zone_id() { + var id = 0; + var z = zones[id]; + while(z != undefined) { + id++; + z=zones[id]; + } + return id; +} diff --git a/exec/ircbots/rpgbot/rpgbot.js b/exec/ircbots/rpgbot/rpgbot.js index 961e157695c089c01874bfe22a7116b092f86082..d2086793d421840698cc7af6b9b136d85e35d098 100644 --- a/exec/ircbots/rpgbot/rpgbot.js +++ b/exec/ircbots/rpgbot/rpgbot.js @@ -2,7 +2,9 @@ var p_dir=this.dir+"players/"; var z_dir=this.dir+"zones/"; var c_file=new File(this.dir+"classes.xml"); var r_file=new File(this.dir+"races.xml"); +var gi_file=new File(this.dir+"items.xml"); +var settings=new Settings(); var players=[]; var zones=[]; var classes=[]; @@ -10,6 +12,7 @@ var races=[]; var global_items=[]; var player_attacks=[]; var mob_attacks=[]; +var global_items=load_items(gi_file); var activity_timeout=300; // seconds var tick_time=4; // seconds @@ -29,7 +32,7 @@ function save() { var p_file=new File(p_dir + player.@name + ".xml"); if(!p_file.open("w+")) continue; debug("saving RPG player: " + player.@name); - p_file.writeln(player.normalize()); + p_file.write(player); p_file.close(); } } @@ -103,47 +106,19 @@ function update_game_status(srv) { return active; } -function create_player(name,player_race,player_class) { - var player= - <player name={name} active="0" zone="0" room="0"> - <experience>0</experience> - <hitpoints>{base_hitpoints}</hitpoints> - <movement>{base_movement}</movement> - <mana>{base_mana}</mana> - <gold>0</gold> - <strength>{base_stats}</strength> - <dexterity>{base_stats}</dexterity> - <wisdom>{base_stats}</wisdom> - <intelligence>{base_stats}</intelligence> - <constitution>{base_stats}</constitution> - <level>1</level> - <travel>walk</travel> - <player_class>{player_class}</player_class> - <player_race>{player_race}</player_race> - <inventory> - {items.item.(@id == "1").copy()} - {items.item.(@id == "2").copy()} - {items.item.(@id == "3").copy()} - {items.item.(@id == "4").copy()} - </inventory> - <equipment> - <slot id="head" qty="1"/> - <slot id="neck" qty="1"/> - <slot id="about body" qty="1"/> - <slot id="body" qty="1"/> - <slot id="waist" qty="1"/> - <slot id="wrist" qty="2"/> - <slot id="hands" qty="1"/> - <slot id="finger" qty="2"/> - <slot id="wielded" qty="1"/> - <slot id="legs" qty="1"/> - <slot id="feet" qty="1"/> - </equipment> - </player>; - return player; -} - /* game objects */ +function Coords(x,y,z) +{ + this.x=x; + this.y=y; + this.z=z; +} +function Settings() +{ + this.autolink=true; + this.title="New Room"; + this.description="A new room."; +} function Attack(attacker,defender) { this.attacker=attacker; @@ -158,8 +133,10 @@ function Move(item,result) /* Init */ load_classes(); load_races(); -load_players(); /* TODO: make zones load on demand, as loading them all at the start could become a problem when the world gets bigger */ load_zones(); +load_players(); + +