File: navigate.pp

package info (click to toggle)
gearhead2 0.701-1
  • links: PTS
  • area: main
  • in suites: buster
  • size: 14,184 kB
  • sloc: pascal: 49,692; makefile: 85; sh: 12
file content (661 lines) | stat: -rw-r--r-- 21,189 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
unit navigate;
	{ This unit is the flow controller for the RPG bits of the game. }
	{ It decides where the PC is, then when the PC exits a scene it }
	{ decides where to go next. }
{
	GearHead2, a roguelike mecha CRPG
	Copyright (C) 2005 Joseph Hewitt

	This library is free software; you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation; either version 2.1 of the License, or (at
	your option) any later version.

	The full text of the LGPL can be found in license.txt.

	This library is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
	General Public License for more details. 

	You should have received a copy of the GNU Lesser General Public License
	along with this library; if not, write to the Free Software Foundation,
	Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
}
{$LONGSTRINGS ON}

interface

uses gears,locale,backpack,
{$IFDEF ASCII}
	vidgfx;
{$ELSE}
	sdlgfx;
{$ENDIF}

Const
	Max_Number_Of_Plots = 40;
	Plots_Per_Generation = 5;

Procedure SaveChar( PC: GearPtr );
Procedure SaveEgg( Egg: GearPtr );

Procedure Navigator( Camp: CampaignPtr; Scene: GearPtr; var PCForces: GearPtr );

Procedure StartCampaign( Egg: GearPtr );
Procedure RestoreCampaign( RDP: RedrawProcedureType );

implementation

uses arenaplay,arenascript,interact,gearutil,narration,texutil,ghprop,rpgdice,ability,
     ghchars,ghweapon,movement,ui4gh,gearparser,playwright,randmaps,mpbuilder,
{$IFDEF ASCII}
	vidmap,vidmenus;
{$ELSE}
	sdlmap,sdlmenus;
{$ENDIF}


Procedure DebugMessage( msg: String );
	{ Display a debugging message, and refresh the screen right away. }
begin
	DialogMsg( msg );
	ClrScreen;
	InfoBox( ZONE_Dialog );
	RedrawConsole;
	DoFlip;
end;

Procedure SaveChar( PC: GearPtr );
	{ Save this character to disk, in the "SaveGame" directory. }
var
	Leader: GearPtr;
	FName: String;		{ Filename for the character. }
	F: Text;		{ The file to write to. }
begin
	Leader := PC;
	while ( Leader <> Nil ) and ( ( Leader^.G <> GG_Character ) or ( NAttValue( Leader^.NA , NAG_CharDescription , NAS_CharType ) <> 0 ) ) do Leader := Leader^.Next;
	if Leader = Nil then Exit;

    FName := SanitizeFilename(GearName(Leader));
	FName := Save_Character_Base + FName + Default_File_Ending;
	Assign( F , FName );
	Rewrite( F );
	WriteCGears( F , PC );
	Close( F );
end;

Procedure SaveEgg( Egg: GearPtr );
	{ Save this character to disk, in the "SaveGame" directory. }
var
	Leader: GearPtr;
	FName: String;		{ Filename for the character. }
	F: Text;		{ The file to write to. }
begin
	Leader := Egg^.SubCom;
	while ( Leader <> Nil ) and ( ( Leader^.G <> GG_Character ) or ( NAttValue( Leader^.NA , NAG_CharDescription , NAS_CharType ) <> 0 ) ) do Leader := Leader^.Next;
	if Leader = Nil then Exit;

    FName := SanitizeFilename( GearName(Leader) );
	FName := Save_Egg_Base + FName + Default_File_Ending;
	Assign( F , FName );
	Rewrite( F );
	WriteCGears( F , Egg );
	Close( F );
end;


Function NoLivingPlayers( PList: GearPtr ): Boolean;
	{ Return TRUE if the provided list of gears contains no }
	{ living characters. Return FALSE if it contains at least }
	{ one. }
var
	it: Boolean;
begin
	{ Start by assuming TRUE, then set to FALSE if a character is found. }
	it := TRUE;

	{ Loop through all the gears in the list. }
	while PList <> Nil do begin
		if ( PList^.G = GG_Character ) and NotDestroyed( PList ) and ( NAttValue( PList^.NA , NAG_CharDescription , NAS_CharType ) = 0 ) then begin
			it := False;
		end;
		PList := PList^.Next;
	end;

	{ Return the result. }
	NoLivingPlayers := it;
