require("./../rur.js");
require("./../translator.js");
require("./animated_images.js");
require("./../programming_api/exceptions.js");
require("./../utils/supplant.js");
/** @function add_new_thing
* @memberof RUR
* @instance
* @summary This method makes it possible to add new "things", represented
* by an image.
*
* If the name of an existing thing is specified with different properties,
* it is replaced by the new one.
*
* **Important** Other than for testing purposes, This method should
* only be called from the "Onload" editor so that it can start fetching
* the required images as soon as possible, and try to ensure that the
* images will be ready to be shown when a program is executed.
*
* @param {Object} thing A Javascript object (similar to a Python dict) that
* describes the properties of the "thing".
*
* @param {string} thing.name The name to be given to the "thing"; an exception
* will be raisd if it is missing.
*
* @param {string} [thing.info] Some information to be displayed about this "thing"
* when a user clicks on "World Info" and then on this thing on the world canvas.
* It is highly recommended to include this.
*
* @param {string} [thing.color] A string representing a valid html color
* (named, rgb, rgba, hsl or #-notation).
* **Either `thing.color`, thing.url` or `thing.images` must be specified.**
*
* @param {string} [thing.url] If a single image is used, this indicated the source.
* **Either `thing.color`, `thing.url` or `thing.images` must be specified.**
*
* @param {strings[]} [thing.images] If multiple images are used
* (for animated "things"), this array (list) contains the various URLs.
* **Either `thing.color`, `thing.url` or `thing.images` must be specified.**
*
* @param {string} [thing.selection_method] For animated "things"; choose one of
*
* * `"sync"`,
* * `"ordered"`,
* * `"random"`,
* * `"cycle stay"` or
* * `"cycle remove"`.
*
* If the selection method is not recognized, `"random"` will
* be used, and no error will be thrown.
*
* @param {object} [thing.goal] If the "things" can be used for an object that can be
* picked up or put down by Reeborg, includes `thing.goal` to describe the image(s),
* following the same pattern as above (`thing.goal.url`, `thing.goal.images`,
* `thing.goal.selection_method`), except that `goal` is ignored if `color` is true.
*
* @param {string} [thing.fatal] Program ends if Reeborg steps on such a "thing" with
* a value that is equivalent to "true" when used as background things or obstacles,
* unless a bridge offering the adequate protection is present or an object
* carried by Reeborg has the right protection defined.
* This value is usually set to the name of the "things" so as to facilitate
* defining objects or bridges which offer the right protection.
* For `fatal` things, `message` should be defined as well.
*
* @param {string} [thing.message] The message shown when Reeborg steps on
* a `fatal` tile.
*
* @param {string} [thing.detectable] If `thing.fatal` and `thing.detectable` are
* both equivalent to "true", Reeborg can detect this "thing" with
* `front_is_clear()` and `right_is_clear()` if it is set as an obstacle
* or a background thing.
*
* @param {strings[]} [thing.protections] Indicates against which `fatal` thing this
* offer protection. Protection is given when things are used as a bridge or
* when they are carried.
*
* @param {boolean} [thing.solid] If sets to `True`, prevents a pushable object
* from sliding onto this "things" when used as a background thing or as an
* obstacle.
*
* @param {integer} [thing.x_offset] By default, "things" are drawn on a set grid.
* Specifying a value for `x_offset` result in the "things" drawn off grid, by a
* number of pixel equal to `x_offset`. This is only valid for images - not for
* colors.
*
* @param {integer} [thing.y_offset] By default, "things" are drawn on a set grid.
* Specifying a value for `y_offset` result in the "thing" drawn off grid, by a
* number of pixel equal to `y_offset`. This is only valid for images - not for
* colors.
*
* @param {object} [thing.transform] See the book
* **Reeborg's World: a Teacher's guide** for an explanation.
*
* @throws Will throw an error if `name` attribute is not specified.
* @throws Will throw an error if no image is supplied (either via the `url`
* or the `images` attribute) and `color` does not evaluate to true.
*/
RUR.add_new_thing = function (thing) {
"use strict";
var name;
name = thing.name;
if (name === undefined){
throw new RUR.ReeborgError("RUR.add_new_thing(thing): thing.name attribute missing.");
}
RUR.KNOWN_THINGS.push(name);
RUR.THINGS[name] = thing;
if (thing.color) {
return;
}
create_images(thing);
// Object goal (not required for decorative objects): either
// a single url or a list for animated images.
if (thing.goal) {
create_images(thing.goal);
}
};
function create_images(obj) {
if (obj.url) {
obj.image = new Image();
obj.image.src = obj.url;
obj.image.onload = RUR.onload_new_image;
} else if (obj.images) {
RUR.animate_images(obj);
} else {
throw new RUR.ReeborgError("Fatal error: need either thing.url or a list [thing.images]");
}
}
/** @function show_all_things
* @memberof RUR
* @instance
*
* @summary This method shows all known "things" in a table, with the exception
* of those defined with the `color` attribute. If a language
* other than English is selected, the translated name appears as well; this
* can be helpful to identify missing translations.
* If multiple images are shown, it means that the "thing" is shown as an
* animation in a world.
* Missing images in the **goal** column indicate that this "thing" cannot
* be used as an object to be picked up by Reeborg.
*
* @param {string} [property] If this argument is provided, only "things" for
* which this property/attribute is defined will be shown,
* and the value of the attribute will be shown as well.
*
* @example
* RUR.show_all_things()
* RUR.show_all_things("fatal")
*/
RUR.show_all_things = function (property) {
var i, j, info, images, name, url, begin, end, prop_str;
if (property !== undefined) {
info = "<h3>Things with property <code>" + property + "</code></h3>";
prop_str = "<th>" + property + "</th>";
} else {
info = '';
prop_str = '';
}
begin = "<table border='1'><tr><th>name</th>";
end = "<th>image(s)</th><th>goal?</th></tr>";
if (RUR.state.human_language != 'en') {
info += begin + "<th>translation</th>" + prop_str + end;
} else {
info += begin + prop_str + end;
}
for (i=0; i< RUR.KNOWN_THINGS.length; i++) {
name = RUR.KNOWN_THINGS[i];
if (property !== undefined) {
if (RUR.THINGS[name][property] === undefined) {
continue;
}
}
if (RUR.THINGS[name].color) {
continue;
}
url = RUR.THINGS[name].url;
images = RUR.THINGS[name].images;
info += "<tr><td>" + name + "</td><td>";
if (RUR.state.human_language != 'en') {
info += RUR.translate(name) + "</td><td>";
}
if (property !== undefined) {
info += RUR.THINGS[name][property] + "</td><td>";
}
if (url !== undefined) {
info += "<img src = '" + RUR.THINGS[name].url + "'><br>" +
RUR.THINGS[name].url + "</td><td>";
} else if (images !== undefined) {
for(j=0; j<images.length; j++) {
info += "<img src = '" + images[j] + "'> ";
}
for(j=0; j<images.length; j++) {
info += "<br>" + images[j];
}
info += "</td><td>";
} else {
info += "Missing image</td><td>";
}
if (RUR.THINGS[name].goal !== undefined) {
info += "<img src = '" + RUR.THINGS[name].goal.url + "'><br>"
+ RUR.THINGS[name].goal.url;
}
info += "</td></tr>";
}
info += "</table>";
RUR._print_html_(info, true); // true will replace existing content
return null; // for the python repl
};
/** @function has_property
* @memberof RUR
* @instance
*
* @summary This method returns "true" if a "thing" has the stated property,
* and "false" otherwise
*
* @param {string} name The name of the "thing".
*
* @param {string} property
*
* @example {@lang python}
* # Python example
* print(RUR.has_property("water", "fatal"))
*
* @example
* // Javascript example
* write(RUR.has_property("water", "fatal"))
*/
RUR.has_property = function (name, property) {
name = RUR.translate_to_english(name);
if (RUR.THINGS[name] === undefined) {
throw new RUR.ReeborgError(RUR.translate("Unknown object").supplant({obj:name}));
}
if (RUR.THINGS[name][property] === undefined) {
return false;
} else {
return true;
}
};
/** @function get_property
* @memberof RUR
* @instance
*
* @summary This method returns the value of a given property for a "thing".
* **Important:** the returned value will be the English default even if a
* translation exists and might appear in other contexts, like the
* "World Info".
*
* If the property is undefined, `null` will be returned (which will be
* converted to `None` if Python is used).
*
* @param {string} name The name of the "thing".
*
* @param {string} property See the examples
*
*
* @example {@lang python}
* print(RUR.get_property("water", "info")) # Python
*
* @example {@lang javascript}
* write(RUR.get_property("water", "fatal")) // Javascript
*/
RUR.get_property = function (name, property) {
var property;
name = RUR.translate_to_english(name);
if (RUR.THINGS[name] === undefined) {
throw new RUR.ReeborgError(RUR.translate("Unknown object").supplant({obj:name}));
}
property = RUR.THINGS[name][property];
if (property === undefined) {
return null;
} else {
return property;
}
};
// Internal function used with name already translated into English;
// we undo the translation to avoid having a warning for a missing
// translation logged in the browser console.
RUR._get_property = function (name, property) {
return RUR.get_property(RUR.translate(name), property);
}
/*=============================
/
/ Deprecated methods below; likely used in Vincent Maille's book
/
/===========================*/
RUR.add_new_object_type = function (name, url, url_goal) {
RUR.add_new_thing({"name": name, "url": url, "goal": {"url": url_goal}});
};
RUR.add_object_image = RUR.add_new_object_type;