nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
################################################################################
#
# Four Worlds
# Copyright (c) 2009-????
#
#    File: editor.py
#
# This module allows you to directly edit instances of objects, rooms, and mobs
# utilizing the automatic-OLC system.
#
# The editor can be extended, similarly to the built-in OLC system. In fact, it
# is compatible, so you can extend this system with a single extra line of
# code if you've already written an extender for the traditional OLC.
#
# Author: Stendec
#
################################################################################

import mud, mudsys, main, olc

room_bits = []
obj_bits  = ['notake']

try:
	from mudsys import get_bitvector
except:
	def get_bitvector(bitvector):
		if bitvector == 'room_bits': return ', '.join(room_bits)
		elif bitvector == 'obj_bits': return ', '.join(obj_bits)
		else: return ''

land_bits = ['Inside','City','Road','Alley','Bridge','Shallow Water',
						 'Deep Water','Ocean','Underwater','Field','Plains','Meadow',
						 'Forest','Deep Forest','Hills','High Hills','Mountains','Swamp',
						 'Deep Swamp','Sand','Desert','Ice','Glacier','Cavern']

worn_types = ['shirt','weapon']

################################################################################
# Extender
################################################################################

room_exts = { }
obj_exts	= { }
obj_types = { }
mob_exts	= { }

def extend(editor, key, menu_func, chooser_func, parser_func = None):
	'''Register a new menu extender. To stay mostly compatible with the built-in OLC
		 extender, types are medit, redit, and oedit.'''
	if		editor == 'medit':
		mob_exts[key]		= (menu_func, chooser_func, parser_func)
	elif	editor == 'oedit':
		obj_exts[key]		= (menu_func, chooser_func, parser_func)
	elif	editor == 'redit':
		room_Exts[key]	= (menu_func, chooser_func, parser_func)
	else:
		raise KeyError('Invalid editor value.')

def item_add_olc(itemtype, menu_func, chooser_func, parse_func):
	'''Registers a new item type handler.'''
	obj_types[itemtype] = (menu_func, chooser_func, parse_func)

################################################################################
# Room Editor
################################################################################

def edit_room(sock, room):
	'''Start the OLC for an instance of a room.'''
	# Set our options for editing a room directly.
	opts = {
		'_protos'   : { 'name' : 'Inherits from prototypes' },
		'_desc'     : { 'multi' : True },
		'_terrain'  : { 'name' : 'Land Type', 'choices' : land_bits },
		'_bits'     : { 'force' : list, 'name' : 'Set Bits', 'same-line' : True, 'choices' : get_bitvector('room_bits').split(', ') },
		'_edit_exit': { 'value' : None, 'raw_renderer' : lambda s,d,k,v: '', 'parser' : do_edit_exit },
		'_fill_exit': { 'value' : None, 'raw_renderer' : list_exits, 'parser' : fill_exit, 'prompt' : 'What is the name of the exit you wish to fill: ' },
		
		'name'      : 'RoomInstance: ' + room.proto,
		'keys'      : {'terrain' : 'L', 'bits' : 'B', 'edit_exit' : 'E', 'fill_exit' : 'F' },
		'order'     : ['protos','','name','desc','terrain','bits'],
		'readonly'  : ['protos']
	}
	
	# Add any registered extenders to the menu.
	if len(room_exts) > 0:
		opts['order'].append('')
		for k,v in room_exts.iteritems():
			key = 'roomext' + k
			opts['keys'][key] = k
			opts['order'].append(key)
			opts['_'+key] = { 'name' : '', 'value' : None, 'extend_renderer' : v[0], 'extend_handler' : v[1], 'extend_parser' : v[2] }
	
	# Put the exits last.
	opts['order'].extend(['','edit_exit','fill_exit'])
	
	# Start the editor.
	main.do_olc(sock, room, None, None, opts, True)

