Jump to content

Home

SAGA specifications


Slunker

Recommended Posts

I don't know why, but everyone who works on this seems to think they have the right to keep it secret from the other mappers on the forum. I have figured everything out, so I'm here to help you guys get started pumping out those Saga maps!!!

 

What's a saga map? The saga gametype is sort of an all-purpose gametype. Meaning that it's essentially for creating your own, simple game times. Saga games are situational games where both teams have a/some objective(s) they're trying to complete, while keeping the other team from completing their objectives. A good example would be a Battle of Hoth scenario where one team (the imperials) is trying to destroy the rebels' shield generator, and the rebels are trying to defend it for 10 minutes. Or some such.

 

Part I: Playing saga maps

 

Why is saga a big deal? Because it's already in your Jedi Knight 2 game! It's sitting around in the .qvm files, just waiting to be used! So! The first thing to do is to make it so that Saga shows up in your JK2 game! It's actually quite simple: since saga's already programmed into JK2, all you have to do is make it show up in your menus! So, open up your assets1.pk3 files, go into the ui/jk2mp directory, and export the file gameinfo.txt from the .pk3.

 

Pop it open, and let's look inside! In two places in the file, you should see:

 

{ "Free For All" 0 }

{ "Holocron FFA" 1 }

{ "Jedi Master" 2 }

{ "Duel" 3 }

{ "Team FFA" 5 }

{ "Capture the Flag" 7 }

{ "Capture the Ysalimari" 8 }

 

