

ludi_runner.world = {
	items: [],
	actions: [],
	directions: [],
	attributes: []
};

// dynamic data: id, index (to ludi_runner.world), gameIndex (to ludi_game)
ludi_runner.worldIndexes = { 
	items: [],
	actions: [],
	directions: [],
	attributes: []
};

// messages from reactions
ludi_runner.clientAction = [];

/***************************************************************************************
	INTERFACE 
***************************************************************************************/

ludi_runner.ludi_getLoc = function (indexItem) {
	return arrayObjectIndexOf(ludi_runner.world.items, "id", ludi_runner.world.items[indexItem].loc);
}

ludi_runner.ludi_getId = function (indexItem) {
	return ludi_runner.world.items[indexItem].id;
}

ludi_runner.ludi_getItemType = function (indexItem) {
	return  ludi_runner.world.items[indexItem].type;
}


/***************************************************************************************
	End of INTERFACE 
***************************************************************************************/

ludi_runner.executeActionRequest = function  (par_c) {

	ludi_runner.userState.profile.turnCounter ++;

	if (typeof socket == "object" && user.name) { 
	
		socket.emit('mlif-action', {
			'user': user,
			'message': {par_c: par_c }
		});
	
	}
	
	if (ludi_runner.checkActionEnabled(par_c)) {
		// execute action and get reaction list
		ludi_runner.executeAction (ludi_runner.userState, par_c);

		// set items as known and stamp (where and when) them if they are refereed and they are here
		if (par_c.item1 >= 0) {

			var originalItem1 = par_c.item1;
			// considering rare case: when two PC are exchanged 
			if (par_c.action == ludi_root.actEnum.BECOME.value) {
				originalItem1 = par_c.pc;
			}		
			
			var indexLoc = arrayObjectIndexOf(ludi_runner.world.items, "id", ludi_runner.world.items[originalItem1].loc);
			
			// to-do: when asking for a never seen item... suddenly pc will know where it is!
			// to-do: if (!itemNotHere(originalItem1)) 
			ludi_runner.world.items[ludi_runner.userState.profile.indexPC].state.itemsMemory[par_c.item1] = {
				whereWas: indexLoc,
				lastTime: ludi_runner.userState.profile.turnCounter 
			};

			
		}

		
		if (par_c.item2 >= 0)
			ludi_runner.world.items[ludi_runner.userState.profile.indexPC].state.itemsMemory[par_c.item2] = { 
				whereWas: par_c.loc, 
				lastTime: ludi_runner.userState.profile.turnCounter
			};
		
	} else {
		CA_ShowMsg("Action not allowed");
	}		
	
	// returning data
	var result = {
		clientAction: ludi_runner.clientAction,
		userState: ludi_runner.userState
	};

	ludi_runner.clientAction = [];
	
	return result;
	
}
	
ludi_runner.actionIsEnabled = function (action, item1, item2) {
	
	var gameIndex = ludi_runner.worldIndexes.actions[action].gameIndex;
	if (gameIndex >=0 ) {
		if (typeof ludi_game.reactions[gameIndex].enabled == 'function') {
			var status = ludi_game.reactions[gameIndex].enabled(item1, item2);
			if (status) return true;
			if (status == false) return false;
		}
		// but... if not defined, lib level!
	}
	
	// lib level
	var libIndex = ludi_runner.worldIndexes.actions[action].libIndex;
	if (libIndex >= 0) 	return ludi_lib.reactions[libIndex].enabled(item1, item2);
	// hidden by default
	return false;

}

// to-do: three layers:  item turn(), if not: game turn(), if not: lib turn()... and then back
ludi_runner.worldTurn = function () {

    for(var i in ludi_runner.world.items) {

		var gameIndex = ludi_runner.worldIndexes.items [i].gameIndex;
		var status;
		if (gameIndex>=0) {
			if (typeof ludi_game.items [gameIndex].turn == 'function') {
				status = ludi_game.items [gameIndex].turn();
				if (status == true) continue;
			} 
		}
			
		// try game turn()
		if (typeof ludi_game.turn == 'function') {
			status = ludi_game.turn(i);
			if (status == true) continue;
		}
		
		// try lib turn()
		if (typeof ludi_lib.turn == 'function') {
			ludi_lib.turn(i);
		}
    }
}

