23.08.2015, 09:25
(
Последний раз редактировалось Gammix; 07.01.2018 в 21:27.
)
Capture Zone Tutorial
Make capturable zone in one line - checkpoint based captures
Last Updated: 12 Sept, 2017
What makes this different and better from other tutorials/methods used in gamemodes, mostly COD named:
- Array based data collecting
- One line one capture zone (Core feature)
- You can add as many capture zones you want and teams eligible to capture them
- Progress bar and a textdraw included for cpature info.
- Short code and efficient
- Easily portable or you can even use this as a filterscript
progress2.inc - https://sampforum.blast.hk/showthread.php?tid=629401
streamer.inc (.dll/.so too) - https://sampforum.blast.hk/showthread.php?tid=102865
After you install them correctly, if you haven't. Now you have to include them in your script:
PHP код:
#include <progress2>
#include <streamer>
Now we will declare our constants on the very top so its easier to modify in-game related changes easily.
We are using enumerator, you can use define statement if you want, Doesn't matter.
PHP код:
#define CAPTURE_TIME 30 // how many seconds to capture a zone
PHP код:
#define COLOR_TOMATO \
0xFF6347FF
#define COLOR_GREEN \
0x00FF00FF
COLOR_GREEN - For success messages
Step3: Function(s)
This is a small one, we just need this function/macro to adjust alpha values of our colors so we don't rely on the colors user specify in our "TEAM" array. We basically require this for making opacity of gangzone color not full (255/FF).
So add this after the colors constants:
PHP код:
#define ALPHA(%1,%2) \
((%1 & ~0xFF) | clamp(%2, 0x00, 0xFF))
This one needs a little attention.
Team Array:
So first of all, we will make an enumerator-array for our Teams data, we just need the team names and colors so we can use them for information while flashing a gangzone and capture messages.
Since we don't need to change anything in the array, we simply make it constant:
PHP код:
enum E_TEAM
{
bool:E_TEAM_VALID,
E_TEAM_NAME[32],
E_TEAM_COLOR
};
new const TEAM[][E_TEAM] =
{
{true, "team 1", 0xFF0000FF}, // the color's opacity doesn't matter, we will use the function "ALPHA" to change it later
{true, "team 2", 0x00FF00FF}
};
E_TEAM_VALID: This is basically for those scripts whose teamid are not reliable to array indexes. So lets say you are Team:0 but when your team is set by "SetPlayerTeam", the id there used is 10, so my script will use 10 as an index for the array declared below "TEAM", but the array size is 2, so for that case we use this element, you have to do a bit hardwork here, you have to make 8 more entries in array, all empty but the 10th team with its data included. So it will look like this:
PHP код:
new const TEAM[][E_TEAM] =
{
{true, "team 1", 0xFF0000FF}, // the color's opacity doesn't matter, we will use the function "ALPHA" to change it later
{true, "team 2", 0x00FF00FF},
{},
{},
{},
{},
{},
{},
{},
{true, "team 10", 0x0000FFFF} // this is the "SetPlayerTeam(playerid, 10)" entry
};
Similar to TEAM array but this time its name is not in Caps, which means its not a constant, yes we need to make changes to this array later on in script.
PHP код:
enum E_CAPTURE_ZONE
{
E_CAPTURE_ZONE_NAME[64],
Float:E_CAPTURE_ZONE_GANGZONE_OFFSET[4], // the capture zone's gangzone minx/y and maxx/y
Float:E_CAPTURE_ZONE_CP_OFFSET[3], // the x,y,z offset for the checkpoint the players will stay in and capture
E_CAPTURE_ZONE_OWNER,
E_CAPTURE_ZONE_ATTACKER,
E_CAPTURE_ZONE_COUNTDOWN,
E_CAPTURE_ZONE_GANGZONE,
E_CAPTURE_ZONE_CP,
E_CAPTURE_ZONE_AREA,
E_CAPTURE_ZONE_TIMER,
E_CAPTURE_ZONE_PLAYERS_IN_ZONE
};
new captureZone[][E_CAPTURE_ZONE] =
{
{"Big Ear", {-437.5, 1513.671875, -244.140625, 1636.71875}, {-311.0136, 1542.9733, 75.5625}, 0},
{"Area 51", {-46.875, 1697.265625, 423.828125, 2115.234375}, {254.4592, 1802.8997, 7.4285}, 1} // "1" means the zone owner will be "team 2", notice the number relates to array "TEAM"'s indexes
};
For example: if i need to add a new zone, i simply do...
PHP код:
new captureZone[][E_CAPTURE_ZONE] =
{
{"Big Ear", {-437.5, 1513.671875, -244.140625, 1636.71875}, {-311.0136, 1542.9733, 75.5625}, 0},
{"Area 51", {-46.875, 1697.265625, 423.828125, 2115.234375}, {254.4592, 1802.8997, 7.4285}, 1}, // "1" means the zone owner will be "team 2", notice the number relates to array "TEAM"'s indexes
{"New zone", {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, 10}, // the new zone
};
PHP код:
{"zone name", {minx, miny, maxx, maxy}, {cpx, cpy, cpz}, owner_team_index}
Lastly we need two player variables to store PlayerProgressBar and PlayerTextDraw ids:
PHP код:
new PlayerText:capturePlayerTextDraw[MAX_PLAYERS];
new PlayerBar:capturePlayerBar[MAX_PLAYERS];
If you are using filterscript use: OnFilterScriptInit
PHP код:
public OnGameModeInit()
{
for (new i; i < sizeof captureZone; i++)
{
captureZone[i][E_CAPTURE_ZONE_ATTACKER] = INVALID_PLAYER_ID; // set the zone attacker to none
captureZone[i][E_CAPTURE_ZONE_COUNTDOWN] = 0;
captureZone[i][E_CAPTURE_ZONE_GANGZONE] = GangZoneCreate(captureZone[i][E_CAPTURE_ZONE_GANGZONE_OFFSET][0], captureZone[i][E_CAPTURE_ZONE_GANGZONE_OFFSET][1], captureZone[i][E_CAPTURE_ZONE_GANGZONE_OFFSET][2], captureZone[i][E_CAPTURE_ZONE_GANGZONE_OFFSET][3]);
captureZone[i][E_CAPTURE_ZONE_CP] = CreateDynamicCP(captureZone[i][E_CAPTURE_ZONE_CP_OFFSET][0], captureZone[i][E_CAPTURE_ZONE_CP_OFFSET][1], captureZone[i][E_CAPTURE_ZONE_CP_OFFSET][2], 1.0);
captureZone[i][E_CAPTURE_ZONE_AREA] = CreateDynamicRectangle(captureZone[i][E_CAPTURE_ZONE_GANGZONE_OFFSET][0], captureZone[i][E_CAPTURE_ZONE_GANGZONE_OFFSET][1], captureZone[i][E_CAPTURE_ZONE_GANGZONE_OFFSET][2], captureZone[i][E_CAPTURE_ZONE_GANGZONE_OFFSET][3]);
}
return 1;
}
Now initializing when a player connect, i.e. creating textdraw and progress bar:
PHP код:
public OnPlayerConnect(playerid)
{
capturePlayerBar[playerid] = CreatePlayerProgressBar(playerid, 44.000000, 318.000000, 89.500000, 3.700000, -1429936641, CAPTURE_TIME, 0);
capturePlayerTextDraw[playerid] = CreatePlayerTextDraw(playerid, 87.000000, 308.000000, "Capturing !...");
PlayerTextDrawBackgroundColor(playerid, capturePlayerTextDraw[playerid], 255);
PlayerTextDrawFont(playerid, capturePlayerTextDraw[playerid], 1);
PlayerTextDrawLetterSize(playerid, capturePlayerTextDraw[playerid], 0.290000, 1.099999);
PlayerTextDrawColor(playerid, capturePlayerTextDraw[playerid], -1);
PlayerTextDrawAlignment(playerid, capturePlayerTextDraw[playerid], 2);
PlayerTextDrawSetOutline(playerid, capturePlayerTextDraw[playerid], 1);
return 1;
}
So here we loop through the array "captureZone" and show gangzone to player.
In the second part we see if the gangzone is under attack, if it is then flash it for player with the attacker's team color.
PHP код:
public OnPlayerSpawn(playerid)
{
for (new i; i < sizeof captureZone; i++)
{
// show gangzones
GangZoneShowForPlayer(playerid, captureZone[i][E_CAPTURE_ZONE_GANGZONE], ALPHA(TEAM[captureZone[i][E_CAPTURE_ZONE_OWNER]][E_TEAM_COLOR], 100));
// flash gangzone if under attack
if (captureZone[i][E_CAPTURE_ZONE_ATTACKER] != INVALID_PLAYER_ID)
{
GangZoneFlashForPlayer(playerid, captureZone[i][E_CAPTURE_ZONE_GANGZONE], ALPHA(TEAM[GetPlayerTeam(captureZone[i][E_CAPTURE_ZONE_ATTACKER])][E_TEAM_COLOR], 100));
}
}
return 1;
}
This callback is called when you enter a dynamic area created through streamer plugin. So if you noticed in Initializing step, we made a DynamicRectangle exactly to the coordinates of our capture zones. So each gangzone have its own dynamic area too. So now we can use it to detect if a player enters that zone and we simply show him the zone name as a GameText!
PHP код:
public OnPlayerEnterDynamicArea(playerid, areaid)
{
for (new i; i < sizeof captureZone; i++)
{
if (areaid == captureZone[i][E_CAPTURE_ZONE_AREA]) // matches the player area to the gangzone area
{
new string[64 + 3];
format(string, sizeof string, "~w~%s", captureZone[i][E_CAPTURE_ZONE_NAME]);
GameTextForPlayer(playerid, string, 5000, 6);
return 1;
}
}
return 1;
}
Similar to dynamic areas, when a player enteres a dynamic checkpoint, this callback is called. And so it goes:
PHP код:
public OnPlayerEnterDynamicCP(playerid, checkpointid)
{
for (new i; i < sizeof captureZone; i++)
{
if (checkpointid == captureZone[i][E_CAPTURE_ZONE_CP]) // player entered checkpoint of the gangzone
{
if (IsPlayerInAnyVehicle(playerid)) // this little check disallows the player to capture in a vehicle
{
SendClientMessage(playerid, COLOR_TOMATO, "You cannot capture a zone in a vehicle.");
return 1;
}
if (captureZone[i][E_CAPTURE_ZONE_ATTACKER] == INVALID_PLAYER_ID) // this check tells us that gangzone is not under attack so we can proceed with start attacking here!
{
if ((GetPlayerTeam(playerid) >= 0 && GetPlayerTeam(playerid) < sizeof TEAM) && TEAM[GetPlayerTeam(playerid)][E_TEAM_VALID] && GetPlayerTeam(playerid) != captureZone[i][E_CAPTURE_ZONE_OWNER]) // here the team ids play its role, we check the index is valid - The second part is if whether the player isn't of team that zone owner is
{
captureZone[i][E_CAPTURE_ZONE_ATTACKER] = playerid;
captureZone[i][E_CAPTURE_ZONE_PLAYERS_IN_ZONE] = 1;
captureZone[i][E_CAPTURE_ZONE_COUNTDOWN] = 0;
KillTimer(captureZone[i][E_CAPTURE_ZONE_TIMER]);
captureZone[i][E_CAPTURE_ZONE_TIMER] = SetTimerEx("OnCaptureZoneUpdate", 1000, true, "i", i);
// falsh gangzone for all
GangZoneFlashForAll(captureZone[i][E_CAPTURE_ZONE_GANGZONE], ALPHA(TEAM[GetPlayerTeam(playerid)][E_TEAM_COLOR], 100));
// display capture message to player
SendClientMessage(playerid, COLOR_GREEN, "Stay in the checkpoint to for "#CAPTURE_TIME" seconds to capture the zone.");
// display provocation message to all
new string[150];
format(string, sizeof string, "%s is trying to capture %s away from team %s.", TEAM[GetPlayerTeam(playerid)][E_TEAM_NAME], captureZone[i][E_CAPTURE_ZONE_NAME], TEAM[captureZone[i][E_CAPTURE_ZONE_OWNER]][E_TEAM_NAME]);
SendClientMessageToAll(COLOR_TOMATO, string);
}
else return 1;
}
else if (GetPlayerTeam(playerid) == GetPlayerTeam(captureZone[i][E_CAPTURE_ZONE_ATTACKER])) // already being captured and player is of the same team that of attacker is
{
// increase number of players capturing
captureZone[i][E_CAPTURE_ZONE_PLAYERS_IN_ZONE]++;
SendClientMessage(playerid, COLOR_GREEN, "Stay in the checkpoint to assist your teammate in capturing the zone.");
}
// show progress bar and capture textdraw!
PlayerTextDrawShow(playerid, capturePlayerTextDraw[playerid]);
ShowPlayerProgressBar(playerid, capturePlayerBar[playerid]);
return 1;
}
}
return 1;
}
This is the main part!
PHP код:
forward OnCaptureZoneUpdate(zoneid);
public OnCaptureZoneUpdate(zoneid)
{
// the zone capture rate depends on the number of players capturing it
// so if its "1", the capture time taken will be 30s
// if its "2", the capture time decreases to half i.e. 15s
// given you set the CAPTURE_TIME to 30
captureZone[zoneid][E_CAPTURE_ZONE_COUNTDOWN] += captureZone[zoneid][E_CAPTURE_ZONE_PLAYERS_IN_ZONE];
new string[150];
format(string, sizeof string, "Capturing Zone In %i...", CAPTURE_TIME - captureZone[zoneid][E_CAPTURE_ZONE_COUNTDOWN]);
// update progress bar and textdraw for all players capturing
for (new i, j = GetPlayerPoolSize(); i <= j; i++)
{
if (IsPlayerInDynamicCP(i, captureZone[zoneid][E_CAPTURE_ZONE_CP]) &&
!IsPlayerInAnyVehicle(i) &&
GetPlayerTeam(i) == GetPlayerTeam(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER]))
{
PlayerTextDrawSetString(i, capturePlayerTextDraw[i], string);
SetPlayerProgressBarValue(i, capturePlayerBar[i], captureZone[zoneid][E_CAPTURE_ZONE_COUNTDOWN]);
}
}
// zone has been captured
if (captureZone[zoneid][E_CAPTURE_ZONE_COUNTDOWN] > CAPTURE_TIME)
{
GetPlayerName(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER], string, MAX_PLAYER_NAME);
format(string, sizeof string, "Good job. You assisted %s to capture %s. +$250", string, captureZone[zoneid][E_CAPTURE_ZONE_NAME]);
for (new i, j = GetPlayerPoolSize(); i <= j; i++)
{
if (IsPlayerInDynamicCP(i, captureZone[zoneid][E_CAPTURE_ZONE_CP]) &&
!IsPlayerInAnyVehicle(i) &&
GetPlayerTeam(i) == GetPlayerTeam(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER]))
{
PlayerTextDrawHide(i, capturePlayerTextDraw[i]);
HidePlayerProgressBar(i, capturePlayerBar[i]);
// giving reward to teammates who assisted
if (i != captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER])
{
SendClientMessage(i, COLOR_GREEN, string);
GivePlayerMoney(i, 250);
}
}
}
// giving reward to the attacker who startd capturing initially
SetPlayerScore(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER], GetPlayerScore(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER]) + 1);
GivePlayerMoney(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER], 1000);
format(string, sizeof string, "Good job. You successfully captured %s from %s. +1 Score, +$1000", captureZone[zoneid][E_CAPTURE_ZONE_NAME], TEAM[captureZone[zoneid][E_CAPTURE_ZONE_OWNER]][E_TEAM_NAME]);
SendClientMessage(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER], COLOR_GREEN, string);
// announcing capture to all players
format(string, sizeof string, "<Capture Zone>: Team %s have captured %s from %s.", TEAM[GetPlayerTeam(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER])][E_TEAM_NAME], captureZone[zoneid][E_CAPTURE_ZONE_NAME], TEAM[captureZone[zoneid][E_CAPTURE_ZONE_OWNER]][E_TEAM_NAME]);
SendClientMessageToAll(-1, string);
// reset capture zone variables and modify Owner to attacker's team id
captureZone[zoneid][E_CAPTURE_ZONE_OWNER] = GetPlayerTeam(captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER]);
captureZone[zoneid][E_CAPTURE_ZONE_ATTACKER] = INVALID_PLAYER_ID;
KillTimer(captureZone[zoneid][E_CAPTURE_ZONE_TIMER]);
GangZoneStopFlashForAll(captureZone[zoneid][E_CAPTURE_ZONE_GANGZONE]);
GangZoneShowForAll(captureZone[zoneid][E_CAPTURE_ZONE_GANGZONE], ALPHA(TEAM[captureZone[zoneid][E_CAPTURE_ZONE_OWNER]][E_TEAM_COLOR], 100));
}
return 1;
}
This part is for what happens when a player leaves the capture checkpoint while capturing...
PHP код:
public OnPlayerLeaveDynamicCP(playerid, checkpointid)
{
for (new i; i < sizeof captureZone; i++)
{
if (checkpointid == captureZone[i][E_CAPTURE_ZONE_CP])
{
if (captureZone[i][E_CAPTURE_ZONE_ATTACKER] != INVALID_PLAYER_ID)
{
if (GetPlayerTeam(playerid) == GetPlayerTeam(captureZone[i][E_CAPTURE_ZONE_ATTACKER]))
{
// hide progress bar and textdraw
PlayerTextDrawHide(playerid, capturePlayerTextDraw[playerid]);
HidePlayerProgressBar(playerid, capturePlayerBar[playerid]);
captureZone[i][E_CAPTURE_ZONE_PLAYERS_IN_ZONE]--;
if (captureZone[i][E_CAPTURE_ZONE_PLAYERS_IN_ZONE] == 0) // if there is no one left in capture checkpoint but the zone was still being captured
{
// stop capture and reset it to the team it was before
captureZone[i][E_CAPTURE_ZONE_ATTACKER] = INVALID_PLAYER_ID;
KillTimer(captureZone[i][E_CAPTURE_ZONE_TIMER]);
GangZoneStopFlashForAll(captureZone[i][E_CAPTURE_ZONE_GANGZONE]);
SendClientMessage(playerid, COLOR_TOMATO, "You failed to capture the zone. You left the checkpoint before time!");
}
else
{
if (playerid == captureZone[i][E_CAPTURE_ZONE_ATTACKER]) // if the main player who started capturing initially left
{
// we will select another player who is in capture checkpoint and set him as the main attacker
// since attackers recieve bigger reward
for (new x, y = GetPlayerPoolSize(); x <= y; x++)
{
if (IsPlayerInDynamicCP(x, captureZone[i][E_CAPTURE_ZONE_CP]) &&
!IsPlayerInAnyVehicle(x) &&
GetPlayerTeam(x) == GetPlayerTeam(captureZone[i][E_CAPTURE_ZONE_ATTACKER]))
{
captureZone[i][E_CAPTURE_ZONE_ATTACKER] = x;
new string[150];
GetPlayerName(playerid, string, MAX_PLAYER_NAME);
format(string, sizeof string, "%s is no longer the initial zone attacker. You are now holding that position.", string);
SendClientMessage(x, COLOR_TOMATO, string);
SendClientMessage(x, COLOR_TOMATO, "Stay in checkpoint to finish capture and recieve maximum reward.");
GetPlayerName(x, string, MAX_PLAYER_NAME);
format(string, sizeof string, "You left the checkpoint before time! You are no longer the initial capturer, %s is!", string);
SendClientMessage(playerid, COLOR_TOMATO, string);
break;
}
}
}
else
{
SendClientMessage(playerid, COLOR_TOMATO, "You left the checkpoint before time! You are no longer assisting your teammates in capturing!");
}
}
}
}
}
}
return 1;
}
We simply destroy what we made in Initializing step.
Also, if you are using filterscript, use OnFilterScriptExit.
PHP код:
public OnGameModeExit()
{
for (new i; i < sizeof captureZone; i++)
{
GangZoneDestroy(captureZone[i][E_CAPTURE_ZONE_GANGZONE]);
DestroyDynamicCP(captureZone[i][E_CAPTURE_ZONE_CP]);
DestroyDynamicArea(captureZone[i][E_CAPTURE_ZONE_AREA]);
}
return 1;
}
If you have any questions, feel free to ask them here in this thread!