diff --git a/changelog b/changelog index da8f1a9f38cc..f8247708a772 100644 --- a/changelog +++ b/changelog @@ -120,6 +120,13 @@ Version 1.13.4+dev: of the time in the overall schedule (eg, 1 would be dawn in the default schedule). Optional second argument takes a time area ID, to set local instead of global time. + * New wesnoth.add_fog and wesnoth.remove_fog functions allow changing fog + on the map. The [lift_fog] and [clear_fog] tags now use this. + * New wesnoth.add_sound_source, wesnoth.remove_sound_source, and + wesnoth.get_sound_source functions to allow manipulation of sound + sources. The [sound_source] and [remove_sound_source] now use these. + * New wesnoth.log function for printing log messages. The [wml_message] + and [deprecated_message] tags now use this. * WML tables defined in Lua now accept string keys with array values (where "array" is a table whose keys are all integers). This joins the elements of the array with commas and produces a single string diff --git a/data/lua/wml-tags.lua b/data/lua/wml-tags.lua index da547ad42dfb..7a79945c6320 100644 --- a/data/lua/wml-tags.lua +++ b/data/lua/wml-tags.lua @@ -1003,3 +1003,39 @@ wml_actions.teleport = function(cfg) end wesnoth.teleport(unit, cfg.check_passability == false, cfg.clear_shroud ~= false, cfg.animate) end + +function wml_actions.remove_sound_source(cfg) + wesnoth.remove_sound_source(cfg.id) +end + +function wml_actions.sound_source(cfg) + wesnoth.add_sound_source(cfg) +end + +function wml_actions.deprecated_message(cfg) + wesnoth.log('wml', cfg.message) +end + +function wml_actions.wml_message(cfg) + local logger = logger_aliases[cfg.logger] or '' + wesnoth.log(cfg.logger or 'warn', cfg.message. cfg.to_chat) +end + +local function parse_fog_cfg(cfg) + -- Side filter + local ssf = helper.child(cfg, "filter_side") + local sides = wesnoth.get_sides(ssf or {}) + -- Location filter + local locs = wesnoth.get_locations(cfg) + return locs, sides +end + +function wml_actions.lift_fog(cfg) + local locs, sides = parse_fog_cfg(cfg) + wesnoth.remove_fog(sides, locs, not cfg.multiturn) +end + +function wml_actions.reset_fog(cfg) + local locs, sides = parse_fog_cfg(cfg) + wesnoth.add_fog(sides, locs, cfg.reset_view) +end diff --git a/data/lua/wml/message.lua b/data/lua/wml/message.lua index d116e3fbf495..16c36e51847c 100644 --- a/data/lua/wml/message.lua +++ b/data/lua/wml/message.lua @@ -5,10 +5,7 @@ local location_set = wesnoth.require "lua/location_set.lua" local _ = wesnoth.textdomain "wesnoth" local function log(msg, level) - wesnoth.wml_actions.wml_message({ - message = msg, - logger = level, - }) + wesnoth.log(level, msg, true) end local function get_image(cfg, speaker) diff --git a/src/game_events/action_wml.cpp b/src/game_events/action_wml.cpp index 2693b072cf36..54eeb1153cb9 100644 --- a/src/game_events/action_wml.cpp +++ b/src/game_events/action_wml.cpp @@ -205,71 +205,8 @@ namespace { // Support functions } return path; } - - /** - * Implements the lifting and resetting of fog via WML. - * Keeping affect_normal_fog as false causes only the fog override to be affected. - * Otherwise, fog lifting will be implemented similar to normal sight (cannot be - * individually reset and ends at the end of the turn), and fog resetting will, in - * addition to removing overrides, extend the specified teams' normal fog to all - * hexes. - */ - void toggle_fog(const bool clear, const vconfig& cfg, const bool affect_normal_fog=false) - { - // Filter the sides. - const vconfig &ssf = cfg.child("filter_side"); - const side_filter s_filter(ssf.null() ? vconfig::empty_vconfig() : ssf, resources::filter_con); - const std::vector sides = s_filter.get_teams(); - - // Filter the locations. - std::set locs; - const terrain_filter t_filter(cfg, resources::filter_con); - t_filter.get_locations(locs, true); - - // Loop through sides. - for (const int &side_num : sides) - { - team &t = (*resources::teams)[side_num-1]; - if ( !clear ) - { - // Extend fog. - t.remove_fog_override(locs); - if ( affect_normal_fog ) - t.refog(); - } - else if ( !affect_normal_fog ) - // Force the locations clear of fog. - t.add_fog_override(locs); - else - // Simply clear fog from the locations. - for (const map_location &hex : locs) { - t.clear_fog(hex); - } - } - - // Flag a screen update. - resources::screen->recalculate_minimap(); - resources::screen->invalidate_all(); - } } // end anonymous namespace (support functions) -void handle_deprecated_message(const config& cfg) -{ - // Note: no need to translate the string, since only used for deprecated things. - const std::string& message = cfg["message"]; - lg::wml_error() << message << '\n'; -} - -void handle_wml_log_message(const config& cfg) -{ - const std::string& logger = cfg["logger"]; - const std::string& msg = cfg["message"]; - bool in_chat = cfg["to_chat"].to_bool(true); - - resources::game_events->pump().put_wml_message(logger,msg,in_chat); -} - - /** * Using this constructor for a static object outside action_wml.cpp * will likely lead to a static initialization fiasco. @@ -324,11 +261,6 @@ WML_HANDLER_FUNCTION(clear_global_variable,,pcfg) verify_and_clear_global_variable(pcfg); } -WML_HANDLER_FUNCTION(deprecated_message,, cfg) -{ - handle_deprecated_message( cfg.get_parsed_config() ); -} - static void on_replay_error(const std::string& message, bool /*b*/) { ERR_NG << "Error via [do_command]:" << std::endl; @@ -385,11 +317,6 @@ WML_HANDLER_FUNCTION(get_global_variable,,pcfg) verify_and_get_global_variable(pcfg); } -WML_HANDLER_FUNCTION(lift_fog,, cfg) -{ - toggle_fog(true, cfg, !cfg["multiturn"].to_bool(false)); -} - WML_HANDLER_FUNCTION(modify_turns,, cfg) { config::attribute_value value = cfg["value"]; @@ -573,10 +500,6 @@ WML_HANDLER_FUNCTION(recall,, cfg) LOG_WML << "A [recall] tag with the following content failed:\n" << cfg.get_config().debug(); } -WML_HANDLER_FUNCTION(remove_sound_source,, cfg) -{ - resources::soundsources->remove(cfg["id"]); -} namespace { struct map_choice : public mp_sync::user_choice { @@ -616,6 +539,7 @@ namespace { }; } + /// Experimental map replace /// @todo Finish experimenting. WML_HANDLER_FUNCTION(replace_map,, cfg) @@ -676,11 +600,6 @@ WML_HANDLER_FUNCTION(replace_map,, cfg) ai::manager::raise_map_changed(); } -WML_HANDLER_FUNCTION(reset_fog,, cfg) -{ - toggle_fog(false, cfg, cfg["reset_view"].to_bool(false)); -} - /// Experimental data persistence /// @todo Finish experimenting. WML_HANDLER_FUNCTION(set_global_variable,,pcfg) @@ -1029,19 +948,6 @@ WML_HANDLER_FUNCTION(set_variables,, cfg) } } -WML_HANDLER_FUNCTION(sound_source,, cfg) -{ - config parsed = cfg.get_parsed_config(); - try { - soundsource::sourcespec spec(parsed); - resources::soundsources->add(spec); - } catch (bad_lexical_cast &) { - ERR_NG << "Error when parsing sound_source config: bad lexical cast." << std::endl; - ERR_NG << "sound_source config was: " << parsed.debug() << std::endl; - ERR_NG << "Skipping this sound source..." << std::endl; - } -} - /// Store the relative direction from one hex to another in a WML variable. /// This is mainly useful as a diagnostic tool, but could be useful /// for some kind of scenario. @@ -1274,11 +1180,6 @@ WML_HANDLER_FUNCTION(volume,, cfg) } -WML_HANDLER_FUNCTION(wml_message,, cfg) -{ - handle_wml_log_message( cfg.get_parsed_config() ); -} - WML_HANDLER_FUNCTION(on_undo,, cfg) { if(cfg["delayed_variable_substitution"].to_bool(false)) { diff --git a/src/game_events/action_wml.hpp b/src/game_events/action_wml.hpp index 09834a8e1c27..717f3a894fe5 100644 --- a/src/game_events/action_wml.hpp +++ b/src/game_events/action_wml.hpp @@ -72,11 +72,6 @@ namespace game_events */ void change_terrain(const map_location &loc, const t_translation::t_terrain &t, terrain_type_data::tmerge_mode mode, bool replace_if_failed); - - /** Used for [deprecated_message]. */ - void handle_deprecated_message(const config& cfg); - /** Used for [wml_message]. */ - void handle_wml_log_message(const config& cfg); } #endif // GAME_EVENTS_ACTION_WML_H_INCLUDED diff --git a/src/scripting/game_lua_kernel.cpp b/src/scripting/game_lua_kernel.cpp index 83e9e03c5ccc..f8230f4b69b7 100644 --- a/src/scripting/game_lua_kernel.cpp +++ b/src/scripting/game_lua_kernel.cpp @@ -85,6 +85,7 @@ #include "sdl/utils.hpp" // for surface #include "side_filter.hpp" // for side_filter #include "sound.hpp" // for commit_music_changes, etc +#include "soundsource.hpp" #include "synced_context.hpp" // for synced_context, etc #include "synced_user_choice.hpp" #include "team.hpp" // for team, village_owner @@ -121,6 +122,7 @@ #include // for set #include // for operator<<, basic_ostream, etc #include // for pair +#include #include // for vector, etc #include // for SDL_GetTicks #include // for SDL_Color, SDL_Surface @@ -4233,6 +4235,139 @@ int game_lua_kernel::intf_teleport(lua_State *L) return 0; } +/** + * Removes a sound source by its ID + * Arg 1: sound source ID + */ +int game_lua_kernel::intf_remove_sound_source(lua_State *L) +{ + soundsource::manager* man = play_controller_.get_soundsource_man(); + std::string id = luaL_checkstring(L, 1); + man->remove(id); + return 0; +} + +/** + * Add a new sound source + * Arg 1: Table containing keyword arguments + */ +int game_lua_kernel::intf_add_sound_source(lua_State *L) +{ + soundsource::manager* man = play_controller_.get_soundsource_man(); + config cfg = luaW_checkconfig(L, 1); + try { + soundsource::sourcespec spec(cfg); + man->add(spec); + } catch (bad_lexical_cast &) { + ERR_LUA << "Error when parsing sound_source config: invalid parameter." << std::endl; + ERR_LUA << "sound_source config was: " << cfg.debug() << std::endl; + ERR_LUA << "Skipping this sound source..." << std::endl; + } + return 0; +} + +/** + * Get an existing sound source + * Arg 1: The sound source ID + * Return: Config of sound source info, or nil if it didn't exist + * This is a copy of the sound source info, so you need to call + * add_sound_source again after changing it. + */ +int game_lua_kernel::intf_get_sound_source(lua_State *L) +{ + soundsource::manager* man = play_controller_.get_soundsource_man(); + std::string id = luaL_checkstring(L, 1); + config cfg = man->get(id); + if(cfg.empty()) { + return 0; + } + // Sound sources do not know their own string ID + // Thus, we need to add this manually + cfg["id"] = id; + luaW_pushconfig(L, cfg); + return 1; +} + +/** + * Logs a message + * Arg 1: (optional) Logger; "wml" for WML errors or deprecations + * Arg 2: Message + * Arg 3: Whether to print to chat (always true if arg 1 is "wml") + */ +int game_lua_kernel::intf_log(lua_State *L) +{ + const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) : ""; + const std::string& msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1); + + if(logger == "wml" || logger == "WML") { + lg::wml_error() << msg << '\n'; + } else { + bool in_chat = luaW_toboolean(L, -1); + game_state_.events_manager_->pump().put_wml_message(logger,msg,in_chat); + } + return 0; +} + +/** + * Implements the lifting and resetting of fog via WML. + * Keeping affect_normal_fog as false causes only the fog override to be affected. + * Otherwise, fog lifting will be implemented similar to normal sight (cannot be + * individually reset and ends at the end of the turn), and fog resetting will, in + * addition to removing overrides, extend the specified teams' normal fog to all + * hexes. + * + * Arg 1: (optional) Side number, or list of side numbers + * Arg 2: List of locations; each is a two-element array or a table with x and y keys + * Arg 3: (optional) boolean + */ +int game_lua_kernel::intf_toggle_fog(lua_State *L, const bool clear) +{ + bool affect_normal_fog = false; + if(lua_isboolean(L, -1)) { + affect_normal_fog = luaW_toboolean(L, -1); + } + std::set sides; + if(lua_isnumber(L, 1)) { + sides.insert(lua_tonumber(L, 1)); + } else if(lua_istable(L, 2)) { + const auto& v = lua_check>(L, 1); + sides.insert(v.begin(), v.end()); + } else { + for(const team& t : teams()) { + sides.insert(t.side()+1); + } + } + const auto& v_locs = lua_check>(L, lua_istable(L, 2) ? 2 : 1); + std::set locs(v_locs.begin(), v_locs.end()); + + for(const int &side_num : sides) { + if(side_num < 1 || static_cast(side_num) > teams().size()) { + continue; + } + team &t = teams()[side_num-1]; + if(!clear) { + // Extend fog. + t.remove_fog_override(locs); + if(affect_normal_fog) { + t.refog(); + } + } else if(!affect_normal_fog) { + // Force the locations clear of fog. + t.add_fog_override(locs); + } else { + // Simply clear fog from the locations. + for(const map_location &hex : locs) { + t.clear_fog(hex); + } + } + } + + // Flag a screen update. + game_display_->recalculate_minimap(); + game_display_->invalidate_all(); + return 0; +} + // END CALLBACK IMPLEMENTATION game_board & game_lua_kernel::board() { @@ -4325,8 +4460,10 @@ game_lua_kernel::game_lua_kernel(CVideo * video, game_state & gs, play_controlle { "unit_resistance", &intf_unit_resistance }, { "unsynced", &intf_do_unsynced }, { "add_event_handler", &dispatch<&game_lua_kernel::intf_add_event > }, + { "add_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true > }, { "add_tile_overlay", &dispatch<&game_lua_kernel::intf_add_tile_overlay > }, { "add_time_area", &dispatch<&game_lua_kernel::intf_add_time_area > }, + { "add_sound_source", &dispatch<&game_lua_kernel::intf_add_sound_source > }, { "allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn > }, { "allow_undo", &dispatch<&game_lua_kernel::intf_allow_undo > }, { "animate_unit", &dispatch<&game_lua_kernel::intf_animate_unit > }, @@ -4354,6 +4491,7 @@ game_lua_kernel::game_lua_kernel(CVideo * video, game_state & gs, play_controlle { "get_recall_units", &dispatch<&game_lua_kernel::intf_get_recall_units > }, { "get_selected_tile", &dispatch<&game_lua_kernel::intf_get_selected_tile > }, { "get_sides", &dispatch<&game_lua_kernel::intf_get_sides > }, + { "get_sound_source", &dispatch<&game_lua_kernel::intf_get_sound_source > }, { "get_starting_location", &dispatch<&game_lua_kernel::intf_get_starting_location > }, { "get_terrain", &dispatch<&game_lua_kernel::intf_get_terrain > }, { "get_terrain_info", &dispatch<&game_lua_kernel::intf_get_terrain_info > }, @@ -4371,6 +4509,7 @@ game_lua_kernel::game_lua_kernel(CVideo * video, game_state & gs, play_controlle { "kill", &dispatch<&game_lua_kernel::intf_kill > }, { "label", &dispatch<&game_lua_kernel::intf_label > }, { "lock_view", &dispatch<&game_lua_kernel::intf_lock_view > }, + { "log", &dispatch<&game_lua_kernel::intf_log > }, { "match_location", &dispatch<&game_lua_kernel::intf_match_location > }, { "match_side", &dispatch<&game_lua_kernel::intf_match_side > }, { "match_unit", &dispatch<&game_lua_kernel::intf_match_unit > }, @@ -4385,8 +4524,10 @@ game_lua_kernel::game_lua_kernel(CVideo * video, game_state & gs, play_controlle { "random", &dispatch<&game_lua_kernel::intf_random > }, { "redraw", &dispatch<&game_lua_kernel::intf_redraw > }, { "remove_event_handler", &dispatch<&game_lua_kernel::intf_remove_event > }, + { "remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false > }, { "remove_tile_overlay", &dispatch<&game_lua_kernel::intf_remove_tile_overlay > }, { "remove_time_area", &dispatch<&game_lua_kernel::intf_remove_time_area > }, + { "remove_sound_source", &dispatch<&game_lua_kernel::intf_remove_sound_source > }, { "replace_schedule", &dispatch<&game_lua_kernel::intf_replace_schedule > }, { "scroll", &dispatch<&game_lua_kernel::intf_scroll > }, { "scroll_to_tile", &dispatch<&game_lua_kernel::intf_scroll_to_tile > }, diff --git a/src/scripting/game_lua_kernel.hpp b/src/scripting/game_lua_kernel.hpp index 8d13b1a124ca..ebf599e5af91 100644 --- a/src/scripting/game_lua_kernel.hpp +++ b/src/scripting/game_lua_kernel.hpp @@ -164,6 +164,11 @@ class game_lua_kernel : public lua_kernel_base int intf_fire_event(lua_State *L); int intf_fire_wml_menu_item(lua_State *L); int intf_teleport(lua_State *L); + int intf_remove_sound_source(lua_State *L); + int intf_add_sound_source(lua_State *L); + int intf_get_sound_source(lua_State *L); + int intf_log(lua_State *L); + int intf_toggle_fog(lua_State *L, const bool clear); //private helpers std::string synced_state(); diff --git a/src/soundsource.cpp b/src/soundsource.cpp index d3d3e08ac44f..d8cbe1cb7c65 100644 --- a/src/soundsource.cpp +++ b/src/soundsource.cpp @@ -67,6 +67,16 @@ void manager::add(const sourcespec &spec) } } +config manager::get(const std::string &id) +{ + config cfg; + positional_source_iterator it = sources_.find(id); + if(it != sources_.end()) { + it->second->write_config(cfg); + } + return cfg; +} + void manager::remove(const std::string &id) { positional_source_iterator it; diff --git a/src/soundsource.hpp b/src/soundsource.hpp index 4127104d14b4..6f7a472a9e1c 100644 --- a/src/soundsource.hpp +++ b/src/soundsource.hpp @@ -93,6 +93,7 @@ class manager : public events::observer, public savegame::savegame_config { // add or replace a soundsource void add(const sourcespec &source); void remove(const std::string &id); + config get(const std::string &id); void update(); // checks which sound sources are visible diff --git a/src/storyscreen/controller.cpp b/src/storyscreen/controller.cpp index 49bb615e0c63..56b88bbf815b 100644 --- a/src/storyscreen/controller.cpp +++ b/src/storyscreen/controller.cpp @@ -26,8 +26,9 @@ #include "asserts.hpp" #include "variable.hpp" -#include "game_events/action_wml.hpp" #include "game_events/conditional_wml.hpp" +#include "game_events/manager.hpp" +#include "game_events/pump.hpp" #include "game_data.hpp" #include "gettext.hpp" #include "intro.hpp" @@ -129,13 +130,13 @@ void controller::resolve_wml(const vconfig& cfg) // [deprecated_message] else if(key == "deprecated_message") { // Won't appear until the scenario start event finishes. - game_events::handle_deprecated_message(node.get_parsed_config()); + lg::wml_error() << node["message"] << '\n'; } // [wml_message] else if(key == "wml_message") { - // Pass to game events handler. As with [deprecated_message], + // As with [deprecated_message], // it won't appear until the scenario start event is complete. - game_events::handle_wml_log_message(node.get_parsed_config()); + resources::game_events->pump().put_wml_message(node["logger"],node["message"],node["in_chat"]); } } } diff --git a/src/storyscreen/part.cpp b/src/storyscreen/part.cpp index ce292bacb85a..6dc4c974dc34 100644 --- a/src/storyscreen/part.cpp +++ b/src/storyscreen/part.cpp @@ -25,8 +25,9 @@ #include "config.hpp" #include "game_data.hpp" -#include "game_events/action_wml.hpp" #include "game_events/conditional_wml.hpp" +#include "game_events/manager.hpp" +#include "game_events/pump.hpp" #include "image.hpp" #include "serialization/string_utils.hpp" #include "util.hpp" @@ -349,13 +350,13 @@ void part::resolve_wml(const vconfig &cfg) // [deprecated_message] else if(key == "deprecated_message") { // Won't appear until the scenario start event finishes. - game_events::handle_deprecated_message(node.get_parsed_config()); + lg::wml_error() << node["message"] << '\n'; } // [wml_message] else if(key == "wml_message") { - // Pass to game events handler. As with [deprecated_message], + // As with [deprecated_message], // it won't appear until the scenario start event is complete. - game_events::handle_wml_log_message(node.get_parsed_config()); + resources::game_events->pump().put_wml_message(node["logger"],node["message"],node["in_chat"]); } } }