ludi_runner.executeAction = function (statusInput, par_c) {
	
	var levels = []; // levels: 0: lib / 1: generic game / 2: item specific / -1; error
	
	ludi_runner.userState = statusInput;

	levels[0] = false;
	if (typeof ludi_lib.reactions[par_c.action] == 'object' )
		levels[0] = (typeof ludi_lib.reactions[par_c.action].reaction == 'function');
	
	var posGameReaction = arrayObjectIndexOf (ludi_game.reactions, "index", +par_c.action);
	levels[1] = ((posGameReaction >= 0) && (typeof ludi_game.reactions[posGameReaction].reaction == 'function'));
	
	var actionId, gameIndex=-1;
	if (par_c.item1 >=0) {
		actionId = ludi_runner.world.actions[par_c.action].id;
		gameIndex = ludi_runner.worldIndexes.items[par_c.item1].gameIndex;
	}
	levels[2] = ((gameIndex>=0) && (typeof ludi_game.items[gameIndex][actionId] == 'function'));
	
	// from startLevel to 0 if return code is false
	var status;
	for (var level = 2; level>=0 ; level--) {
		
		if (!levels[level]) continue; // try in lower level

		if (level == 2) { // item level
			status = ludi_game.items[gameIndex][actionId](par_c); 
		} else if (level == 1) { // game level
			status = ludi_game.reactions[posGameReaction].reaction(par_c, status);
		} else if (level == 0) { // lib level
			status = ludi_lib.reactions[par_c.action].reaction(par_c, status);
		} 
		
		if (status || status == undefined) break; // by default, if not explicit, the method ends in its own level
	}
	
	if ( ((status==false) || (status == 'undefined')) && !levels[0] ) { // if no reaction handler at all
		ludi_runner.clientAction.push ({type:ludi_root.caEnum.MSG.value, txt:"You cannot do it"}); 
	}
	
	// markedItem = -1 if it is not longer present
	if (ludi_runner.userState.profile.markedItem!=-1) {
		if ( ! IT_CarriedOrHere(ludi_runner.userState.profile.markedItem)	) {
			ludi_runner.userState.profile.markedItem = -1;
		}
	}
	
	ludi_runner.worldTurn();
	
	// save state for undo action
	// ludi_runner.userState_2 = ludi_runner.userState;
	
	
}

ludi_runner.undo = function () {
	// ludi_runner.userState = ludi_runner.userState_2;
}


