]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/bgmscript.qc
take3: format 903 files
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / bgmscript.qc
1 #include "bgmscript.qh"
2
3 #include <common/util.qh>
4 #include <client/autocvars.qh>
5 #include <client/defs.qh>
6 #include <client/main.qh>
7
8 #define CONSTANT_SPEED_DECAY
9
10 float bgmscriptbuf;
11 float bgmscriptbufsize;
12 float bgmscriptbufloaded;
13
14 class(BGMScript).float bgmscriptline;
15 class(BGMScript).float bgmscriptline0;
16 class(BGMScript).float bgmscriptvolume;
17 class(BGMScript).float bgmscripttime;
18 class(BGMScript).float bgmscriptstate;
19 class(BGMScript).float bgmscriptstatetime;
20
21 float GetAttackDecaySustainAmplitude(float a, float d, float s, float t)
22 {
23         // phase:
24         //   attack: from 0 to 1, in time a for a full length
25         //   decay: from 1 to s, in time d
26         //   sustain: s
27
28         if (t < 0) {
29                 return 0;
30         }
31
32         if (a) {
33                 if (t <= a) {
34                         return t / a;
35                 }
36         }
37
38         if (d) {
39                 if (t <= a + d) {
40                         return ((t - a) / d) * (s - 1) + 1;
41                 }
42         }
43
44         return s;
45 }
46
47 float GetReleaseAmplitude(float d, float s, float r, float t)
48 {
49         float decayval, releaseval;
50
51         if (!r) {
52                 return 0;
53         }
54
55         if (t > r) {
56                 return 0;
57         }
58
59         releaseval = s * (1 - t / r);
60
61         if (t < -d) {
62                 return 1;
63         }
64
65         if (t < 0 && t >= -d) {
66                 // pre-time decay
67                 // value is s at time  0
68                 //          1 at time -d
69                 decayval = ((t + d) / d) * (s - 1) + 1;
70                 return max(decayval, releaseval);
71         }
72
73         return releaseval;
74 }
75
76 float GetAttackTime(float a, float amp)
77 {
78         return amp * a;
79 }
80
81 float GetReleaseTime(float d, float s, float r, float amp)
82 {
83         float decaytime, releasetime;
84
85         if (!s) {
86                 return 0;
87         }
88
89         // if amp > s, we may be in the attack or in the prolonged decay curve
90         releasetime = (1 - amp / s) * r;
91
92         if (amp > s) {
93                 if (s == 1) { // gracefully handle division by zero here
94                         return 0;
95                 }
96
97                 // pre-time decay
98                 // value is s at time  0
99                 //          1 at time -d
100                 decaytime = (amp - 1) / (s - 1) * d - d;
101                 return max(decaytime, releasetime);
102         }
103
104         return releasetime;
105 }
106
107 void BGMScript_Init()
108 {
109         string s;
110         float fh;
111         bgmscriptbuf = bgmscriptbufsize = 0;
112         bgmscriptbufloaded = 1;
113         s = strcat("maps/", mi_shortname, ".bgs");
114         fh = fopen(s, FILE_READ);
115         if (fh < 0) {
116                 return;
117         }
118         bgmscriptbuf = buf_create();
119         while ((s = fgets(fh))) {
120                 bufstr_set(bgmscriptbuf, bgmscriptbufsize, s);
121                 ++bgmscriptbufsize;
122         }
123         fclose(fh);
124 }
125
126 void BGMScript_InitEntity(entity e)
127 {
128         float l;
129         string m;
130         if (e.bgmscript != "") {
131                 if (!bgmscriptbufloaded) {
132                         BGMScript_Init();
133                 }
134
135                 float i;
136
137                 m = strcat(e.bgmscript, " ");
138                 l = strlen(m);
139
140                 e.bgmscriptline0 = -1;
141                 for (i = 0; i < bgmscriptbufsize; ++i) {
142                         if (substring(bufstr_get(bgmscriptbuf, i), 0, l) == m) {
143                                 break;
144                         }
145                 }
146                 e.bgmscriptline = e.bgmscriptline0 = i;
147                 if (i >= bgmscriptbufsize) {
148                         LOG_INFOF("ERROR: bgmscript does not define %s", e.bgmscript);
149                         strunzone(e.bgmscript);
150                         e.bgmscript = string_null;
151                 }
152         }
153 }
154
155 float GetCurrentAmplitude(entity e, float trel)
156 {
157         if (e.bgmscriptstate) {
158                 return GetAttackDecaySustainAmplitude(e.bgmscriptattack, e.bgmscriptdecay, e.bgmscriptsustain, trel) * e.bgmscriptvolume;
159         } else {
160 #ifdef CONSTANT_SPEED_DECAY
161                 return GetReleaseAmplitude(e.bgmscriptdecay, e.bgmscriptsustain * e.bgmscriptvolume, e.bgmscriptrelease, trel);
162 #else
163                 return GetReleaseAmplitude(e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscriptrelease, trel) * e.bgmscriptvolume;
164 #endif
165         }
166 }
167
168 float GetTimeForAmplitude(entity e, float amp)
169 {
170         if (e.bgmscriptstate) {
171                 return GetAttackTime(e.bgmscriptattack, amp / e.bgmscriptvolume);
172         } else {
173 #ifdef CONSTANT_SPEED_DECAY
174                 return GetReleaseTime(e.bgmscriptdecay, e.bgmscriptsustain * e.bgmscriptvolume, e.bgmscriptrelease, amp);
175 #else
176                 return GetReleaseTime(e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscriptrelease, amp / e.bgmscriptvolume);
177 #endif
178         }
179 }
180
181 float doBGMScript(entity e)
182 {
183         float amp, vel;
184
185         if (e.bgmscript == "") {
186                 return 1;
187         }
188
189         if (autocvar_bgmvolume <= 0) {
190                 return -1;
191         }
192
193         e.just_toggled = false;
194
195         if (bgmtime < 0) {
196                 return -1;
197         }
198
199         if (bgmtime < e.bgmscripttime) {
200                 amp = GetCurrentAmplitude(e, e.bgmscripttime - e.bgmscriptstatetime + drawframetime);
201
202                 e.bgmscriptline = e.bgmscriptline0;
203                 e.bgmscripttime = bgmtime;
204
205                 // treat this as a stop event for all notes, to prevent sticking keys
206                 e.bgmscriptstate = false;
207                 e.bgmscriptvolume = 1;
208                 e.bgmscriptstatetime = bgmtime - GetTimeForAmplitude(e, amp);
209         }
210
211         // find the CURRENT line
212         for ( ; ; ) {
213                 tokenize_console(bufstr_get(bgmscriptbuf, e.bgmscriptline));
214                 if (stof(argv(1)) >= bgmtime || argv(0) != e.bgmscript) {
215                         e.bgmscripttime = bgmtime;
216                         return GetCurrentAmplitude(e, bgmtime - e.bgmscriptstatetime);
217                 } else if (bgmtime >= stof(argv(1))) {
218                         e.bgmscriptline += 1;
219                         e.bgmscripttime = stof(argv(1));
220
221                         amp = GetCurrentAmplitude(e, e.bgmscripttime - e.bgmscriptstatetime);
222
223                         // time code reached!
224                         vel = stof(argv(2));
225                         if (vel > 0) {
226                                 e.just_toggled = e.bgmscriptstate = true;
227                                 e.bgmscriptvolume = vel;
228                         } else {
229                                 e.just_toggled = e.bgmscriptstate = false;
230                         }
231
232                         e.bgmscriptstatetime = e.bgmscripttime - GetTimeForAmplitude(e, amp);
233                 }
234         }
235 }