Merge pull request #962 from wesnoth/wml_tag_porting
Porting WML tags to Lua (part 2)
This commit is contained in:
commit
cdca6b79d8
19 changed files with 642 additions and 252 deletions
24
changelog
24
changelog
|
@ -17,6 +17,28 @@ Version 1.13.7+dev:
|
|||
required to be a space adjacent to the unit.
|
||||
* New modifiable theme attribute in wesnoth.game_config
|
||||
* New wesnoth.zoom() function allows changing the zoom level
|
||||
* The wesnoth.scroll function scrolls the screen by an offset, similar to [scroll].
|
||||
For example, wesnoth.scroll(5, -2)
|
||||
* New is_local attribute in side proxy table allows you to detect whether or not the side is
|
||||
controlled by a network player. Note that use of this has the potential for OOS errors.
|
||||
* New wesnoth.music_list table which allows controlling the music playlist:
|
||||
* wesnoth.music_list[1] etc returns mutable information about a specific track on the playlist
|
||||
* #wesnoth.music_list counts the number of tracks on the playlist
|
||||
* wesnoth.music_list.current returns mutable information about the currently-playing track
|
||||
* wesnoth.music_list.current_i returns the index of the current track on the list
|
||||
It is writeable, allowing you to switch to any track on the list. This respects fade values.
|
||||
* wesnoth.music_list.all returns a copy of the playlist that can be stored in a variable.
|
||||
* wesnoth.music_list.play plays a specific track (as [music]play_once=yes)
|
||||
* wesnoth.music_list.add appends a track to the playlist (as [music]append=yes)
|
||||
* wesnoth.music_list.clear clears the current playlist
|
||||
* wesnoth.music_list.next fades out the current track and moves to a new track on the playlist
|
||||
* wesnoth.music_list.force_refresh forces any pending playlist changes to be immediately applied
|
||||
* wesnoth.music_list.volume attribute gets/sets the current music volume, as [volume]music=
|
||||
* Each track has modifiable shuffle, once, ms_before, ms_after attributes and
|
||||
read-only append, immediate, name, title attributes. They are also
|
||||
comparable with == or ~=
|
||||
* wesnoth.set_music is now deprecated, in favour of the above new API
|
||||
* New wesnoth.sound_volume function gets/sets the current sound volume, as [volume]sound=
|
||||
* Multiplayer:
|
||||
* Fixed statistics being lost when reloading an MP game.
|
||||
* Performance:
|
||||
|
@ -44,6 +66,8 @@ Version 1.13.7+dev:
|
|||
* Empty tags are no longer written to the configs of [unit]s and [side]s.
|
||||
* New [change_theme] tag to change the theme mid-scenario
|
||||
* New [zoom] tag allows changing the zoom level from an event
|
||||
* [kill]animate=yes now plays victory animations if applicable
|
||||
* Fix [volume] not accepting 100% as the new volume.
|
||||
|
||||
Version 1.13.7:
|
||||
* AI:
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
local helper = wesnoth.require "lua/helper.lua"
|
||||
|
||||
function wesnoth.set_music(cfg)
|
||||
wesnoth.wml_actions.music(cfg)
|
||||
end
|
||||
|
||||
-- Calling wesnoth.fire isn't the same as calling wesnoth.wml_actions[name] due to the passed vconfig userdata
|
||||
-- which also provides "constness" of the passed wml object from the point of view of the caller.
|
||||
-- So please don't remove since it's not deprecated.
|
||||
|
|
|
@ -266,7 +266,35 @@ function wml_actions.lua(cfg)
|
|||
end
|
||||
|
||||
function wml_actions.music(cfg)
|
||||
wesnoth.set_music(cfg)
|
||||
if cfg.play_once then
|
||||
wesnoth.music_list.play(cfg.name)
|
||||
else
|
||||
if not cfg.append then
|
||||
if cfg.immediate then
|
||||
wesnoth.music_list.current.once = true
|
||||
end
|
||||
wesnoth.music_list.clear()
|
||||
end
|
||||
wesnoth.music_list.add(cfg.name, not not cfg.immediate, cfg.ms_before or 0, cfg.ms_after or 0)
|
||||
local n = #wesnoth.music_list
|
||||
if cfg.shuffle == false then
|
||||
wesnoth.music_list[n].shuffle = false
|
||||
end
|
||||
if cfg.title ~= nil then
|
||||
wesnoth.music_list[n].title = cfg.title
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.volume(cfg)
|
||||
if cfg.music then
|
||||
local rel = tonumber(cfg.music) or 100.0
|
||||
wesnoth.music_list.volume = rel
|
||||
end
|
||||
if cfg.sound then
|
||||
local rel = tonumber(cfg.sound) or 100.0
|
||||
wesnoth.sound_volume(rel)
|
||||
end
|
||||
end
|
||||
|
||||
-- This is mainly for use in unit test macros, but maybe it can be useful elsewhere too
|
||||
|
@ -919,7 +947,16 @@ function wml_actions.replace_schedule(cfg)
|
|||
end
|
||||
|
||||
function wml_actions.scroll(cfg)
|
||||
wesnoth.scroll(cfg)
|
||||
local sides = utils.get_sides(cfg)
|
||||
local have_human = false
|
||||
for i, side in ipairs(sides) do
|
||||
if side.controller == 'human' and side.is_local then
|
||||
have_human = true
|
||||
end
|
||||
end
|
||||
if have_human or #sides == 0 then
|
||||
wesnoth.scroll(cfg.x, cfg.y)
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.color_adjust(cfg)
|
||||
|
@ -950,8 +987,73 @@ function wml_actions.inspect(cfg)
|
|||
wesnoth.gamestate_inspector(cfg)
|
||||
end
|
||||
|
||||
local kill_recursion_preventer = wesnoth.require("lua/location_set.lua").create()
|
||||
function wml_actions.kill(cfg)
|
||||
wesnoth.kill(cfg)
|
||||
local number_killed = 0
|
||||
local secondary_unit = helper.get_child(cfg, "secondary_unit")
|
||||
local killer_loc = {0, 0}
|
||||
if secondary_unit then
|
||||
secondary_unit = wesnoth.get_units(secondary_unit)[1]
|
||||
if cfg.fire_event then
|
||||
if secondary_unit then
|
||||
killer_loc = {secondary_unit.loc}
|
||||
else
|
||||
wesnoth.log("warn", "failed to match [secondary_unit] in [kill] with a single on-board unit")
|
||||
end
|
||||
end
|
||||
end
|
||||
local dead_men_walking = wesnoth.get_units(cfg)
|
||||
for i,unit in ipairs(dead_men_walking) do
|
||||
local death_loc = {x = tonumber(unit.x) or 0, y = tonumber(unit.y) or 0}
|
||||
if not secondary_unit then killer_loc = death_loc end
|
||||
local can_fire = false
|
||||
|
||||
local recursion = (kill_recursion_preventer:get(death_loc.x, death_loc.y) or 0) + 1
|
||||
if cfg.fire_event then
|
||||
kill_recursion_preventer:insert(death_loc.x, death_loc.y, recursion)
|
||||
can_fire = true
|
||||
if death_loc.x == wesnoth.current.event.x1 and death_loc.y == wesnoth.current.event.y1 then
|
||||
if wesnoth.current.event.name == "die" or wesnoth.current.event.name == "last breath" then
|
||||
if recursion >= 10 then
|
||||
can_fire = false;
|
||||
wesnoth.log("error", "tried to fire 'die' or 'last breath' event on unit from the unit's 'die' or 'last breath' event with first_time_only=no!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if can_fire then
|
||||
wesnoth.fire_event("last breath", death_loc, killer_loc)
|
||||
end
|
||||
if cfg.animate then
|
||||
wesnoth.scroll_to_tile(death_loc)
|
||||
local anim = wesnoth.create_animator()
|
||||
-- Possible TODO: Add weapon selection? (That's kinda a pain right now; see animate_unit defn)
|
||||
anim:add(unit, "death", "kill")
|
||||
if secondary_unit then
|
||||
anim:add(secondary_unit, "victory", "kill")
|
||||
end
|
||||
anim:run()
|
||||
end
|
||||
wml_actions.redraw{}
|
||||
|
||||
if can_fire then
|
||||
wesnoth.fire_event("die", death_loc, killer_loc)
|
||||
end
|
||||
if cfg.fire_event then
|
||||
if recursion <= 1 then
|
||||
kill_recursion_preventer:remove(death_loc.x, death_loc.y)
|
||||
else
|
||||
kill_recursion_preventer:insert(death_loc.x, death_loc.y, recursion)
|
||||
end
|
||||
end
|
||||
-- Test that it's valid (and still on the map) first, in case the event erased (or extracted) it.
|
||||
if unit.valid == "map" then unit:erase() end
|
||||
|
||||
number_killed = number_killed + 1
|
||||
end
|
||||
|
||||
-- TODO: Do I need to check recall lists or was that covered by the above loop?
|
||||
return number_killed
|
||||
end
|
||||
|
||||
function wml_actions.label( cfg )
|
||||
|
|
|
@ -154,8 +154,6 @@ function utils.handle_event_commands(cfg, scope_type)
|
|||
end
|
||||
current_exit = "none"
|
||||
end
|
||||
-- Apply music alterations once all the commands have been processed.
|
||||
wesnoth.set_music()
|
||||
return current_exit
|
||||
end
|
||||
|
||||
|
|
|
@ -2581,6 +2581,12 @@
|
|||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Scripting\</ObjectFileName>
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Scripting\</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\scripting\lua_audio.cpp">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Scripting\</ObjectFileName>
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Scripting\</ObjectFileName>
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Scripting\</ObjectFileName>
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Scripting\</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\scripting\lua_common.cpp">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Scripting\</ObjectFileName>
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Scripting\</ObjectFileName>
|
||||
|
@ -3866,6 +3872,7 @@
|
|||
<ClInclude Include="..\..\src\scripting\application_lua_kernel.hpp" />
|
||||
<ClInclude Include="..\..\src\scripting\debug_lua.hpp" />
|
||||
<ClInclude Include="..\..\src\scripting\game_lua_kernel.hpp" />
|
||||
<ClInclude Include="..\..\src\scripting\lua_audio.hpp" />
|
||||
<ClInclude Include="..\..\src\scripting\lua_common.hpp" />
|
||||
<ClInclude Include="..\..\src\scripting\lua_cpp_function.hpp" />
|
||||
<ClInclude Include="..\..\src\scripting\lua_fileops.hpp" />
|
||||
|
|
|
@ -1529,6 +1529,9 @@
|
|||
<ClCompile Include="..\..\src\font\text_surface.cpp">
|
||||
<Filter>Font</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\scripting\lua_audio.cpp">
|
||||
<Filter>Scripting</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\addon\client.hpp">
|
||||
|
@ -2972,6 +2975,9 @@
|
|||
<ClInclude Include="..\..\src\font\pango\stream_ops.hpp">
|
||||
<Filter>Font\Pango</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\scripting\lua_audio.hpp">
|
||||
<Filter>Scripting</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="..\..\src\tests\test_sdl_utils.hpp">
|
||||
|
|
|
@ -338,6 +338,7 @@ savegame.cpp
|
|||
scripting/application_lua_kernel.cpp
|
||||
scripting/debug_lua.cpp
|
||||
scripting/game_lua_kernel.cpp
|
||||
scripting/lua_audio.cpp
|
||||
scripting/lua_common.cpp
|
||||
scripting/lua_cpp_function.cpp
|
||||
scripting/lua_fileops.cpp
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "variable.hpp" // vconfig
|
||||
#include "game_data.hpp"
|
||||
#include "units/unit.hpp"
|
||||
#include "sound.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
|
@ -113,6 +114,7 @@ namespace {
|
|||
|
||||
game_events::queued_event q(tag, "", map_location(x1, y1, wml_loc()), map_location(x2, y2, wml_loc()), e.data);
|
||||
resources::lua_kernel->run_wml_action("command", vconfig(e.commands), q);
|
||||
sound::commit_music_changes();
|
||||
|
||||
x1 = oldx1; y1 = oldy1;
|
||||
x2 = oldx2; y2 = oldy2;
|
||||
|
|
|
@ -929,34 +929,6 @@ WML_HANDLER_FUNCTION(unit,, cfg)
|
|||
|
||||
}
|
||||
|
||||
WML_HANDLER_FUNCTION(volume,, cfg)
|
||||
{
|
||||
|
||||
int vol;
|
||||
float rel;
|
||||
std::string music = cfg["music"];
|
||||
std::string sound = cfg["sound"];
|
||||
|
||||
if(!music.empty()) {
|
||||
vol = preferences::music_volume();
|
||||
rel = atof(music.c_str());
|
||||
if (rel >= 0.0f && rel < 100.0f) {
|
||||
vol = static_cast<int>(rel*vol/100.0f);
|
||||
}
|
||||
sound::set_music_volume(vol);
|
||||
}
|
||||
|
||||
if(!sound.empty()) {
|
||||
vol = preferences::sound_volume();
|
||||
rel = atof(sound.c_str());
|
||||
if (rel >= 0.0f && rel < 100.0f) {
|
||||
vol = static_cast<int>(rel*vol/100.0f);
|
||||
}
|
||||
sound::set_sound_volume(vol);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WML_HANDLER_FUNCTION(on_undo, event_info, cfg)
|
||||
{
|
||||
if(cfg["delayed_variable_substitution"].to_bool(false)) {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "reports.hpp"
|
||||
#include "scripting/game_lua_kernel.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "soundsource.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
@ -127,6 +128,7 @@ void event_handler::handle_event(const queued_event& event_info, handler_ptr& ha
|
|||
// *WARNING*: At this point, dereferencing this could be a memory violation!
|
||||
|
||||
lk.run_wml_action("command", vcfg, event_info);
|
||||
sound::commit_music_changes();
|
||||
}
|
||||
|
||||
bool event_handler::matches_name(const std::string &name, const game_data * gd) const
|
||||
|
|
|
@ -323,6 +323,7 @@ LEVEL_RESULT playsingle_controller::play_scenario(const config& level)
|
|||
// instead should they want special music.
|
||||
const std::string& end_music = select_music(is_victory);
|
||||
if((!is_victory || end_level.transient.carryover_report) && !end_music.empty()) {
|
||||
sound::empty_playlist();
|
||||
sound::play_music_once(end_music);
|
||||
}
|
||||
persist_.end_transaction();
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
#include "recall_list_manager.hpp" // for recall_list_manager
|
||||
#include "replay.hpp" // for get_user_choice, etc
|
||||
#include "reports.hpp" // for register_generator, etc
|
||||
#include "scripting/lua_audio.hpp"
|
||||
#include "scripting/lua_unit.hpp"
|
||||
#include "scripting/lua_unit_attacks.hpp"
|
||||
#include "scripting/lua_common.hpp"
|
||||
|
@ -2512,6 +2513,7 @@ int game_lua_kernel::intf_simulate_combat(lua_State *L)
|
|||
*/
|
||||
static int intf_set_music(lua_State *L)
|
||||
{
|
||||
lg::wml_error() << "set_music is deprecated; please use the wesnoth.playlist table instead!\n";
|
||||
if (lua_isnoneornil(L, 1)) {
|
||||
sound::commit_music_changes();
|
||||
return 0;
|
||||
|
@ -2536,6 +2538,26 @@ int game_lua_kernel::intf_play_sound(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets/sets the current sound volume
|
||||
* - Arg 1: (optional) New volume to set
|
||||
* - Return: Original volume
|
||||
*/
|
||||
static int intf_sound_volume(lua_State* L)
|
||||
{
|
||||
int vol = preferences::sound_volume();
|
||||
lua_pushnumber(L, sound::get_sound_volume() * 100.0f / vol);
|
||||
if(lua_isnumber(L, 1)) {
|
||||
float rel = lua_tonumber(L, 1);
|
||||
if(rel < 0.0f || rel > 100.0f) {
|
||||
return luaL_argerror(L, 1, "volume must be in range 0..100");
|
||||
}
|
||||
vol = static_cast<int>(rel*vol / 100.0f);
|
||||
sound::set_sound_volume(vol);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls to given tile.
|
||||
* - Arg 1: location.
|
||||
|
@ -3231,165 +3253,6 @@ int game_lua_kernel::intf_delay(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
namespace { // Types
|
||||
|
||||
class recursion_preventer {
|
||||
typedef std::map<map_location, int> counter;
|
||||
static counter counter_;
|
||||
static const int max_recursion = 10;
|
||||
|
||||
map_location loc_;
|
||||
bool too_many_recursions_;
|
||||
|
||||
public:
|
||||
recursion_preventer(map_location& loc) :
|
||||
loc_(loc),
|
||||
too_many_recursions_(false)
|
||||
{
|
||||
counter::iterator inserted = counter_.emplace(loc_, 0).first;
|
||||
++inserted->second;
|
||||
too_many_recursions_ = inserted->second >= max_recursion;
|
||||
}
|
||||
~recursion_preventer()
|
||||
{
|
||||
counter::iterator itor = counter_.find(loc_);
|
||||
if (--itor->second == 0)
|
||||
{
|
||||
counter_.erase(itor);
|
||||
}
|
||||
}
|
||||
bool too_many_recursions() const
|
||||
{
|
||||
return too_many_recursions_;
|
||||
}
|
||||
};
|
||||
recursion_preventer::counter recursion_preventer::counter_;
|
||||
typedef std::unique_ptr<recursion_preventer> recursion_preventer_ptr;
|
||||
} // end anonymouse namespace (types)
|
||||
|
||||
int game_lua_kernel::intf_kill(lua_State *L)
|
||||
{
|
||||
vconfig cfg(luaW_checkvconfig(L, 1));
|
||||
|
||||
const game_events::queued_event &event_info = get_event_info();
|
||||
|
||||
size_t number_killed = 0;
|
||||
|
||||
bool secondary_unit = cfg.has_child("secondary_unit");
|
||||
game_events::entity_location killer_loc(map_location(0, 0));
|
||||
if(cfg["fire_event"].to_bool() && secondary_unit)
|
||||
{
|
||||
secondary_unit = false;
|
||||
const unit_filter ufilt(cfg.child("secondary_unit"), &game_state_);
|
||||
for(unit_map::const_unit_iterator unit = units().begin(); unit; ++unit) {
|
||||
if ( ufilt( *unit) )
|
||||
{
|
||||
killer_loc = game_events::entity_location(*unit);
|
||||
secondary_unit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!secondary_unit) {
|
||||
WRN_LUA << "failed to match [secondary_unit] in [kill] with a single on-board unit" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//Find all the dead units first, because firing events ruins unit_map iteration
|
||||
std::vector<unit *> dead_men_walking;
|
||||
const unit_filter ufilt(cfg, &game_state_);
|
||||
for (unit & u : units()){
|
||||
if ( ufilt(u) ) {
|
||||
dead_men_walking.push_back(&u);
|
||||
}
|
||||
}
|
||||
|
||||
for(unit * un : dead_men_walking) {
|
||||
map_location loc(un->get_location());
|
||||
bool fire_event = false;
|
||||
game_events::entity_location death_loc(*un);
|
||||
if(!secondary_unit) {
|
||||
killer_loc = game_events::entity_location(*un);
|
||||
}
|
||||
|
||||
if (cfg["fire_event"].to_bool())
|
||||
{
|
||||
// Prevent infinite recursion of 'die' events
|
||||
fire_event = true;
|
||||
recursion_preventer_ptr recursion_prevent;
|
||||
|
||||
if (event_info.loc1 == death_loc && (event_info.name == "die" || event_info.name == "last breath"))
|
||||
{
|
||||
recursion_prevent.reset(new recursion_preventer(death_loc));
|
||||
|
||||
if(recursion_prevent->too_many_recursions())
|
||||
{
|
||||
fire_event = false;
|
||||
|
||||
ERR_LUA << "tried to fire 'die' or 'last breath' event on primary_unit inside its own 'die' or 'last breath' event with 'first_time_only' set to false!" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fire_event) {
|
||||
play_controller_.pump().fire("last_breath", death_loc, killer_loc);
|
||||
}
|
||||
|
||||
// Visual consequences of the kill.
|
||||
if (game_display_) {
|
||||
if (cfg["animate"].to_bool()) {
|
||||
game_display_->scroll_to_tile(loc);
|
||||
if (unit_map::iterator iun = units().find(loc)) {
|
||||
unit_display::unit_die(loc, *iun);
|
||||
}
|
||||
} else {
|
||||
// Make sure the unit gets (fully) cleaned off the screen.
|
||||
game_display_->invalidate(loc);
|
||||
if (unit_map::iterator iun = units().find(loc)) {
|
||||
iun->anim_comp().invalidate(*game_display_);
|
||||
}
|
||||
}
|
||||
game_display_->redraw_minimap();
|
||||
}
|
||||
|
||||
if (fire_event) {
|
||||
play_controller_.pump().fire("die", death_loc, killer_loc);
|
||||
unit_map::iterator iun = units().find(death_loc);
|
||||
if ( death_loc.matches_unit(iun) ) {
|
||||
units().erase(iun);
|
||||
}
|
||||
}
|
||||
else units().erase(loc);
|
||||
|
||||
++number_killed;
|
||||
}
|
||||
|
||||
// If the filter doesn't contain positional information,
|
||||
// then it may match units on all recall lists.
|
||||
const config::attribute_value cfg_x = cfg["x"];
|
||||
const config::attribute_value cfg_y = cfg["y"];
|
||||
if((cfg_x.empty() || cfg_x == "recall")
|
||||
&& (cfg_y.empty() || cfg_y == "recall"))
|
||||
{
|
||||
//remove the unit from the corresponding team's recall list
|
||||
for(std::vector<team>::iterator pi = teams().begin();
|
||||
pi!=teams().end(); ++pi)
|
||||
{
|
||||
for(std::vector<unit_ptr>::iterator j = pi->recall_list().begin(); j != pi->recall_list().end();) { //TODO: This block is really messy, cleanup somehow...
|
||||
scoped_recall_unit auto_store("this_unit", pi->save_id(), j - pi->recall_list().begin());
|
||||
if (ufilt( *(*j), map_location() )) {
|
||||
j = pi->recall_list().erase(j);
|
||||
++number_killed;
|
||||
} else {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushinteger(L, number_killed);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_label(lua_State *L)
|
||||
{
|
||||
if (game_display_) {
|
||||
|
@ -3748,21 +3611,11 @@ int game_lua_kernel::intf_set_time_of_day(lua_State * L)
|
|||
|
||||
int game_lua_kernel::intf_scroll(lua_State * L)
|
||||
{
|
||||
vconfig cfg = luaW_checkvconfig(L, 1);
|
||||
int x = luaL_checkinteger(L, 1), y = luaL_checkinteger(L, 2);
|
||||
|
||||
if (game_display_) {
|
||||
const std::vector<int> side_list = get_sides_vector(cfg);
|
||||
bool side_match = false;
|
||||
for (int side : side_list) {
|
||||
if(board().get_team(side).is_local_human()) {
|
||||
side_match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((cfg["side"].empty() && !cfg.has_child("filter_side")) || side_match) {
|
||||
game_display_->scroll(cfg["x"], cfg["y"], true);
|
||||
game_display_->draw(true,true);
|
||||
}
|
||||
game_display_->scroll(x, y, true);
|
||||
game_display_->draw(true, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -4110,6 +3963,7 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "modify_ai", &intf_modify_ai_old },
|
||||
{ "remove_modifications", &intf_remove_modifications },
|
||||
{ "set_music", &intf_set_music },
|
||||
{ "sound_volume", &intf_sound_volume },
|
||||
{ "transform_unit", &intf_transform_unit },
|
||||
{ "unit_defense", &intf_unit_defense },
|
||||
{ "unit_movement_cost", &intf_unit_movement_cost },
|
||||
|
@ -4163,7 +4017,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit > },
|
||||
{ "highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex > },
|
||||
{ "is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy > },
|
||||
{ "kill", &dispatch<&game_lua_kernel::intf_kill > },
|
||||
{ "label", &dispatch<&game_lua_kernel::intf_label > },
|
||||
{ "lock_view", &dispatch<&game_lua_kernel::intf_lock_view > },
|
||||
{ "log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
|
||||
|
@ -4292,6 +4145,9 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
lua_setfield(L, -2, "current");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Create the playlist table with its metatable
|
||||
cmd_log_ << lua_audio::register_table(L);
|
||||
|
||||
// Create the wml_actions table.
|
||||
cmd_log_ << "Adding wml_actions table...\n";
|
||||
|
||||
|
|
|
@ -149,7 +149,6 @@ class game_lua_kernel : public lua_kernel_base
|
|||
int intf_remove_event(lua_State *L);
|
||||
int intf_color_adjust(lua_State *L);
|
||||
int intf_delay(lua_State *L);
|
||||
int intf_kill(lua_State *L);
|
||||
int intf_label(lua_State *L);
|
||||
int intf_redraw(lua_State *L);
|
||||
int intf_replace_schedule(lua_State *l);
|
||||
|
|
295
src/scripting/lua_audio.cpp
Normal file
295
src/scripting/lua_audio.cpp
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
Copyright (C) 2017 by the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "lua_audio.hpp"
|
||||
|
||||
#include "lua/lua.h"
|
||||
#include "lua/lauxlib.h"
|
||||
#include "scripting/lua_common.hpp"
|
||||
#include "scripting/push_check.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "sound_music_track.hpp"
|
||||
#include "config_assign.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include <set>
|
||||
|
||||
static const char* Track = "music track";
|
||||
|
||||
class music_track {
|
||||
std::shared_ptr<sound::music_track> track;
|
||||
public:
|
||||
explicit music_track(int i) : track(sound::get_track(i)) {}
|
||||
bool valid() const {
|
||||
return track && track->valid();
|
||||
}
|
||||
sound::music_track& operator*() {
|
||||
return *track;
|
||||
}
|
||||
const sound::music_track& operator*() const {
|
||||
return *track;
|
||||
}
|
||||
std::shared_ptr<sound::music_track> operator->() {
|
||||
return track;
|
||||
}
|
||||
std::shared_ptr<const sound::music_track> operator->() const {
|
||||
return track;
|
||||
}
|
||||
};
|
||||
|
||||
static music_track* push_track(lua_State* L, int i) {
|
||||
music_track* trk = new(L) music_track(i);
|
||||
luaL_setmetatable(L, Track);
|
||||
return trk;
|
||||
}
|
||||
|
||||
static music_track* get_track(lua_State* L, int i) {
|
||||
return static_cast<music_track*>(luaL_checkudata(L, i, Track));
|
||||
}
|
||||
|
||||
static int impl_music_get(lua_State* L) {
|
||||
if(lua_isnumber(L, 2)) {
|
||||
push_track(L, lua_tointeger(L, 2) - 1);
|
||||
return 1;
|
||||
}
|
||||
const char* m = luaL_checkstring(L, 2);
|
||||
if(strcmp(m, "current") == 0) {
|
||||
push_track(L, sound::get_current_track());
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "current_i") == 0) {
|
||||
size_t i = sound::get_current_track();
|
||||
if(i == sound::get_num_tracks()) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushinteger(L, i + 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "all") == 0) {
|
||||
config playlist;
|
||||
sound::write_music_play_list(playlist);
|
||||
const auto& range = playlist.child_range("music");
|
||||
std::vector<config> tracks(range.begin(), range.end());
|
||||
lua_push(L, tracks);
|
||||
return 1;
|
||||
}
|
||||
// This calculation reverses the one used in [volume] to get back the relative volume level.
|
||||
// (Which is the same calculation that's duplicated in impl_music_set.)
|
||||
return_float_attrib("volume", sound::get_music_volume() * 100.0f / preferences::music_volume());
|
||||
return luaW_getmetafield(L, 1, m);
|
||||
}
|
||||
|
||||
static int impl_music_set(lua_State* L) {
|
||||
if(lua_isnumber(L, 2)) {
|
||||
unsigned int i = lua_tointeger(L, 2) - 1;
|
||||
config cfg;
|
||||
if(lua_isnil(L, 3)) {
|
||||
if(i < sound::get_num_tracks()) {
|
||||
sound::remove_track(i);
|
||||
}
|
||||
} else if(luaW_toconfig(L, 3, cfg)) {
|
||||
// Don't allow play_once=yes
|
||||
if(cfg["play_once"]) {
|
||||
return luaL_argerror(L, 3, "For play_once, use wesnoth.music_list.play instead");
|
||||
}
|
||||
if(i < sound::get_num_tracks()) {
|
||||
sound::play_music_config(cfg);
|
||||
} else {
|
||||
// Remove the track at that index and add the new one in its place
|
||||
// It's a little inefficient though...
|
||||
sound::remove_track(i);
|
||||
sound::play_music_config(cfg, i);
|
||||
}
|
||||
} else {
|
||||
music_track& track = *get_track(L, 3);
|
||||
if(i < sound::get_num_tracks()) {
|
||||
sound::set_track(i, track.operator->());
|
||||
} else {
|
||||
track->write(cfg, true);
|
||||
sound::play_music_config(cfg);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const char* m = luaL_checkstring(L, 2);
|
||||
modify_float_attrib_check_range("volume", sound::set_music_volume(value * preferences::music_volume() / 100.0f), 0.0, 100.0);
|
||||
modify_int_attrib_check_range("current_i", sound::play_track(value - 1), 1, static_cast<int>(sound::get_num_tracks()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_music_len(lua_State* L) {
|
||||
lua_pushinteger(L, sound::get_num_tracks());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int intf_music_play(lua_State* L) {
|
||||
sound::play_music_once(luaL_checkstring(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_music_next(lua_State* L) {
|
||||
size_t n = sound::get_num_tracks();
|
||||
if(n > 0) {
|
||||
sound::play_track(n);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_music_add(lua_State* L) {
|
||||
int i = -1;
|
||||
if(lua_isinteger(L, 1)) {
|
||||
i = lua_tointeger(L, 1);
|
||||
lua_remove(L, 1);
|
||||
}
|
||||
config cfg = config_of
|
||||
("name", luaL_checkstring(L, 1))
|
||||
("append", true);
|
||||
bool found_ms_before = false, found_ms_after = false, found_imm = false;
|
||||
for(int i = 2; i <= lua_gettop(L); i++) {
|
||||
if(lua_isboolean(L, i)) {
|
||||
if(found_imm) {
|
||||
return luaL_argerror(L, i, "only one boolean argument may be passed");
|
||||
} else {
|
||||
cfg["immediate"] = luaW_toboolean(L, i);
|
||||
}
|
||||
} else if(lua_isnumber(L, i)) {
|
||||
if(found_ms_after) {
|
||||
return luaL_argerror(L, i, "only two integer arguments may be passed");
|
||||
} else if(found_ms_before) {
|
||||
cfg["ms_after"] = lua_tointeger(L, i);
|
||||
found_ms_after = true;
|
||||
} else {
|
||||
cfg["ms_before"] = lua_tointeger(L, i);
|
||||
found_ms_before = true;
|
||||
}
|
||||
} else {
|
||||
return luaL_argerror(L, i, "unrecognized argument");
|
||||
}
|
||||
}
|
||||
sound::play_music_config(cfg, i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_music_clear(lua_State*) {
|
||||
sound::empty_playlist();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_music_remove(lua_State* L) {
|
||||
// Use a non-standard comparator to ensure iteration in descending order
|
||||
std::set<int, std::greater<int>> to_remove;
|
||||
for(int i = 1; i <= lua_gettop(L); i++) {
|
||||
to_remove.insert(luaL_checkinteger(L, i));
|
||||
}
|
||||
for(int i : to_remove) {
|
||||
sound::remove_track(i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_music_commit(lua_State*) {
|
||||
sound::commit_music_changes();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_track_get(lua_State* L) {
|
||||
music_track& track = *get_track(L, 1);
|
||||
const char* m = luaL_checkstring(L, 2);
|
||||
return_bool_attrib("valid", track.valid());
|
||||
if(!track.valid()) {
|
||||
return luaL_error(L, "Tried to access member of track that is no longer valid.");
|
||||
}
|
||||
return_bool_attrib("append", track->append());
|
||||
return_bool_attrib("shuffle", track->shuffle());
|
||||
return_bool_attrib("immediate", track->immediate());
|
||||
return_bool_attrib("once", track->play_once());
|
||||
return_int_attrib("ms_before", track->ms_before());
|
||||
return_int_attrib("ms_after", track->ms_after());
|
||||
return_string_attrib("name", track->id());
|
||||
return_string_attrib("title", track->title());
|
||||
return luaW_getmetafield(L, 1, m);
|
||||
}
|
||||
|
||||
static int impl_track_set(lua_State* L) {
|
||||
music_track& track = *get_track(L, 1);
|
||||
const char* m = luaL_checkstring(L, 2);
|
||||
modify_bool_attrib("shuffle", track->set_shuffle(value));
|
||||
modify_bool_attrib("once", track->set_play_once(value));
|
||||
modify_int_attrib("ms_before", track->set_ms_before(value));
|
||||
modify_int_attrib("ms_after", track->set_ms_after(value));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_track_eq(lua_State* L) {
|
||||
music_track* a = get_track(L, 1);
|
||||
music_track* b = get_track(L, 2);
|
||||
if(!a || !b) {
|
||||
// This implies that one argument is not a music track, though I suspect this is dead code...?
|
||||
// Does Lua ever call this if the arguments are not of the same type?
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
if(!a->valid() && !b->valid()) {
|
||||
lua_pushboolean(L, true);
|
||||
return 1;
|
||||
}
|
||||
if(a->valid() && b->valid()) {
|
||||
music_track& lhs = *a;
|
||||
music_track& rhs = *b;
|
||||
lua_pushboolean(L, lhs->id() == rhs->id() && lhs->shuffle() == rhs->shuffle() && lhs->play_once() == rhs->play_once() && lhs->ms_before() == rhs->ms_before() && lhs->ms_after() == rhs->ms_after());
|
||||
return 1;
|
||||
}
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
namespace lua_audio {
|
||||
std::string register_table(lua_State* L) {
|
||||
// The music playlist metatable
|
||||
lua_getglobal(L, "wesnoth");
|
||||
lua_newuserdata(L, 0);
|
||||
lua_createtable(L, 0, 4);
|
||||
static luaL_Reg pl_callbacks[] = {
|
||||
{ "__index", impl_music_get },
|
||||
{ "__newindex", impl_music_set },
|
||||
{ "__len", impl_music_len },
|
||||
{ "play", intf_music_play },
|
||||
{ "add", intf_music_add },
|
||||
{ "clear", intf_music_clear },
|
||||
{ "remove", intf_music_remove },
|
||||
{ "next", intf_music_next },
|
||||
{ "force_refresh", intf_music_commit },
|
||||
{ nullptr, nullptr },
|
||||
};
|
||||
luaL_setfuncs(L, pl_callbacks, 0);
|
||||
lua_pushstring(L, "music playlist");
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setfield(L, -2, "music_list");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// The music track metatable
|
||||
luaL_newmetatable(L, Track);
|
||||
static luaL_Reg track_callbacks[] = {
|
||||
{ "__index", impl_track_get },
|
||||
{ "__newindex", impl_track_set },
|
||||
{ "__eq", impl_track_eq },
|
||||
{ nullptr, nullptr },
|
||||
};
|
||||
luaL_setfuncs(L, track_callbacks, 0);
|
||||
lua_pushstring(L, Track);
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
|
||||
return "Adding music playlist table...";
|
||||
}
|
||||
}
|
20
src/scripting/lua_audio.hpp
Normal file
20
src/scripting/lua_audio.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright (C) 2017 by the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
struct lua_State;
|
||||
|
||||
namespace lua_audio {
|
||||
std::string register_table(lua_State*);
|
||||
}
|
|
@ -74,6 +74,7 @@ static int impl_side_get(lua_State *L)
|
|||
return_tstring_attrib("faction_name", t.faction_name());
|
||||
return_string_attrib("color", t.color());
|
||||
return_cstring_attrib("controller", t.controller().to_string().c_str());
|
||||
return_bool_attrib("is_local", t.is_local());
|
||||
return_string_attrib("defeat_condition", t.defeat_condition().to_string());
|
||||
return_string_attrib("share_vision", t.share_vision().to_string());
|
||||
return_float_attrib("carryover_bonus", t.carryover_bonus());
|
||||
|
@ -206,7 +207,7 @@ namespace lua_team {
|
|||
};
|
||||
luaL_setfuncs(L, callbacks, 0);
|
||||
|
||||
lua_pushstring(L, "side");
|
||||
lua_pushstring(L, Team);
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
// Side methods
|
||||
luaW_getglobal(L, "wesnoth", "match_side");
|
||||
|
|
170
src/sound.cpp
170
src/sound.cpp
|
@ -158,11 +158,57 @@ std::vector<std::string> played_before;
|
|||
// Use the music_track default constructor to avoid trying to
|
||||
// invoke a log object while resolving paths.
|
||||
//
|
||||
std::vector<sound::music_track> current_track_list;
|
||||
sound::music_track current_track;
|
||||
sound::music_track last_track;
|
||||
std::vector<std::shared_ptr<sound::music_track>> current_track_list;
|
||||
std::shared_ptr<sound::music_track> current_track;
|
||||
unsigned int current_track_index = 0;
|
||||
|
||||
std::vector<std::shared_ptr<sound::music_track>>::const_iterator find_track(const sound::music_track& track) {
|
||||
return std::find_if(current_track_list.begin(), current_track_list.end(), [&track](const std::shared_ptr<const sound::music_track>& ptr) {
|
||||
return *ptr == track;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sound {
|
||||
unsigned int get_current_track() {
|
||||
return current_track_index;
|
||||
}
|
||||
|
||||
unsigned int get_num_tracks() {
|
||||
return current_track_list.size();
|
||||
}
|
||||
|
||||
std::shared_ptr<music_track> get_track(unsigned int i) {
|
||||
if(i < current_track_list.size()) {
|
||||
return current_track_list[i];
|
||||
}
|
||||
if(i == current_track_list.size()) {
|
||||
return current_track;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void set_track(unsigned int i, const std::shared_ptr<music_track>& to) {
|
||||
if(i < current_track_list.size() && find_track(*to) != current_track_list.end()) {
|
||||
current_track_list[i] = std::make_shared<music_track>(*to);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_track(unsigned int i) {
|
||||
if(i >= current_track_list.size()) {
|
||||
return;
|
||||
}
|
||||
if(i == current_track_index) {
|
||||
// Let the track finish playing
|
||||
current_track->set_play_once(true);
|
||||
// Set current index to the new size of the list
|
||||
current_track_index = current_track_list.size() - 1;
|
||||
} else if(i < current_track_index) {
|
||||
current_track_index--;
|
||||
}
|
||||
current_track_list.erase(current_track_list.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
static bool track_ok(const std::string& id)
|
||||
|
@ -171,7 +217,7 @@ static bool track_ok(const std::string& id)
|
|||
|
||||
// If they committed changes to list, we forget previous plays, but
|
||||
// still *never* repeat same track twice if we have an option.
|
||||
if (id == current_track.file_path())
|
||||
if (id == current_track->file_path())
|
||||
return false;
|
||||
|
||||
if (current_track_list.size() <= 3)
|
||||
|
@ -222,7 +268,7 @@ static bool track_ok(const std::string& id)
|
|||
}
|
||||
|
||||
|
||||
static const sound::music_track &choose_track()
|
||||
static std::shared_ptr<sound::music_track> choose_track()
|
||||
{
|
||||
assert(!current_track_list.empty());
|
||||
|
||||
|
@ -230,21 +276,21 @@ static const sound::music_track &choose_track()
|
|||
current_track_index = 0;
|
||||
}
|
||||
|
||||
if (current_track_list[current_track_index].shuffle()) {
|
||||
if (current_track_list[current_track_index]->shuffle()) {
|
||||
unsigned int track = 0;
|
||||
|
||||
if (current_track_list.size() > 1) {
|
||||
do {
|
||||
track = rand()%current_track_list.size();
|
||||
} while (!track_ok( current_track_list[track].file_path() ));
|
||||
} while (!track_ok( current_track_list[track]->file_path() ));
|
||||
}
|
||||
|
||||
current_track_index = track;
|
||||
}
|
||||
|
||||
//LOG_AUDIO << "Next track will be " << current_track_list[track].file_path() << "\n";
|
||||
played_before.push_back( current_track_list[current_track_index].file_path() );
|
||||
return current_track_list[current_track_index++];
|
||||
DBG_AUDIO << "Next track will be " << current_track_list[current_track_index]->file_path() << "\n";
|
||||
played_before.push_back( current_track_list[current_track_index]->file_path() );
|
||||
return current_track_list[current_track_index];
|
||||
}
|
||||
|
||||
static std::string pick_one(const std::string &files)
|
||||
|
@ -456,9 +502,9 @@ void stop_UI_sound() {
|
|||
|
||||
void play_music_once(const std::string &file)
|
||||
{
|
||||
// Clear list so it's not replayed.
|
||||
current_track_list.clear();
|
||||
current_track = music_track(file);
|
||||
current_track = std::make_shared<music_track>(file);
|
||||
current_track->set_play_once(true);
|
||||
current_track_index = current_track_list.size();
|
||||
play_music();
|
||||
}
|
||||
|
||||
|
@ -469,10 +515,23 @@ void empty_playlist()
|
|||
|
||||
void play_music()
|
||||
{
|
||||
if(!current_track) {
|
||||
return;
|
||||
}
|
||||
music_start_time = 1; //immediate (same as effect as SDL_GetTicks())
|
||||
want_new_music=true;
|
||||
no_fading=false;
|
||||
fadingout_time=current_track.ms_after();
|
||||
fadingout_time = current_track->ms_after();
|
||||
}
|
||||
|
||||
void play_track(unsigned int i) {
|
||||
if(i >= current_track_list.size()) {
|
||||
current_track = choose_track();
|
||||
} else {
|
||||
current_track_index = i;
|
||||
current_track = current_track_list[i];
|
||||
}
|
||||
play_music();
|
||||
}
|
||||
|
||||
static void play_new_music()
|
||||
|
@ -480,11 +539,11 @@ static void play_new_music()
|
|||
music_start_time = 0; //reset status: no start time
|
||||
want_new_music = true;
|
||||
|
||||
if(!preferences::music_on() || !mix_ok || !current_track.valid()) {
|
||||
if(!preferences::music_on() || !mix_ok || !current_track->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& filename = current_track.file_path();
|
||||
const std::string& filename = current_track->file_path();
|
||||
|
||||
auto itor = music_cache.find(filename);
|
||||
if(itor == music_cache.end()) {
|
||||
|
@ -499,11 +558,10 @@ static void play_new_music()
|
|||
return;
|
||||
}
|
||||
itor = music_cache.emplace(filename, music).first;
|
||||
last_track=current_track;
|
||||
}
|
||||
|
||||
LOG_AUDIO << "Playing track '" << filename << "'\n";
|
||||
int fading_time=current_track.ms_before();
|
||||
int fading_time = current_track->ms_before();
|
||||
if(no_fading)
|
||||
{
|
||||
fading_time=0;
|
||||
|
@ -525,16 +583,19 @@ void play_music_repeatedly(const std::string &id)
|
|||
return;
|
||||
|
||||
current_track_list.clear();
|
||||
current_track_list.push_back(music_track(id));
|
||||
current_track_list.emplace_back(new music_track(id));
|
||||
|
||||
std::shared_ptr<music_track> last_track = current_track;
|
||||
current_track = current_track_list.back();
|
||||
current_track_index = 0;
|
||||
|
||||
// If we're already playing it, don't interrupt.
|
||||
if (current_track != id) {
|
||||
current_track = music_track(id);
|
||||
if(!last_track || *last_track != *current_track) {
|
||||
play_music();
|
||||
}
|
||||
}
|
||||
|
||||
void play_music_config(const config &music_node)
|
||||
void play_music_config(const config &music_node, int i)
|
||||
{
|
||||
music_track track( music_node );
|
||||
|
||||
|
@ -544,7 +605,8 @@ void play_music_config(const config &music_node)
|
|||
|
||||
// If they say play once, we don't alter playlist.
|
||||
if (track.play_once()) {
|
||||
current_track = track;
|
||||
current_track = std::make_shared<music_track>(track);
|
||||
current_track_index = current_track_list.size();
|
||||
play_music();
|
||||
return;
|
||||
}
|
||||
|
@ -554,29 +616,35 @@ void play_music_config(const config &music_node)
|
|||
current_track_list.clear();
|
||||
}
|
||||
|
||||
if(track.valid()) {
|
||||
// Avoid 2 tracks with the same name, since that can cause an infinite loop
|
||||
// in choose_track(), 2 tracks with the same name will always return the
|
||||
// current track and track_ok() doesn't allow that.
|
||||
std::vector<music_track>::const_iterator itor = current_track_list.begin();
|
||||
while(itor != current_track_list.end()) {
|
||||
if(track == *itor) break;
|
||||
++itor;
|
||||
}
|
||||
if(!track.valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(itor == current_track_list.end()) {
|
||||
current_track_list.push_back(track);
|
||||
auto iter = find_track(track);
|
||||
// Avoid 2 tracks with the same name, since that can cause an infinite loop
|
||||
// in choose_track(), 2 tracks with the same name will always return the
|
||||
// current track and track_ok() doesn't allow that.
|
||||
if(iter == current_track_list.end()) {
|
||||
if(i < 0 || static_cast<size_t>(i) >= current_track_list.size()) {
|
||||
current_track_list.emplace_back(new music_track(track));
|
||||
iter = current_track_list.end() - 1;
|
||||
} else {
|
||||
ERR_AUDIO << "tried to add duplicate track '" << track.file_path() << "'" << std::endl;
|
||||
iter = current_track_list.emplace(current_track_list.begin() + 1, new music_track(track));
|
||||
if(current_track_index >= static_cast<size_t>(i)) {
|
||||
current_track_index++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ERR_AUDIO << "tried to add duplicate track '" << track.file_path() << "'" << std::endl;
|
||||
}
|
||||
|
||||
// They can tell us to start playing this list immediately.
|
||||
if (track.immediate()) {
|
||||
current_track = track;
|
||||
current_track = *iter;
|
||||
current_track_index = iter - current_track_list.begin();
|
||||
play_music();
|
||||
} else if (!track.append()) { // Make sure the current track is finished
|
||||
current_track.set_play_once(true);
|
||||
current_track->set_play_once(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,13 +711,14 @@ void commit_music_changes()
|
|||
played_before.clear();
|
||||
|
||||
// Play-once is OK if still playing.
|
||||
if (current_track.play_once())
|
||||
if (current_track->play_once())
|
||||
return;
|
||||
|
||||
// If current track no longer on playlist, change it.
|
||||
for (const music_track &m : current_track_list) {
|
||||
if (current_track == m)
|
||||
for(auto m : current_track_list) {
|
||||
if(*current_track == *m) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Victory empties playlist: if next scenario doesn't specify one...
|
||||
|
@ -665,8 +734,8 @@ void write_music_play_list(config& snapshot)
|
|||
{
|
||||
// First entry clears playlist, others append to it.
|
||||
bool append = false;
|
||||
for (music_track &m : current_track_list) {
|
||||
m.write(snapshot, append);
|
||||
for(auto m : current_track_list) {
|
||||
m->write(snapshot, append);
|
||||
append = true;
|
||||
}
|
||||
}
|
||||
|
@ -866,6 +935,14 @@ void play_UI_sound(const std::string& files)
|
|||
}
|
||||
}
|
||||
|
||||
int get_music_volume()
|
||||
{
|
||||
if(mix_ok) {
|
||||
return Mix_VolumeMusic(-1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_music_volume(int vol)
|
||||
{
|
||||
if(mix_ok && vol >= 0) {
|
||||
|
@ -876,6 +953,15 @@ void set_music_volume(int vol)
|
|||
}
|
||||
}
|
||||
|
||||
int get_sound_volume()
|
||||
{
|
||||
if(mix_ok) {
|
||||
// Since set_sound_volume sets all main channels to the same, just return the volume of any main channel
|
||||
return Mix_Volume(source_channel_start, -1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_sound_volume(int vol)
|
||||
{
|
||||
if(mix_ok && vol >= 0) {
|
||||
|
|
|
@ -41,7 +41,7 @@ void stop_UI_sound();
|
|||
void stop_bell();
|
||||
|
||||
// Read config entry, alter track list accordingly.
|
||||
void play_music_config(const config &music_node);
|
||||
void play_music_config(const config &music_node, int i = -1);
|
||||
// Act on any track list changes from above.
|
||||
void commit_music_changes();
|
||||
|
||||
|
@ -96,11 +96,18 @@ public:
|
|||
// Save music playlist for snapshot
|
||||
void write_music_play_list(config& snapshot);
|
||||
|
||||
int get_music_volume();
|
||||
int get_sound_volume();
|
||||
void set_music_volume(int vol);
|
||||
void set_sound_volume(int vol);
|
||||
void set_bell_volume(int vol);
|
||||
void set_UI_volume(int vol);
|
||||
|
||||
unsigned int get_current_track();
|
||||
unsigned int get_num_tracks();
|
||||
void remove_track(unsigned int i);
|
||||
void play_track(unsigned int i);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define SOUND_MUSIC_TRACK_HPP_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class config;
|
||||
|
||||
|
@ -30,7 +31,7 @@ class music_track
|
|||
public:
|
||||
music_track();
|
||||
music_track(const config& node);
|
||||
music_track(const std::string& v_name);
|
||||
explicit music_track(const std::string& v_name);
|
||||
void write(config& parent_node, bool append) const;
|
||||
|
||||
bool valid() const { return file_path_.empty() != true; }
|
||||
|
@ -47,6 +48,9 @@ public:
|
|||
const std::string& title() const { return title_; }
|
||||
|
||||
void set_play_once(bool v) { once_ = v; }
|
||||
void set_shuffle(bool v) { shuffle_ = v; }
|
||||
void set_ms_before(int v) { ms_before_ = v; }
|
||||
void set_ms_after(int v) { ms_after_ = v; }
|
||||
|
||||
private:
|
||||
void resolve();
|
||||
|
@ -63,6 +67,9 @@ private:
|
|||
bool shuffle_;
|
||||
};
|
||||
|
||||
std::shared_ptr<music_track> get_track(unsigned int i);
|
||||
void set_track(unsigned int i, const std::shared_ptr<music_track>& to);
|
||||
|
||||
} /* end namespace sound */
|
||||
|
||||
inline bool operator==(const sound::music_track& a, const sound::music_track& b) {
|
||||
|
|
Loading…
Add table
Reference in a new issue