// menu options from ludi_runner.userState.profile.panelOptionsCach tables to menu structures
ludi_runner.updateBaseMenus = function () { 

	// if not game loaded yet
	if (ludi_runner.world.items.length == 0) return;

	ludi_runner.panelOptionsCache = [];
	
	// exits
	var table_Exits = ludi_runner.getCSExits( ludi_runner.ludi_getLoc (ludi_runner.userState.profile.indexPC));
	ludi_runner.panelOptionsCache[ludi_root.p1Def.Exit.value]=[]; 
	ludi_runner.panelOptionsCache[ludi_root.p1Def.Exit.value].menu = table_Exits.slice();
	
	// direct actions
	var table_DA = ludi_runner.getCSActions();
	ludi_runner.panelOptionsCache[ludi_root.p1Def.DirectAction.value]=[]; 
	ludi_runner.panelOptionsCache[ludi_root.p1Def.DirectAction.value].menu = table_DA.slice();

	// notHere
	var table_NotHere = ludi_runner.getCSItemNotHere(ludi_runner.userState.profile.markedItem);
	ludi_runner.panelOptionsCache[ludi_root.p1Def.NotHere.value]=[]; // [{item, actions:[]}]
	ludi_runner.panelOptionsCache[ludi_root.p1Def.NotHere.value].menu = table_NotHere.slice();
	
	// items
	var table_Here = ludi_runner.getCSItemTable(ludi_runner.world.items[ludi_runner.userState.profile.indexPC].loc); 
	ludi_runner.panelOptionsCache[ludi_root.p1Def.Here.value]=[]; // [{item, actions:[]}]
	ludi_runner.panelOptionsCache[ludi_root.p1Def.Here.value].menu = table_Here.slice();
	
	var table_Carried = ludi_runner.getCSItemTable(ludi_runner.world.items[ludi_runner.userState.profile.indexPC].id);
	ludi_runner.panelOptionsCache[ludi_root.p1Def.Carried.value]=[]; // [{item, actions:[]}]
	ludi_runner.panelOptionsCache[ludi_root.p1Def.Carried.value].menu = table_Carried.slice();

	if (ludi_runner.userState.profile.markedItem >=0) {
		var table_Marked =  ludi_runner.getCSItemTable(ludi_runner.world.items[ludi_runner.userState.profile.markedItem].id);
		ludi_runner.panelOptionsCache[ludi_root.p1Def.Marked.value]=[]; // [{item, actions:[]}]
		ludi_runner.panelOptionsCache[ludi_root.p1Def.Marked.value].menu = table_Marked.slice();
	} else {
		ludi_runner.panelOptionsCache[ludi_root.p1Def.Marked.value]=[];
		ludi_runner.panelOptionsCache[ludi_root.p1Def.Marked.value].menu = [];
	}
	
	var table_On = ludi_runner.getCSItemTable(-1); // to-do
	ludi_runner.panelOptionsCache[ludi_root.p1Def.On.value]=[]; // [{item, actions:[]}]
	ludi_runner.panelOptionsCache[ludi_root.p1Def.On.value].menu = table_On.slice();
	
	ludi_runner.userState.profile.currentLoc =  ludi_runner.ludi_getLoc (ludi_runner.userState.profile.indexPC);
	ludi_runner.userState.profile.currentLocId = ludi_runner.world.items[ludi_runner.userState.profile.indexPC].loc;

	
}

// assuming point of view of current pc
// to-do: check exits for any subject (any pc/npc)
ludi_runner.getCSExits = function (indexLoc) { 

	var table = [];
	
	if (indexLoc==-1) return table;
	
	if (! ludi_runner.world.items[indexLoc].address) return table;
	for(var i = 0, len = ludi_runner.world.items[indexLoc].address.length; i < len; i++) {
		// control if specified exit is availabe in this moment
		var dir = ludi_runner.world.items[indexLoc].address[i].dir;
		var dirIndex = arrayObjectIndexOf(ludi_runner.world.directions, "id", dir);

		// target resolution
		var targetId;
		if (typeof ludi_runner.world.items[indexLoc].address[i].target == 'undefined')
			targetId = "dyn"; // dynamic: use method
		else
			targetId = ludi_runner.world.items[indexLoc].address[i].target;
		var target = arrayObjectIndexOf(ludi_runner.world.items, "id", targetId);
		if ((target==-1) && (targetId != "dyn")) continue; // not defined directin
		
		var actions = [];
		
		var gameIndex = ludi_runner.worldIndexes.items[indexLoc].gameIndex;
		var isEnabled = true;
		if (gameIndex>=0) {
			if (targetId == "dyn"){
				if (typeof ludi_game.items[gameIndex].target == 'function'){
					targetId = ludi_game.items[gameIndex].target (dir);
					target = arrayObjectIndexOf(ludi_runner.world.items, "id", targetId);
					if (target==-1) {
						if (targetId != "locked") continue; // "" (not shown exit) or bad indirect location
					}
				} else {
					continue; // not method defined
				}
			}
		}
	
		if (isEnabled) actions.push (ludi_root.actEnum.GO.value);
		
		// if direction from here was declared
		if(typeof  ludi_runner.world.items[indexLoc].address[i].lookDir != 'undefined'){ 
			actions.push (ludi_root.actEnum.LOOK_DIR.value);
		}

		if (actions.length > 0) {
			table.push ({dir:dir, target:target, dir:dirIndex, actions: actions }); // direction instead of target
		}

	}
	return table;
	
}

