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) { 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;