The first is for the create-a-server menu, the second is for the join-a-server menu. In both of these, add this line among the game types somewhere (doesn't matter in what order you have the gametypes, really):

 

{ "Saga" 6 }

 

After adding saga to both lists, save the file, put it back into the .pk3 file (replacing the old copy) and save the .pk3 file. (This should take a minute. Relax!)

 

Congratulations! Your JK2 game is now set up to play with saga maps!

 

Part II: Creating saga maps

 

Saga maps are very much like, say, a capture the flag map in several ways. They have different spawn points according to which team you're on, they allow you to choose your team, they have an objective you must reach, and they require an info_player_intermission or info_player_deathmatch so that the camera has somewhere to be while the player chooses their team/options. There are a few major differences, though, that every mapper should note:

 

1. Spawn points. For spawn points, the info_player_rebel and info_player_imperial entities are used.

 

2. Objectives. The info_saga_objective entity is used for achieving objectives. It basically works the same way as your average target_*** entity works. When another entity fires at it, the objective is marked as complete. The info_saga_objective entity has two key/value pairs, "side" and "objective". The "side" value is either 1 (for imperial) or 2 (for rebel). As you can probably guess, this value is for who the objective "belongs" to. That is, whether the objective is an imperial or rebel objective. The second value, "objective" can be any positive integer (whole number), and signifies which objective (for its team) will be marked as completed when the entity is used. Further objective information will be contained in the .saga file, which we will talk about next section.

 

3. Arena file. To count as a saga map, make sure that "saga" is contained in the game type section of the .arena file.

 

4. Saga file. A .saga file must be in the map directory of your map's .pk3. I will talk about the .saga specification in the next section.

 

Part III: .saga Files

 

As with the .arena file, the .saga file's name must be the same as the map's. However, rather than be in the scripts directory, the .saga file must be in the maps directory. Odd, but there you are. The .saga file consists of two things: groups and paired values.

 

Groups are large portions of the file, taking up several lines, which are enclosed in curly brackets: {}. Every group has its title in front of the curly brackets. Groups can contain smaller groups, paired values, or both.

 

A paired value is a value with a name. For instance, the "rebel_message" paired value has the name rebel_message, and a value of, say, "The Imperials destroyed the shield generator!"

 

Now! There are a number of different groups and paired values that you must know to make an effective .saga file. So! Let's look at an example and pick it apart!

 

Imperial

{

Objective1

{

message_imperial "The shield generator has been destroyed!"

message_rebel "Our shield generator has been destroyed!"

}

wonround "We have destroyed the shield generator!"

lostround "The rebels have escaped!"

RequiredObjectives 1

attackers 1

}

Rebel

{

Objective1

{

message_rebel "Everyone has managed to evacuate!"

message_imperial "We were unable to destroy the shield generator in time!"

}

wonround "Our forces have evacuated!"

lostround "The Imperials destroyed our shield generator!"

RequiredObjectives 1

attackers 0

}

 

Notice that there are two main groups: "Imperial" and "Rebel". These two groups have everything important in them. There should be nothing outside these groups. Both groups should have, basically, the same things in them, so let's just look at the imperial group. If you understand one, you understand the other...

 

Objective1

{

message_imperial "The shield generator has been destroyed!"

}

wonround "We have destroyed the shield generator!"

lostround "The rebels have escaped!"

RequiredObjectives 1

attackers 1

 

Okay! First thing we have is a subgroup named Objective1. For each objective that must be completed, you should have a subgroup named Objective#. If you do not, the game will completely ignore it when you achieve the objective that you left out.

 

Notice that there's an objective 1 for BOTH the rebels and imperials. They are NOT the same thing, since there's an individual info_saga_objective for each. Even though both info_saga_objective entities have their objective value as one, one has its side value as 1, and the other has its side value as 2, so that makes them different. The point: you can repeat objectives for different teams, JK2 knows the difference.

 

Okay! So anyway, let's look at something else that has to do with the Objective1 group:

 

message_imperial "The shield generator has been destroyed!"

 

One might assume that this tells the imperials "The shield generator has been destroyed!" when the objective is completed. That person would be bright, but wrong. Here's why:

 

When an objective is completed, it checks to see if that objective is the last one necessary for a win. If not, it tells the imperials the message_imperial value, and tells the rebels the message_rebel value in the objective's group. However, if it IS the last necessary objective, it totally skips the message_imperial and message_rebel, and instead uses the wonround and lostround values that I'll talk about in a sec. So! It would be perfectly acceptable for me to have written:

 

Objective1

{

}

 

instead! So why didn't I? I was trying to make a point... anyhow.

 

So! Let's look at the rest of those paired values in the Imperial group, shall we?

 

wonround "We have destroyed the shield generator!"

lostround "The rebels have escaped!"

RequiredObjectives 1

attackers 1

 

wonround:

 

If the Imperial team wins, every player on that team is told: "We have destroyed the shield generator!"

 

lostround:

 

If the Imperial team loses, every player on that team is told: "The rebels have escaped!"

 

RequiredObjectives:

 

If this isn't set, then this team will AUTOMATICALLY WIN when they complete an objective. If you have multiple objectives, this is BAD. Set this to how many objectives the team must complete before they win.

 

Attackers:

 

One can assume that bot support in this gametype can be pretty difficult. However, Raven did an... okay job, considering the circumstances... Basically, how bot support in this works is:

 

For every objective on both sides, it finds the info_saga_objective. Then, it figures out what targets the info_saga_objective. Then, it figures out what targets that. And it just goes on down the line until it figures out the first targeting thing that eventually leads to the info_saga_objecive. Like I said, it does this for every objective on both teams.

 

Then, if attackers for the team the bot is on is set to 1, it picks out an objective for its team, and attacks in its general direction. If possible, it attacks the entity that first targets the objective... if not possible it at least runs up to it... I'm assuming that if neither one of these things is possible (or if neither one of them completes the objective) the bot will stand around the objective shooting everyone on the other team until it dies.

 

If attackers is set to 0, however, the bot does pretty much the opposite. It picks out an objective on the OTHER team, makes its way to the objective, and defends it against the opposite team... pretty cool, huh? Not effective all the time, but if you keep your maps relatively simple (until someone sets up better Saga AI) bots should react perfectly!

 

Well, that covers the entire .saga file specification. So, what're you waiting for! I, for one, am bouncing in my seat wondering what kind of great multiplayer scenarios you guys will pump out! So snap to it!

 

-Slunker

 

p.s. Do you guys think this would be a good thing to sticky?

Link to comment
Share on other sites

  • Replies 186
  • Created
  • Last Reply

You can go to http://taintedg.tripod.com. I have screenies from one of my past projects that I closed since it was a 14-18-map-long SP campaign, and no one wanted to help... no screnies on hoth, yet, since I just finished figuring out about 30 minutes ago... I had a test map that was very small, with a sky shader around it, and two breakables that triggered objective1's for the opposing teams (and therefore one the game for them) Not much to look at at all, but it helped me figure this all out. The other problem with my work is that since I've been doing SP maps up until now, I have NO idea how to set up NPC routing in MP... anyhow! I will set up Battle of Hoth, and it should be pretty neat. I also encourage other people who want to try as well, even if it's on Battle of Hoth. I'd like to see the whole community working on pumping out a few sweet saga maps and servers so that we can start playing it ASAP!

 

-Slunker

Link to comment
Share on other sites

*blinks* Because of my work here or because of the cave map? *frowns* I actually have to confess that I'm not that great of a mapper, because I have a horrible lack of attention to detail... I'm trying to become a modder, because I've always been a programmer... but I work with C++ usually, not C... I'm learning, though... managed to figure all this out... now's the hard part: writing NEW code... If I mod, it will likely be for saga mode, though, since it intrigues me... likely a new bot system that involves bot directives in the .saga file... Like, with targetnames, so, like...

 

Bot

{

Directive1

{

target gun1

priority 0

DESTROY gun1

}

Directive2

{

target gun2

priority 0

DESTROY gun2

}

Directive3

{

priority 1

DESTROY generator

}

}

 

And, like, priority 0 is only do it when the target is keeping you from your other priorities... or something. Dunno! It'd be pretty cool, though, having more intelligent bots for this... of course, since I can't bot-route, I'm not truly sure just how intelligents bots are in the first place... so. Having the ability for no-lightsaber games would be good for saga too, since sometimes in scenarios you just want weapons...

 

-Slunker

Link to comment
Share on other sites

good job.

however there are still some bugs in the saga code. i am sure you will be able to see them right away.

 

also this is not the full saga specifications. There's more than simple objective completion till RequiredObjective is 1.

since you posted it, I might as well tell them:

 

there are 2 more fields in Objective# group

 

final - may have a value of 0 or 1. If the objective has value of final 1 on completion it ends the game immediately with a win for the team. "Do A+B+C or D"

 

target - has the entity name to activate upon ccompletion. Possible use ranges from simple opening door to activation of some defense mechanism.

 

[edit]

some more fields that you missed:

roundover_sound_wewon/roundover_sound_welost - sound to play on win/loss

sound_imperial/sound_rebel - same, only for a specific objective

 

[/edit]

 

Also, AB_Legend, the reason we kept it in secret was simple: same why Promod source is not being released. It may have bugs/missing features (in fact there are already 2 major bugs in saga code that I had to fix). Not because we want it to be closed.

Link to comment
Share on other sites

wow, great!

 

Now, i know that i will be alot of work making a saga-map so why dont we make one toghether, cooperating with eachother !? I have tried the saga maptype on a very simple map, and i succeded! But i dont really have the time it will take to make a GOOD saga-map, because im in school.

 

If anyone is intereted in working with me on this one, please respond!

 

-Buck out :duel:

Link to comment
Share on other sites

oh and not really related, but there's also g_gametype 4.

 

the name is GT_SINGLE_PLAYER. Before you drool with happiness, I must shatter your hopes: it does nothing. Or more precisely almost nothing. It makes bots a little bit more aggressive.

:(

 

[edit]

 

<rant>

and of course Slunker gets the glory and me and Tchouky are forgotten.

</rant>

 

seriously, let me direct you here:

http://www.lucasforums.com/showthread.php?s=&threadid=62141

 

This was my original thread on SAGA, which died because I missed the Objective# part in the code.

Link to comment
Share on other sites

also

since Tchouky is urging me to be nice :)

 

here are the bug fixes:

 

1) objective can be triggered by any team member:

 

