I implemented this a while ago for someone who needed a customizable class system. But since he didn't needed it after all, I decided to make it public. So that others could use it if they want to. This is exactly what the title says. But first the code:
// --------------------------------------------------------------------------------------
_SkinSet <- [];
_SkinNav <- null;
// --------------------------------------------------------------------------------------
enum _STATE {
Playing = 0,
SkinSel = 1
}
// --------------------------------------------------------------------------------------
_Key_Left <- null;
_Key_Right <- null;
_Key_Up <- null;
_Key_Down <- null;
_Key_Enter <- null;
// --------------------------------------------------------------------------------------
class _Client
{
// ----------------------------------------------------------------------------------
function constructor(i_player)
{
// See the the specified argument is actually a player instance
if (typeof i_player != "instance") {
// Print some debugging information
print("Cannot create a Client instance from an invalid player instance");
// No point in going forward now
return null;
} else {
// Store the player instance
Instance = i_player
}
}
// ----------------------------------------------------------------------------------
function Connect()
{
// Initialization...
}
// ----------------------------------------------------------------------------------
function Disconnect(reason)
{
// Deinitialization...
}
// ----------------------------------------------------------------------------------
function SkinSelect()
{
// Do not allow the player to move during this time
Instance.Frozen = true;
// Make the player invisible during the skin selection
Instance.SetAlpha(0, 0);
// Save the current player position
Position = Instance.Pos;
// Move the player to an isolated area for the skin sellection
Instance.Pos = Vector(-922.0, 797.5, 20.0);
// Position the player camera to view the available skins
Instance.SetCameraPos(Vector(-920.0, 797.5, 26.0), Vector(-900.0, 797.5, 20.0));
// Set the player in the skin selection state
_CLIENTS[Instance.ID].State = _STATE.SkinSel;
// Display the first set of skins
_SkinSet[SkinSel.SET][SkinSel.IDX].Spr.ShowForPlayer(Instance);
_SkinSet[SkinSel.SET][SkinSel.IDX].Tex.ShowForPlayer(Instance);
// Display information about the skin navigation keys
_SkinNav.ShowForPlayer(Instance);
}
// ----------------------------------------------------------------------------------
function Spawn()
{
// Hide the information about the skin navigation keys
_SkinNav.HideFromPlayer(Instance);
// Hide the skin selection
_SkinSet[SkinSel.SET][SkinSel.IDX].Spr.HideFromPlayer(Instance);
_SkinNav.HideFromPlayer(Instance);
// Apply the selected player skin
Instance.Skin = _SkinSet[SkinSel.SET][SkinSel.IDX].ID;
// Restore the player position
Instance.Pos = Position;
// Restore the player camera
Instance.RestoreCamera();
// Unfrease the player
Instance.Frozen = false;
// Make the player visible again
Instance.SetAlpha(255, 0);
}
// ----------------------------------------------------------------------------------
function KeyPress(bind_id)
{
// See if the player is in the skin selection state
if (State == _STATE.SkinSel) {
// Hide the current sprite so we can show the next one
_SkinSet[SkinSel.SET][SkinSel.IDX].Spr.HideFromPlayer(Instance);
_SkinSet[SkinSel.SET][SkinSel.IDX].Tex.HideFromPlayer(Instance);
// See which key was pressed
switch (bind_id)
{
case _Key_Left:
// Go to the previous skin in the set
SkinSel.IDX -= 1;
// Cycle back so we don't get out of bounds
if (SkinSel.IDX < 0) {
SkinSel.IDX = _SkinSet[SkinSel.SET].len()-1;
}
break;
case _Key_Right:
// Go to the next skin in the set
SkinSel.IDX += 1;
// Cycle back so we don't get out of bounds
if (SkinSel.IDX >= _SkinSet[SkinSel.SET].len()) {
SkinSel.IDX = 0;
}
break;
case _Key_Up:
// Go to the next set
SkinSel.SET += 1;
// Cycle back so we don't get out of bounds
if (SkinSel.SET >= _SkinSet.len()) {
SkinSel.SET = 0;
}
break;
case _Key_Down:
// Go to the previous set
SkinSel.SET -= 1;
// Cycle back so we don't get out of bounds
if (SkinSel.SET < 0) {
SkinSel.SET = _SkinSet.len()-1;
}
break;
case _Key_Enter:
// See if we have to skip th enter key once
if (SkinSel.ENT == true) {
// Don't skip the enter key next time
SkinSel.ENT = false;
// No point in going forward now
break;
}
// Allow the player to spawn with the selected skin
Spawn();
// Set the player in the playing state
_CLIENTS[Instance.ID].State = _STATE.Playing;
break;
}
// See if the player is still in the skin selection
if (State == _STATE.SkinSel) {
// Display the currently selected sprite in the set
_SkinSet[SkinSel.SET][SkinSel.IDX].Spr.ShowForPlayer(Instance);
_SkinSet[SkinSel.SET][SkinSel.IDX].Tex.ShowForPlayer(Instance);
}
}
}
// ----------------------------------------------------------------------------------
function KeyRelease(bind_id)
{
}
// ----------------------------------------------------------------------------------
Instance = null
// ----------------------------------------------------------------------------------
Position = null
// ----------------------------------------------------------------------------------
State = _STATE.Playing
// ----------------------------------------------------------------------------------
SkinSel = {SET = 0, IDX = 0, ENT = false}
}
// --------------------------------------------------------------------------------------
_CLIENTS <- [];
// --------------------------------------------------------------------------------------
function onServerStart()
{
// Reserve enough space to hold the maximum number of Clients that the server can hold
_CLIENTS = array(GetMaxPlayers());
// Attempt to find any previously connected Clients in case of a script reload
foreach (idx, inst in _CLIENTS) {
// Attempt to find the player by it's unique identifier
local i_player = FindPlayer(idx);
// See if a valid player instance was found at this ID
if (i_player != null) {
// Create a Client instance for this player instance
inst = _Client(i_player);
// Allow the Client instance to initialize
inst.Connect();
}
}
// Bind to some generic keyboard buttons
_Key_Left <- ::BindKey(true, 0x25, 0, 0); // Arrow Left
_Key_Right <- ::BindKey(true, 0x27, 0, 0); // Arrow Right
_Key_Up <- ::BindKey(true, 0x26, 0, 0); // Arrow Up
_Key_Down <- ::BindKey(true, 0x28, 0, 0); // Arrow Down
_Key_Enter <- ::BindKey(true, 0x0D, 0, 0); // Enter, Return
// Create the skin navigation info textdraw
_SkinNav <- ::CreateTextdraw("Left|Right: change skin, Up|Down: change set, Enter: select",
200, 124, 0xFF00CED1);
// Create the first set of skins
_SkinSet.append([
{
Spr = ::CreateSprite("Skin_0.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Basic Skin: %s",
::GetSkinName(0)), 200, 148, 0xFF1E90FF),
ID = 0,
CLS = -1 // Create a custom class here
},
{
Spr = ::CreateSprite("Skin_9.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Basic Skin: %s",
::GetSkinName(9)), 200, 148, 0xFF1E90FF),
ID = 9,
CLS = -1 // Create a custom class here
},
{
Spr = ::CreateSprite("Skin_10.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Basic Skin: %s",
::GetSkinName(10)), 200, 148, 0xFF1E90FF),
ID = 10,
CLS = -1 // Create a custom class here
}
]);
// Create the second set of skins
_SkinSet.append([
{
Spr = ::CreateSprite("Skin_11.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Clan Skin: %s",
::GetSkinName(11)), 200, 148, 0xFF1E90FF),
ID = 11,
CLS = -1 // Create a custom class here
},
{
Spr = ::CreateSprite("Skin_12.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Clan Skin: %s",
::GetSkinName(12)), 200, 148, 0xFF1E90FF),
ID = 12,
CLS = -1 // Create a custom class here
},
{
Spr = ::CreateSprite("Skin_13.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Clan Skin: %s",
::GetSkinName(13)), 200, 148, 0xFF1E90FF),
ID = 13,
CLS = -1 // Create a custom class here
}
]);
// Create the third set of skins
_SkinSet.append([
{
Spr = ::CreateSprite("Skin_16.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Premium Skin: %s",
::GetSkinName(16)), 200, 148, 0xFF1E90FF),
ID = 16,
CLS = -1 // Create a custom class here
},
{
Spr = ::CreateSprite("Skin_28.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Premium Skin: %s",
::GetSkinName(28)), 200, 148, 0xFF1E90FF),
ID = 28,
CLS = -1 // Create a custom class here
},
{
Spr = ::CreateSprite("Skin_89.png", 24, 112, 0, 0, 0.0, 255),
Tex = ::CreateTextdraw(::format("Set: Premium Skin: %s",
::GetSkinName(89)), 200, 148, 0xFF1E90FF),
ID = 89,
CLS = -1 // Create a custom class here
}
]);
}
// --------------------------------------------------------------------------------------
function onServerStop()
{
// Attempt to deinitialize all connected players from the server
foreach (idx, client in _CLIENTS) {
// See if there was any Client instance in this slot
if (client != null) {
// Allow the Client instance to deinitialize
client.Disconnect();
// Clear the slot
_CLIENTS[i_player.ID] = null;
}
}
// Clear the skin sets
foreach (set in _SkinSet) {
foreach (skin in set) {
skin.Spr.Delete();
skin.Tex.Delete();
}
}
}
// --------------------------------------------------------------------------------------
function onPlayerJoin(i_player)
{
// Create a Client instance for this player instance
_CLIENTS[i_player.ID] = _Client(i_player);
// Allow the newlly created Client instance to initialize
_CLIENTS[i_player.ID].Connect();
// Force the player to spawn and enter the skin selection
i_player.Spawn();
}
// --------------------------------------------------------------------------------------
function onPlayerPart(i_player, reason)
{
// Allow the Client instance to deinitialize
_CLIENTS[i_player.ID].Disconnect(reason);
// Clear the slot for other Clients
_CLIENTS[i_player.ID] = null;
}
// --------------------------------------------------------------------------------------
function onPlayerSpawn(i_player)
{
_CLIENTS[i_player.ID].SkinSelect();
}
// --------------------------------------------------------------------------------------
function onKeyDown(i_player, bind_id)
{
// Allow the Client to process the event
_CLIENTS[i_player.ID].KeyPress(bind_id);
}
// --------------------------------------------------------------------------------------
function OnKeyBindUp(i_player, bind_id)
{
// Allow the Client to process the event
_CLIENTS[i_player.ID].KeyRelease(bind_id);
}
// --------------------------------------------------------------------------------------
function onPlayerCommand(i_player, cmd, args)
{
switch (cmd)
{
// ------------------------------------------------------------------------------
case "skinsel":
// When using the command we must skip the enter key once
_CLIENTS[i_player.ID].SkinSel.ENT = true;
// Enter the skin selection mode
_CLIENTS[i_player.ID].SkinSelect();
break;
}
}
Save the code above in a simple script file and set that as your game mode. Then Download these
sample images (https://drive.google.com/file/d/0B4b1YKqY2NtSZFg4Q2RZUC0xV1U/view?usp=sharing) used by the script and extract them in your server path as well. The images should end up in the storage folder.
As soon as you wish to spawn, you'll be teleported to a remote location and asked to select a skin. More explained bellow.
Skins are grouped into sets. So that you can have sets of skins like:
- Basic: For example, these could be free for all
- Clan: These could be only for specific clans
- Premium: These can be only for premium/vip members.
- etc.
To cycle through the sets of skins, use the
up and
down arrows. To cycle through the skins of a set, use the
left and
right arrows. Once you've decided on your skin, use
enter key to select. To go to the skin selection menu, use the follwing command:
/skinselYou can customize this in many ways. Have certain weapons, health, armour etc. per skin. Your imagination is the limit.
Just to be clear. This is just an example of how something like this could implemented and to give an idea to anyone who wants to have a set of images that could identify certain things in a script. Which is why the word "gallery" is in the title.
(https://forum.vc-mp.org/proxy.php?request=http%3A%2F%2Fs28.postimg.org%2F49e2scuex%2FUntitled.jpg&hash=7c27e7afdbf2038524960ae8f6414f0b8aacb32e) (http://postimg.org/image/49e2scuex/)
I suggest you to specify the number of textdraws used, because it may go over the textdraws' limit (http://wiki.vc-mp.org/wiki/Scripting/Squirrel/Constants) in some servers.
Quote from: VC:MP WikiMAX_TEXTDRAWS 256
Anyway, the idea sounds good. I hope I will test it soon, and come with an edit. 8)
OFF: No offence, but there is no need to write [SNIPPET] in title, since you posted this script in the Snippet Showroom. :D
Quote from: sseebbyy on Mar 30, 2015, 01:20 PMOFF: No offence, but there is no need to write [SNIPPET] in title, since you posted this script in the Snippet Showroom. :D
You're probably right. I have this nasty habit label most of my stuff :D
Quote from: sseebbyy on Mar 30, 2015, 01:20 PMQuote from: VC:MP WikiMAX_TEXTDRAWS 256
That's one of the issues I've encountered. But there's no fixed number of text-draws. These are added by user and he's in charge of accounting them :D
Quote from: S.L.C on Mar 30, 2015, 01:22 PMThat's one of the issues I've encountered. But there's no fixed number of text-draws. These are added by user and he's in charge of accounting them :D
I was talking only about your example. :P
I believe that it's users will use it as it is left, for a while.
( maybe they will just change the pictures, but leaving the same name, for it to fit with the script, haha )
I have the exact same thing but its for vehicles. :D