Index: cpp/frams/param/mutableparam.cpp
===================================================================
--- cpp/frams/param/mutableparam.cpp	(revision 477)
+++ cpp/frams/param/mutableparam.cpp	(revision 478)
@@ -171,6 +171,6 @@
 ParamEntry *pe=new ParamEntry();
 pe->fun1=0; pe->fun2=0;
-pe->group=(short)group;
-pe->flags=(short)(flags | MUTPARAM_ALLOCENTRY);
+pe->group=(paInt)group;
+pe->flags=(paInt)(flags | MUTPARAM_ALLOCENTRY);
 pe->offset=(intptr_t)data;
 pe->id=strdup(id);
Index: cpp/frams/param/param.cpp
===================================================================
--- cpp/frams/param/param.cpp	(revision 477)
+++ cpp/frams/param/param.cpp	(revision 478)
@@ -602,5 +602,5 @@
 			if (v.type == TObj)
 			{
-				logPrintf("ParamInterface", "set", LOG_WARN, "Getting integer value from object reference (%s)", v.getString().c_str());
+				logPrintf("ParamInterface", "set", LOG_ERROR, "Setting int '%s.%s' from object reference (%s)", getName(), id(i), v.getString().c_str());
 				return 0;
 			}
@@ -614,5 +614,5 @@
 			if (v.type == TObj)
 			{
-				logPrintf("ParamInterface", "set", LOG_WARN, "Getting floating point value from object reference (%s)", v.getString().c_str());
+				logPrintf("ParamInterface", "set", LOG_ERROR, "Setting float '%s.%s' from object reference (%s)", getName(), id(i), v.getString().c_str());
 				return 0;
 			}
@@ -621,5 +621,10 @@
 		}
 	case 's': { SString t = v.getString(); return setString(i, t); }
-	case 'o': return setObject(i, v.getObject());
+	case 'o':
+		if ((v.type!=TUnknown)&&(v.type!=TObj))
+			logPrintf("ParamInterface", "set", LOG_ERROR, "Setting object '%s.%s' from %s", getName(), id(i), v.typeAndValue().c_str());
+		else
+			return setObject(i, v.getObject());
+		break;
 	case 'x': return setExtValue(i, v);
 	default: logPrintf("ParamInterface", "set", LOG_ERROR, "'%s.%s' is not a field", getName(), id(i));
@@ -645,5 +650,5 @@
 			if ((after == NULL) || (*after))
 			{
-				logPrintf("ParamInterface", "set", LOG_WARN, "serialization format mismatch in %s.%s", (getName() ? getName() : "<Unknown>"), id(i));
+				logPrintf("ParamInterface", "set", LOG_ERROR, "serialization format mismatch in %s.%s", (getName() ? getName() : "<Unknown>"), id(i));
 				e.setEmpty();
 			}
@@ -956,4 +961,20 @@
 	ParamEntry *pe = entry(i);
 	if (pe->flags&PARAM_READONLY) return PSET_RONLY;
+	if (pe->flags&PARAM_OBJECTSET)
+		{
+		ExtObject o=getObject(i);
+		Param tmp;
+		ParamInterface* oif=o.getParamInterface(tmp);
+		int ass;
+		if (oif && ((ass=oif->findId("assign"))>=0))
+			{
+			ExtValue arg=x;
+			oif->call(ass,&arg,&v);
+			}
+		else
+			logPrintf("SimpleAbstractParam", "setObject", LOG_ERROR,
+				  "'%s.%s' is PARAM_OBJECTSET but no 'assign()' in %s", getName(), pe->id, o.interfaceName());
+		return PSET_CHANGED;
+		}
 	ExtObject xcopy = x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
 	if (pe->fun2)
Index: cpp/frams/param/param.h
===================================================================
--- cpp/frams/param/param.h	(revision 477)
+++ cpp/frams/param/param.h	(revision 478)
@@ -33,4 +33,5 @@
 #define PARAM_DEPRECATED  8192  //< this member is deprecated
 #define PARAM_LINECOMMENT 16384 //< Param::load() adds "@line ..." comment when loading multiline (internal use)