end;

Procedure CampaignUpkeep( Camp: CampaignPtr );
	{ Do some gardening work on this campaign. This procedure keeps }
	{ everything in the CAMP structure shiny, fresh, and working. }
	{ - Load a new PLOT, if appropriate. }
	{ - Delete dynamic scenes. }
var
	Part,Part2: GearPtr;
	N,N2: NAttPtr;
begin
	{ Get rid of any dynamic scenes that have outlived their usefulness. }
	{ If a SCENE is found in the InvComs, it must be dynamic. }
	Part := Camp^.Source^.InvCom;
	while Part <> Nil do begin
		Part2 := Part^.Next;

		if ( Part^.G = GG_Scene ) or ( Part^.G = GG_MetaScene ) then begin
			DeleteFrozenLocation( GearName( Part ) , Camp^.Maps );
			RemoveGear( Camp^.Source^.InvCom , Part );
		end;

		Part := Part2;
	end;

	{ Get rid of any PlotStatuses saved for plots which have concluded. }
	N := Camp^.Source^.NA;
	while N <> Nil do begin
		N2 := N^.Next;

		if ( N^.G = NAG_PlotStatus ) and ( N^.S > 0 ) then begin
			Part := SeekGearByIDTag( Camp^.Source^.InvCom , NAG_PlotStatus , N^.S , 1 );
			if Part = Nil then begin
				RemoveNAtt( Camp^.Source^.NA , N );
			end;
		end;

		N := N2;
	end;
end;

Procedure Navigator( Camp: CampaignPtr; Scene: GearPtr; var PCForces: GearPtr );
	{ This is the role-playing flow controller. It decides what scene }
	{ of an adventure gear to load next. }
var
	N: Integer;
