Properly port [music] to Lua
This commit is contained in:
parent
0a591cd424
commit
6906ced4e7
15 changed files with 343 additions and 5 deletions
13
changelog
13
changelog
|
@ -21,6 +21,19 @@ Version 1.13.7+dev:
|
|||
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
|
||||
* 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.force_refresh forces any pending playlist changes to be immediately applied
|
||||
* 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
|
||||
* Multiplayer:
|
||||
* Fixed statistics being lost when reloading an MP game.
|
||||
* Performance:
|
||||
|
|
|
@ -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,24 @@ 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
|
||||
|
||||
-- This is mainly for use in unit test macros, but maybe it can be useful elsewhere too
|
||||
|
|
|
@ -155,7 +155,7 @@ function utils.handle_event_commands(cfg, scope_type)
|
|||
current_exit = "none"
|
||||
end
|
||||
-- Apply music alterations once all the commands have been processed.
|
||||
wesnoth.set_music()
|
||||
wesnoth.music_list.force_refresh()
|
||||
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>
|
||||
|
@ -3864,6 +3870,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
|
||||
|
|
|
@ -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;
|
||||
|
@ -4122,6 +4124,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";
|
||||
|
||||
|
|
225
src/scripting/lua_audio.cpp
Normal file
225
src/scripting/lua_audio.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
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 "sound.hpp"
|
||||
#include "sound_music_track.hpp"
|
||||
#include "config_assign.hpp"
|
||||
|
||||
static const char* Track = "music track";
|
||||
|
||||
class music_track {
|
||||
int i;
|
||||
sound::music_track& track;
|
||||
public:
|
||||
explicit music_track(int i) : i(i), track(sound::get_track(i)) {}
|
||||
bool valid() {
|
||||
sound::music_track& current = sound::get_track(i);
|
||||
return &track == ¤t && track.valid() && track.id() == current.id();
|
||||
}
|
||||
sound::music_track& operator*() {
|
||||
return track;
|
||||
}
|
||||
sound::music_track* operator->() {
|
||||
return &track;
|
||||
}
|
||||
int index() {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
return luaW_getmetafield(L, 1, m);
|
||||
}
|
||||
|
||||
static int impl_music_set(lua_State* L) {
|
||||
if(lua_isnumber(L, 2)) {
|
||||
music_track& track = *get_track(L, 3);
|
||||
sound::set_track(lua_tointeger(L, 2), *track);
|
||||
return 0;
|
||||
}
|
||||
// TODO: Set "current" and "current_i"
|
||||
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_add(lua_State* L) {
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_music_clear(lua_State*) {
|
||||
sound::empty_playlist();
|
||||
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 },
|
||||
{ "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*);
|
||||
}
|
|
@ -207,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");
|
||||
|
|
|
@ -165,6 +165,33 @@ unsigned int current_track_index = 0;
|
|||
|
||||
}
|
||||
|
||||
namespace sound {
|
||||
unsigned int get_current_track() {
|
||||
return current_track_index;
|
||||
}
|
||||
|
||||
unsigned int get_num_tracks() {
|
||||
return current_track_list.size();
|
||||
}
|
||||
|
||||
music_track& get_track(unsigned int i) {
|
||||
static music_track dummy;
|
||||
if(i < current_track_list.size()) {
|
||||
return current_track_list[i];
|
||||
}
|
||||
if(i == current_track_list.size()) {
|
||||
return current_track;
|
||||
}
|
||||
return dummy;
|
||||
}
|
||||
|
||||
void set_track(unsigned int i, const music_track& to) {
|
||||
if(i < current_track_list.size()) {
|
||||
current_track_list[i] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool track_ok(const std::string& id)
|
||||
{
|
||||
LOG_AUDIO << "Considering " << id << "\n";
|
||||
|
@ -456,9 +483,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.set_play_once(true);
|
||||
current_track_index = current_track_list.size();
|
||||
play_music();
|
||||
}
|
||||
|
||||
|
@ -530,6 +557,7 @@ void play_music_repeatedly(const std::string &id)
|
|||
// If we're already playing it, don't interrupt.
|
||||
if (current_track != id) {
|
||||
current_track = music_track(id);
|
||||
current_track_index = 0;
|
||||
play_music();
|
||||
}
|
||||
}
|
||||
|
@ -545,6 +573,7 @@ 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_index = current_track_list.size();
|
||||
play_music();
|
||||
return;
|
||||
}
|
||||
|
@ -574,6 +603,7 @@ void play_music_config(const config &music_node)
|
|||
// They can tell us to start playing this list immediately.
|
||||
if (track.immediate()) {
|
||||
current_track = track;
|
||||
current_track_index = current_track_list.size() - 1;
|
||||
play_music();
|
||||
} else if (!track.append()) { // Make sure the current track is finished
|
||||
current_track.set_play_once(true);
|
||||
|
|
|
@ -101,6 +101,9 @@ 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();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,6 +47,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 +66,9 @@ private:
|
|||
bool shuffle_;
|
||||
};
|
||||
|
||||
music_track& get_track(unsigned int i);
|
||||
void set_track(unsigned int i, const 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