+#define PARAM_OBJECTSET 32768   //< setting this field is handled by the object's assign(...) function and cannot change the object reference
 
 typedef int32_t paInt;
@@ -216,5 +217,5 @@
 {
 	const char *id;
-	short group, flags;
+	paInt group, flags;
 	const char *name, *type;
 	intptr_t offset;
@@ -227,5 +228,5 @@
 {
 public:
-	ParamEntryConstructor(const char *_id, short _group = 0, short _flags = 0, const char *_name = 0, const char *_type = 0, intptr_t _offset = 0, void *_fun1 = 0, void *_fun2 = 0, const char *_help = 0)
+	ParamEntryConstructor(const char *_id, paInt _group = 0, paInt _flags = 0, const char *_name = 0, const char *_type = 0, intptr_t _offset = 0, void *_fun1 = 0, void *_fun2 = 0, const char *_help = 0)
 	{
 		id = _id; group = _group; flags = _flags; name = _name; type = _type; offset = _offset; fun1 = _fun1; fun2 = _fun2; help = _help;
Index: cpp/frams/param/paramobj.cpp
===================================================================
--- cpp/frams/param/paramobj.cpp	(revision 477)
+++ cpp/frams/param/paramobj.cpp	(revision 478)
@@ -41,6 +41,6 @@
 	if (addnew) count++;
 	t = tab = (ParamEntry*)malloc(sizeof(ParamEntry)*(count + gcount + 1));
-	t->group = (short)gcount;
-	t->flags = (short)count;
+	t->group = (paInt)gcount;
+	t->flags = (paInt)count;
 	t->name = maybedup(dupentries, rename ? rename : pi->getName());
 	t->type = maybedup(dupentries, pi->getDescription());
@@ -82,6 +82,6 @@
 					offset += sizeof(ExtValue);
 					}
-				t->group = (short)(stripgroups ? 0 : pi->group(i));
-				t->flags = (short)pi->flags(i);
+				t->group = (paInt)(stripgroups ? 0 : pi->group(i));
+				t->flags = (paInt)pi->flags(i);
 				if (readonly_into_userreadonly && (t->flags & PARAM_READONLY))
 					t->flags=(t->flags & ~PARAM_READONLY) | PARAM_USERREADONLY;
Index: cpp/frams/param/paramtabobj.cpp
===================================================================
--- cpp/frams/param/paramtabobj.cpp	(revision 477)
+++ cpp/frams/param/paramtabobj.cpp	(revision 478)
@@ -25,5 +25,5 @@
 memmove(tab+siz-count,p,sizeof(ParamEntry)*count);
 memset(tab+siz,0,sizeof(ParamEntry));
-if (siz>0) tab[0].flags=(short)(siz-tab[0].group);
+if (siz>0) tab[0].flags=(paInt)(siz-tab[0].group);
 return siz-1;
 }
@@ -34,5 +34,5 @@
 resize(siz-count);
 memset(tab+siz,0,sizeof(ParamEntry));
-if (siz>0) tab[0].flags=(short)(siz-tab[0].group);
+if (siz>0) tab[0].flags=(paInt)(siz-tab[0].group);
 }
 
Index: cpp/frams/util/extvalue.cpp
===================================================================
--- cpp/frams/util/extvalue.cpp	(revision 477)
+++ cpp/frams/util/extvalue.cpp	(revision 478)
@@ -104,4 +104,20 @@
 }
 
+bool ExtObject::callDelegate(const char* delegate,ExtValue *args,ExtValue *ret)
+{
+Param tmp;
+ParamInterface *pi=getParamInterface(tmp);
+if (pi)
+	{
+	int f=pi->findId(delegate);
+	if (f>=0)
+		{
+		pi->call(f,args,ret);
+		return true;
+		}
+	}
+logPrintf("Genotype","get", LOG_ERROR, "Could not call delegate '%s.%s'", pi?pi->getName():"NULL",delegate);
+return false;
+}
 
 SString ExtObject::toString() const