ludi_runner.getCSActions = function () { 
	var table = ludi_runner.getRelatedActionsIntransitive ();
	
	return table;
}

ludi_runner.getCSItemNotHere = function (item2) { 
	// not here, nor on, nor carried, but they must by known!
	var table = [];
	
	for (index in ludi_runner.world.items) {
		if (ludi_runner.world.items[index].type == "loc") continue;
	
		if (ludi_runner.world.items[index].loc == ludi_runner.world.items[ludi_runner.userState.profile.indexPC].loc) continue; // here
		if (ludi_runner.world.items[index].loc == ludi_runner.world.items[ludi_runner.userState.profile.indexPC].id) continue; // carried
		// to-do : item worn on
		
		// if it is not known, pass over
		if (typeof ludi_runner.world.items[ludi_runner.userState.profile.indexPC].state.itemsMemory[index] == "undefined") continue; 
		
		// ignore items in limbo too?

		// so, else:

		var actions = [];
		
		//if (ludi_runner.world.items[ludi_runner.userState.profile.indexPC].state.itemsMemory[index] != "undefined" ) { // item itself is known
		if (ludi_runner.world.items[ludi_runner.userState.profile.indexPC].state.itemsMemory[index] != null ) { // item itself is known
		
			// if location is known: ACT_WHERE
			if (ludi_runner.world.items[ludi_runner.userState.profile.indexPC].state.itemsMemory[index].whereWas >=0) {
				actions.push ({action:ludi_root.actEnum.WHERE.value, numPars:1});

				// if it is a PC, allow BECOME
				if (ludi_runner.world.items[index].type == "pc" ) {
					actions.push ({action:ludi_root.actEnum.BECOME.value, numPars:1});
				}
			}
					
			// if item marked: ASK_ABOUT_IT
			if (item2 > 0)
				actions.push ({action:ludi_root.actEnum.ASK_ABOUT.value, numPars:2, obj2:item2}); 
		}
		
		table.push ({item:index, actions:actions})
	}
	
	return table;

}

ludi_runner.getCSItemTable = function (itemId) { 
	var table = [];
	
	if (typeof itemId=="undefined") return table;
	
	for (var index in ludi_runner.world.items) {
		var itemType = ludi_runner.ludi_getItemType(index);
		if  (ludi_runner.world.items[index].loc == itemId) {
			if ((index == ludi_runner.userState.profile.indexPC) || ((itemType != "obj") && (itemType != "npc") && (itemType != "pc")))	 {
				continue;
			}
			
			// for each item, get allowed actions
			table.push ({item:index, actions:ludi_runner.getRelatedActionsTransitive (index)})
		} 
	}
	
	return table;
}


ludi_runner.checkActionEnabled = function (par_c) {

	if (ludi_runner.world.actions[par_c.action].numpar == "0")
		return ludi_runner.checkIntransitiveActionEnabled (par_c.action);
	else {
		
		if (par_c.action == ludi_root.actEnum.GO.value) return true; // to-do
		if (par_c.action == ludi_root.actEnum.LOOK_DIR.value) return true; // to-do
		
		return ludi_runner.checkTransitiveActionEnabled (par_c.action, par_c.item1);
	}

}

ludi_runner.checkIntransitiveActionEnabled = function (indexAct) {
	if (ludi_runner.world.actions[indexAct].numpar != "0") return false;

	var level = -1; // 0: lib / 1: generic game / -1; error

	// from more specific to more general
	var posGameReaction = arrayObjectIndexOf (ludi_game.reactions, "index", +indexAct); 
	if ((posGameReaction >= 0) && (typeof ludi_game.reactions[posGameReaction].enabled == 'function')) level = 1;

	if (level == -1)
		if (typeof ludi_lib.reactions[indexAct].enabled == 'function') level = 0;
	
	if (level == -1) return false;
	
	if (level == 0) {
		if (ludi_lib.reactions[indexAct].enabled()) return true;
	} else {
		if (ludi_game.reactions[posGameReaction].enabled()) return true;
	}
	return false;
}

