~jadedctrl/gem-xwx-moe
~jadedctrl/gem-xwx-moe/gemujo_ludo/mods.niaj/fasado/subtitles/BaseTextSubtitleDisplay.lua
~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; |