~jadedctrl/gem-xwx-moe

~jadedctrl/gem-xwx-moe/gemujo_ludo/mods.niaj/fasado/subtitles/BaseTextSubtitleDisplay.lua
 ..
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 BaseTextSubtitleDisplay class, a base class for subtitle displays using text HUDs.
26
27
28
29 local S = subtitles.S;
30
31
32 --- A base class for subtitle displays using text HUDs.
33 -- @type BaseTextSubtitleDisplay
34 subtitles.BaseTextSubtitleDisplay = subtitles.SubtitleDisplay:extend();
35
36 subtitles.BaseTextSubtitleDisplay.DYNAMIC = true;
37
38 function subtitles.BaseTextSubtitleDisplay:init()
39 self.entries_by_slot = {};
40 self.entries_by_sound = {};
41 self.entries_by_merge = {};
42
43 self.margin = 56;
44 self.size = {x = 360, y = 40};
45 self.font_size = 1.0;
46 self.style = subtitles.util.TextStyle.REGULAR;
47 self.position = {x = 1, y = 1};
48 self.alignment = {x = 0, y = -1};
49 self.z_index = 100;
50 self.spacing = 0;
51 self.direction = -1;
52 self.offset = {x = 0, y = 0};
53 end;
54
55 function subtitles.BaseTextSubtitleDisplay:add_sound(sound)
56 local player = self:get_player();
57 if not player then
58 return;
59 end;
60
61 local merge_key = sound:get_merge_key();
62 local entry = merge_key and self.entries_by_merge[merge_key];
63 if entry then
64 entry:ref();
65 else
66 local slot = self:get_free_slot();
67 entry = self.Entry(self, sound, slot);
68 entry:show();
69 self.entries_by_slot[slot] = entry;
70 end;
71 self.entries_by_sound[sound] = entry;
72 if merge_key then
73 self.entries_by_merge[merge_key] = entry;
74 end;
75 end;
76
77 function subtitles.BaseTextSubtitleDisplay:remove_sound(sound)
78 local entry = self.entries_by_sound[sound];
79 if not entry then
80 return;
81 end;
82 entry:unref();
83 self.entries_by_sound[sound] = nil;
84 end;
85
86 function subtitles.BaseTextSubtitleDisplay:update_sound(sound)
87 local entry = self.entries_by_sound[sound];
88 if not entry then
89 return;
90 end;
91 local player = self:get_player();
92 if not player then
93 return;
94 end;
95 entry.relative_pos = sound:get_relative_pos_for_player(player);
96 entry:show();
97 end;
98
99 --- Removes a subtitle entry.
100 -- @param entry The `Entry` object to remove.
101 function subtitles.BaseTextSubtitleDisplay:remove_entry(entry)
102 self.entries_by_slot[entry.slot] = nil;
103 local merge_key = entry.sound:get_merge_key();
104 if merge_key then
105 self.entries_by_merge[merge_key] = nil;
106 end;
107 end;
108
109 --- Returns the next available slot.
110 -- @return The slot index, starting from 1.
111 function subtitles.BaseTextSubtitleDisplay:get_free_slot()
112 local slot = 0;
113 while self.entries_by_slot[slot] do
114 slot = slot + 1;
115 end;
116 return slot;
117 end;
118
119 --- Generates the HUDs to display a subtitle entry.
120 -- @param entry The `Entry` object.
121 -- @return A table of {hud_key1 = hud_def1, hud_key2 = hud_def2}.
122 -- HUD keys can be strings or integers.
123 -- @abstract
124 function subtitles.BaseTextSubtitleDisplay:get_huds(entry)
125 error('Not implemented.');
126 end;
127
128 --- Returns the default properties for a HUD for the specified entry.
129 -- @param entry An `Entry` object.
130 -- @return A partial HUD definition.
131 function subtitles.BaseTextSubtitleDisplay:get_hud_defaults(entry)
132 local hud = {};
133 hud.position = self.position;
134 hud.alignment = self.alignment;
135 hud.z_index = self.z_index;
136 hud.direction = subtitles.util.HUDDirection.LEFT_TO_RIGHT;
137 return hud;
138 end;
139
140
141 --- Represents a single subtitle.
142 -- @type BaseTextSubtitleDisplay.Entry
143 subtitles.BaseTextSubtitleDisplay.Entry = subtitles.Object:extend();
144
145 --- Constructor.
146 -- @param display The `SubtitleDisplay` object.
147 -- @param sound The `Sound` object.
148 -- @param slot The integer subtitle slot.
149 function subtitles.BaseTextSubtitleDisplay.Entry:new(display, sound, slot)
150 self.display = display;
151 self.sound = sound;
152 self.slot = slot;
153
154 self.ref_count = 1;
155 self.huds = {};
156 self.hud_ids = {};
157 self.hud_defaults = self.display:get_hud_defaults(self);
158 self.relative_pos = self.sound:get_relative_pos_for_player(self.display:get_player());
159 self.max_fade_distance = self.sound:get_max_distance() / 2;
160 end;
161
162 --- Shows or updates the subtitle.
163 function subtitles.BaseTextSubtitleDisplay.Entry:show()
164 local player = self.display:get_player()
165 if not player then
166 return;
167 end;
168
169 local huds = self.display:get_huds(self);
170 for key, hud in pairs(huds) do
171 local old_hud = self.huds[key];
172 if old_hud then
173 local hud_id = self.hud_ids[key];
174 subtitles.util.update_hud(player, hud_id, old_hud, hud);
175 else
176 hud = subtitles.util.update({}, self.hud_defaults, hud);
177 self.hud_ids[key] = player:hud_add(hud);
178 end;
179 self.huds[key] = hud;
180 end;
181 end;
182
183 --- Hides the subtitle.
184 function subtitles.BaseTextSubtitleDisplay.Entry:hide()
185 local player = self.display:get_player();
186 if player then
187 for __, hud_id in pairs(self.hud_ids) do
188 player:hud_remove(hud_id);
189 end;
190 end;
191 self.hud_ids = {};
192 self.huds = {};
193 end;
194
195 --- Gets the entry's offset based on its slot.
196 -- @return A table of {x: integer, y: integer}.
197 function subtitles.BaseTextSubtitleDisplay.Entry:get_offset()
198 local base = self.display.offset;
199 local offset =
200 {
201 x = base.x;
202 y = base.y + (self.slot - 1) * (self.display.size.y + self.display.spacing) * self.display.direction;
203 };
204 if self.display.direction < 0 then
205 offset.y = offset.y - self.display.size.y;
206 end;
207 return offset;
208 end;
209
210 --- Returns a shade representing the distance of the sound from the player.
211 -- @return A number from 0.0 to 1.0.
212 function subtitles.BaseTextSubtitleDisplay.Entry:get_distance_shade()
213 local distance = 0.0;
214 if self.relative_pos then
215 distance = vector.length(self.relative_pos);
216 end;
217 local shade = 1.0 - distance / self.max_fade_distance;
218 return subtitles.util.clamp(shade, 0.0, 1.0);
219 end;
220
221 --- Calculates which direction the sound is in from the player's perspective, and how far.
222 -- @return A number from -1.0 (far left) to 1.0 (far right).
223 function subtitles.BaseTextSubtitleDisplay.Entry:get_pan()
224 local rel_pos = self.relative_pos;
225 if (not rel_pos) or vector.length(rel_pos) < 1.0 then
226 return 0.0;
227 end;
228
229 return vector.normalize(rel_pos).x;
230 end;
231
232 --- Increases the entry's reference count.
233 function subtitles.BaseTextSubtitleDisplay.Entry:ref()
234 self.ref_count = self.ref_count + 1;
235 end;
236
237 --- Decreases the entry's reference count, possibly removing it.
238 function subtitles.BaseTextSubtitleDisplay.Entry:unref()
239 self.ref_count = self.ref_count - 1;
240 if self.ref_count <= 0 then
241 self:hide()
242 self.display:remove_entry(self);
243 end;
244 end;