ludi_runner.checkTransitiveActionEnabled = function (indexAct, indexItem) {

	var typeStr  = ludi_runner.ludi_getItemType(indexItem);
	if (ludi_runner.world.actions[indexAct].numpar == "0") return false;
	pos =  ludi_runner.world.actions[indexAct].on.indexOf (typeStr);
	if ( pos<0 ) return false;
	if ( (ludi_runner.userState.profile.markedItem!=-1) ||
		 ((ludi_runner.userState.profile.markedItem==-1) && (ludi_runner.world.actions[indexAct].numpar == "1")) ) {
		if (ludi_runner.actionIsEnabled(indexAct, indexItem, ludi_runner.userState.profile.markedItem))
			return true;
	}
	return false;
}

ludi_runner.getRelatedActionsIntransitive = function ( ) {
	var table = [];
	
	for (indexAct in ludi_runner.world.actions) {
		if (ludi_runner.checkIntransitiveActionEnabled  (indexAct)) {
			table.push ({action:indexAct, actionId:ludi_runner.world.actions[indexAct].id});
		}
	} 
	return table;

}



ludi_runner.getRelatedActionsTransitive = function (indexItem ) {
	var table = [];
	
	for (indexAct in ludi_runner.world.actions) {
		if (ludi_runner.checkTransitiveActionEnabled (indexAct, indexItem)) {
			table.push ({
				action:indexAct, 
				numPars: +ludi_runner.world.actions[indexAct].numpar, 
				obj2:ludi_runner.userState.profile.markedItem
			});
		}		
	} 
	return table;

}

/*******************************************

  End of getcurrentState section
  
********************************************/


// game creator can define these initial states, but usually will be as follows if they are not defined
ludi_runner.defaultItemState = function() {

	for (var indexItem in ludi_runner.world.items) {
		
		// "memory" of each PC (player character)
		if ((ludi_runner.world.items[indexItem].type == "pc")) {
			
			// if state is not defined, it is created now
			if (typeof ludi_runner.world.items[indexItem].state == "undefined") ludi_runner.world.items[indexItem].state = {};		
			
			// A item i is known if itemsMemory[i] exists.
			// the last container where item i was seen: itemsMemory[i].whereWas
			// the time it was seen: itemsMemory[i].lastTime
			
			// if itemsMemory is not defined, it is created now
			if (typeof ludi_runner.world.items[indexItem].state.itemsMemory == "undefined")
				ludi_runner.world.items[indexItem].state.itemsMemory = []; 
		}
	} 	
}