def list_exits(sock, data, key, value):
	'''Render a list of exits out.'''
	buf = []
	exits = ['north','east','south','west','up','down','northeast','southeast','southwest','northwest']
	for e in data.object.exnames:
		if not e in exits: exits.append(e)
	
	for e in exits:
		ex = data.object.exit(e)
		if ex is not None and type(ex.dest) == type(data.object):
			if ex.dest.locale == data.object.locale: en = "{c" + ex.dest.protoname
			else: en = "{c" + ex.dest.proto
		else: en = "{ynowhere"
		buf.append('   {g%-10s : %s' % (e, en))
	
	val = ''
	i   = True
	for b in buf:
		val += '%-44s' % b
		i = not i
		if i: val += '\r\n'

	return '\r\n' + val[:-2]

def fill_exit(sock, data, key, val):
	'''Fill an exit.'''
	data.object.fill(val)
	return True

def render_exit(sock, data, key, value):
	if type(value) == type(data.object.room):
		if value.locale == data.object.room.locale: return '{c' + value.protoname
		else: return '{c' + value.proto
	else: return '{ynowhere'

def render_boolint(sock, data, key, value):
	if value == 0: return 'no'
	else: return 'yes'

def handle_boolint(sock, data, key):
	if type(data.object) == dict:
		v = not bool(data.object[key])
		if v: data.object[key] = 1
		else: data.object[key] = 0
	else:
		v = not bool(getattr(data.object, key))
		if v: setattr(data.object, key, 1)
		else: setattr(data.object, key, 0)
	return main.MENU_NOCHOICE

def render_closable(sock, data, key, value):
	'''Show the 'needs name and keywords' tag if necessary.'''
	if bool(data.object.is_closable):
		# Closable. Need the name and such.
		if not data.object.name or not data.object.keywords: return 'yes {r* exit also needs name and keywords'
		else: return 'yes'
	else:
		return 'no'

def handle_closable(sock, data, key):
	'''Swap the closable tag.'''
	closable = not bool(data.object.is_closable)
	
	# Should we be closing a door?
	if not closable: data.object.filldoor()
	else: data.object.makedoor(data.object.name, data.object.keywords, data.object.opposite, data.object.is_closed, data.object.is_locked)
	return main.MENU_NOCHOICE

def handle_closed(sock, data, key):
	if bool(data.object.is_closed): data.object.open()
	else: data.object.close()
	return main.MENU_NOCHOICE

def handle_locked(sock, data, key):
	if bool(data.object.is_locked): data.object.unlock()
	else: data.object.lock()
	return main.MENU_NOCHOICE

def do_edit_exit(sock, data, key, val):
	'''Enter the exit editor from the room editor.'''
	ex = data.object.exit(val)
	if ex is None:
		ex = data.object.dig(val, '')
	edit_exit(sock, ex)
	return True

def edit_exit(sock, ex):
	'''Edit an exit.'''
	# Set our options for editing an exit directly.
	try: dir = ex.room.exdir(ex)
	except:
		dir = ex.name
		if not dir: return str(ex.uid)
	
	opts = {
		'_name'       : { 'name' : 'Door Name', },
		'_keywords'   : { 'name' : 'Door Keywords', },
		'_leave_mssg' : { 'name' : 'Leave Message', 'empty' : '<DEFAULT>' },
		'_enter_mssg' : { 'name' : 'Enter Message', 'empty' : '<DEFAULT>' },
		
		'_dest'       : { 'name' : 'Exits To', 'renderer' : render_exit },
		'_key'        : { 'same-line' : True, },
		'_is_closable': { 'renderer' : render_closable, 'handler' : handle_closable },
		'_is_closed'  : { 'renderer' : render_boolint,  'handler' : handle_closed },
		'_is_locked'  : { 'renderer' : render_boolint,  'handler' : handle_locked },
		
		'_pick_diff'  : { 'prompt' : 'Enter a new lock difficulty: ' },
		'_spot_diff'  : { 'prompt' : 'Enter a new spot difficulty: ' },
		'_opposite'   : { 'same-line' : True, 'empty' : '<DEFAULT>', 'prompt' : "What is this exit's opposite direction: " },
		
		'name'    : 'ExitInstance: ' + dir,
		'order'   : ['name','keywords','leave_mssg','enter_mssg','desc','','dest','key','is_closable','is_closed','is_locked','pick_diff','spot_diff','opposite'],
		'keys'    : { 'pick_diff' : 'P', 'spot_diff' : 'S', 'opposite' : 'O' }
	}
	
	# Start the editor and return True.
	main.do_olc(sock, ex, None, None, opts, True)