g_saga.c, sagaTriggerUseFunction

 

below

if (activator && activator->client)
{ //activator will hopefully be the person who triggered this event
	clUser = activator->s.number;
}

add

if(ent->side != activator->client->sess.sessionTeam) // only allow Imperial to trigger imperial objs and vice-versa
{
	return;
}

[edit]

note: Tchouky told me that this behaviour is undesirable sometimes. That's what I did:

 

i add a field to .saga specifications called "bothteams". if it's 1, both teams can trigger the objective (of course it will count for the originally intended team - something like "be careful or it blows up in your face".

 

here are the changes:

 

in sagatriggeruse:

int				bothteams=0;

 

in the if (GetValueGroup(objectives, objectivestr, desiredobjective)) if add at the top:

 

if (GetPairedValue(desiredobjective, "bothteams", teamstr))
		{

			bothteams = atoi(teamstr);

		}

		if(!bothteams)	
		{
			if(ent->side != activator->client->sess.sessionTeam) // only allow Imperial to trigger imperial objs and vice-versa
			{
				return;
			}
		}

and remove the original if(ent->side!=.... block.

it could do something like: we have a highly explosive substance. Defend it from imperial, but be careful so it does not explode

[/edit]

2) objective can be triggered multiple times:

 

here i decided to use an array where the elements are 1 or 0 depending if the objective was completed. This effectively limits the max objective number for each team, but 50 should be more than enough:

