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