// "compile" ids into indexes
ludi_runner.compileIndexes = function() {
	
	var errorText = "";
	var dumpText = "";
	var warningText = "";
	var id="", gameIndex =-1, libIndex = -1;

	// attributes ------------------------------------------------------------
	// attributes: indexes and error
	for (var index=0; index<ludi_runner.world.attributes.length;index++) {
		id = ludi_runner.world.attributes[index].id;
		libIndex = arrayObjectIndexOf(ludi_lib.attribute, "id", id);
		gameIndex = arrayObjectIndexOf(ludi_game.attribute, "id", id);
		ludi_runner.worldIndexes.attributes[index] = {
			id: id,
			index: index,
			libIndex: libIndex,
			gameIndex: gameIndex
		}
		
	}
	
	//  attributes: warnings if dead code
	for (var index=0; index<ludi_lib.attribute.length;index++) {
		if (arrayObjectIndexOf(ludi_runner.world.attributes, "id", ludi_lib.attribute[index].id) < 0) {
			warningText += "Warning: unknown lib attribute [" + ludi_lib.attribute[index].id + "]<br/>\n";
		}
	}
	for (var index=0; index<ludi_game.attribute.length;index++) {
		if (arrayObjectIndexOf(ludi_runner.world.attributes, "id", ludi_game.attribute[index].id) < 0) {
			warningText += "Warning: unknown game attribute [" + ludi_game.attribute[index].id + "]<br/>\n";
		}
	}
	
	// items ------------------------------------------------------------
	// items: indexes and error
	for (var index = 0; index<ludi_runner.world.items.length;index++) {
		id = ludi_runner.world.items[index].id;
		libIndex = -1; // ??: libIndex: arrayObjectIndexOf(ludi_lib.items, "id", id);
		gameIndex = arrayObjectIndexOf(ludi_game.items, "id", id);
		
		ludi_runner.worldIndexes.items[index] = {
			id: id,
			index: index, // itself
			gameIndex: gameIndex
		};
		// warning if item with no special treatment (maybe, red herrings = useless objects)
		if (gameIndex <0) {
			// warningText += "warningText: item [" + id + "] with not game index (useless object?)<br/>\n";
		} else {
			// references
			ludi_game.items[gameIndex].index = index;
			ludi_game.items[gameIndex].gameIndex = gameIndex;
		}
		
		// properties values of item attributes
		ludi_runner.setDefaultAttributeProperties (index);
	} 

	// game items: warnings if dead code
	for (var index=0; index<ludi_game.items.length;index++) {
		if (arrayObjectIndexOf(ludi_runner.world.items, "id", ludi_game.items[index].id) < 0) {
			warningText += "Warning: unknown game item [" + ludi_game.items[index].id + "]<br/>\n";
		}
	}
	

	// directions ------------------------------------------------------------
	// directions: indexes and error
	for (var index=0; index<ludi_runner.world.directions.length;index++) {
		id = ludi_runner.world.directions[index].id;
		ludi_runner.worldIndexes.directions[index] = {
			id: id,
			index: index
			// ??: libIndex: arrayObjectIndexOf(ludi_lib.direction, "id", id), 
			// ??: gameIndex: arrayObjectIndexOf(ludi_game.direction, "id", id)
		}
	}
	
	// actions ------------------------------------------------------------
	// actions and reactions: indexes and error
	for (var index=0; index<ludi_runner.world.actions.length;index++) {
		id = ludi_runner.world.actions[index].id;
		libIndex = arrayObjectIndexOf(ludi_lib.reactions, "id", id);
		gameIndex = arrayObjectIndexOf(ludi_game.reactions, "id", id);

		ludi_runner.worldIndexes.actions[index] = {
			id: id,
			index: index,
			libIndex: libIndex ,
			gameIndex: gameIndex
		}
		
		if (libIndex >=0) {
			ludi_lib.reactions[libIndex].index = index; 
			ludi_lib.reactions[libIndex].libIndex = libIndex; 
		}
		if (gameIndex >=0) {
			ludi_game.reactions[gameIndex].index = index; 
			ludi_game.reactions[gameIndex].gameIndex = gameIndex; 
			ludi_game.reactions[gameIndex].libIndex = libIndex; 
		}		
		
		// check
		if ( (libIndex <0) && (gameIndex <0) ) {
			errorText += "Error: action [" + id + "] with not lib index nor game index<br/><br/>\n";
		}
	}
	
	//  reaction: warnings if dead code
	for (var index=0; index<ludi_lib.reactions.length;index++) {
		if (arrayObjectIndexOf(ludi_runner.world.actions, "id", ludi_lib.reactions[index].id) < 0) {
			warningText += "Warning: unknown lib reaction [" + ludi_lib.reactions[index].id + "]<br/>\n";
		}
	}
	for (var index=0; index<ludi_game.reactions.length;index++) {
		if (arrayObjectIndexOf(ludi_runner.world.actions, "id", ludi_game.reactions[index].id) < 0) {
			warningText += "Warning: unknown game reaction [" + ludi_game.reactions[index].id + "]<br/>\n";
		}
	}
	
	// dump of errors
	
	if (errorText != "") {
		errorText += "<p>Language " + JSON.stringify(ludi_game.language) + "</p>";
		errorText = "<p>Error compiling game. Restart LUDI</p><br/>" +  errorText;
		errorText += "<p>Warnings:</p><br/>" + warningText;
		errorText += "<p>Dump:</p><br/>" + dumpText;
		errorText += "<p>You must restart LUDI</p><br/>";
		

		console.log ("Eroor: " + errorText);
		return false;
	}
	return true;
	
}


