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