################################################################################
# Object Editor
################################################################################

def it_container(sock, object):
	'''Start the container editing menu for the given object.'''
	# Is this a container? If not, make it.
	if not object.istype('container'):
		object.settype('container')
	
	opts = {
		'_container_capacity'			: { 'name' : 'Capacity', 'places' : 2, 'prompt' : 'Enter a new weight capacity for the container: ' },
		'_container_key'					: { 'name' : 'Key', 'same-line' : True, 'prompt' : 'Enter a key object prototype for (un)locking the container: ' },
		'_container_is_closable'	:	{ 'name' : 'Closable', 'renderer' : render_boolint, 'handler' : handle_boolint },
		'_container_pick_diff'		: { 'name' : 'Pick diff', 'prompt' : 'How difficult is the lock to pick: ' },
		
		'name'	: 'Container',
		'order'	: ['container_capacity','container_key','container_is_closable','container_pick_diff']
	}
	main.do_olc(sock, object, None, None, opts, True)
obj_types['container'] = it_container

def it_furniture(sock, object):
	'''Start the furniture editing menu for the given object.'''
	# Is this a furniture object? If not, make it.
	if not object.istype('furniture'):
		object.settype('furniture')
	
	opts = {
		'_furniture_capacity'	: { 'name' : 'Capacity' },
		'_furniture_type'			: { 'name' : 'Sit Type', 'choices' : ['at','on'] },
		
		'name'	: 'Furniture',
		'order'	: ['furniture_capacity','furniture_type']
	}
	main.do_olc(sock, object, None, None, opts, True)
obj_types['furniture'] = it_furniture

def it_portal(sock, object):
	'''Start the portal editing menu for the given object.'''
	# Is this a portal object? If not, make it.
	if not object.istype('portal'):
		object.settype('portal')
	
	opts = {
		'_portal_dest'				: { 'name' : 'Destination', 'same-line' : True },
		'_portal_enter_mssg'	: { 'name' : 'Enter Message' },
		'_portal_leave_mssg'	: { 'name' : 'Leave Message' },
		
		'name'	: 'Portal',
		'order'	: ['portal_dest','portal_enter_mssg','portal_leave_mssg']
	}
	main.do_olc(sock, object, None, None, opts, True)
obj_types['portal'] = it_portal

def it_worn(sock, object):
	'''Start the worn editing menu for the given object.'''
	# Is this a worn object? If not, make it.
	if not object.istype('worn'):
		object.settype('worn')
	
	opts = {
		'_worn_type'	: { 'name' : 'Type', 'choices' : worn_types },
		'_worn_locs'	: { 'name' : 'Equips To', 'same-line' : True },
		
		'name'				: 'Worn',
		'order'				: ['worn_type','worn_locs'],
		'readonly'		: ['worn_locs']
	}
	main.do_olc(sock, object, None, None, opts, True)
obj_types['worn'] = it_worn

def itype_menu(sock, data):
	'''Render the item types menu.'''
	sock.send("{nEditable item types:")
	buf = []
	for k in sorted(obj_types.keys()):
		if data.istype(k): buf.append('{y'+k)
		else: buf.append('{g'+k)
	
	if hasattr(sock,'cols'): w = sock.cols
	else: w = 80
	num_cols = w / 19
	
	i = 0
	for b in buf:
		sock.send_raw('  %-19s' % b)
		i += 1
		if i >= num_cols:
			sock.send("")
			i = 0
	if i > 0: sock.send("")
	
	sock.send("{n\r\n    E) Edit Type")

def itype_chooser(sock, data, opt):
	if opt.lower() == 'e':
		sock.send_raw("Which item type would you like to edit: ")
		return 1
	else: return olc.MENU_CHOICE_INVALID

def itype_parser(sock, data, opt, val):
	if val not in obj_types.keys(): return False
	v = obj_types[val]
	
	# Is this a list of 3 functions? If so, handle it the
	# old way. Else, run the function.
	if type(v) in (list,tuple):
		# Is this object of the specified type?
		if not data.istype(val):
			data.settype(val)
		
		obj = data.get_type_data(val)
		if obj is None: return False
		
		olc.do_olc(sock, v[0], v[1], v[2], None, obj, True)
	else:
		v(sock, data)
	return True