ludi_runner.itemWithAttException = function (indexItem, attId_def) {
	
	var indexException = arrayObjectIndexOf(ludi_runner.world.attExceptions, "id", attId_def);
	if (indexException >= 0) { // exists exception for this attribute?
		// look item in exceptionLinst
		for (var i=0; i< ludi_runner.world.attExceptions[indexException].exceptionList.length; i++) {
			if (ludi_runner.world.attExceptions[indexException].exceptionList[i] == ludi_runner.world.items[indexItem].id)  return true;
		}
		
	}
	return false;
}


ludi_runner.setDefaultAttributeProperties = function (indexItem) {

	var attId_def; // attribute definition
	for (var indexAtt = 0; indexAtt < ludi_runner.world.attributes.length; indexAtt++) { 
		// elements like {id: "isDark", restrictedTo: Array[1], properties: Array[1], asignedTo: Array[1], enabledActions: Array[6]}
		attId_def = ludi_runner.world.attributes[indexAtt].id;
		var isException = ludi_runner.itemWithAttException(indexItem, attId_def);
		
		if (typeof ludi_runner.world.items[indexItem].att == 'undefined') 
			ludi_runner.world.items[indexItem].att ={}; // item without any attribute
		
		var attOnItem = false;
		// each attribute in item definition
		for (var attId_item in ludi_runner.world.items[indexItem].att) {
			if (attId_item == attId_def) {
				attOnItem = true;
				break;
			}
		}
		
		var attToBeImported = false;
		
		if (attOnItem) { 
		
			if (isException) { // remove it
				delete ludi_runner.world.items[indexItem].att[attId_def];
			}
		
			attToBeImported = true;
		} else {
			if ((ludi_runner.world.attributes[indexAtt].asignedTo != 'undefined')  // att mandatory to item (global rule)
				 && !isException ) {  // not an exception
				for (var aTo in ludi_runner.world.attributes[indexAtt].asignedTo) {
					if (ludi_runner.world.items[indexItem].type == ludi_runner.world.attributes[indexAtt].asignedTo[aTo]) { 
						// item must have this attribute
						attToBeImported = true;
						break;
					}
				}			
			} 
		}

		if (attToBeImported) {
			// get properties from attribute definition (at least: some of them) 
			var propAlready;
			for (var pDef in ludi_runner.world.attributes[indexAtt].properties) {
				propAlready = false;
				var proId = ludi_runner.world.attributes[indexAtt].properties[pDef].id;
				for (var pItem in ludi_runner.world.items[indexItem].att[attId_def]) {
					// if match; do not import
					if (typeof ludi_runner.world.items[indexItem].att[attId_def][pItem][proId] != 'undefined') {
						propAlready = true;
						break;
					}
				}
				if (!propAlready) { // new property from definition
					if (typeof ludi_runner.world.items[indexItem].att[attId_def] == 'undefined')
						ludi_runner.world.items[indexItem].att[attId_def] = [];
					var isOptional = true;  // by default property is optional
					if (typeof ludi_runner.world.attributes[indexAtt].properties[pDef].use != 'undefined') {
						if (ludi_runner.world.attributes[indexAtt].properties[pDef].use == 'mandatory') {
							isOptional = false;
						}
					}
					// only add mandatory properties
					if (!isOptional) {
						ludi_runner.world.items[indexItem].att[attId_def].push (ludi_runner.world.attributes[indexAtt].properties[pDef]);
					}
				}
			}
		}
		
	}

}

// note: for use during game development
ludi_runner.GD_CreateMsg = function   (indexLang, idMsg, txtMsg) {
	ludi_root.GD_CreateMsg (indexLang, idMsg, txtMsg);
};