at the start of the file add this:

#define MAX_OBJ_NUM 50
int			rebel_obj[MAX_OBJ_NUM];
int			imperial_obj[MAX_OBJ_NUM];

in InitSagaMode()

// Just to be safe, reset both arrays to 0
for(i=0; i<MAX_OBJ_NUM; i++)
{
	imperial_obj[i]=0;
	rebel_obj[i]=0;
}

 

better be safe than sorry

 

in sagaTriggerUse:

 

// =============       Disable multiple triggering of one entity over and over ========//
if(ent->side == SAGATEAM_IMPERIAL)
{
	if(imperial_obj[ent->objective]==1)  // already triggered 
	{
		return;
	}
	imperial_obj[ent->objective]=1;
}
else
{
	if(rebel_obj[ent->objective]==1)  // already triggered 
	{
		return;
	}
	rebel_obj[ent->objective]=1;
}

 

these fixes are simple but important.

 

the bug that remains is team score not updating. It is a fairly simple edit to do, but I didn't have the patience.

Link to comment
Share on other sites

I'll add my two cents on this. ASk, Tchouky, the test mappers and I were only keeping it a secret until we got the full specs figured out and tested. ASk posted about this game mode months ago, but noone ever came forward with a solution.

 

Slunker does have functional specs but he didn't post the FULL specs. We fully intended to release the information once we had figured it out. In fact, we were just about ready when this was posted. There was no malice involved.

 

It should also be noted that the .saga file scanner code is real bitchy. Here's my notes on the subject:

 

Definition Group:

 

You MUST have a definition group in the following formats or it wouldn’t work correctly:

 

//Definition group name, followed by ONE space and a ‘{‘.

TargetName {

}

 

//Definition group name follow immediately by a carriage return (AKA ‘\n’).

//The function will scan thru everything after that point until it finds a ‘{‘.

TargetName

{

}

 

Basically, it’s rather easy to confuse the code, so try to pick a name for your definition group that isn’t used prior to the definition group. I also notice that there isn’t any comment deletion system in group scan code, so be careful with your comments as they could be mistaken for the beginning of a definition group. In addition, be sure you have an equal amount of open and close brackets (‘{‘ and ‘}’) or the scan will prematurely terminate.

 

Varibles:

 

The variable value must be followed immediately by a carriage return, but spaces and such seem to be allowed.

Link to comment
Share on other sites

THis works:

 

blah

{

}

 

