0 |
--[[ |
1 |
Subtitles — adds subtitles to Minetest. |
2 |
|
3 |
Copyright © 2022‒2023, Silver Sandstone <@SilverSandstone@craftodon.social> |
4 |
|
5 |
Permission is hereby granted, free of charge, to any person obtaining a |
6 |
copy of this software and associated documentation files (the "Software"), |
7 |
to deal in the Software without restriction, including without limitation |
8 |
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
9 |
and/or sell copies of the Software, and to permit persons to whom the |
10 |
Software is furnished to do so, subject to the following conditions: |
11 |
|
12 |
The above copyright notice and this permission notice shall be included |
13 |
in all copies or substantial portions of the Software. |
14 |
|
15 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
20 |
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
21 |
DEALINGS IN THE SOFTWARE. |
22 |
]] |
23 |
|
24 |
|
25 |
--- Provides the SubtitleDisplay class, which is the base class for subtitle displays. |
26 |
|
27 |
|
28 |
--- The base class for subtitle displays. |
29 |
-- @type SubtitleDisplay |
30 |
subtitles.SubtitleDisplay = subtitles.Object:extend(); |
31 |
|
32 |
subtitles.SubtitleDisplay.implementations = {}; |
33 |
|
34 |
subtitles.SubtitleDisplay.NAME = nil; |
35 |
subtitles.SubtitleDisplay.ICON = 'subtitles_mode_unknown.png'; |
36 |
subtitles.SubtitleDisplay.TITLE = nil; |
37 |
subtitles.SubtitleDisplay.DESCRIPTION = nil; |
38 |
subtitles.SubtitleDisplay.DYNAMIC = false; |
39 |
|
40 |
--- Constructor. |
41 |
-- @param username The username of the player. |
42 |
function subtitles.SubtitleDisplay:new(username) |
43 |
self.username = username; |
44 |
self.sounds = {}; |
45 |
self._timers = {}; |
46 |
|
47 |
self:init(); |
48 |
end; |
49 |
|
50 |
--- Initialises the display. |
51 |
function subtitles.SubtitleDisplay:init() |
52 |
end; |
53 |
|
54 |
--- Registers the display, making it available to use. |
55 |
function subtitles.SubtitleDisplay:register() |
56 |
assert(self.NAME, 'This subtitle display does not specify NAME!'); |
57 |
table.insert(subtitles.SubtitleDisplay.implementations, self); |
58 |
end; |
59 |
|
60 |
--- Handles a call to `minetest.sound_play()` or an equivalent event. |
61 |
-- @param sound A `Sound` object. |
62 |
function subtitles.SubtitleDisplay:handle_sound_play(sound) |
63 |
if sound:is_exempt() then |
64 |
return; |
65 |
end; |
66 |
|
67 |
self.sounds[sound.handle or sound] = sound; |
68 |
local duration = sound:get_duration(); |
69 |
if duration then |
70 |
self._timers[sound] = minetest.after(duration, function() self:handle_sound_stop(sound); end); |
71 |
end; |
72 |
self:add_sound(sound); |
73 |
end; |
74 |
|
75 |
--- Handles a call to `minetest.sound_stop()` or an equivalent event. |
76 |
-- @param sound_or_handle The sound's numeric handle, or the `Sound` object |
77 |
-- if it's ephemeral. |
78 |
function subtitles.SubtitleDisplay:handle_sound_stop(sound_or_handle) |
79 |
local sound; |
80 |
if type(sound_or_handle) == 'table' then |
81 |
sound = sound_or_handle; |
82 |
else |
83 |
sound = self.sounds[sound_or_handle]; |
84 |
end; |
85 |
|
86 |
if not sound then |
87 |
return; |
88 |
end; |
89 |
local job = self._timers[sound]; |
90 |
if job then |
91 |
job:cancel(); |
92 |
end; |
93 |
self.sounds[sound.handle or sound] = nil; |
94 |
self:remove_sound(sound); |
95 |
end; |
96 |
|
97 |
--- Destroys the display. |
98 |
function subtitles.SubtitleDisplay:destroy() |
99 |
for __, sound in pairs(self.sounds) do |
100 |
self:handle_sound_stop(sound); |
101 |
end; |
102 |
self.sounds = {}; |
103 |
end; |
104 |
|
105 |
--- Returns the display's owner. |
106 |
-- @return The player's ObjectRef, or nil. |
107 |
function subtitles.SubtitleDisplay:get_player() |
108 |
return minetest.get_player_by_name(self.username); |
109 |
end; |
110 |
|
111 |
--- Handles a subtitle being added. |
112 |
-- @param sound A `Sound` object. |
113 |
-- @abstract |
114 |
function subtitles.SubtitleDisplay:add_sound(sound) |
115 |
error('Not implemented.'); |
116 |
end; |
117 |
|
118 |
--- Handles a subtitle being removed. |
119 |
-- @param sound A `Sound` object. |
120 |
-- @abstract |
121 |
function subtitles.SubtitleDisplay:remove_sound(sound) |
122 |
error('Not implemented.'); |
123 |
end; |
124 |
|
125 |
--- Handles a sound changing. |
126 |
-- @param sound A `Sound` object. |
127 |
function subtitles.SubtitleDisplay:update_sound(sound) |
128 |
end; |
129 |
|
130 |
--- Called every game tick. |
131 |
-- @param dtime Seconds since the last tick. |
132 |
function subtitles.SubtitleDisplay:step(dtime) |
133 |
if self.DYNAMIC then |
134 |
for key, sound in pairs(self.sounds) do |
135 |
self:update_sound(sound); |
136 |
end; |
137 |
end; |
138 |
end; |
139 |
|
140 |
function subtitles.SubtitleDisplay:__tostring() |
141 |
return ('[Subtitle display %q for player %q]'):format(self.NAME, self.username); |
142 |
end; |
143 |
|
144 |
-- Static methods: |
145 |
|
146 |
--- Returns the subtitle display class with the specified name. |
147 |
-- @param name A display name string. |
148 |
-- @return A subclass of `SubtitleDisplay` or nil. |
149 |
function subtitles.SubtitleDisplay.get_by_name(name) |
150 |
for __, impl in ipairs(subtitles.SubtitleDisplay.implementations) do |
151 |
if impl.NAME == name then |
152 |
return impl; |
153 |
end; |
154 |
end; |
155 |
return nil; |
156 |
end; |