{
    // OOP-Setup, thanks to Florian Rappl for this piece of Code
    // Source: 
    // http://www.florian-rappl.de/Articles/Page/116/super-mario5-article

    var reflection = {};
    
    (function(){
	var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
	
	// The base Class implementation (does nothing)
	this.Class = function(){ };
	
	// Create a new Class that inherits from this class
	Class.extend = function(prop, ref_name) {
	    if(ref_name)
		reflection[ref_name] = Class;
	    
	    var _super = this.prototype;

	    // Instantiate a base class (but only create the instance,
	    // don't run the init constructor)
	    initializing = true;
	    var prototype = new this();
	    initializing = false;
	    
	    // Copy the properties over onto the new prototype
	    for (var name in prop) {
		// Check if we're overwriting an existing function
		prototype[name] = typeof prop[name] == "function" && 
		    typeof _super[name] == "function" && fnTest.test(prop[name]) ?
		    (function(name, fn) {
			return function() {
			    var tmp = this._super;

			    // Add a new ._super() method that is the same method
			    // but on the super-class
			    this._super = _super[name];

			    // The method only need to be bound temporarily, so we
			    // remove it when we're done executing
			    var ret = fn.apply(this, arguments);        
			    this._super = tmp;

			    return ret;
			};
		    })(name, prop[name]) :
		prop[name];
	    }
	    
	    // The dummy class constructor
	    function Class() {
		// All construction is actually done in the init method
		if ( !initializing && this.init )
		    this.init.apply(this, arguments);
	    }
	    
	    // Populate our constructed prototype object
	    Class.prototype = prototype;
	    
	    // Enforce the constructor to be what we expect
	    Class.prototype.constructor = Class;

	    // And make this class extendable
	    Class.extend = arguments.callee;
	    
	    return Class;
	};
    })();
    
    // Helper-Funcs

    var clearoutput = function() {
	$('#output').empty();
    };

    var clearui = function() {
	$('#output').empty();
	$('#ui').empty();
    };
    
    // Probe-Function: checks, whether a given "effort" is succesfull, according to the rules of DS
    // Not yet implemented: Tests for values larger than 20
    var dice = function(probe) {
	var diceval = Math.floor(Math.random() * (21 - 1)) + 1;
	if ( diceval <= probe ) {
	    return diceval;
	}
	else {
	    if (diceval === 20) {
		alert ("Patzer!"); // We need alert here, because most "Patzer"
		// affect gameplay (dropping equipement and so on)
	    };
	    return 0;
	};
    };

    // Own Classes for Creatures, derived from the Basic Object class from above

    // Creature is the base class for every "living" (or dead) "being"
    var Creature = Class.extend({
	init: function(info,attributes,properties,drawinfo,battle){
	    /* info cotains basic information about a Creature:
	     * pname (playername), experience, race, size (as a categorie)
	     *
	     * attributes are personal values that don't change very often:
	     * body, agility and spirit
	     *
	     * properties may change more often, i.e. by weapon bonuses:
	     * strength, hardness, movement, skill (in a crafty way), mind, aura
	     *
	     * drawinfo contains informations needed for graphical representation:
	     * x,y,w,h,col,cx,cy (Coord. of center)
	     */
	    this.info = {"pname":  info["pname"]+"", "experience": info["experience"]*1,
			 "race":info["race"]+"", "size":info["size"]*1}; 
	    this.attributes = {"body": attributes["body"]*1, "agility": attributes["agility"]*1,
			       "spirit": attributes["spirit"]*1};
	    this.properties = {"strength": properties["strength"]*1 , "hardness": properties["hardness"]*1,
			       "movement": properties["movement"]*1, "skill": properties["skill"]*1,
			       "mind": properties["mind"]*1, "aura": properties["aura"]*1};
	    this.graphelement = Crafty.e("Creature").creature(this.info["pname"],drawinfo["x"],drawinfo["y"],
							      drawinfo["w"],drawinfo["h"],drawinfo["col"]);
	    
	    /* Battle-info may either be set as an argument, or need to be calculated from attributes
	     * and properties
	     */
	    this.battle_base = {};
	    this.battle={};
	    
	    if(battle) {
		this.battle_base={"life": battle["life"]*1, "defense": battle["defense"]*1,"ini":battle["ini"]*1,
				  "walk": battle["walk"]*1, "melee": battle["melee"]*1,
				  "shoot": battle["shoot"]*1, "chant": battle["chant"]*1,
				  "shoot_chant": battle["shoot_chant"]*1};
	    }
	    else {
		this.calc_battle_base();
	    };

	    for ( var i in this.battle_base ) {
		this.battle[i] = this.battle_base[i];
	    };

	    /* Some special attributes: We need two "life" variables, one to have the maximum value
	     * (the one used within battle), the other to see whether a character is dead or unconscious.
	     */
	    this.life = this.battle_base["life"]*1;
	    /* Curent attack and whether it is defendible
	     */
	    this.att = "melee";
	    this.defendible = true;
	},

	/* Attack by now follows only basic rules, the gamemaster has to be sure that an attack is allowed
	 * (distance), and that no modifier applies (distance-shot, other targets within range)
	 */
	attack: function(enem) {
	    clearoutput();
	    var output=$('#output');
	    output.html(this.info.cname+" greift "+enem.info.cname+" an!<br>");
	    var attack_val = dice(this.battle[this.att]);
	    if (! attack_val ) { 
		output.append('Angriff Fehlgeschlagen!<br>');
	    }
	    else {
		output.append(this.info.cname + " trifft mit " + attack_val + "<br>");
		if ( this.defendible ) {
		    var defense_val = dice(this.battle.defense);
		    console.log(attack_val + ", " + defense_val);
		    attack_val -= defense_val;
		    attack_val = Math.max(attack_val, 0);
		    if ( ! defense_val ) {
			output.append(enem.info.cname + " kann sich nicht wehren!<br>");
		    }
		    else {
			output.append(enem.info.cname + " wehrt sich mit " + defense_val+".<br>");
		    }
		    if ( attack_val === 0 ) {
			output.append("Vollständig Abgewehrt!<br>");
			return;
		    } 
		}
		else {
		    output.append("Nicht abwehrbar!<br>");
		};
		output.append("Schaden: "+attack_val+"<br>");
		enem.life -= attack_val;
		// If an "enemie" dies, he should really die, a player-char should only get "unconcious"
		if( enem.life <= 0 ) {
		    if ( enem.enem ){
			output.append(enem.info.cname + " ist besiegt<br>");
			enem.die();
		    }
		    else {
			output.append(enem.info.cname + " ist bewusstlos<br>");
			enem.unconsc();
		    };
		}
	    }
	},
	
	calc_battle_base: function() {
	    // This is given by DS-Rules
	    this.battle_base["life"] = this.attributes.body + this.properties.strength + 10;;
	    this.battle_base["defense"] = this.attributes.body + this.properties.hardness;
	    this.battle_base["ini"] = this.attributes.agility + this.properties.movement;
	    this.battle_base["walk"] = (this.attributes.agility / 2) +1;
	    this.battle_base["melee"] = this.attributes.body + this.properties.strength;
	    this.battle_base["shoot"] = this.attributes.agility + this.properties.mind;
	    this.battle_base["chant"] = this.attributes.spirit + this.properties.aura;
	    this.battle_base["shoot_chant"] = this.attributes.spirit + this.properties.skill;
	},
	
	// The next three funcs are self-explaining
	die: function() {
	    this.graphelement.destroy();
	    delete(player[this.info.pname]);
	},
	
	unconsc: function() {
	    this.graphelement.oldcolor = this.graphelement.color();
	    this.graphelement.color("#A0A0C0");
	},
	
	consc: function() {
	    if ( this.graphelement.oldcolor ) {
		this.graphelement.color(this.graphelement.oldcolor);
	    }
	},

	// Write out the UI for attack-selection, including binding.
	sel_att: function() {
	    var outstr="<p>";
	    outstr += "Angriffstyp?<br>";
	    outstr += "<select id='att'>";
	    outstr += "<option value='melee' id='melee'>Melee (" + this.battle["melee"] + ")</option>";
	    outstr += "<option value='shoot' id='shoot'>Schuss (" + this.battle["shoot"] + ")</option>";
	    outstr += "<option value='shoot_chant' id='shoot_chant'>Zielzauber (" +this.battle["shoot_chant"]
		+")</option>";
	    outstr += "</select><br>";
	    
	    outstr += "Abwehrbar? ";
	    outstr += "<select id='defendible'>";
	    outstr += "<option value=true selected=true>Ja</option>";
	    outstr += "<option value=false>Nein</option>";
	    outstr += "</p>";
	    
	    $('#uiatt').html(outstr);
	    
	    $('#att').bind("change", function() {
		cur_sel.att=this.value;
		cur_sel.printoutput();
	    });
	    $('#defendible').bind("change",
				  function() { 
				      cur_sel.defendible = (this.value === "true" ? true : false);
				      cur_sel.printoutput();
				  });
	    $("#"+this.att).get(0).selected="true";
	},

	calc_dist: function() {
	    var g = this.graphelement;
	    g.calc_center();
	    var dx = Math.abs(g.cx - g.old_pos["cx"]);
	    var dy = Math.abs(g.cy - g.old_pos.cy);
	    var distpx= Math.sqrt(dx*dx + dy*dy);
	    return (distpx/meters).toFixed(1);
	}
    });
    
    // Maybe the most important class in here
    var Player = Creature.extend({
	init: function(info,attributes,properties,drawinfo,battle){ // x,y,w,h,col) {
	    /* Additional to Creature, Player has:
	     * info: Charactername, Class (Human? Elve), Level
	     * extern: for battle modifiers such as weapons
	     * An info-function printing the basic informations
	     */
	    this._super(info,attributes,properties,drawinfo,battle);
	    this.info["cname"]= info["cname"]+"";
	    this.info["class"]= info["class"]+"";
	    this.info["level"]= info["level"]*1;
	    
	    // Set default battle-properties.
	    this.extern = {"armor":0,"chant":0,"weapon_far":0,"weapon_near":0};
	    this.recalc_battle();
	},
	recalc_battle: function() {
	    this.battle.defense = this.battle_base.defense + this.extern.armor;
	    this.battle.melee = this.battle_base.melee + this.extern.weapon_near;
	    this.battle.shoot = this.battle_base.shoot + this.extern.weapon_far;
	    this.battle.chant = this.battle_base.chant + this.extern.chant - this.extern.armor;
	    this.battle.shoot_chant = this.battle_base.chant + this.extern.chant - this.extern.armor;
	},
	// Write out the UI for bonus-selection.
	sel_bon: function() {
	    var outstr="<p>";
	    outstr += "Boni?<br>";
	    outstr += "Waffenbonus Nahkampf: <input id='wbn' type=numer step=1 min=0></input><br />";
	    outstr += "Waffenbonus Fernkampf: <input id='wbf' type=numer step=1 min=0></input><br>";
	    outstr += "Panzerungsbonus: <input id='pb' type=numer step=1 min=0></input><br>";
	    outstr += "Zauberbonus: <input id='zb' type=numer step=1 min=0></input><br>";

	    $('#uibon').html(outstr);
	    $('#wbn').attr("value", cur_sel.extern.weapon_near+"")
		.bind("change", 
		      function() {cur_sel.extern.weapon_near = this.value*1;
				  cur_sel.recalc_battle();
				  cur_sel.printoutput();
				 }
		     );
	    $('#wbf').attr("value", cur_sel.extern.weapon_far)
		.bind("change", 
		      function() {cur_sel.extern.weapon_far = this.value*1;
				  cur_sel.recalc_battle();
				  cur_sel.printoutput();
				 }
		     );
	    $('#pb').attr("value",cur_sel.extern.armor)
		.bind("change", 
		      function() {cur_sel.extern.armor = this.value*1;
				  cur_sel.recalc_battle();
				  cur_sel.printoutput();
				 }
		     );
	    $('#zb').attr("value",cur_sel.extern.chant)
		.bind("change", 
		      function() {cur_sel.extern.chant = this.value*1; 
				  cur_sel.recalc_battle();
				  cur_sel.printoutput();
				 }
		     );
	},
	printoutput: function() {
	    outstr = "<p>";
	    outstr += this.info.cname + " (gespielt von "+this.info.pname +")<br>";
	    outstr += "Greift derzeit durch " + this.att + " mit einer Stärke von "
		+ this.battle[this.att] + " an.<br>";
	    outstr += "Ist "+this.calc_dist()+"m von der alten Position entfernt (max ??)<br>";
	    outstr += "Verteidigungsstärke: " + this.battle.defense + "<br>";
	    outstr += "Kraftpunkte: " + this.life;
	    outstr += "</p>";
	    $('#output').html(outstr);
	}
    });
	
    var Beast = Creature.extend({
	init: function(info,attributes,properties,drawinfo,battle){ 
	    /* Additional to Creature, a Beast has:
	     * Cname for compability reasons in some functions, set to match pname
	     * the "enem"-bool, specifying it as an enemy that really dies
	     * A print-function as in Player
	     */
	    
	    this._super(info,attributes,properties,drawinfo,battle);
	    this.info.cname=this.info.pname;
	    this.enem = true;
	},
	printoutput: function() {
	    clearoutput();
	    outstr = "<p>";
	    outstr += "Ein "+ this.info.race + "<br>";
	    outstr += "Greift derzeit durch " + this.att + " mit einer Stärke von "
		+ this.battle[this.att] + " an.<br>";
	    outstr += "Ist "+this.calc_dist()+"m von der alten Position entfernt (max ??)<br>";
	    outstr += "Verteidigungsstärke: " + this.battle.defense + "<br>";
	    outstr += "Kraftpunkte: " + this.life;
	    outstr += "</p>";
	    $('#output').html(outstr);
	}
    });
}