@@ -644,5 +660,5 @@
 	else
 	{
-		logPrintf("ExtValue", "divide", LOG_CRITICAL, "Division by zero: %d/0", idata());
+		logPrintf("ExtValue", "divide", LOG_ERROR, "Division by zero: %d/0", idata());
 		setInvalid();
 	}
@@ -653,5 +669,5 @@
 	if (a == 0.0)
 	{
-		logPrintf("ExtValue", "divide", LOG_CRITICAL, "Division by zero: %s/0.0", getString().c_str());
+		logPrintf("ExtValue", "divide", LOG_ERROR, "Division by zero: %s/0.0", getString().c_str());
 		setInvalid();
 	}
@@ -662,5 +678,5 @@
 		if (!finite(tmp))
 		{
-			logPrintf("ExtValue", "divide", LOG_CRITICAL, "Overflow %s/%g", getString().c_str(), a); setInvalid();
+			logPrintf("ExtValue", "divide", LOG_ERROR, "Overflow %s/%g", getString().c_str(), a); setInvalid();
 		}
 		else
@@ -868,5 +884,5 @@
 	case TString: return getInt(sdata().c_str());
 	case TObj:
-		logPrintf("ExtValue", "getInt", LOG_WARN, "Getting integer value from object reference (%s)", getString().c_str());
+		logPrintf("ExtValue", "getInt", LOG_ERROR, "Getting integer value from object reference (%s)", getString().c_str());
 		return (paInt)(intptr_t)odata().param;
 	default:;
@@ -883,5 +899,5 @@
 	case TString: return getDouble(sdata().c_str());
 	case TObj:
-		logPrintf("ExtValue", "getDouble", LOG_WARN, "Getting floating point value from object reference (%s)", getString().c_str());
+		logPrintf("ExtValue", "getDouble", LOG_ERROR, "Getting floating point value from object reference (%s)", getString().c_str());
 		return (double)(intptr_t)odata().param;
 	default:;
Index: cpp/frams/util/extvalue.h
===================================================================
--- cpp/frams/util/extvalue.h	(revision 477)
+++ cpp/frams/util/extvalue.h	(revision 478)
@@ -67,4 +67,5 @@
 	void* getTarget() const { return (subtype & 1) ? dbobject : object; }
 	void* getTarget(const char* classname, bool through_barrier = true, bool warn = true) const;
+	bool callDelegate(const char* delegate,ExtValue *args,ExtValue *ret);
 	void setEmpty() { decref(); subtype = 0; param = NULL; object = NULL; }
 	int isEmpty() const { return !param; }