begin
	repeat
		if SCene <> Nil then N := ScenePlayer( Camp , Scene , PCForces );

		{ Move to the destination scene, if appropriate. }
		if N > 0 then begin
			{ Perform upkeep on the campaign- delete dynamic scenes, }
			{ load new plots, yadda yadda yadda. }
			CampaignUpkeep( Camp );

			Scene := FindActualScene( Camp^.Source , N );

		end else if N < 0 then begin
			Scene := FindMetascene( Camp^.Source , N );

		{ If no destination scene was implied, check to see if there's }
		{ a dynamic scene waiting to be processed. }
		end else if SCRIPT_DynamicEncounter <> Nil then begin
			Scene := SCRIPT_DynamicEncounter;

			{ Stick the scene into the campaign. Normally scenes }
			{ are filed under SubComs, but in this case we'll store }
			{ it as an InvCom so we'll remember to delete it later. }
			InsertInvCom( Camp^.Source , Scene );

			{ Set the DynamicEncounter var to Nil, since we've moved }
			{ the scene to the campaign and don't want the ArenaScript }
			{ procedures to try and modify or delete it any more. }
			SCRIPT_DynamicEncounter := Nil;

			{ Set N to >0, since we don't want the "until..." }
			{ condition to exit. }
			N := 1;
		end;

	until ( N = 0 ) or NoLivingPlayers( PCForces ) or ( Scene = Nil );

	{ If the game is over because the PC died, do a [MORE] prompt. }
	if NoLivingPlayers( PCForces ) then begin
		MoreKey;
	end;
end;

Procedure InitializeCampaignScenes( Adv: GearPtr; var HiSceneID: Integer );
	{ Initialize the scenes of this adventure. This involves providing them all }
	{ with unique IDs, inserting content where needed, adding entrances to superscenes. }
	Procedure CheckAlongPath( S: GearPtr );
		{ Search along this path, initializing everything. }
	begin
		while S <> Nil do begin
			if S^.G = GG_Scene then begin
				S^.S := HiSceneID;
				Inc( HiSceneID );

				if AStringHasBString( SAttValue( S^.SA , 'TYPE' ) , 'DUNGEON' ) and ( SAttValue( S^.SA , 'DENTRANCE' ) <> '' ) and ( NAttValue( S^.NA , NAG_Narrative , NAS_DungeonLevel ) = 0 ) then begin
					ExpandDungeon( S );
				end;

				ConnectScene( S , True );
			end;

			CheckAlongPath( S^.SubCom );

			S := S^.Next;
		end;
	end;
	Procedure InitNamedExits( Adv,Part: GearPtr );
		{ Search for gears with a DESTINATION string attribute. Try to }
		{ set the destination stat for these gears. }
	var
		dest: String;
		scene: GearPtr;
	begin
		while Part <> Nil do begin
			dest := SAttValue( Part^.SA , 'DESTINATION' );
			if dest <> '' then begin
				scene := SeekGearByName( Adv , dest );
				if ( Scene <> Nil ) and (( Scene^.G = GG_Scene ) or ( Scene^.G = GG_World )) and ( Part^.G = GG_MetaTerrain ) then begin
					Part^.Stat[ STAT_Destination ] := Scene^.S;
				end;
			end;
			InitNamedExits( Adv , Part^.SubCom );
			InitNamedExits( Adv , Part^.InvCom );
			Part := Part^.Next;
		end;
	end;
begin
	CheckAlongPath( Adv );

	InitNamedExits( Adv , Adv );

	{ Store the HiSceneID for later use. }
	SetNAtt( Adv^.NA , NAG_Narrative , NAS_MaxSceneID , HiSceneID );
end;

Procedure InitializeCampaignNPCs( Adv: GearPtr );
	{ Provide unique character IDs for each of the pre-loaded characters. }
	Procedure CheckAlongPath( P: GearPtr );
	var
		S: GearPtr;	{ The Persona for this NPC. }
		CID: LongInt;
	begin
		while P <> Nil do begin
			if P^.G = GG_Character then begin
				CID := NewCID( Adv );
				SetNAtt( P^.NA , NAG_Personal , NAS_CID , CID );
				S := SeekGearByName( Adv , GearName( P ) + ' PERSONA' );
				if S <> Nil then S^.S := CID;
			end;
			CheckAlongPath( P^.SubCom );
			CheckAlongPath( P^.InvCom );
			P := P^.Next;
		end;
	end;
begin
	CheckAlongPath( Adv );
end;

Procedure InitializeAdventureContent( Adv,HomeTown,Egg: GearPtr );
	{ Initialize the static adventure content. This consists mostly of }
	{ searching through the structure for content requests and filling }
	{ those recursively as needed. }
	{ Also add the content contained within the Egg. Most of this content }
	{ will require quests for placement/initialization. }
const
	NumSubQuests = 8;
var
	MasterList: GearPtr;	{ The master list of adventure content components. }

	Procedure CheckAlongPath( LList: GearPtr );
		{ Check along this list of scenes for content requests, }
		{ also checking the sub- and inv-coms. }
	var
		ConReq: String;
		SA: SAttPtr;
	begin
		while LList <> Nil do begin
			if LList^.G = GG_Scene then begin
				{ A scene can have multiple quests defined. }
				SA := LList^.SA;
				while SA <> Nil do begin
					if HeadMatchesString( 'QUEST' , SA^.Info ) then begin
						ConReq := RetrieveAString( SA^.Info );
						if not AddQuest( Adv , FindRootScene( LList ) , Nil , MasterList , ConReq ) then begin
							if XXRan_Debug then DialogMsg( 'ERROR: AddQuest failed for ' + ConReq );
						end;
					end;
					SA := SA^.Next;
				end;
			end;
			CheckAlongPath( LList^.SubCom );
			CheckAlongPath( LList^.InvCom );
			LList := LList^.Next;
		end;
	end;
	Procedure PlaceEggNPCs( LList: GearPtr );
		{ Take the NPCs from the egg and place them in the adventure. }
		{ Actually, we won't be placing them, but clones of them... }
		{ anyhow, go and do it. }
	const
		Default_NPC_Quest = '*EGG_Default';
	var
		ConReq: String;
	begin
		while LList <> Nil do begin
			if LList^.G = GG_Character then ExpandCharacter( LList );

			ConReq := SAttValue( LList^.SA , 'QUEST' );
			{ If this content request is empty, assign the default value. }
			if ConReq = '' then ConReq := Default_NPC_Quest;

			if not AddQuest( Adv , HomeTown , LList , MasterList , ConReq ) then begin
				if XXRan_Debug then DialogMsg( 'ERROR: AddQuest failed for ' + ConReq + '/' + GearName( LList ) );
			end;

			LList := LList^.Next;
		end;
	end;
begin
	{ Load the component library. }
	MasterList := LoadQuestFragments;

	{ Start checing the adventure scenes for content requests. }
	CheckAlongPath( Adv^.SubCom );

	{ Place the NPCs from the egg. Some of these will likely make use }
	{ of the quest fragments loaded above. }
	PlaceEggNPCs( Egg^.InvCom );

	{ Get rid of the master list. }
	DisposeGear( MasterList );
end;


Procedure VerifySceneExits( LList: GearPtr );
	{ Check all of the exits you can find. If any of them are negative, this is a bad thing. }
	{ Fix the problem by pointing them to their parent scene. }
	Function GetScene( S: GearPtr ): GearPtr;
		{ Locate the scene that's the most recent ancestor of S. }
	begin
		while ( S <> Nil ) and ( S^.G <> GG_Scene ) do S := S^.Parent;
		GetScene := S;
	end;
var
	Scene: GearPtr;
begin
	while LList <> Nil do begin
		if ( LList^.G = GG_MetaTerrain ) and ( LList^.Stat[ STAT_Destination ] < 0 ) then begin
			{ This metaterrain is in violation. Fix it. }
			Scene := GetScene( LList );
			if ( Scene <> Nil ) and ( Scene^.Parent <> Nil ) then begin
				LList^.Stat[ STAT_Destination ] := Scene^.Parent^.S;
				if LList^.Stat[ STAT_Destination ] < 1 then begin
					DialogMsg( 'ERROR: Invalid SceneID for ' + GearName( LList ) + '.' );
					LList^.Stat[ STAT_Destination ] := 0;
				end;
			end else begin
				DialogMsg( 'ERROR: Parent scene not found for ' + GearName( LList ) + '.' );
			end;
		end;
		VerifySceneExits( LList^.SubCom );
		VerifySceneExits( LList^.InvCom );
		LList := LList^.Next;
	end;

end;

Procedure StartCampaign( Egg: GearPtr );
	{ Start a new RPG campaign. }
	{ - Load the atlas files, then assemble them into an adventure. }
	{ - Initialize all the cities. }
	{ - Insert the PC's central story. }
const
	Default_Residence_Desig = '*EGG_RESIDENCE_Apartment';
var
	Camp: CampaignPtr;
	PCForces,TruePC,Atlas,S,S2,W,Story,Club: GearPtr;
	Factions,Artifacts: GearPtr;
	HighWorldID: Integer;
	Base,Changes: String;	{ Context Strings. }
begin
{$IFNDEF ASCII}
	Idle_Display;
{$ENDIF}

	{ Extract the PCForces from the Egg. }
	PCForces := Nil;
	while Egg^.SubCom <> Nil do begin
		S := Egg^.SubCom;
		DelinkGear( Egg^.SubCom , S );
		AppendGear( PCForces , S );
	end;

	{ Locate the TruePC. }
	TruePC := PCForces;
	while ( TruePC <> Nil ) and ( ( TruePC^.G <> GG_Character ) or ( NAttValue( TruePC^.NA , NAG_CharDescription , NAS_CharType ) <> 0 ) ) do TruePC := TruePC^.Next;

	{ Expand the TruePC. Maybe. }
	if TruePC^.SubCom = Nil then begin
		ExpandCharacter( TruePC );
	end;

	{ Give the PC a personal communicator. Maybe. }
	if TruePC^.InvCom = Nil then begin
		Artifacts := LoadNewItem( 'Personal Communicator' );
		if Artifacts <> Nil then InsertInvCom( TruePC , Artifacts );
	end;

	Camp := NewCampaign;
	Camp^.Source := LoadFile( 'adventurestub.txt' , Series_Directory );

	{ The Adventure source needs to store the PC's faction. }
	SetNAtt( Camp^.Source^.NA , NAG_Personal , NAS_FactionID , NAttValue( TruePC^.NA , NAG_Personal , NAS_FactionID ) );

	Atlas := AggregatePattern( 'ATLAS_*.txt' , Series_Directory );

	{ Insert the factions into the adventure. }
	Factions := AggregatePattern( 'FACTIONS_*.txt' , Series_Directory );
	InsertInvCom( Camp^.Source , Factions );

	{ Insert the artifacts into the adventure. }
	Artifacts := AggregatePattern( 'ARTIFACT_*.txt' , Series_Directory );
	S := AddGear( Camp^.Source^.InvCom , Camp^.Source );
	SetSAtt( S^.SA , 'name <Artifact Collection>' );
	S^.G := GG_ArtifactSet;
	InsertInvCom( S , Artifacts );

	{ Insert the unique scene content into the adventure. }
	Artifacts := AggregatePattern( 'UNICON_*.txt' , Series_Directory );
	S := AddGear( Camp^.Source^.InvCom , Camp^.Source );
	SetSAtt( S^.SA , 'name <Unique Scene Content>' );
	S^.G := GG_ContentSet;
	InsertInvCom( S , Artifacts );
	{ Assign unique IDs to all the content bits. }
	HighWorldID := 1;
	S := Artifacts;
	while S <> Nil do begin
		SetNAtt( S^.NA , NAG_Narrative , NAS_ContentID , HighWorldID );
		S := S^.Next;
		Inc( HighWorldID );
	end;


	{ Assemble the subcoms of the adventure. }
	{ First, move over all the WORLDs. Then, move over all the SCENEs. }
	{ Assign unique IDs for everything. }
	S := Atlas;
	HighWorldID := 1;
	while S <> Nil do begin
		S2 := S^.Next;

		if S^.G = GG_World then begin
			DelinkGear( Atlas , S );
			InsertSubCom( Camp^.Source , S );
			S^.S := HighWorldID;
			Inc( HighWorldID );
		end;
		S := S2;
	end;

	{ Proceed with the scenes. }
	S := Atlas;
	while S <> Nil do begin
		S2 := S^.Next;

		if S^.G = GG_Scene then begin
			DelinkGear( Atlas , S );
			W := SeekGearByName( Camp^.Source , SAttValue( S^.SA , 'WORLD' ) );
			if ( W <> Nil ) and ( ( W^.G = GG_Scene ) or ( W^.G = GG_World ) ) then begin
				InsertSubCom( W , S );
			end else begin
				InsertSubCom( Camp^.Source , S );
			end;
		end;

		S := S2;
	end;

	{ We are now finished with the atlas. Dispose of it. }
	DisposeGear( Atlas );

	{ Locate the PC's home town. Insert a "Cavalier Club" as the starting location. }
	{ Also insert the PC's residence. The residence type should be listed }
	{ in the EGG. }
	S := SeekGearByName( Camp^.Source , SAttValue( TruePC^.SA , 'HOMETOWN' ) );
	if S <> Nil then S := SeekUrbanArea( S );
	if S <> Nil then begin
		Club := LoadSingleMecha( 'stub_cavalierclub.txt' , Series_Directory );
		InsertSubCom( S , Club );

		Atlas := LoadFile( 'EGG_scenes.txt' , Series_Directory );
		Base := SAttValue( Egg^.SA , 'RESIDENCE' );
		if Base = '' then Base := Default_Residence_Desig;
		Club := SeekGearByDesig( Atlas , Base );
		if Club <> Nil then begin
			DelinkGear( Atlas , Club );
			SetSAtt( Club^.SA , 'DESIG <PCHOME>' );
			InsertSubCom( S , Club );
		end;
		DisposeGear( Atlas );
	end;

	{ Once everything is sorted where it's supposed to go, initialize the scenes. }
	{ They all need unique ID numbers, the dungeons need expansion and the cities }
	{ need random content. }
	InitializeCampaignScenes( Camp^.Source , HighWorldID );

	{ Next initialize the NPCs. }
	InitializeCampaignNPCs( Camp^.Source );

	{ Locate the PC's home town again, this time to record the scene ID. }
	{ We're also going to need this scene ID for the central story below. }
	S := SeekGearByName( Camp^.Source , SAttValue( TruePC^.SA , 'HOMETOWN' ) );
	if S <> Nil then begin
		SetNAtt( TruePC^.NA , NAG_Narrative , NAS_HomeTownID , S^.S );
	end;

	{ Insert the central story. }
	Story := LoadFile( 'corestorystub.txt' , Series_Directory );
	SetNAtt( Story^.NA , NAG_ElementID , XRP_EpisodeScene , S^.S );
	SetNAtt( Story^.NA , NAG_ElementID , XRP_AllyFac , NAttValue( TruePC^.NA , NAG_Personal , NAS_FactionID ) );
	SetNAtt( Camp^.Source^.NA , NAG_Personal , NAS_FactionID , NAttValue( TruePC^.NA , NAG_Personal , NAS_FactionID ) );

	{ Copy the PC's personal context to the story. }
	Base := SAttValue( Story^.SA , 'CONTEXT' );
	Changes := SAttValue( TruePC^.SA , 'CONTEXT' );
	AlterDescriptors( Base , Changes );
	SetSAtt( Story^.SA , 'CONTEXT <' + Base + '>' );

	InsertInvCom( Camp^.Source , Story );

	{ Insert the static adventure quests. }
	InitializeAdventureContent( Camp^.Source , S , Egg );

	{ Verify that the exits have been handled correctly. }
	VerifySceneExits( Camp^.Source );

	{ By now, we should be finished with the EGG. Get rid of it. }
	DisposeGear( Egg );

	{ Locate the Cavalier Club. This is to be the starting location. }
	{ Being the first location entered by the PC, the Cavalier Club has }
	{ the imaginative designation of "00000". }
	S := SeekGearByDesig( Camp^.Source , 'PCHOME' );
	if S <> Nil then begin
		Navigator ( Camp , S , PCForces );
	end;

	DisposeCampaign( Camp );
	DisposeGear( PCForces );
end;

Procedure RestoreCampaign( RDP: RedrawProcedureType );
	{ Select a previously saved unit from the menu. If no unit is }
	{ found, jump to the CreateNewUnit procedure above. }
var
	RPM: RPGMenuPtr;
	rpgname: String;	{ Campaign Name }
	Camp: CampaignPtr;
	F: Text;		{ A File }
	PC,Part,P2: GearPtr;
	DoSave: Boolean;
begin
	{ Create a menu listing all the units in the SaveGame directory. }
	RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Title_Screen_Menu );
	BuildFileMenu( RPM , Save_Campaign_Base + Default_Search_Pattern );

	PC := Nil;

	{ If any units are found, allow the player to load one. }
	if RPM^.NumItem > 0 then begin
		RPMSortAlpha( RPM );
		DialogMSG('Select campaign file to load.');

		rpgname := SelectFile( RPM , RDP );

		if rpgname <> '' then begin
			Assign(F, Save_Game_Directory + rpgname );
			reset(F);
			Camp := ReadCampaign(F);
			Close(F);

			Navigator( Camp , Camp^.GB^.Scene , PC );
			DoSave := Camp^.Source^.V <> 0;
			DisposeCampaign( Camp );
		end else begin
			DoSave := False;
		end;

	end else begin
		{ The menu was empty... print the info message. }
		DialogMsg( MsgString( 'NEWRPGCAMP_NoCamps' ) );
		DoSave := False;
	end;

	if ( PC <> Nil ) and ( DoSave or Always_Save_Character ) then begin
		if not NoLivingPlayers( PC ) then begin
			Part := PC;
			while Part <> Nil do begin
				P2 := Part^.Next;
				{ Lancemates don't get saved to the character file. }
				if ( Part^.G = GG_Character ) and ( NAttValue( Part^.NA , NAG_CharDescription , NAS_CharType ) <> NAV_CTPrimary ) then begin
					RemoveGear( PC , Part );
				end else begin
					{ Everything else does get saved. }
					StripNAtt( Part , NAG_Visibility );
					StripNAtt( Part , NAG_EpisodeData );
					StripNAtt( Part , NAG_WeaponModifier );
					StripNAtt( Part , NAG_Action );
					StripNAtt( Part , NAG_Location );
					StripNAtt( Part , NAG_Damage );
					StripNAtt( Part , NAG_ReactionScore );
					StripNAtt( Part , NAG_FactionScore );
					StripNAtt( Part , NAG_Condition );
					StripNAtt( Part , NAG_StatusEffect );
					StripNAtt( Part , NAG_Narrative );
				end;
				Part := P2;
			end;
			SaveChar( PC );
		end;
	end;
	if PC <> Nil then DisposeGear( PC );

	DisposeRPGMenu( RPM );
end;

end.