JSDSH/src/jsds.js

383 lines
13 KiB
JavaScript

{
// 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
// 1
var dice = function(probe) {
var diceval=new Array();
var rolls=Math.floor(probe/20)+1; // Player may be allowed to roll the dice several times.
var tvalues=new Array();
var result=0;
var tmpval;
for(var i=0; i<rolls; ++i) {
diceval.push(Math.floor(Math.random() * (21 - 1)) + 1);
tvalues.push(20);
};
console.log(diceval);
tvalues[rolls-1]=probe%20; //the last value is the lowest
if (diceval[0]===20) {
alert ("Patzer!");
return 0;
};
diceval.sort(function(a,b){
if(a===1) return -1;
if(b===1) return 1;
return b-a;}
);
console.log(diceval);
for(var i=0; i<rolls-1; ++i) { //First, evaluate the testvalues "20", that is each except the last one
tmpval=diceval[i];
result+= (tmpval == 1)?20:tmpval; //If result of dice is "1", return maximum.
};
tmpval=diceval[rolls-1];
testval=tvalues[rolls-1];
if(tmpval <= testval){
result+= (tmpval ==1)?tvalues[rolls-1]:tmpval;
}
return result;
};
// 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 == 0 ) {
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' class='bon' type=numer step=1 min=0></input><br />";
outstr += "Waffenbonus Fernkampf: <input id='wbf' class='bon' type=numer step=1 min=0></input><br>";
outstr += "Panzerungsbonus: <input id='pb' class='bon' type=numer step=1 min=0></input><br>";
outstr += "Zauberbonus: <input id='zb' class='bon' type=numer step=1 min=0></input><br>";
$('#uibon').html(outstr);
$('.bon').bind("change", function() {
cur_sel.extern.weapon_near = $('#wbn').val()*1;
cur_sel.extern.weapon_far = $('#wbf').val()*1;
cur_sel.extern.armor = $('#pb').val()*1;
cur_sel.extern.chant = $('#zb').val()*1;
cur_sel.recalc_battle();
cur_sel.sel_att();
cur_sel.printoutput();
});
$('#wbn').attr("value", cur_sel.extern.weapon_near+"");
$('#wbf').attr("value", cur_sel.extern.weapon_far);
$('#pb').attr("value",cur_sel.extern.armor);
$('#zb').attr("value",cur_sel.extern.chant);
},
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);
}
});
}