OpenTTD Source 15.0-beta2
music_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD 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, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "openttd.h"
12#include "base_media_base.h"
13#include "base_media_music.h"
15#include "window_gui.h"
16#include "strings_func.h"
17#include "window_func.h"
18#include "sound_func.h"
19#include "gfx_func.h"
20#include "zoom_func.h"
21#include "core/random_func.hpp"
22#include "core/mem_func.hpp"
23#include "error.h"
25#include "string_func.h"
26#include "settings_type.h"
27#include "settings_gui.h"
28#include "dropdown_func.h"
29#include "dropdown_type.h"
30#include "slider_func.h"
31#include "mixer.h"
32
34
35#include "table/strings.h"
36#include "table/sprites.h"
37
38#include "safeguards.h"
39
40
43 const MusicSet *set;
44 uint set_index;
45
47 bool IsValid() const { return !this->songname.empty(); }
48 };
49 typedef std::vector<PlaylistEntry> Playlist;
50
51 enum PlaylistChoices : uint8_t {
52 PLCH_ALLMUSIC,
53 PLCH_OLDSTYLE,
54 PLCH_NEWSTYLE,
55 PLCH_EZYSTREET,
56 PLCH_CUSTOM1,
57 PLCH_CUSTOM2,
58 PLCH_THEMEONLY,
59 PLCH_MAX,
60 };
61
62 Playlist active_playlist{};
63 Playlist displayed_playlist{};
64 Playlist music_set{};
65
66 PlaylistChoices selected_playlist{};
67
68 void BuildPlaylists();
69
70 void ChangePlaylist(PlaylistChoices pl);
71 void ChangeMusicSet(const std::string &set_name);
72 void Shuffle();
73 void Unshuffle();
74
75 void Play();
76 void Stop();
77 void Next();
78 void Prev();
79 void CheckStatus();
80
81 bool IsPlaying() const;
82 bool IsShuffle() const;
83 PlaylistEntry GetCurrentSong() const;
84
85 bool IsCustomPlaylist() const;
86 void PlaylistAdd(size_t song_index);
87 void PlaylistRemove(size_t song_index);
88 void PlaylistClear();
89
90private:
91 uint GetSetIndex();
92 void SetPositionBySetIndex(uint set_index);
93 void ChangePlaylistPosition(int ofs);
94 int playlist_position = 0;
95
96 void SaveCustomPlaylist(PlaylistChoices pl);
97
98 std::array<Playlist, PLCH_MAX> standard_playlists{};
99};
100
101MusicSystem _music;
102
103
106{
107 const MusicSet *set = BaseMusic::GetUsedSet();
108
109 /* Clear current playlists */
110 for (auto &playlist : this->standard_playlists) playlist.clear();
111 this->music_set.clear();
112
113 /* Build standard playlists, and a list of available music */
114 for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
115 PlaylistEntry entry(set, i);
116 if (!entry.IsValid()) continue;
117
118 this->music_set.push_back(entry);
119
120 /* Add theme song to theme-only playlist */
121 if (i == 0) this->standard_playlists[PLCH_THEMEONLY].push_back(std::move(entry));
122
123 /* Don't add the theme song to standard playlists */
124 if (i > 0) {
125 this->standard_playlists[PLCH_ALLMUSIC].push_back(entry);
126 uint theme = (i - 1) / NUM_SONGS_CLASS;
127 this->standard_playlists[PLCH_OLDSTYLE + theme].push_back(std::move(entry));
128 }
129 }
130
131 /* Load custom playlists
132 * Song index offsets are 1-based, zero indicates invalid/end-of-list value */
133 for (uint i = 0; i < NUM_SONGS_PLAYLIST; i++) {
135 PlaylistEntry entry(set, _settings_client.music.custom_1[i] - 1);
136 if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM1].push_back(std::move(entry));
137 }
139 PlaylistEntry entry(set, _settings_client.music.custom_2[i] - 1);
140 if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM2].push_back(std::move(entry));
141 }
142 }
143}
144
149void MusicSystem::ChangePlaylist(PlaylistChoices pl)
150{
151 assert(pl < PLCH_MAX && pl >= PLCH_ALLMUSIC);
152
153 if (pl != PLCH_THEMEONLY) _settings_client.music.playlist = pl;
154
155 if (_game_mode != GM_MENU || pl == PLCH_THEMEONLY) {
156 this->displayed_playlist = this->standard_playlists[pl];
158 this->selected_playlist = pl;
159 this->playlist_position = 0;
160
162 if (_settings_client.music.playing) this->Play();
163 }
164
167}
168
173void MusicSystem::ChangeMusicSet(const std::string &set_name)
174{
175 BaseMusic::SetSetByName(set_name);
176 BaseMusic::ini_set = set_name;
177
178 this->BuildPlaylists();
179 this->ChangePlaylist(this->selected_playlist);
180
184}
185
191{
192 auto it = std::ranges::find(this->active_playlist, set_index, &PlaylistEntry::set_index);
193 if (it != std::end(this->active_playlist)) this->playlist_position = std::distance(std::begin(this->active_playlist), it);
194}
195
201{
202 return static_cast<size_t>(this->playlist_position) < this->active_playlist.size()
203 ? this->active_playlist[this->playlist_position].set_index
204 : UINT_MAX;
205}
206
211{
213
214 uint set_index = this->GetSetIndex();
216 for (size_t i = 0; i < this->active_playlist.size(); i++) {
217 size_t shuffle_index = InteractiveRandom() % (this->active_playlist.size() - i);
218 std::swap(this->active_playlist[i], this->active_playlist[i + shuffle_index]);
219 }
220 this->SetPositionBySetIndex(set_index);
221
224}
225
230{
232
233 uint set_index = this->GetSetIndex();
235 this->SetPositionBySetIndex(set_index);
236
239}
240
243{
244 /* Always set the playing flag, even if there is no music */
247 /* Make sure playlist_position is a valid index, if playlist has changed etc. */
248 this->ChangePlaylistPosition(0);
249
250 /* If there is no music, don't try to play it */
251 if (this->active_playlist.empty()) return;
252
253 MusicSongInfo song = this->active_playlist[this->playlist_position];
254 if (_game_mode == GM_MENU && this->selected_playlist == PLCH_THEMEONLY) song.loop = true;
256
258}
259
268
277
286
289{
290 if ((_game_mode == GM_MENU) != (this->selected_playlist == PLCH_THEMEONLY)) {
291 /* Make sure the theme-only playlist is active when on the title screen, and not during gameplay */
292 this->ChangePlaylist((_game_mode == GM_MENU) ? PLCH_THEMEONLY : (PlaylistChoices)_settings_client.music.playlist);
293 }
294 if (this->active_playlist.empty()) return;
295 /* If we were supposed to be playing, but music has stopped, move to next song */
296 if (this->IsPlaying() && !MusicDriver::GetInstance()->IsSongPlaying()) this->Next();
297}
298
301{
302 return _settings_client.music.playing && !this->active_playlist.empty();
303}
304
307{
309}
310
313{
314 if (!this->IsPlaying()) return PlaylistEntry(BaseMusic::GetUsedSet(), 0);
315 return this->active_playlist[this->playlist_position];
316}
317
320{
321 return (this->selected_playlist == PLCH_CUSTOM1) || (this->selected_playlist == PLCH_CUSTOM2);
322}
323
329void MusicSystem::PlaylistAdd(size_t song_index)
330{
331 if (!this->IsCustomPlaylist()) return;
332
333 /* Pick out song from the music set */
334 if (song_index >= this->music_set.size()) return;
335 PlaylistEntry entry = this->music_set[song_index];
336
337 /* Check for maximum length */
338 if (this->standard_playlists[this->selected_playlist].size() >= NUM_SONGS_PLAYLIST) return;
339
340 /* Add it to the appropriate playlist, and the display */
341 this->standard_playlists[this->selected_playlist].push_back(entry);
342 this->displayed_playlist.push_back(entry);
343
344 /* Add it to the active playlist, if playback is shuffled select a random position to add at */
345 if (this->active_playlist.empty()) {
346 this->active_playlist.push_back(std::move(entry));
347 if (this->IsPlaying()) this->Play();
348 } else if (this->IsShuffle()) {
349 /* Generate a random position between 0 and n (inclusive, new length) to insert at */
350 size_t maxpos = this->displayed_playlist.size();
351 size_t newpos = InteractiveRandom() % maxpos;
352 this->active_playlist.insert(this->active_playlist.begin() + newpos, entry);
353 /* Make sure to shift up the current playback position if the song was inserted before it */
354 if ((int)newpos <= this->playlist_position) this->playlist_position++;
355 } else {
356 this->active_playlist.push_back(std::move(entry));
357 }
358
359 this->SaveCustomPlaylist(this->selected_playlist);
360
362}
363
368void MusicSystem::PlaylistRemove(size_t song_index)
369{
370 if (!this->IsCustomPlaylist()) return;
371
372 Playlist &pl = this->standard_playlists[this->selected_playlist];
373 if (song_index >= pl.size()) return;
374
375 /* Remove from "simple" playlists */
376 PlaylistEntry song = pl[song_index];
377 pl.erase(pl.begin() + song_index);
378 this->displayed_playlist.erase(this->displayed_playlist.begin() + song_index);
379
380 /* Find in actual active playlist (may be shuffled) and remove,
381 * if it's the current song restart playback */
382 for (size_t i = 0; i < this->active_playlist.size(); i++) {
383 Playlist::iterator s2 = this->active_playlist.begin() + i;
384 if (s2->filename == song.filename && s2->cat_index == song.cat_index) {
385 this->active_playlist.erase(s2);
386 if ((int)i == this->playlist_position && this->IsPlaying()) this->Play();
387 break;
388 }
389 }
390
391 this->SaveCustomPlaylist(this->selected_playlist);
392
394}
395
401{
402 if (!this->IsCustomPlaylist()) return;
403
404 this->standard_playlists[this->selected_playlist].clear();
405 this->ChangePlaylist(this->selected_playlist);
406
407 this->SaveCustomPlaylist(this->selected_playlist);
408}
409
416{
417 if (this->active_playlist.empty()) {
418 this->playlist_position = 0;
419 } else {
420 this->playlist_position += ofs;
421 while (this->playlist_position >= (int)this->active_playlist.size()) this->playlist_position -= (int)this->active_playlist.size();
422 while (this->playlist_position < 0) this->playlist_position += (int)this->active_playlist.size();
423 }
424}
425
430void MusicSystem::SaveCustomPlaylist(PlaylistChoices pl)
431{
432 uint8_t *settings_pl;
433 if (pl == PLCH_CUSTOM1) {
434 settings_pl = _settings_client.music.custom_1;
435 } else if (pl == PLCH_CUSTOM2) {
436 settings_pl = _settings_client.music.custom_2;
437 } else {
438 return;
439 }
440
441 size_t num = 0;
442 MemSetT(settings_pl, 0, NUM_SONGS_PLAYLIST);
443
444 for (const auto &song : this->standard_playlists[pl]) {
445 /* Music set indices in the settings playlist are 1-based, 0 means unused slot */
446 settings_pl[num++] = (uint8_t)song.set_index + 1;
447 }
448}
449
450
456{
457 _music.CheckStatus();
458}
459
464void ChangeMusicSet(int index)
465{
466 if (BaseMusic::GetIndexOfUsedSet() == index) return;
467 _music.ChangeMusicSet(BaseMusic::GetSet(index)->name);
468}
469
475{
476 _music.BuildPlaylists();
477}
478
479
482 {
483 this->InitNested(number);
488 }
489
490 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
491 {
492 switch (widget) {
493 case WID_MTS_PLAYLIST:
494 return GetString(STR_PLAYLIST_PROGRAM, STR_MUSIC_PLAYLIST_ALL + _settings_client.music.playlist);
495
496 case WID_MTS_CAPTION:
497 return GetString(STR_PLAYLIST_MUSIC_SELECTION_SETNAME, BaseMusic::GetUsedSet()->name);
498
499 default:
500 return this->Window::GetWidgetString(widget, stringid);
501 }
502 }
503
509 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
510 {
511 if (!gui_scope) return;
512 for (int i = 0; i < 6; i++) {
514 }
516
517 if (data == 1) {
518 this->ReInit();
519 } else {
520 this->SetDirty();
521 }
522 }
523
524 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
525 {
526 switch (widget) {
527 case WID_MTS_PLAYLIST: {
528 Dimension d = {0, 0};
529
530 for (int i = 0; i < 6; i++) {
531 d = maxdim(d, GetStringBoundingBox(GetString(STR_PLAYLIST_PROGRAM, STR_MUSIC_PLAYLIST_ALL + i)));
532 }
533 d.width += padding.width;
534 d.height += padding.height;
535 size = maxdim(size, d);
536 break;
537 }
538
540 Dimension d = {0, 0};
541
542 for (const auto &song : _music.music_set) {
543 Dimension d2 = GetStringBoundingBox(GetString(STR_PLAYLIST_TRACK_NAME, song.tracknr, 2, song.songname));
544 d.width = std::max(d.width, d2.width);
545 d.height += d2.height;
546 }
547 d.width += padding.width;
548 d.height += padding.height;
549 size = maxdim(size, d);
550 break;
551 }
552 }
553 }
554
555 void DrawWidget(const Rect &r, WidgetID widget) const override
556 {
557 switch (widget) {
558 case WID_MTS_LIST_LEFT: {
560
561 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
562 for (const auto &song : _music.music_set) {
563 DrawString(tr, GetString(STR_PLAYLIST_TRACK_NAME, song.tracknr, 2, song.songname));
564 tr.top += GetCharacterHeight(FS_SMALL);
565 }
566 break;
567 }
568
569 case WID_MTS_LIST_RIGHT: {
571
572 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
573 for (const auto &song : _music.active_playlist) {
574 DrawString(tr, GetString(STR_PLAYLIST_TRACK_NAME, song.tracknr, 2, song.songname));
575 tr.top += GetCharacterHeight(FS_SMALL);
576 }
577 break;
578 }
579 }
580 }
581
582 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
583 {
584 switch (widget) {
585 case WID_MTS_LIST_LEFT: { // add to playlist
586 int y = this->GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL));
587 _music.PlaylistAdd(y);
588 break;
589 }
590
591 case WID_MTS_LIST_RIGHT: { // remove from playlist
592 int y = this->GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL));
593 _music.PlaylistRemove(y);
594 break;
595 }
596
597 case WID_MTS_MUSICSET: {
598 int selected = 0;
599 ShowDropDownList(this, BuildSetDropDownList<BaseMusic>(&selected), selected, widget);
600 break;
601 }
602
603 case WID_MTS_CLEAR: // clear
604 _music.PlaylistClear();
605 break;
606
607 case WID_MTS_ALL: case WID_MTS_OLD: case WID_MTS_NEW:
608 case WID_MTS_EZY: case WID_MTS_CUSTOM1: case WID_MTS_CUSTOM2: // set playlist
609 _music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_MTS_ALL));
610 break;
611 }
612 }
613
614 void OnDropdownSelect(WidgetID widget, int index) override
615 {
616 switch (widget) {
617 case WID_MTS_MUSICSET:
618 ChangeMusicSet(index);
619 break;
620 default:
621 NOT_REACHED();
622 }
623 }
624};
625
626static constexpr NWidgetPart _nested_music_track_selection_widgets[] = {
628 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
629 NWidget(WWT_CAPTION, COLOUR_GREY, WID_MTS_CAPTION),
630 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_MTS_MUSICSET), SetStringTip(STR_PLAYLIST_CHANGE_SET, STR_PLAYLIST_TOOLTIP_CHANGE_SET),
631 EndContainer(),
632 NWidget(WWT_PANEL, COLOUR_GREY),
633 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
634 /* Left panel. */
636 NWidget(WWT_LABEL, INVALID_COLOUR), SetFill(1, 0), SetStringTip(STR_PLAYLIST_TRACK_INDEX),
637 NWidget(WWT_PANEL, COLOUR_GREY, WID_MTS_LIST_LEFT), SetFill(1, 1), SetMinimalSize(180, 194), SetToolTip(STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK), EndContainer(),
639 EndContainer(),
640 /* Middle buttons. */
642 NWidget(NWID_SPACER), SetMinimalSize(60, 30), // Space above the first button from the title bar.
643 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_ALL), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_ALL, STR_MUSIC_TOOLTIP_SELECT_ALL_TRACKS_PROGRAM),
644 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_OLD), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_OLD_STYLE, STR_MUSIC_TOOLTIP_SELECT_OLD_STYLE_MUSIC),
645 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_NEW), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_NEW_STYLE, STR_MUSIC_TOOLTIP_SELECT_NEW_STYLE_MUSIC),
646 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_EZY), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_EZY_STREET, STR_MUSIC_TOOLTIP_SELECT_EZY_STREET_STYLE),
647 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_CUSTOM1), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_CUSTOM_1, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_1_USER_DEFINED),
648 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_CUSTOM2), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_CUSTOM_2, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_2_USER_DEFINED),
649 NWidget(NWID_SPACER), SetMinimalSize(0, 16), // Space above 'clear' button
650 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_MTS_CLEAR), SetFill(1, 0), SetStringTip(STR_PLAYLIST_CLEAR, STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1),
652 EndContainer(),
653 /* Right panel. */
655 NWidget(WWT_LABEL, INVALID_COLOUR, WID_MTS_PLAYLIST), SetFill(1, 0),
656 NWidget(WWT_PANEL, COLOUR_GREY, WID_MTS_LIST_RIGHT), SetFill(1, 1), SetMinimalSize(180, 194), SetToolTip(STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK), EndContainer(),
658 EndContainer(),
659 EndContainer(),
660 EndContainer(),
661};
662
663static WindowDesc _music_track_selection_desc(
664 WDP_AUTO, nullptr, 0, 0,
666 {},
667 _nested_music_track_selection_widgets
668);
669
670static void ShowMusicTrackSelection()
671{
672 AllocateWindowDescFront<MusicTrackSelectionWindow>(_music_track_selection_desc, 0);
673}
674
675struct MusicWindow : public Window {
676 MusicWindow(WindowDesc &desc, WindowNumber number) : Window(desc)
677 {
678 this->InitNested(number);
681
682 UpdateDisabledButtons();
683 }
684
685 void UpdateDisabledButtons()
686 {
687 /* Disable music control widgets if there is no music
688 * -- except Programme button! So you can still select a music set. */
690 BaseMusic::GetUsedSet()->num_available == 0,
693 );
694 }
695
696 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
697 {
698 switch (widget) {
699 /* Make sure that WID_M_SHUFFLE and WID_M_PROGRAMME have the same size.
700 * This can't be done by using NWidContainerFlag::EqualSize as the WID_M_INFO is
701 * between those widgets and of different size. */
702 case WID_M_SHUFFLE: case WID_M_PROGRAMME: {
703 Dimension d = maxdim(GetStringBoundingBox(STR_MUSIC_PROGRAM), GetStringBoundingBox(STR_MUSIC_SHUFFLE));
704 d.width += padding.width;
705 d.height += padding.height;
706 size = maxdim(size, d);
707 break;
708 }
709
710 case WID_M_TRACK_NR: {
711 Dimension d = GetStringBoundingBox(STR_MUSIC_TRACK_NONE);
712 d.width += padding.width;
713 d.height += padding.height;
714 size = maxdim(size, d);
715 break;
716 }
717
718 case WID_M_TRACK_NAME: {
719 Dimension d = GetStringBoundingBox(STR_MUSIC_TITLE_NONE);
720 for (const auto &song : _music.music_set) {
721 d = maxdim(d, GetStringBoundingBox(GetString(STR_MUSIC_TITLE_NAME, song.songname)));
722 }
723 d.width += padding.width;
724 d.height += padding.height;
725 size = maxdim(size, d);
726 break;
727 }
728
729 /* Hack-ish: set the proper widget data; only needs to be done once
730 * per (Re)Init as that's the only time the language changes. */
731 case WID_M_PREV: this->GetWidget<NWidgetCore>(WID_M_PREV)->SetSprite(_current_text_dir == TD_RTL ? SPR_IMG_SKIP_TO_NEXT : SPR_IMG_SKIP_TO_PREV); break;
732 case WID_M_NEXT: this->GetWidget<NWidgetCore>(WID_M_NEXT)->SetSprite(_current_text_dir == TD_RTL ? SPR_IMG_SKIP_TO_PREV : SPR_IMG_SKIP_TO_NEXT); break;
733 case WID_M_PLAY: this->GetWidget<NWidgetCore>(WID_M_PLAY)->SetSprite(_current_text_dir == TD_RTL ? SPR_IMG_PLAY_MUSIC_RTL : SPR_IMG_PLAY_MUSIC); break;
734 }
735 }
736
737 void DrawWidget(const Rect &r, WidgetID widget) const override
738 {
739 switch (widget) {
740 case WID_M_TRACK_NR: {
742 if (BaseMusic::GetUsedSet()->num_available == 0) {
743 break;
744 }
745 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
746 if (_music.IsPlaying()) {
747 DrawString(ir, GetString(STR_MUSIC_TRACK_DIGIT, _music.GetCurrentSong().tracknr, 2));
748 } else {
749 DrawString(ir, STR_MUSIC_TRACK_NONE);
750 }
751 break;
752 }
753
754 case WID_M_TRACK_NAME: {
756 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
757
759 if (BaseMusic::GetUsedSet()->num_available == 0) {
760 DrawString(ir, STR_MUSIC_TITLE_NOMUSIC, TC_FROMSTRING, SA_HOR_CENTER);
761 } else if (_music.IsPlaying()) {
762 DrawString(ir, GetString(STR_MUSIC_TITLE_NAME, entry.songname), TC_FROMSTRING, SA_HOR_CENTER);
763 } else {
764 DrawString(ir, STR_MUSIC_TITLE_NONE, TC_FROMSTRING, SA_HOR_CENTER);
765 }
766
767 break;
768 }
769
770 case WID_M_MUSIC_VOL:
771 DrawSliderWidget(r, 0, INT8_MAX, 0, _settings_client.music.music_vol, nullptr);
772 break;
773
774 case WID_M_EFFECT_VOL:
775 DrawSliderWidget(r, 0, INT8_MAX, 0, _settings_client.music.effect_vol, nullptr);
776 break;
777 }
778 }
779
785 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
786 {
787 if (!gui_scope) return;
788 for (int i = 0; i < 6; i++) {
790 }
791
792 UpdateDisabledButtons();
793
794 if (data == 1) {
795 this->ReInit();
796 } else {
797 this->SetDirty();
798 }
799 }
800
801 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
802 {
803 switch (widget) {
804 case WID_M_PREV: // skip to prev
805 _music.Prev();
806 break;
807
808 case WID_M_NEXT: // skip to next
809 _music.Next();
810 break;
811
812 case WID_M_STOP: // stop playing
813 _music.Stop();
814 break;
815
816 case WID_M_PLAY: // start playing
817 _music.Play();
818 break;
819
820 case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { // volume sliders
822 if (ClickSliderWidget(this->GetWidget<NWidgetBase>(widget)->GetCurrentRect(), pt, 0, INT8_MAX, 0, vol)) {
823 if (widget == WID_M_MUSIC_VOL) {
825 } else {
826 SetEffectVolume(vol);
827 }
828 this->SetWidgetDirty(widget);
830 }
831
832 if (click_count > 0) this->mouse_capture_widget = widget;
833 break;
834 }
835
836 case WID_M_SHUFFLE: // toggle shuffle
837 if (_music.IsShuffle()) {
838 _music.Unshuffle();
839 } else {
840 _music.Shuffle();
841 }
844 break;
845
846 case WID_M_PROGRAMME: // show track selection
847 ShowMusicTrackSelection();
848 break;
849
850 case WID_M_ALL: case WID_M_OLD: case WID_M_NEW:
851 case WID_M_EZY: case WID_M_CUSTOM1: case WID_M_CUSTOM2: // playlist
852 _music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_M_ALL));
853 break;
854 }
855 }
856};
857
858static constexpr NWidgetPart _nested_music_window_widgets[] = {
860 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
861 NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_MUSIC_JAZZ_JUKEBOX_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
862 NWidget(WWT_SHADEBOX, COLOUR_GREY),
863 NWidget(WWT_STICKYBOX, COLOUR_GREY),
864 EndContainer(),
865
868 NWidget(WWT_PANEL, COLOUR_GREY, -1), SetFill(1, 1), EndContainer(),
870 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_PREV), SetMinimalSize(22, 22), SetSpriteTip(SPR_IMG_SKIP_TO_PREV, STR_MUSIC_TOOLTIP_SKIP_TO_PREVIOUS_TRACK),
871 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_NEXT), SetMinimalSize(22, 22), SetSpriteTip(SPR_IMG_SKIP_TO_NEXT, STR_MUSIC_TOOLTIP_SKIP_TO_NEXT_TRACK_IN_SELECTION),
872 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_STOP), SetMinimalSize(22, 22), SetSpriteTip(SPR_IMG_STOP_MUSIC, STR_MUSIC_TOOLTIP_STOP_PLAYING_MUSIC),
873 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_PLAY), SetMinimalSize(22, 22), SetSpriteTip(SPR_IMG_PLAY_MUSIC, STR_MUSIC_TOOLTIP_START_PLAYING_MUSIC),
874 EndContainer(),
875 NWidget(WWT_PANEL, COLOUR_GREY, -1), SetFill(1, 1), EndContainer(),
876 EndContainer(),
877 NWidget(WWT_PANEL, COLOUR_GREY, WID_M_SLIDERS),
878 NWidget(NWID_HORIZONTAL), SetPIP(4, 0, 4),
880 NWidget(WWT_LABEL, INVALID_COLOUR, -1), SetFill(1, 0), SetStringTip(STR_MUSIC_MUSIC_VOLUME),
881 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_M_MUSIC_VOL), SetMinimalSize(67, 0), SetPadding(2), SetMinimalTextLines(1, 0), SetFill(1, 0), SetToolTip(STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC),
882 EndContainer(),
884 NWidget(WWT_LABEL, INVALID_COLOUR, -1), SetFill(1, 0), SetStringTip(STR_MUSIC_EFFECTS_VOLUME),
885 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_M_EFFECT_VOL), SetMinimalSize(67, 0), SetPadding(2), SetMinimalTextLines(1, 0), SetFill(1, 0), SetToolTip(STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC),
886 EndContainer(),
887 EndContainer(),
888 EndContainer(),
889 EndContainer(),
890 NWidget(WWT_PANEL, COLOUR_GREY, WID_M_BACKGROUND),
891 NWidget(NWID_HORIZONTAL), SetPIP(6, 0, 6),
894 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_SHUFFLE), SetMinimalSize(50, 8), SetStringTip(STR_MUSIC_SHUFFLE, STR_MUSIC_TOOLTIP_TOGGLE_PROGRAM_SHUFFLE),
896 EndContainer(),
897 NWidget(NWID_VERTICAL), SetPadding(0, 0, 3, 3),
898 NWidget(WWT_LABEL, INVALID_COLOUR, WID_M_TRACK), SetFill(0, 0), SetStringTip(STR_MUSIC_TRACK),
900 EndContainer(),
901 NWidget(NWID_VERTICAL), SetPadding(0, 3, 3, 0),
902 NWidget(WWT_LABEL, INVALID_COLOUR, WID_M_TRACK_TITLE), SetFill(1, 0), SetStringTip(STR_MUSIC_XTITLE),
903 NWidget(WWT_INSET, COLOUR_GREY, WID_M_TRACK_NAME), SetFill(1, 0), EndContainer(),
904 EndContainer(),
907 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_M_PROGRAMME), SetMinimalSize(50, 8), SetStringTip(STR_MUSIC_PROGRAM, STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION),
909 EndContainer(),
910 EndContainer(),
911 EndContainer(),
913 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_ALL), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_ALL, STR_MUSIC_TOOLTIP_SELECT_ALL_TRACKS_PROGRAM),
914 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_OLD), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_OLD_STYLE, STR_MUSIC_TOOLTIP_SELECT_OLD_STYLE_MUSIC),
915 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_NEW), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_NEW_STYLE, STR_MUSIC_TOOLTIP_SELECT_NEW_STYLE_MUSIC),
916 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_EZY), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_EZY_STREET, STR_MUSIC_TOOLTIP_SELECT_EZY_STREET_STYLE),
917 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_CUSTOM1), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_CUSTOM_1, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_1_USER_DEFINED),
918 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_CUSTOM2), SetFill(1, 0), SetStringTip(STR_MUSIC_PLAYLIST_CUSTOM_2, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_2_USER_DEFINED),
919 EndContainer(),
920};
921
922static WindowDesc _music_window_desc(
923 WDP_AUTO, "music", 0, 0,
925 {},
926 _nested_music_window_widgets
927);
928
929void ShowMusicWindow()
930{
931 AllocateWindowDescFront<MusicWindow>(_music_window_desc, 0);
932}
Generic functions for replacing base data (graphics, sounds).
Generic functions for replacing base music data.
static const uint NUM_SONGS_AVAILABLE
Maximum number of songs in the full playlist; theme song + the classes.
static const uint NUM_SONGS_CLASS
Maximum number of songs in the 'class' playlists.
static const uint NUM_SONGS_PLAYLIST
Maximum number of songs in the (custom) playlist.
static const MusicSet * GetUsedSet()
Return the used set.
static const MusicSet * GetSet(int index)
Get the name of the graphics set at the specified index.
static int GetIndexOfUsedSet()
Get the index of the currently active graphics set.
static bool SetSetByName(const std::string &name)
Set the set to be used.
static std::string ini_set
The set as saved in the config file.
virtual void StopSong()=0
Stop playing the current song.
static MusicDriver * GetInstance()
Get the currently active instance of the music driver.
virtual void PlaySong(const MusicSongInfo &song)=0
Play a particular song.
virtual void SetVolume(uint8_t vol)=0
Set the volume, if possible.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:29
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:407
Functions related to the drop down widget.
Types related to the drop down widget.
Functions related to errors.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:852
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:658
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:115
Functions related to the gfx engine.
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:246
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:378
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetSpriteTip(SpriteID sprite, StringID tip={})
Widget part function for setting the sprite and tooltip.
constexpr NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:945
Functions related to memory operations.
void MemSetT(T *ptr, uint8_t value, size_t num=1)
Type-safe version of memset().
Definition mem_func.hpp:36
Functions to mix sound samples.
Base for all music playback.
void MusicLoop()
Check music playback status and start/stop/song-finished.
void ChangeMusicSet(int index)
Change the configured music set and reset playback.
void InitializeMusic()
Prepare the music system for use.
Types related to the music widgets.
@ WID_MTS_OLD
Old button.
@ WID_MTS_CUSTOM2
Custom2 button.
@ WID_MTS_ALL
All button.
@ WID_MTS_CUSTOM1
Custom1 button.
@ WID_MTS_LIST_LEFT
Left button.
@ WID_MTS_EZY
Ezy button.
@ WID_MTS_CLEAR
Clear button.
@ WID_MTS_LIST_RIGHT
Right button.
@ WID_MTS_NEW
New button.
@ WID_MTS_PLAYLIST
Playlist.
@ WID_MTS_MUSICSET
Music set selection.
@ WID_MTS_CAPTION
Window caption.
@ WID_M_PREV
Previous button.
@ WID_M_TRACK_NAME
Track name.
@ WID_M_NEXT
Next button.
@ WID_M_NEW
New button.
@ WID_M_TRACK_TITLE
Track title.
@ WID_M_STOP
Stop button.
@ WID_M_BACKGROUND
Background of the window.
@ WID_M_ALL
All button.
@ WID_M_CUSTOM2
Custom2 button.
@ WID_M_CUSTOM1
Custom1 button.
@ WID_M_TRACK
Track playing.
@ WID_M_OLD
Old button.
@ WID_M_SLIDERS
Sliders.
@ WID_M_SHUFFLE
Shuffle button.
@ WID_M_EZY
Ezy button.
@ WID_M_MUSIC_VOL
Music volume.
@ WID_M_EFFECT_VOL
Effect volume.
@ WID_M_TRACK_NR
Track number.
@ WID_M_PROGRAMME
Program button.
@ WID_M_PLAY
Play button.
Some generic types.
static const uint8_t PC_BLACK
Black palette colour.
Pseudo random number generator.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:58
Functions for setting GUIs.
Types related to global configuration settings.
void DrawSliderWidget(Rect r, int min_value, int max_value, int nmarks, int value, SliderMarkFunc *mark_func)
Draw a slider widget with knob at given value.
Definition slider.cpp:31
bool ClickSliderWidget(Rect r, Point pt, int min_value, int max_value, int nmarks, int &value)
Handle click on a slider widget to change the value.
Definition slider.cpp:91
Functions related to sound.
This file contains all sprite-related enums and defines.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:57
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
MusicSettings music
settings related to music/sound
Dimensions (a width and height) of a rectangle in 2D.
All data of a music set.
uint8_t effect_vol
The requested effects volume.
bool playing
Whether music is playing.
uint8_t music_vol
The requested music volume.
uint8_t custom_1[33]
The order of the first custom playlist.
uint8_t custom_2[33]
The order of the second custom playlist.
uint8_t playlist
The playlist (number) to play.
bool shuffle
Whether to shuffle the music.
Metadata about a music track.
std::string songname
name of song displayed in UI
uint8_t tracknr
track number of song displayed in UI
std::string filename
file on disk containing song (when used in MusicSet class)
bool loop
song should play in a tight loop if possible, never ending
int cat_index
entry index in CAT file, for filetype==MTT_MPSMIDI
const MusicSet * set
music set the song comes from
Definition music_gui.cpp:43
uint set_index
index of song in set
Definition music_gui.cpp:44
void SaveCustomPlaylist(PlaylistChoices pl)
Save a custom playlist to settings after modification.
void CheckStatus()
Check that music is playing if it should, and that appropriate playlist is active for game/main menu.
void ChangeMusicSet(const std::string &set_name)
Change to named music set, and reset playback.
void ChangePlaylist(PlaylistChoices pl)
Switch to another playlist, or reload the current one.
void Shuffle()
Enable shuffle mode.
void Stop()
Stop playback and set flag that we don't intend to play music.
void BuildPlaylists()
Rebuild all playlists for the current music set.
void Next()
Skip to next track.
void Play()
Start/restart playback at current song.
bool IsPlaying() const
Is the player getting music right now?
void Unshuffle()
Disable shuffle mode.
void PlaylistRemove(size_t song_index)
Remove a song from a custom playlist.
PlaylistEntry GetCurrentSong() const
Return the current song, or a dummy if none.
void PlaylistAdd(size_t song_index)
Append a song to a custom playlist.
void ChangePlaylistPosition(int ofs)
Change playlist position pointer by the given offset, making sure to keep it within valid range.
Playlist displayed_playlist
current playlist as displayed in GUI, never in shuffled order
Definition music_gui.cpp:63
Playlist active_playlist
current play order of songs, including any shuffle
Definition music_gui.cpp:62
void Prev()
Skip to previous track.
void PlaylistClear()
Remove all songs from the current custom playlist.
void SetPositionBySetIndex(uint set_index)
Set playlist position by set index.
bool IsCustomPlaylist() const
Is one of the custom playlists selected?
bool IsShuffle() const
Is shuffle mode enabled?
Playlist music_set
all songs in current music set, in set order
Definition music_gui.cpp:64
uint GetSetIndex()
Get set index from current playlist position.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Partial widget specification to allow NWidgets to be written nested.
Coordinates of a point in 2D.
Specification of a rectangle with absolute coordinates of all edges.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
High level window description.
Definition window_gui.h:168
Number to differentiate different windows of the same class.
Data structure for an opened window.
Definition window_gui.h:274
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:957
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:556
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:504
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
Definition window_gui.h:327
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:516
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:442
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:212
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:461
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1749
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h:41
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WWT_LABEL
Centered label.
Definition widget_type.h:47
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:69
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:65
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:45
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:56
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:54
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:51
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:67
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:59
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:38
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:60
@ EqualSize
Containers should keep all their (resizing) children equally large.
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3132
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3224
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:145
int WidgetID
Widget ID.
Definition window_type.h:20
@ WN_GAME_OPTIONS_GAME_OPTIONS
Game options.
Definition window_type.h:28
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:47
@ WC_MUSIC_TRACK_SELECTION
Music track selection; Window numbers:
@ WC_MUSIC_WINDOW
Music window; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers:
Functions related to zooming.