def render_item_types(sock, data, key, value):
	'''Render a list of item types.'''
	return data.object.get_types()

def handle_item_types(sock, data, key):
	'''Enter the item type submenu.'''
	olc.do_olc(sock, itype_menu, itype_chooser, itype_parser, None, data.object, True)

def edit_object(sock, object):
	'''Start the OLC for an instance of an item.'''
		# Set our options for editing an object directly.
	opts = {
		'_prototypes' : { 'name' : 'Inherits from prototypes' },
		'_desc'				: { 'multi' : True },
		'_bits'				: { 'force' : list, 'name' : 'Set Bits', 'same-line' : True, 'choices' : get_bitvector('obj_bits').split(', ') },
		'_weight_raw'	: { 'name' : 'Weight', 'places' : 3 },
		'_edit_types'	: { 'name' : 'Edit Item Types', 'value' : None, 'renderer' : render_item_types, 'handler': handle_item_types },
	
		'name'        : 'ObjectInstance: ' + object.name,
		'keys'        : {'weight_raw':'W', 'bits':'B', 'edit_types':'I'},
		'order'     	: ['prototypes','','name','mname','keywords','rdesc','mdesc','desc','','weight_raw','bits','edit_types'],
		'readonly'  	: ['prototypes']
	}
	
	# Add any registered extenders to the menu.
	if len(obj_exts) > 0:
		opts['order'].append('')
		for k,v in obj_exts.iteritems():
			key = 'objext' + k
			opts['keys'][key] = k
			opts['order'].append(key)
			opts['_'+key] = { 'name' : '', 'value' : None, 'extend_renderer' : v[0], 'extend_handler' : v[1], 'extend_parser' : v[2] }
	
	# Start the editor.
	main.do_olc(sock, object, None, None, opts, True)

################################################################################
# Character Editor
################################################################################

def edit_character(sock, ch):
	'''Start the OLC for an instance of a mob, or a player character.'''
	# Set our options for editing a character object directly.
	opts = {
		'_is_pc'			: { 'name' : 'Is Player', 'renderer' : render_boolint },
		'_prototypes'	: { 'name' : 'Inherits from prototypes' },
		'_desc'				: { 'multi' : True },
		
		'_gender'			: { 'choices' : ['male','female','neutral'] },
		'_race'				: { 'choices' : mud.list_races(ch.is_pc).split(', ') },
		
		'name'				: 'Character: ' + ch.name,
		'order'				: ['is_pc','prototypes','','name','mname','keywords','rdesc','mdesc','desc','','race','gender'],
		'keys'				: {'race':'R','gender':'G'},
		'readonly'		: ['is_pc','prototypes']
	}

	# Add any registered extenders to the menu.
	if len(mob_exts) > 0:
		opts['order'].append('')
		for k,v in mob_exts.iteritems():
			key = 'mobext' + k
			opts['keys'][key] = k
			opts['order'].append(key)
			opts['_'+key] = { 'name' : '', 'value' : None, 'extend_renderer' : v[0], 'extend_handler' : v[1], 'extend_parser' : v[2] }
	
	# Start the editor.
	main.do_olc(sock, ch, None, None, opts, True)

################################################################################
# Commands
################################################################################

def cmd_iedit(ch, cmd, arg):
	'''Usage: iedit <direction|room|object|person>
	
		 Attempt to edit the person, object, exit, or room specified. This command
		 edits living instances, NOT prototypes, and changes are carried out as you
		 make them. Be careful with this.'''
	try:
		found, type = mud.parse_args(ch, True, cmd, arg, "{ exit room obj.room.inv ch.world.invis_ok }")
	except: return
	
	if		type == 'char':
		edit_character(ch.sock, found)
	elif	type == 'obj':
		edit_object(ch.sock, found)
	elif	type == 'room':
		edit_room(ch.sock, found)
	elif	type == 'exit':
		edit_exit(ch.sock, found)
	else:
		ch.send(type)

################################################################################
# Initialization
################################################################################
mudsys.add_cmd("iedit", None, cmd_iedit, "wizard", False)

def __unload__():
	mudsys.remove_cmd("iedit")