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