1 //----------------------------------------------------------------------------
2 //  EDGE Radius Trigger / Tip Code
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2010  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // -KM- 1998/11/25 Fixed problems created by DDF.
20 //   Radius Triggers can be added to wad files.  RSCRIPT is the lump.
21 //   Tip function can handle graphics.
22 //   New functions: ondeath, #version
23 //   Radius Triggers with radius < 0 affect entire map.
24 //   Radius triggers used to save compatibility with hacks in Doom/Doom2
25 //       (eg MAP07, E2M8, E3M8, MAP32 etc..)
26 //
27 // -AJA- 1999/10/23: Began work on a state model for RTS actions.
28 //
29 // -AJA- 1999/10/24: Split off actions into rad_act.c, and structures
30 //       into the rad_main.h file.
31 //
32 // -AJA- 2000/01/04: Split off parsing code into rad_pars.c.
33 //
34 
35 #include "i_defs.h"
36 
37 #include "epi/file.h"
38 #include "epi/filesystem.h"
39 
40 #include "dm_defs.h"
41 #include "dm_state.h"
42 #include "e_main.h"
43 #include "hu_draw.h"
44 #include "hu_style.h"
45 #include "hu_stuff.h"
46 #include "g_game.h"
47 #include "m_argv.h"
48 #include "m_misc.h"
49 #include "m_random.h"
50 #include "p_local.h"
51 #include "p_spec.h"
52 #include "rad_trig.h"
53 #include "rad_act.h"
54 #include "r_defs.h"
55 #include "am_map.h"
56 #include "r_colormap.h"
57 #include "r_draw.h"
58 #include "r_modes.h"
59 #include "s_sound.h"
60 #include "w_wad.h"
61 #include "z_zone.h"
62 
63 
64 #define MAXRTSLINE  2048
65 
66 
67 // Static Scripts.  Never change once all scripts have been read in.
68 rad_script_t *r_scripts = NULL;
69 
70 // Dynamic Triggers.  These only exist for the current level.
71 rad_trigger_t *active_triggers = NULL;
72 
73 
74 class rts_menu_c
75 {
76 private:
77 	static const int MAX_TITLE  = 24;
78 	static const int MAX_CHOICE = 9;
79 
80 	rad_trigger_t *trigger;
81 
82 	style_c *style;
83 
84 	std::string title;
85 
86 	std::vector< std::string > choices;
87 
88 public:
rts_menu_c(s_show_menu_t * menu,rad_trigger_t * _trigger,style_c * _style)89 	rts_menu_c(s_show_menu_t *menu, rad_trigger_t *_trigger, style_c *_style) :
90 		trigger(_trigger), style(_style), title(), choices()
91 	{
92 		const char * text = menu->title;
93 		if (menu->use_ldf)
94 			text = language[text];
95 
96 		title = text;
97 
98 		bool no_choices = (! menu->options[0] || ! menu->options[1]);
99 
100 		for (int idx = 0; (idx < 9) && menu->options[idx]; idx++)
101 			AddChoice(no_choices ? 0 : ('1' + idx), menu->options[idx], menu->use_ldf);
102 	}
103 
~rts_menu_c()104 	~rts_menu_c() { /* nothing to do */ }
105 
106 private:
AddChoice(char key,const char * text,bool use_ldf)107 	void AddChoice(char key, const char *text, bool use_ldf)
108 	{
109 		if (use_ldf)
110 			text = language[text];
111 
112 		std::string choice_line = text;
113 
114 		if (key)
115 		{
116 			char buffer[8];
117 			sprintf(buffer, "%c. ", key);
118 
119 			choice_line = std::string(buffer) + choice_line;
120 		}
121 
122 		choices.push_back(choice_line);
123 	}
124 
125 public:
NumChoices() const126 	int NumChoices() const
127 	{
128 		return (int)choices.size();
129 	}
130 
NotifyResult(int result)131 	void NotifyResult(int result)
132 	{
133 		trigger->menu_result = result;
134 	}
135 
Drawer()136 	void Drawer()
137 	{
138 		style->DrawBackground();
139 
140 		HUD_Reset();
141 
142 		HUD_SetAlignment(0, -1);
143 		HUD_SetTextColor(T_WHITE);  // TODO changeable
144 
145 		float total_h = HUD_StringHeight(title.c_str());
146 		total_h += HUD_FontHeight() * (NumChoices() + 1);
147 
148 		float y = 100 - total_h / 2.0f;
149 
150 		HUD_DrawText(160, y, title.c_str());
151 
152 		y += HUD_StringHeight(title.c_str());
153 		y += HUD_FontHeight();
154 
155 		HUD_SetTextColor(T_LTBLUE);  // TODO changeable
156 
157 		for (int c = 0; c < NumChoices(); c++, y += HUD_FontHeight())
158 		{
159 			HUD_DrawText(160, y, choices[c].c_str());
160 		}
161 
162 		HUD_SetAlignment();
163 		HUD_SetTextColor();
164 	}
165 
CheckKey(int key)166 	int CheckKey(int key)
167 	{
168 		if ('a' <= key && key <= 'z')
169 			key = toupper(key);
170 
171 		if (key == 'Q' || key == 'X')  // cancel
172 			return 0;
173 
174 		if ('1' <= key && key <= ('0' + NumChoices()))
175 			return key - '0';
176 
177 		if (NumChoices() < 2 &&
178 			(key == KEYD_SPACE || key == KEYD_ENTER || key == 'Y'))
179 			return 1;
180 
181 		return -1;  /* invalid */
182 	}
183 };
184 
185 // RTS menu active ?
186 bool rts_menuactive = false;
187 static rts_menu_c *rts_curr_menu = NULL;
188 
189 // Current RTS file or lump being parsed.
190 static byte *rad_memfile;
191 static byte *rad_memfile_end;
192 static byte *rad_memptr;
193 static int rad_memfile_size;
194 
195 
RAD_FindScriptByName(const char * map_name,const char * name)196 rad_script_t * RAD_FindScriptByName(const char *map_name, const char *name)
197 {
198 	rad_script_t *scr;
199 
200 	for (scr=r_scripts; scr; scr=scr->next)
201 	{
202 		if (scr->script_name == NULL)
203 			continue;
204 
205 		if (strcmp(scr->mapid, map_name) != 0)
206 			continue;
207 
208 		if (DDF_CompareName(scr->script_name, name) == 0)
209 			return scr;
210 	}
211 
212 	I_Error("RTS: No such script `%s' on map %s.\n", name, map_name);
213 	return NULL;
214 }
215 
216 
RAD_FindTriggerByName(const char * name)217 rad_trigger_t * RAD_FindTriggerByName(const char *name)
218 {
219 	rad_trigger_t *trig;
220 
221 	for (trig=active_triggers; trig; trig=trig->next)
222 	{
223 		if (trig->info->script_name == NULL)
224 			continue;
225 
226 		if (DDF_CompareName(trig->info->script_name, name) == 0)
227 			return trig;
228 	}
229 
230 	I_Warning("RTS: No such trigger `%s'.\n", name);
231 	return NULL;
232 }
233 
234 
RAD_FindTriggerByScript(const rad_script_t * scr)235 rad_trigger_t * RAD_FindTriggerByScript(const rad_script_t *scr)
236 {
237 	rad_trigger_t *trig;
238 
239 	for (trig=active_triggers; trig; trig=trig->next)
240 	{
241 		if (trig->info == scr)
242 			return trig;
243 	}
244 
245 	return NULL;  // no worries if none.
246 }
247 
248 
RAD_FindStateByLabel(rad_script_t * scr,char * label)249 rts_state_t * RAD_FindStateByLabel(rad_script_t *scr, char *label)
250 {
251 	rts_state_t *st;
252 
253 	for (st=scr->first_state; st; st=st->next)
254 	{
255 		if (st->label == NULL)
256 			continue;
257 
258 		if (DDF_CompareName(st->label, label) == 0)
259 			return st;
260 	}
261 
262 	// NOTE: no error message, unlike the other Find funcs
263 	return NULL;
264 }
265 
266 //
267 // Looks for all current triggers with the given tag number, and
268 // either enables them or disables them (based on `disable').
269 // Actor can be NULL.
270 //
RAD_EnableByTag(mobj_t * actor,int tag,bool disable)271 void RAD_EnableByTag(mobj_t *actor, int tag, bool disable)
272 {
273 	rad_trigger_t *trig;
274 
275 	if (tag <= 0)
276 		I_Error("INTERNAL ERROR: RAD_EnableByTag: bad tag %d\n", tag);
277 
278 	for (trig=active_triggers; trig; trig=trig->next)
279 	{
280 		if (trig->info->tag == tag)
281 			break;
282 	}
283 
284 	// were there any ?
285 	if (! trig)
286 		return;
287 
288 	for (; trig; trig=trig->tag_next)
289 	{
290 		if (disable)
291 			trig->disabled = true;
292 		else
293 			trig->disabled = false;
294 	}
295 }
296 
RAD_WithinRadius(mobj_t * mo,rad_script_t * r)297 bool RAD_WithinRadius(mobj_t * mo, rad_script_t * r)
298 {
299 	if (r->rad_x >= 0 && fabs(r->x - mo->x) > r->rad_x + mo->radius)
300 		return false;
301 
302 	if (r->rad_y >= 0 && fabs(r->y - mo->y) > r->rad_y + mo->radius)
303 		return false;
304 
305 	if (r->rad_z >= 0 && fabs(r->z - MO_MIDZ(mo)) > r->rad_z + mo->height/2)
306 	{
307 		return false;
308 	}
309 
310 	return true;
311 }
312 
313 
RAD_AlivePlayers(void)314 static int RAD_AlivePlayers(void)
315 {
316 	int result = 0;
317 
318 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
319 	{
320 		player_t *p = players[pnum];
321 
322 		if (p && p->playerstate != PST_DEAD)
323 			result |= (1 << pnum);
324 	}
325 
326 	return result;
327 }
328 
RAD_AllPlayersInRadius(rad_script_t * r,int mask)329 static int RAD_AllPlayersInRadius(rad_script_t * r, int mask)
330 {
331 	int result = 0;
332 
333 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
334 	{
335 		player_t *p = players[pnum];
336 
337 		if (p && (mask & (1 << pnum)) && RAD_WithinRadius(p->mo, r))
338 			result |= (1 << pnum);
339 	}
340 
341 	return result;
342 }
343 
RAD_AllPlayersUsing(int mask)344 static int RAD_AllPlayersUsing(int mask)
345 {
346 	int result = 0;
347 
348 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
349 	{
350 		player_t *p = players[pnum];
351 
352 		if (p && p->usedown)
353 			result |= (1 << pnum);
354 	}
355 
356 	return result & mask;
357 }
358 
RAD_AllPlayersCheckCond(rad_script_t * r,int mask)359 static int RAD_AllPlayersCheckCond(rad_script_t * r, int mask)
360 {
361 	int result = 0;
362 
363 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
364 	{
365 		player_t *p = players[pnum];
366 
367 		if (p && (mask & (1 << pnum)) && G_CheckConditions(p->mo, r->cond_trig))
368 			result |= (1 << pnum);
369 	}
370 
371 	return result;
372 }
373 
374 
RAD_CheckBossTrig(rad_trigger_t * trig,s_ondeath_t * cond)375 static bool RAD_CheckBossTrig(rad_trigger_t *trig, s_ondeath_t *cond)
376 {
377 	mobj_t *mo;
378 
379 	int count = 0;
380 
381 	// lookup thing type if we haven't already done so
382 	if (! cond->cached_info)
383 	{
384 		if (cond->thing_name)
385 			cond->cached_info = mobjtypes.Lookup(cond->thing_name);
386 		else
387 		{
388 			cond->cached_info = mobjtypes.Lookup(cond->thing_type);
389 
390 			if (cond->cached_info == NULL)
391 				I_Error("RTS ONDEATH: Unknown thing type %d.\n",
392 						cond->thing_type);
393 		}
394 	}
395 
396 	// scan the remaining mobjs to see if all bosses are dead
397 	for (mo=mobjlisthead; mo != NULL; mo=mo->next)
398 	{
399 		if (mo->info == cond->cached_info && mo->health > 0)
400 		{
401 			count++;
402 
403 			if (count > cond->threshhold)
404 				return false;
405 		}
406 	}
407 
408 	return true;
409 }
410 
411 
RAD_CheckHeightTrig(rad_trigger_t * trig,s_onheight_t * cond)412 static bool RAD_CheckHeightTrig(rad_trigger_t *trig,
413 		s_onheight_t *cond)
414 {
415 	float h;
416 
417 	// lookup sector if we haven't already done so
418 	if (! cond->cached_sector)
419 	{
420 		if (cond->sec_num >= 0)
421 		{
422 			if (cond->sec_num >= numsectors)
423 				I_Error("RTS ONHEIGHT: no such sector %d.\n", cond->sec_num);
424 
425 			cond->cached_sector = & sectors[cond->sec_num];
426 		}
427 		else
428 		{
429 			cond->cached_sector = R_PointInSubsector(trig->info->x,
430 					trig->info->y)->sector;
431 		}
432 	}
433 
434 	if (cond->is_ceil)
435 		h = cond->cached_sector->c_h;
436 	else
437 		h = cond->cached_sector->f_h;
438 
439 	return (cond->z1 <= h && h <= cond->z2);
440 }
441 
RAD_CheckReachedTrigger(mobj_t * thing)442 bool RAD_CheckReachedTrigger(mobj_t * thing)
443 {
444 	rad_script_t * scr = (rad_script_t *) thing->path_trigger;
445 	rad_trigger_t * trig;
446 
447 	rts_path_t *path;
448 	int choice;
449 
450 	if (! RAD_WithinRadius(thing, scr))
451 		return false;
452 
453 	// Thing has reached this path node. Update so it starts following
454 	// the next node.  Handle any PATH_EVENT too.  Enable the associated
455 	// trigger (could be none if there were no states).
456 
457 	trig = RAD_FindTriggerByScript(scr);
458 
459 	if (trig)
460 		trig->disabled = false;
461 
462 	if (scr->path_event_label)
463 	{
464 		statenum_t state = P_MobjFindLabel(thing, scr->path_event_label);
465 
466 		if (state)
467 			P_SetMobjStateDeferred(thing, state + scr->path_event_offset, 0);
468 	}
469 
470 	if (scr->next_path_total == 0)
471 	{
472 		thing->path_trigger = NULL;
473 		return true;
474 	}
475 	else if (scr->next_path_total == 1)
476 		choice = 0;
477 	else
478 		choice = P_Random() % scr->next_path_total;
479 
480 	path = scr->next_in_path;
481 	SYS_ASSERT(path);
482 
483 	for (; choice > 0; choice--)
484 	{
485 		path = path->next;
486 		SYS_ASSERT(path);
487 	}
488 
489 	if (! path->cached_scr)
490 		path->cached_scr = RAD_FindScriptByName(scr->mapid, path->name);
491 
492 	SYS_ASSERT(path->cached_scr);
493 
494 	thing->path_trigger = path->cached_scr;
495 	return true;
496 }
497 
DoRemoveTrigger(rad_trigger_t * trig)498 static void DoRemoveTrigger(rad_trigger_t *trig)
499 {
500 	// handle tag linkage
501 	if (trig->tag_next)
502 		trig->tag_next->tag_prev = trig->tag_prev;
503 
504 	if (trig->tag_prev)
505 		trig->tag_prev->tag_next = trig->tag_next;
506 
507 	// unlink and free it
508 	if (trig->next)
509 		trig->next->prev = trig->prev;
510 
511 	if (trig->prev)
512 		trig->prev->next = trig->next;
513 	else
514 		active_triggers = trig->next;
515 
516     S_StopFX(&trig->sfx_origin);
517 
518 	// FIXME: delete !
519     Z_Free(trig);
520 }
521 
522 //
523 // Radius Trigger Event handler.
524 //
RAD_RunTriggers(void)525 void RAD_RunTriggers(void)
526 {
527 	rad_trigger_t *trig, *next;
528 
529 	// Start looking through the trigger list.
530 	for (trig=active_triggers; trig; trig=next)
531 	{
532 		next = trig->next;
533 
534 		// stop running all triggers when an RTS menu becomes active
535 		if (rts_menuactive)
536 			break;
537 
538 		// Don't process, if disabled
539 		if (trig->disabled)
540 			continue;
541 
542 		// Handle repeat delay (from TAGGED_REPEATABLE).  This must be
543 		// done *before* all the condition checks, and that's what makes
544 		// it different from `wait_tics'.
545 		//
546 		if (trig->repeat_delay > 0)
547 		{
548 			trig->repeat_delay--;
549 			continue;
550 		}
551 
552 		// Independent, means you don't have to stay within the trigger
553 		// radius for it to operate, It will operate on it's own.
554 
555 		if (! (trig->info->tagged_independent && trig->activated))
556 		{
557 			int mask = RAD_AlivePlayers();
558 
559 			// Immediate triggers are just that. Immediate.
560 			// Not within range so skip it.
561 			//
562             if (!trig->info->tagged_immediate)
563             {
564                 mask = RAD_AllPlayersInRadius(trig->info, mask);
565                 if (mask == 0)
566                     continue;
567             }
568 
569             // Check for use key trigger.
570             if (trig->info->tagged_use)
571             {
572                 mask = RAD_AllPlayersUsing(mask);
573                 if (mask == 0)
574                     continue;
575             }
576 
577 			// height check...
578 			if (trig->info->height_trig)
579 			{
580 				s_onheight_t *cur;
581 
582 				for (cur=trig->info->height_trig; cur; cur=cur->next)
583 					if (! RAD_CheckHeightTrig(trig, cur))
584 						break;
585 
586 				// if they all succeeded, then cur will be NULL...
587 				if (cur)
588 					continue;
589 			}
590 
591 			// ondeath check...
592 			if (trig->info->boss_trig)
593 			{
594 				s_ondeath_t *cur;
595 
596 				for (cur=trig->info->boss_trig; cur; cur=cur->next)
597 					if (! RAD_CheckBossTrig(trig, cur))
598 						break;
599 
600 				// if they all succeeded, then cur will be NULL...
601 				if (cur)
602 					continue;
603 			}
604 
605 			// condition check...
606 			if (trig->info->cond_trig)
607 			{
608                 mask = RAD_AllPlayersCheckCond(trig->info, mask);
609                 if (mask == 0)
610                     continue;
611 			}
612 
613 			trig->activated = true;
614 			trig->acti_players = mask;
615 		}
616 
617 		// If we are waiting, decrement count and skip it.
618 		// Note that we must do this *after* all the condition checks.
619 		//
620 		if (trig->wait_tics > 0)
621 		{
622 			trig->wait_tics--;
623 			continue;
624 		}
625 
626 		// Waiting until monsters are dead?
627 		while (trig->wait_tics == 0 && trig->wud_count <= 0)
628 		{
629 			// Execute current command
630 			rts_state_t *state = trig->state;
631 			SYS_ASSERT(state);
632 
633 			// move to next state.  We do this NOW since the action itself
634 			// may want to change the trigger's state (to support GOTO type
635 			// actions and other possibilities).
636 			//
637 			trig->state = trig->state->next;
638 
639 			(*state->action)(trig, state->param);
640 
641 			if (! trig->state)
642 				break;
643 
644 			trig->wait_tics += trig->state->tics;
645 
646 			if (trig->disabled || rts_menuactive)
647 				break;
648 		}
649 
650 		if (trig->state)
651 			continue;
652 
653 		// we've reached the end of the states.  Delete the trigger unless
654 		// it is Tagged_Repeatable and has some more repeats left.
655 		//
656 		if (trig->info->repeat_count != REPEAT_FOREVER)
657 			trig->repeats_left--;
658 
659 		if (trig->repeats_left > 0)
660 		{
661 			trig->state = trig->info->first_state;
662 			trig->wait_tics = trig->state->tics;
663 			trig->repeat_delay = trig->info->repeat_delay;
664 			continue;
665 		}
666 
667 		DoRemoveTrigger(trig);
668 	}
669 }
670 
RAD_MonsterIsDead(mobj_t * mo)671 void RAD_MonsterIsDead(mobj_t *mo)
672 {
673 	if (mo->hyperflags & HF_WAIT_UNTIL_DEAD)
674 	{
675 		mo->hyperflags &= ~HF_WAIT_UNTIL_DEAD;
676 
677 		rad_trigger_t *trig;
678 
679 		for (trig = active_triggers ; trig ; trig = trig->next)
680 		{
681 			if (trig->wud_tag == mo->tag)
682 			{
683 				trig->wud_count--;
684 			}
685 		}
686 	}
687 }
688 
689 
690 //
691 // Called from RAD_SpawnTriggers to set the tag_next & tag_prev fields
692 // of each rad_trigger_t, keeping all triggers with the same tag in a
693 // linked list for faster handling.
694 //
RAD_GroupTriggerTags(rad_trigger_t * trig)695 void RAD_GroupTriggerTags(rad_trigger_t *trig)
696 {
697 	rad_trigger_t *cur;
698 
699 	trig->tag_next = trig->tag_prev = NULL;
700 
701 	// find first trigger with the same tag #
702 	for (cur=active_triggers; cur; cur=cur->next)
703 	{
704 		if (cur == trig)
705 			continue;
706 
707 		if (cur->info->tag == trig->info->tag)
708 			break;
709 	}
710 
711 	if (! cur)
712 		return;
713 
714 	// link it in
715 
716 	trig->tag_next = cur;
717 	trig->tag_prev = cur->tag_prev;
718 
719 	if (cur->tag_prev)
720 		cur->tag_prev->tag_next = trig;
721 
722 	cur->tag_prev = trig;
723 }
724 
725 
RAD_SpawnTriggers(const char * map_name)726 void RAD_SpawnTriggers(const char *map_name)
727 {
728 	rad_script_t *scr;
729 	rad_trigger_t *trig;
730 
731 #ifdef DEVELOPERS
732 	if (active_triggers)
733 		I_Error("RAD_SpawnTriggers without RAD_ClearTriggers\n");
734 #endif
735 
736 	for (scr=r_scripts; scr; scr=scr->next)
737 	{
738 		// This is from a different map!
739 		if (strcmp(map_name, scr->mapid) != 0 && strcmp(scr->mapid, "ALL") != 0)
740 			continue;
741 
742 		// -AJA- 1999/09/25: Added skill checks.
743 		if (! G_CheckWhenAppear(scr->appear))
744 			continue;
745 
746 		// -AJA- 2000/02/03: Added player num checks.
747 		if (numplayers < scr->min_players || numplayers > scr->max_players)
748 			continue;
749 
750 		// ignore empty scripts (e.g. path nodes)
751 		if (! scr->first_state)
752 			continue;
753 
754 		// OK, spawn new dynamic trigger
755 		trig = Z_New(rad_trigger_t, 1);
756 
757 		Z_Clear(trig, rad_trigger_t, 1);
758 
759 		trig->info = scr;
760 		trig->disabled = scr->tagged_disabled;
761 		trig->repeats_left = (scr->repeat_count < 0 ||
762 				scr->repeat_count == REPEAT_FOREVER) ? 1 : scr->repeat_count;
763 		trig->repeat_delay = 0;
764 		trig->tip_slot = 0;
765 		trig->wud_tag = trig->wud_count = 0;
766 
767 		RAD_GroupTriggerTags(trig);
768 
769 		// initialise state machine
770 		trig->state = scr->first_state;
771 		trig->wait_tics = scr->first_state->tics;
772 
773 		// link it in
774 		trig->next = active_triggers;
775 		trig->prev = NULL;
776 
777 		if (active_triggers)
778 			active_triggers->prev = trig;
779 
780 		active_triggers = trig;
781 	}
782 }
783 
784 
RAD_ClearCachedInfo(void)785 static void RAD_ClearCachedInfo(void)
786 {
787 	rad_script_t *scr;
788 	s_ondeath_t *d_cur;
789 	s_onheight_t *h_cur;
790 
791 	for (scr=r_scripts; scr; scr=scr->next)
792 	{
793 		// clear ONDEATH cached info
794 		for (d_cur=scr->boss_trig; d_cur; d_cur=d_cur->next)
795 		{
796 			d_cur->cached_info = NULL;
797 		}
798 
799 		// clear ONHEIGHT cached info
800 		for (h_cur=scr->height_trig; h_cur; h_cur=h_cur->next)
801 		{
802 			h_cur->cached_sector = NULL;
803 		}
804 	}
805 }
806 
807 
RAD_ClearTriggers(void)808 void RAD_ClearTriggers(void)
809 {
810 	// remove all dynamic triggers
811 	while (active_triggers)
812 	{
813 		rad_trigger_t *trig = active_triggers;
814 		active_triggers = trig->next;
815 
816 		Z_Free(trig);
817 	}
818 
819 	RAD_ClearCachedInfo();
820 	RAD_ResetTips();
821 }
822 
823 //
824 // Loads the script file into memory for parsing.
825 //
826 // -AJA- 2000/01/04: written, based on DDF_MainCacheFile
827 // -AJA- FIXME: merge them both into a single utility routine.
828 //       (BETTER: a single utility parsing module).
829 //
RAD_MainCacheFile(const char * filename)830 static void RAD_MainCacheFile(const char *filename)
831 {
832 	FILE *file;
833 
834 	// open the file
835 	file = fopen(filename, "rb");
836 
837 	if (file == NULL)
838 		I_Error("\nRAD_MainReadFile: Unable to open: '%s'", filename);
839 
840 	// get to the end of the file
841 	fseek(file, 0, SEEK_END);
842 
843 	// get the size
844 	rad_memfile_size = ftell(file);
845 
846 	// reset to beginning
847 	fseek(file, 0, SEEK_SET);
848 
849 	// malloc the size
850 	rad_memfile = Z_New(byte, rad_memfile_size + 1);
851 	rad_memfile_end = &rad_memfile[rad_memfile_size];
852 
853 	// read the goodies
854 	fread(rad_memfile, 1, rad_memfile_size, file);
855 
856 	// null Terminated string.
857 	rad_memfile[rad_memfile_size] = 0;
858 
859 	// close the file
860 	fclose(file);
861 }
862 
ReadScriptLine(char * buf,int max)863 static int ReadScriptLine(char *buf, int max)
864 {
865 	int real_num = 1;
866 
867 	while (rad_memptr < rad_memfile_end)
868 	{
869 		if (rad_memptr[0] == '\n')
870 		{
871 			// skip trailing EOLN
872 			rad_memptr++;
873 			break;
874 		}
875 
876 		// line concatenation
877 		if (rad_memptr+2 < rad_memfile_end && rad_memptr[0] == '\\')
878 		{
879 			if (rad_memptr[1] == '\n' ||
880 			    (rad_memptr[1] == '\r' && rad_memptr[2] == '\n'))
881 			{
882 				real_num++;
883 				rad_memptr += (rad_memptr[1] == '\n') ? 2 : 3;
884 				continue;
885 			}
886 		}
887 
888 		// ignore carriage returns
889 		if (rad_memptr[0] == '\r')
890 		{
891 			rad_memptr++;
892 			continue;
893 		}
894 
895 		if (max <= 2)
896 			I_Error("RTS script: line %d too long !!\n", rad_cur_linenum);
897 
898 		*buf++ = *rad_memptr++;  max--;
899 	}
900 
901 	*buf = 0;
902 
903 	return real_num;
904 }
905 
906 
907 //
908 // -ACB- 1998/07/10 Renamed function and used I_Print for functions,
909 //                  Version displayed at all times.
910 //
RAD_ParseScript(void)911 static void RAD_ParseScript(void)
912 {
913 	RAD_ParserBegin();
914 
915 	rad_cur_linenum = 1;
916 	rad_memptr = rad_memfile;
917 
918 	char linebuf[MAXRTSLINE];
919 
920 	while (rad_memptr < rad_memfile_end)
921 	{
922 		int real_num = ReadScriptLine(linebuf, MAXRTSLINE);
923 
924 #if (DEBUG_RTS)
925 		L_WriteDebug("RTS LINE: '%s'\n", linebuf);
926 #endif
927 
928 		RAD_ParseLine(linebuf);
929 
930 		rad_cur_linenum += real_num;
931 	}
932 
933 	RAD_ParserDone();
934 }
935 
936 
RAD_LoadFile(const char * name)937 void RAD_LoadFile(const char *name)
938 {
939 	SYS_ASSERT(name);
940 
941 	L_WriteDebug("RTS: Loading File %s\n", name);
942 
943 	rad_cur_filename = name;
944 
945 	RAD_MainCacheFile(name);
946 
947 	// OK we have the file in memory.  Parse it to death :-)
948 	RAD_ParseScript();
949 
950 	Z_Free(rad_memfile);
951 }
952 
953 
RAD_ReadScript(void * data,int size)954 bool RAD_ReadScript(void *data, int size)
955 {
956 	if (data == NULL)
957 	{
958 		std::string fn = M_ComposeFileName(ddf_dir.c_str(), "rscript.rts");
959 
960 		if (! epi::FS_Access(fn.c_str(), epi::file_c::ACCESS_READ))
961 			return false;
962 
963 		RAD_LoadFile(fn.c_str());
964 		return true;
965 	}
966 
967 	L_WriteDebug("RTS: Loading LUMP (size=%d)\n", size);
968 
969 	rad_cur_filename = "RSCRIPT LUMP";
970 
971 	rad_memfile_size = size;
972 	rad_memfile = Z_New(byte, size + 1);
973 	rad_memfile_end = &rad_memfile[size];
974 
975 	Z_MoveData(rad_memfile, (byte *)data, byte, size);
976 
977 	// Null Terminated string.
978 	rad_memfile[size] = 0;
979 
980 	// OK we have the file in memory.  Parse it to death :-)
981 	RAD_ParseScript();
982 
983 	Z_Free(rad_memfile);
984 
985 	return true;
986 }
987 
988 
RAD_Init(void)989 void RAD_Init(void)
990 {
991 	RAD_InitTips();
992 }
993 
RAD_StartMenu(rad_trigger_t * R,s_show_menu_t * menu)994 void RAD_StartMenu(rad_trigger_t *R, s_show_menu_t *menu)
995 {
996 	SYS_ASSERT(! rts_menuactive);
997 
998 	// find the right style
999 	styledef_c *def = NULL;
1000 
1001 	if (R->menu_style_name)
1002 		def = styledefs.Lookup(R->menu_style_name);
1003 
1004 	if (! def) def = styledefs.Lookup("RTS MENU");
1005 	if (! def) def = styledefs.Lookup("MENU");
1006 	if (! def) def = default_style;
1007 
1008 	rts_curr_menu = new rts_menu_c(menu, R, hu_styles.Lookup(def));
1009 	rts_menuactive = true;
1010 }
1011 
RAD_FinishMenu(int result)1012 void RAD_FinishMenu(int result)
1013 {
1014 	if (! rts_menuactive)
1015 		return;
1016 
1017 	SYS_ASSERT(rts_curr_menu);
1018 
1019 	// zero is cancelled, otherwise result is 1..N
1020 	if (result < 0 || result > MAX(1, rts_curr_menu->NumChoices()))
1021 		return;
1022 
1023 	rts_curr_menu->NotifyResult(result);
1024 
1025 	delete rts_curr_menu;
1026 
1027 	rts_curr_menu = NULL;
1028 	rts_menuactive = false;
1029 }
1030 
RAD_MenuDrawer(void)1031 static void RAD_MenuDrawer(void)
1032 {
1033 	SYS_ASSERT(rts_curr_menu);
1034 
1035 	rts_curr_menu->Drawer();
1036 }
1037 
1038 
RAD_Drawer(void)1039 void RAD_Drawer(void)
1040 {
1041 	if (! automapactive)
1042 		RAD_DisplayTips();
1043 
1044 	if (rts_menuactive)
1045 		RAD_MenuDrawer();
1046 }
1047 
1048 
RAD_Responder(event_t * ev)1049 bool RAD_Responder(event_t * ev)
1050 {
1051 	if (ev->type != ev_keydown)
1052 		return false;
1053 
1054 	if (! rts_menuactive)
1055 		return false;
1056 
1057 	SYS_ASSERT(rts_curr_menu);
1058 
1059 	int check = rts_curr_menu->CheckKey(ev->value.key.sym);
1060 
1061 	if (check >= 0)
1062 	{
1063 		RAD_FinishMenu(check);
1064 		return true;
1065 	}
1066 
1067 	return false;
1068 }
1069 
1070 //--- editor settings ---
1071 // vi:ts=4:sw=4:noexpandtab
1072