Monthly Archives: December 2013

Merry Christmas!

Merry Christmas in Advance! :)

I will be visiting my hometown during the Christmas holidays so there won’t be any posts until early January.
For that time I wish you guys peaceful and happy days and a happy new year! :D

(I know this is too early but I won’t have time to do it during the next weeks. ^.^)

Greetings,
steeffeen

The Bomb is a Thing

Currently the Bomb is still kind of non-existent because everybody can capture goals. That will be changed now.

First of all I prevent anyone from capturing Goals if it isn’t the bomb carrier.
I simply add a check of the player when someone tries to capture:

if (Player.Id != G_BombCarrierId) {
// Not the bomb carrier
continue;
}


Now it’s needed to actually give the bomb to a player so that she/he is able to activate it. This is done when preparing the next round.

I loop through all players of the attacking team and pick one of them based on a few rules.
Each time you carry the bomb timestamp is set. This timestamp is checked when looking for the next carrier so that each player has to carry it at some point.
You will get chosen if:
– You didn’t carry the bomb so far or
– You’re the one that didn’t carry the bomb for the longest duration.

If the team has currently no players the bomb will simply be placed in one of the team’s spawns so that the next joining player can pick it up.


Picking up the bomb? You’ve heard right! That’s the next step.
The most common case in which that will be needed is when the bomb carrier is eliminated.

First lets list all the situation when the bomb gets dropped: It happens every time the bomb carrier…
– Gets eliminated
– Presses backspace
– Switches the team
– Switches to spectator mode
– Leaves the server (not covered yet)

In all of these case I call my new function DropBomb(Player);
It sets the bomb carrier id to NullId and saves the player’s position as the new bomb’s drop position (new global variable).

One note: I check if the given player is the bomb carrier INSIDE the function in order to save some code because this way I don’t have to add the check each time I call the function.


Now the picking up of the bomb!
Whenever the bomb is dropped which is represented by the G_BombCarrierId being NullId I check if a player of the attacking team is close enough to pick it up.

declare Distance = MathLib::Distance(Player.Position, G_BombDropPosition);
if (Distance > C_BombPickUpDistance) {
// Too far away
continue;
}
PickUpBomb(Player);

The Distance function of the MathLib takes two coordinates as Vec3 variables and calculates the distance between them. Easy as that.

Picking up the bomb is possible in a range of 2.5 meters for now, maybe it will be needed to be adjusted. We will see.

#Const C_BombPickUpDistance 2.5


One annoying thing about the current version of the mode is that you can’t really run it without players in each team because the empty team will always instantly lose because its players have been eliminated.
So from now on to win by eliminating there has to be at least one player in the opponent team.

if (ClansNbPlayers[Clan+1] <= 0 || ClansNbPlayersAlive[Clan+1] > 0) {
// No players in the team or not all of them eliminated yet
continue;
}


Two minor things at the end:
1. I’ve added one line which will enable the name tags for the players.

SM::SetupDefaultVisibility();

If you take a look at the SM script you will see that this function configures the displaying of name tags and armor gauges which are all deactivated by default.

2. The constant saving the number of bots has been changed to a script setting so that you can easily deactivate them on a server. (The amount of playing bots is updated at the beginning of rounds.)


That’s it for today.
See you next time.

Check the new script version here: Defusing7.Script.txt

Btw: Take a look at the script: It already has 600 lines! And it doesn’t even have any working User Interface yet which will need a LOT more code. ;)

Eliminating Teams

So far one can win rounds by defending or destroying the goals.
Another aspect of the gameplay is eliminating the whole opponent team.


Until now players immediately respawn as soon as they are eliminated, like that you just can’t eliminate all opponents.
So it’s needed to not spawn players anymore as soon as the round has started. This leads to spectating for the rest of the round which might not be fun for some players so I also added a setting to enable respawning during rounds. (As I said this will automatically prevent winning rounds by eliminating the opponent team.)

When the script is about to spawn a player I simply check if the StartTime is over:

if (!S_EnableRoundRespawn && Now > StartTime) {
     // Round has already started
     continue;
}

Now all members of a team can be eliminated which will lead to a win for the other team.


For knowing how many players of each team are alive I will use the property ClansNbPlayersAlive of CSmMode.
In the round end conditions I check if this numbers drops to 0 for a team and appoint the respective opponent team to the round winner.

Pay attention to a case where a team doesn’t immediately lose by being eliminated!
If the attacking team already planted the bomb it can still win because the defense has to defuse the bomb in order to save the goal and win the round. Because of this I protect the attacking team from losing as long as the bomb is planted.


In my SpeedBall game mode I implemented Friendly Fire which brought the competitive play to another level because you have to care even more about each shot you make to not hit your team members.
Defusing will have the same feature because I really like it. ;)
It can be easily disabled via a new script setting (which is in fact disabled by default).

For preventing hitting team members I checked if an OnHit event represents a hit between players of the same team and discard the event if it’s the case.
From now on the script only checks it as long as the FriendlyFire setting is disabled. While it’s enabled the events will still be handled and passed on leading to damage for the victim.

if (!S_FriendlyFire && Event.Shooter.CurrentClan == Event.Victim.CurrentClan) {
     // Invalid Team Hit
     Discard(Event);
     continue;
}

As players who are hitting their own team members shouldn’t be rewarded for that action I grant them negative points leading to a loss of total points.

if (Event.Shooter.CurrentClan == Event.Victim.CurrentClan) {
     // Team Hit
     Points *= -1;
}

That’s all that needs to be done for friendly fire! An easy but great feature.


During testing the mode you might have noticed that you can’t press backspace yet.
That’s caused by the fact that I Discard() all events that I don’t explicitly handle.
Whenever a player presses backspace there is an OnPlayerRequestRespawn event risen which I from now on simply PassOn().

case CSmModeEvent::EType::OnPlayerRequestRespawn: {
     // Player wants to be despawned
     PassOn(Event);
}

Now you can be despawned by pressing backspace. Keep in mind that you will have to spectate for the rest of the round if the respawning during rounds is disabled. ;)


I’ve also added some delay between rounds to let players breathe a bit. ;)

MB_Sleep(1000);

The function MB_Sleep() is implemented in the ModeBase (a general hint for that is the MB_ prefix) and lets the script wait for the given amount of milliseconds.


You can find the new version of the script here: Defusing6.Script.txt

Have fun eliminating your opponents (and not your team members! :P).

Did you know API-Arrays?

One strange thing about ManiaScript is that built-in API objects and arrays sometimes behave differently than the ones created by users.

Here’s one such behavior about arrays:
They have two types of keys.

Let me explain that with an example: The Players arrays in CSmMode. (It applies to TM as well.)
This arrays holds several player objects which (like all ManiaScript objects) have their own Ids.

In order to take a specific player object out of the array you can use the player’s Id to reference it.
Additionally you can take the Index of the object in the array.

declare Ident SomeId;
declare PlayerById <=> Players[SomeId];

declare Integer SomeIndex;
declare PlayerByIndex <=> Players[SomeIndex];

This can be really helpful. For example the Scores array gets sorted by points so you can access the best score via:

declare BestScore <=> Scores[0];

(Because the object with the index 0 has the most points.)

There’s one thing that you have to keep in mind though.
When using the Id to access an element of such an array which has an Integer key by default: It just searches for the object from beginning to the end, it’s not as performant as an actual key (with a lookup-table).

Unfortunately this is an API only feature. Your own arrays don’t support these two key-types.
You can only access their elements with a key of the type which you’ve set when declaring the array.

Greetings,
steeffeen