Index: cpp/frams/vm/classes/collectionobj.cpp
===================================================================
--- cpp/frams/vm/classes/collectionobj.cpp	(revision 477)
+++ cpp/frams/vm/classes/collectionobj.cpp	(revision 478)
@@ -49,5 +49,5 @@
 ParamEntry dictionary_paramtab[]=
 {
-{"Dictionary",1,10,"Dictionary","Dictionary associates stored values with string keys "
+{"Dictionary",1,11,"Dictionary","Dictionary associates stored values with string keys "
  "(\"key\" is the first argument in get/set/remove functions). Integer key can be "
  "used to enumerate all elements (note that while iterating, the elements are returned in no particular order).\n"
@@ -76,4 +76,6 @@
 {"toString",0,PARAM_READONLY | PARAM_NOSTATIC,"Textual form","s",GETONLY(toString),},
 {"clone",0,PARAM_NOSTATIC,"Create a clone","p oDictionary()",PROCEDURE(p_clone),"The resulting clone is a shallow copy (contains the same object references as the original). A deep copy can be obtained through serialization: String.deserialize(String.serialize(object));"},
+{"assign",0,PARAM_NOSTATIC,"Assign from another object","p(x)",PROCEDURE(p_assign),"Replaces current dictionary with dictionary contents from another object."},
+
 {0,0,0,},
 };
@@ -372,25 +374,26 @@
 }
 
+ExtValue DictionaryObject::get(SString key)
+{
+ExtValue *val=(ExtValue*)hash.get(key);
+if (val)
+	return *val;
+return ExtValue::empty();
+}
+
+ExtValue DictionaryObject::get(int index)
+{
+HashEntryIterator* iter=getIndexIterator(index);
+if (iter && (*iter)->value)
+	return *((ExtValue*)(*iter)->value);
+return ExtValue::empty();
+}
+
 void DictionaryObject::p_get(PARAMPROCARGS)
 {
 if ((args->type==TInt)||(args->type==TDouble))
-	{
-	HashEntryIterator* iter=getIndexIterator(args->getInt());
-	if (iter && (*iter)->value)
-		{
-		*ret=*((ExtValue*)(*iter)->value);
-		return;
-		}
-	}
-else
-	{
-	ExtValue *val=(ExtValue*)hash.get(args[0].getString());
-	if (val)
-		{
-		*ret=*val;
-		return;
-		}
-	}
-*ret=ExtValue();
+	*ret=get(args->getInt());
+else
+	*ret=get(args[0].getString());
 }
 
@@ -406,9 +409,16 @@
 }
 
+ExtValue DictionaryObject::set(SString key,ExtValue new_value)
+{
+ExtValue ret;
+ExtValue *new_ext=(new_value.getType()==TUnknown) ? NULL : new ExtValue(new_value);
+ExtValue *old_ext=(ExtValue*)hash.put(key,new_ext);
+if (old_ext) { ret=*old_ext; delete old_ext;}
+return ret;
+}
+
 void DictionaryObject::p_set(PARAMPROCARGS)
 {
-ExtValue *newval=(args[0].getType()==TUnknown)?0:new ExtValue(args[0]);
-ExtValue *oldval=(ExtValue*)hash.put(args[1].getString(),newval);
-if (oldval) {*ret=*oldval; delete oldval;} else *ret=ExtValue();
+*ret=set(args[1].getString(),args[0]);
 }
 
@@ -461,13 +471,27 @@
 }
 
+void DictionaryObject::copyFrom(DictionaryObject *other)
+{
+for(HashEntryIterator it(other->hash);it.isValid();it++)
+	{
+	ExtValue *v=(ExtValue*)it->value;
+	hash.put(it->key,v?new ExtValue(*v):NULL);
+	}
+}
+
 void DictionaryObject::p_clone(PARAMPROCARGS)
 {
 DictionaryObject *c=new DictionaryObject;
-for(HashEntryIterator it(hash);it.isValid();it++)
-	{
-	ExtValue *v=(ExtValue*)it->value;
-	c->hash.put(it->key,v?new ExtValue(*v):NULL);
-	}
+c->copyFrom(this);
 ret->setObject(ExtObject(&par,c));
+}
+
+void DictionaryObject::p_assign(PARAMPROCARGS)
+{
+clear();
+DictionaryObject *other=DictionaryObject::fromObject(args[0].getObject(),false);
+if (other)
+		copyFrom(other);
+ret->setEmpty();
 }
 
Index: cpp/frams/vm/classes/collectionobj.h
===================================================================
--- cpp/frams/vm/classes/collectionobj.h	(revision 477)
+++ cpp/frams/vm/classes/collectionobj.h	(revision 478)
@@ -72,5 +72,10 @@
 PARAMGETDEF(toString);
 PARAMPROCDEF(p_clone);
+PARAMPROCDEF(p_assign);
 #undef STATRICKCLASS
+ExtValue get(SString key);
+ExtValue get(int index);
+ExtValue set(SString key,ExtValue new_value);
+void copyFrom(DictionaryObject *other);
 SString serialize(SerializationFormat format) const;
 static void p_new(void*,ExtValue*args,ExtValue*ret)