Because it searches through to find the first {.

Only if you do a carriage return immediately AFTEr the definition group name, in this case "blah". If you have a carriage return in that way. The code will scan until it finds a "{".

// works to comment code out, because if a line starts with two slashes, the entire line is converted to slashes.

Only by the GetPairedValue function (which is run after the definition group is scanned in). It's possible to trick the program by making a comment look like a definition group start. It's best designed scanner in the world, but Raven admits that in the code comments.

Link to comment
Share on other sites

Adding team points to the winning team at the end of the game:

 

in the game source (not the cgame), the file g_saga.c:

 

void AddSagaWinningTeamPoints(int team, int winner)

{

int i = 0;

gentity_t *ent;

while (i < MAX_CLIENTS)

{

ent = &g_entities;

if (ent && ent->client && ent->client->sess.sessionTeam == team)

{

if (i == winner)

{

AddScore(ent, ent->client->ps.origin, SAGA_POINTS_TEAMWONROUND+SAGA_POINTS_FINALOBJECTIVECOMPLETED);

}

else

{

AddScore(ent, ent->client->ps.origin, SAGA_POINTS_TEAMWONROUND);

}

}

i++;

}

}

 

After:

 

if (i == winner)

{

AddScore(ent, ent->client->ps.origin, SAGA_POINTS_TEAMWONROUND+SAGA_POINTS_FINALOBJECTIVECOMPLETED);

 

put:

AddTeamScore(ent->s.pos.trBase,team,1);

 

This only changes the score when one team wins... *shrugs* It would be a minor matter to change that to each completed objective, but that might show an unrealistic portrayal of the game, depending on the scenario, so I decided against it.

 

-Slunker

Link to comment
Share on other sites

Here's an idea I just had:

 

So many options that have to do with a scenario are scenario specific. Things like lightsaber-only, no-force-powers, what skins each player can use, etc. change from scenario to scenario, and, to preserve the gameplay quality, would have to be remembered and changed by admin every time they use a map, under normal circumstances. So! What if these options were stuck as paired values under a "World" group in the .saga file? I'd be more than willing to help if you don't want to go to the trouble to do this. It doesn't sound too difficult.

 

-SLunker

Link to comment
Share on other sites

Again, good job on that. We totally missed the UI saga stuff. :)

 

As for specific scenario sets, that would require a medium amount of code modifications as we'd have to add scenario overrides to all the model selection and such. I'm starting to design Saga improvements for MotF and will look into/think about it.

Link to comment
Share on other sites

PROBLEM FIXED.

 

The problem is tabs. In the "game" project, all objectives, when parsed, use the "strip_tabs" function to remove all the tabs in the group. So! This means that if you separate your paired values using tabs, the separation is removed. How to fix this problem? I set up strip_tabs (to be found in g_saga.c) function to remove all but one consecutive tabs. (So, if you have 3 tabs in a row, it turns to 1, if you have 1 tab in a row, nothing happens...)

 

Like so.. Change this line:

 

if (buf != 9)

 

to this:

 

if(buf != 9 || buf[i+1] != 9)

 

This fixes the problem! Everything should work now! (Yayyyy)

 

-Slunker

Link to comment
Share on other sites

well i worked on this problem too this afternoon and there is a more simple way :

 

DON'T USE TABS

use spaces

 

on space is enought!

 

example of a saga file with 3 objectives :

//saga info file
Rebel
{
RequiredObjectives		2
attackers			1
wonround			"rebels objectives done"
lostround			"imperials objectives done"
Objective2
{
	final 			0
	target 			obj1_name
	message_rebel "Congratulations! Objective 1 complete!"
	message_imperial "You suck! Objective 1 was completed"
}
Objective3
{
	final 			0
	target 			obj2_name
	message_rebel "Congratulations! Objective 2 complete!"
	message_imperial "You suck! Objective 2 was completed"
}
Objective4
{
	final 1
	target obj3_name
	message_rebel "Congratulations! FINAL Objective complete!"
	message_imperial "You suck! FINAL Objective was completed"
}
}
//imperial here
Imperial
{
RequiredObjectives 1
attackers 0
wonround "Congratulations imperials win"
lostround "You Suck imperials loose"
Objective1
{
	final 1
	target obj2_name
	message_imperial "Congratulations! Objective 1 complete!"
	message_rebel "You suck! Objective 1 was completed"
}
}

 

enjoy

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.


×
×
  • Create New...