1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6
7 This file is part of the OpenJK source code.
8
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22 #include "g_headers.h"
23
24 #include "g_local.h"
25 #include "../../code/Rufl/hstring.h"
26 #include "../code/qcommon/ojk_saved_game_helper.h"
27
28 #define MAX_GTIMERS 16384
29
30 typedef struct gtimer_s
31 {
32 hstring id; // Use handle strings, so that things work after loading
33 int time;
34 struct gtimer_s *next; // In either free list or current list
35 } gtimer_t;
36
37 gtimer_t g_timerPool[ MAX_GTIMERS ];
38 gtimer_t *g_timers[ MAX_GENTITIES ];
39 gtimer_t *g_timerFreeList;
40
41
TIMER_GetCount(int num)42 static int TIMER_GetCount(int num)
43 {
44 gtimer_t *p = g_timers[num];
45 int count = 0;
46
47 while (p)
48 {
49 count++;
50 p = p->next;
51 }
52
53 return count;
54 }
55
56
57 /*
58 -------------------------
59 TIMER_RemoveHelper
60
61 Scans an entities timer list to remove a given
62 timer from the list and put it on the free list
63
64 Doesn't do much error checking, only called below
65 -------------------------
66 */
TIMER_RemoveHelper(int num,gtimer_t * timer)67 static void TIMER_RemoveHelper( int num, gtimer_t *timer )
68 {
69 gtimer_t *p = g_timers[num];
70
71 // Special case: first timer in list
72 if (p == timer)
73 {
74 g_timers[num] = g_timers[num]->next;
75 p->next = g_timerFreeList;
76 g_timerFreeList = p;
77 return;
78 }
79
80 // Find the predecessor
81 while (p->next != timer)
82 {
83 p = p->next;
84 }
85
86 // Rewire
87 p->next = p->next->next;
88 timer->next = g_timerFreeList;
89 g_timerFreeList = timer;
90 return;
91 }
92
93
94
95
96 /*
97 -------------------------
98 TIMER_Clear
99 -------------------------
100 */
101
TIMER_Clear(void)102 void TIMER_Clear( void )
103 {
104 int i;
105 for (i = 0; i < MAX_GENTITIES; i++)
106 {
107 g_timers[i] = NULL;
108 }
109
110 for (i = 0; i < MAX_GTIMERS - 1; i++)
111 {
112 g_timerPool[i].next = &g_timerPool[i+1];
113 }
114 g_timerPool[MAX_GTIMERS-1].next = NULL;
115 g_timerFreeList = &g_timerPool[0];
116 }
117
118 /*
119 -------------------------
120 TIMER_Clear
121 -------------------------
122 */
123
TIMER_Clear(int idx)124 void TIMER_Clear( int idx )
125 {
126 // rudimentary safety checks, might be other things to check?
127 if ( idx >= 0 && idx < MAX_GENTITIES )
128 {
129 gtimer_t *p = g_timers[idx];
130
131 // No timers at all -> do nothing
132 if (!p)
133 {
134 return;
135 }
136
137 // Find the end of this ents timer list
138 while (p->next)
139 {
140 p = p->next;
141 }
142
143 // Splice the lists
144 p->next = g_timerFreeList;
145 g_timerFreeList = g_timers[idx];
146 g_timers[idx] = NULL;
147 return;
148 }
149 }
TIMER_Clear(gentity_t * ent)150 void TIMER_Clear( gentity_t *ent )
151 {
152 if ( ent )
153 TIMER_Clear( ent->s.number );
154 }
155
156
157 /*
158 -------------------------
159 TIMER_Save
160 -------------------------
161 */
162
TIMER_Save(void)163 void TIMER_Save( void )
164 {
165 int j;
166 gentity_t *ent;
167
168 ojk::SavedGameHelper saved_game(
169 ::gi.saved_game);
170
171 for ( j = 0, ent = &g_entities[0]; j < MAX_GENTITIES; j++, ent++ )
172 {
173 int numTimers = TIMER_GetCount(j);
174
175 if ( !ent->inuse && numTimers)
176 {
177 // Com_Printf( "WARNING: ent with timers not inuse\n" );
178 assert(numTimers);
179 TIMER_Clear( j );
180 numTimers = 0;
181 }
182
183 //Write out the timer information
184 saved_game.write_chunk<int32_t>(
185 INT_ID('T', 'I', 'M', 'E'),
186 numTimers);
187
188 gtimer_t *p = g_timers[j];
189 assert ((numTimers && p) || (!numTimers && !p));
190
191 while(p)
192 {
193 const char *timerID = p->id.c_str();
194 const int length = strlen(timerID) + 1;
195 const int time = p->time - level.time; //convert this back to delta so we can use SET after loading
196
197 assert( length < 1024 );//This will cause problems when loading the timer if longer
198
199 //Write out the string size and data
200 saved_game.write_chunk<int32_t>(
201 INT_ID('T', 'S', 'L', 'N'),
202 length);
203
204 saved_game.write_chunk(
205 INT_ID('T', 'S', 'N', 'M'),
206 timerID,
207 length);
208
209 //Write out the timer data
210 saved_game.write_chunk<int32_t>(
211 INT_ID('T', 'D', 'T', 'A'),
212 time);
213
214 p = p->next;
215 }
216 }
217 }
218
219 /*
220 -------------------------
221 TIMER_Load
222 -------------------------
223 */
224
TIMER_Load(void)225 void TIMER_Load( void )
226 {
227 int j;
228 gentity_t *ent;
229
230 ojk::SavedGameHelper saved_game(
231 ::gi.saved_game);
232
233 for ( j = 0, ent = &g_entities[0]; j < MAX_GENTITIES; j++, ent++ )
234 {
235 int numTimers = 0;
236
237 saved_game.read_chunk<int32_t>(
238 INT_ID('T', 'I', 'M', 'E'),
239 numTimers);
240
241 //Make sure there's something to read
242 if ( numTimers == 0 )
243 continue;
244
245 //Read back all entries
246 for ( int i = 0; i < numTimers; i++ )
247 {
248 int length = 0, time = 0;
249 char tempBuffer[1024]; // Still ugly. Setting ourselves up for 007 AUF all over again. =)
250
251 assert (sizeof(g_timers[0]->time) == sizeof(time) );//make sure we're reading the same size as we wrote
252
253 saved_game.read_chunk<int32_t>(
254 INT_ID('T', 'S', 'L', 'N'),
255 length);
256
257 if ( length >= 1024 ) {
258 assert( 0 );
259 continue;
260 }
261
262 //Read the id and time
263 saved_game.read_chunk(
264 INT_ID('T', 'S', 'N', 'M'),
265 tempBuffer,
266 length);
267
268 tempBuffer[length] = '\0';
269
270 saved_game.read_chunk<int32_t>(
271 INT_ID('T', 'D', 'T', 'A'),
272 time);
273
274 //this is odd, we saved all the timers in the autosave, but not all the ents are spawned yet from an auto load, so skip it
275 if (ent->inuse)
276 { //Restore it
277 TIMER_Set(ent, tempBuffer, time);
278 }
279 }
280 }
281 }
282
283
TIMER_GetNew(int num,const char * identifier)284 static gtimer_t *TIMER_GetNew(int num, const char *identifier)
285 {
286 assert(num < ENTITYNUM_MAX_NORMAL);//don't want timers on NONE or the WORLD
287 gtimer_t *p = g_timers[num];
288
289 // Search for an existing timer with this name
290 while (p)
291 {
292 if (p->id == identifier)
293 { // Found it
294 return p;
295 }
296
297 p = p->next;
298 }
299
300 // No existing timer with this name was found, so grab one from the free list
301 if (!g_timerFreeList)
302 {//oh no, none free!
303 assert(g_timerFreeList);
304 return NULL;
305 }
306
307 p = g_timerFreeList;
308 g_timerFreeList = g_timerFreeList->next;
309 p->next = g_timers[num];
310 g_timers[num] = p;
311 return p;
312 }
313
314
TIMER_GetExisting(int num,const char * identifier)315 gtimer_t *TIMER_GetExisting(int num, const char *identifier)
316 {
317 gtimer_t *p = g_timers[num];
318
319 while (p)
320 {
321 if (p->id == identifier)
322 { // Found it
323 return p;
324 }
325
326 p = p->next;
327 }
328
329 return NULL;
330 }
331
332
333
334 /*
335 -------------------------
336 TIMER_Set
337 -------------------------
338 */
339
TIMER_Set(gentity_t * ent,const char * identifier,int duration)340 void TIMER_Set( gentity_t *ent, const char *identifier, int duration )
341 {
342 assert(ent->inuse);
343 gtimer_t *timer = TIMER_GetNew(ent->s.number, identifier);
344
345 if (timer)
346 {
347 timer->id = identifier;
348 timer->time = level.time + duration;
349 }
350 }
351
352 /*
353 -------------------------
354 TIMER_Get
355 -------------------------
356 */
357
TIMER_Get(gentity_t * ent,const char * identifier)358 int TIMER_Get( gentity_t *ent, const char *identifier )
359 {
360 gtimer_t *timer = TIMER_GetExisting(ent->s.number, identifier);
361
362 if (!timer)
363 {
364 return -1;
365 }
366
367 return timer->time;
368 }
369
370 /*
371 -------------------------
372 TIMER_Done
373 -------------------------
374 */
375
TIMER_Done(gentity_t * ent,const char * identifier)376 qboolean TIMER_Done( gentity_t *ent, const char *identifier )
377 {
378 gtimer_t *timer = TIMER_GetExisting(ent->s.number, identifier);
379
380 if (!timer)
381 {
382 return qtrue;
383 }
384
385 return (qboolean)(timer->time < level.time);
386 }
387
388 /*
389 -------------------------
390 TIMER_Done2
391
392 Returns false if timer has been
393 started but is not done...or if
394 timer was never started
395 -------------------------
396 */
397
TIMER_Done2(gentity_t * ent,const char * identifier,qboolean remove)398 qboolean TIMER_Done2( gentity_t *ent, const char *identifier, qboolean remove )
399 {
400 gtimer_t *timer = TIMER_GetExisting(ent->s.number, identifier);
401 qboolean res;
402
403 if (!timer)
404 {
405 return qfalse;
406 }
407
408 res = (qboolean)(timer->time < level.time);
409
410 if (res && remove)
411 {
412 // Put it back on the free list
413 TIMER_RemoveHelper(ent->s.number, timer);
414 }
415
416 return res;
417 }
418
419 /*
420 -------------------------
421 TIMER_Exists
422 -------------------------
423 */
TIMER_Exists(gentity_t * ent,const char * identifier)424 qboolean TIMER_Exists( gentity_t *ent, const char *identifier )
425 {
426 return (qboolean)(TIMER_GetExisting(ent->s.number, identifier) != NULL);
427 }
428
429
430
431 /*
432 -------------------------
433 TIMER_Remove
434 Utility to get rid of any timer
435 -------------------------
436 */
TIMER_Remove(gentity_t * ent,const char * identifier)437 void TIMER_Remove( gentity_t *ent, const char *identifier )
438 {
439 gtimer_t *timer = TIMER_GetExisting(ent->s.number, identifier);
440
441 if (!timer)
442 {
443 return;
444 }
445
446 // Put it back on the free list
447 TIMER_RemoveHelper(ent->s.number, timer);
448 }
449
450 /*
451 -------------------------
452 TIMER_Start
453 -------------------------
454 */
455
TIMER_Start(gentity_t * self,const char * identifier,int duration)456 qboolean TIMER_Start( gentity_t *self, const char *identifier, int duration )
457 {
458 if ( TIMER_Done( self, identifier ) )
459 {
460 TIMER_Set( self, identifier, duration );
461 return qtrue;
462 }
463 return qfalse;
464 }
465