[CS:DE] Character Customization (Gender & Hair)

This is a tutorial for Crystalshire Developer’s Edition. It will show you how to add basic character customization into your game upon selecting a class. This tutorial will go nicely with this one: [CS:DE] Paperdoll & Gender Based Paperdoll This may be a long tutorial for some.

How It Works:

This will allow you to choose the Gender and Hair of your character when you are creating them. After you have selected a class, you are able to cycle between hair and gender options for your character using buttons. Hair is a separate declaration and will appear independent from the sprite, it can be changed throughout the game. This is just a simple customization you could refer to so that you can add extra customization options if you wish. Please see attachments for the buttons you’ll need to use this tutorial. To fully utilize this tutorial, you should have a set of base characters for use with your project, if anything, you can just extract the gender part out of this tutorial and forget about the hair part.

Screenshots:

!
!
!

The Code:

==============
Server Side

First let’s add the “Hair” declaration to the PlayerRec

In modTypes
Find : Private Type PlayerRec

Find:

Level As Byte

Underneath it Add:

Hair As Long

In modConstants

Change MAX_BUTTONS Value to equal 39 instead of 35

In modPlayer
We’re going to add a function and sub that will return the value of and set the hair, could be used for other source edits in the future.

Find: Function GetPlayerLevel

Above that entire function, add this:

Function GetPlayerHair(ByVal index As Long) As Long
    If index <= 0 Or index > MAX_PLAYERS Then Exit Function
    GetPlayerHair = Player(index).Hair
End Function

Sub SetPlayerHair(ByVal index As Long, ByVal Hair As Long)
    If index <= 0 Or index > MAX_PLAYERS Then Exit Sub
    Player(index).Hair = Hair
End Sub

In modDatabase

Find the AddChar Sub

We’re going to modify our AddChar sub so it can know which hair will be sent from the client, look for this on the same line as the name of the sub:

ByVal Sprite As Long

Directly after it on the same line, we’re going to add this:

, ByVal Hair As Long

That whole line should look like this:

Sub AddChar(ByVal index As Long, ByVal Name As String, ByVal Sex As Byte, ByVal ClassNum As Long, ByVal Sprite As Long, ByVal Hair As Long)

This will set the hair for the player on the server so it can be saved, in the same sub find this:

Player(index).Level = 1

Underneath it, add this:

Player(index).Hair = Hair

The next two additions we’ll make are so that the server saves the hair value.

In the SavePlayer sub,

Find:

PutVar filename, "ACCOUNT", "Level", Val(Player(index).Level)

Underneath it add this:

PutVar filename, "ACCOUNT", "Hair", Val(Player(index).Hair)

In the LoadPlayer sub,

Find:

Player(index).Level = Val(GetVar(filename, "ACCOUNT", "Level"))

Underneath it add this:

Player(index).Hair = Val(GetVar(filename, "ACCOUNT", "Hair"))

Now we handle the sub that actually receives player data from the client. We need to tell it to read the hair value.

In modHandleData
Find the HandleAddChar sub.

Find:

Dim Sprite As Long

Underneath it add this:

Dim Hair As Long

Find:

Sprite = Buffer.ReadLong

Underneath it add this:

Hair = Buffer.ReadLong

Now we tell the AddChar sub to include hair as well

Find:

Call AddChar(index, Name, Sex, Class, Sprite)

Replace it with:

Call AddChar(index, Name, Sex, Class, Sprite, Hair)

In modServerTCP
Find the PlayerData function

Here, were going to send our saved hair value back to the client so it can load it every time.

Find:

Buffer.WriteLong GetPlayerLevel(index)

Underneath it add this:

Buffer.WriteLong GetPlayerHair(index)

That’s it for server side! On to the client.

==============
Client Side

Doing the same thing as the server here…

In modTypes
Find : Private Type PlayerRec

Find:

Level As Byte

Underneath it Add:

Hair As Long

In modClientTCP

Here we’ll modify our SendAddChar to include a hair value to send to the server.

Replace the entire SendAddChar sub with this:

Public Sub SendAddChar(ByVal name As String, ByVal Sex As Long, ByVal ClassNum As Long, ByVal Sprite As Long, ByVal Hair As Long)
Dim Buffer As clsBuffer

    ' If debug mode, handle error then exit out
    If Options.Debug = 1 Then On Error GoTo errorhandler

    Set Buffer = New clsBuffer
    Buffer.WriteLong CAddChar
    Buffer.WriteString name
    Buffer.WriteLong Sex
    Buffer.WriteLong ClassNum
    Buffer.WriteLong Sprite
    Buffer.WriteLong Hair
    SendData Buffer.ToArray()
    Set Buffer = Nothing

    ' Error handler
    Exit Sub
errorhandler:
    HandleError "SendAddChar", "modClientTCP", Err.Number, Err.Description, Err.Source, Err.HelpContext
    Err.Clear
    Exit Sub
End Sub

In modDatabase

Now we’ll add two subs that return or set the hair value.

Add this to the bottom:

Function GetPlayerHair(ByVal index As Long) As Long
    ' If debug mode, handle error then exit out
    If Options.Debug = 1 Then On Error GoTo errorhandler

    If index > MAX_PLAYERS Then Exit Function
    GetPlayerHair = Player(index).Hair

    ' Error handler
    Exit Function
errorhandler:
    HandleError "GetPlayerSprite", "modDatabase", Err.Number, Err.Description, Err.Source, Err.HelpContext
    Err.Clear
    Exit Function
End Function

Sub SetPlayerHair(ByVal index As Long, ByVal Hair As Long)
    ' If debug mode, handle error then exit out
    If Options.Debug = 1 Then On Error GoTo errorhandler

    If index > MAX_PLAYERS Then Exit Sub
    Player(index).Hair = Hair

    ' Error handler
    Exit Sub
errorhandler:
    HandleError "SetPlayerSprite", "modDatabase", Err.Number, Err.Description, Err.Source, Err.HelpContext
    Err.Clear
    Exit Sub
End Sub

In modGlobals

Adding in declarations that hold our gender and hair constants, can’t remember if the “newCharSex” already exists or not, if it does, ignore it and don’t add it.

Find:

' New char
Public newCharSprite As Long
Public newCharClass As Long

Underneath it add this:

Public newCharSex As Long
Public newCharHair As Long

In modGeneral
Find the MenuState sub

We send our hair constant to the server with our modified SendAddChar sub. Also, we tell it to select the gender we’ve picked at character creation.

Find:

Call SendAddChar(sChar, SEX_MALE, newCharClass, newCharSprite)

And Replace it with:

Call SendAddChar(sChar, newCharSex, newCharClass, newCharSprite, newCharHair)

In modHandleData

Find the HandlePlayerData sub,

Here we receive the saved hair constant from the server.

Find:

Call SetPlayerLevel(i, Buffer.ReadLong)

Underneath it, add this:

Call SetPlayerHair(i, Buffer.ReadLong)

Add this to the bottom of the entire modGeneral module:

Public Sub ChangeGender()

    If newCharSex = SEX_MALE Then
        newCharSex = SEX_FEMALE
    Else
        newCharSex = SEX_MALE
    End If

End Sub

Now we add the graphical functions of hair.

In modDirectX8
Find:

Public Tex_Fader As Long

Underneath it add:

' Hair
Public Tex_Hair() As Long

Find:

Public Count_Fog As Long

Underneath it add this:

Public Count_Hair As Long

Find:

Public Const Path_Fog As String = "\data files\graphics\fog\"

Underneath it add:

Public Const Path_Hair As String = "\data files\graphics\characters\hair\"

Find:

' Surfaces
    Count_Surface = 1
    Do While FileExist(App.Path & Path_Surface & Count_Surface & ".png")
        ReDim Preserve Tex_Surface(0 To Count_Surface)
        Tex_Surface(Count_Surface).Path = App.Path & Path_Surface & Count_Surface & ".png"
        Count_Surface = Count_Surface + 1
    Loop
    Count_Surface = Count_Surface - 1

Underneath it add this:

' Hair Textures
    Count_Hair = 1
    Do While FileExist(App.Path & Path_Hair & Count_Hair & ".png")
        ReDim Preserve Tex_Hair(0 To Count_Hair)
        Tex_Hair(Count_Hair) = SetTexturePath(App.Path & Path_Hair & Count_Hair & ".png")
        Count_Hair = Count_Hair + 1
    Loop
    Count_Hair = Count_Hair - 1

Find your DrawPlayer sub

Find:

RenderTexture Tex_Char(Sprite), ConvertMapX(x), ConvertMapY(y), rec.left, rec.top, rec.Width, rec.height, rec.Width, rec.height

Underneath it add this:

DrawHair x, y, GetPlayerHair(index), Anim, spritetop

Underneath the entire DrawPlayer sub, add this:

Public Sub DrawHair(ByVal x2 As Long, ByVal y2 As Long, ByVal Hair As Long, ByVal Anim As Long, ByVal spritetop As Long)
Dim rec As GeomRec

    If Hair < 1 Or Hair > Count_Hair Then Exit Sub

        With rec
            .top = spritetop * (D3DT_TEXTURE(Tex_Hair(Hair)).height / 4)
            .height = (D3DT_TEXTURE(Tex_Hair(Hair)).height / 4)
            .left = Anim * (D3DT_TEXTURE(Tex_Hair(Hair)).Width / 4)
            .Width = (D3DT_TEXTURE(Tex_Hair(Hair)).Width / 4)
        End With

    ' Clip to screen
    If y2 < 0 Then
        With rec
            .top = .top - y2
        End With
        y2 = 0
    End If

    If x2 < 0 Then
        With rec
            .left = .left - x2
        End With
        x2 = 0
    End If

    RenderTexture Tex_Hair(Hair), ConvertMapX(x2), ConvertMapY(y2), rec.left, rec.top, rec.Width, rec.height, rec.Width, rec.height

End Sub

Up next, we will be moving some things around in the New Character screen, as well as adding in our buttons and defining what they do.

Find your DrawNewChar sub

Find:

' sprite preview
    sprite = Class(newCharClass).MaleSprite(newCharSprite)
    'EngineRenderRectangle Tex_Char(sprite), x + 235, y + 123, 32, 0, 32, 32, 32, 32, 32, 32
    RenderTexture Tex_Char(sprite), x + 235, y + 123, 32, 0, 32, 32, 32, 32

Replace it with this:

RenderChar

    RenderTexture Tex_Hair(newCharHair), x + 265, y + 120, 32, 0, 32, 32, 32, 32

This sub will render the correct sprite based on gender on character creation. Above the entire DrawNewChar sub, add this:

Public Sub RenderChar()
Dim Sprite As Long
Dim x As Long
Dim y As Long

    x = GUIWindow(GUI_MAINMENU).x
    y = GUIWindow(GUI_MAINMENU).y

        If newCharSex = SEX_MALE Then
            Sprite = Class(newCharClass).MaleSprite(newCharSprite)
        Else
            Sprite = Class(newCharClass).FemaleSprite(newCharSprite)
        End If

    RenderTexture Tex_Char(Sprite), x + 265, y + 120, 32, 0, 32, 32, 32, 32
End Sub

Time to draw our four buttons. Back in the DrawNewChar sub, were going to add this just above the last "End If"

' position
        For buttonnum = 36 To 39
        x = GUIWindow(GUI_MAINMENU).x + Buttons(buttonnum).x
        y = GUIWindow(GUI_MAINMENU).y + Buttons(buttonnum).y
        Width = Buttons(buttonnum).Width
        height = Buttons(buttonnum).height
        ' render accept button
        If Buttons(buttonnum).state = 2 Then
            ' we're clicked boyo
            'EngineRenderRectangle Tex_Buttons_c(Buttons(buttonnum).PicNum), x, y, 0, 0, width, height, width, height, width, height
            RenderTexture Tex_Buttons(Buttons(buttonnum).PicNum), x, y, 0, 0, Width, height, Width, height
        ElseIf (GlobalX >= x And GlobalX <= x + Buttons(buttonnum).Width) And (GlobalY >= y And GlobalY <= y + Buttons(buttonnum).height) Then
            ' we're hoverin'
            'EngineRenderRectangle Tex_Buttons_h(Buttons(buttonnum).PicNum), x, y, 0, 0, width, height, width, height, width, height
            RenderTexture Tex_Buttons(Buttons(buttonnum).PicNum), x, y, 0, 0, Width, height, Width, height
            ' play sound if needed
            If Not lastButtonSound = buttonnum Then
                Play_Sound Sound_ButtonHover
                lastButtonSound = buttonnum
            End If
        Else
            ' we're normal
            'EngineRenderRectangle Tex_Buttons(Buttons(buttonnum).PicNum), x, y, 0, 0, width, height, width, height, width, height
            RenderTexture Tex_Buttons(Buttons(buttonnum).PicNum), x, y, 0, 0, Width, height, Width, height
            ' reset sound if needed
            If lastButtonSound = buttonnum Then lastButtonSound = 0
        End If
        Next

In modGeneral
Find the InitialiseGUI sub

This is where we defined our buttons, their sizes and position. At the very bottom of the sub but above “End Sub”, were going to add in our new buttons:

    ' main - Select Gender Left
        With Buttons(36)
            .state = 0 'normal
            .x = 175
            .y = 114
            .Width = 19
            .height = 19
            .visible = True
            .PicNum = 23
        End With

    ' main - Select Gender Right
        With Buttons(37)
            .state = 0 'normal
            .x = 211
            .y = 114
            .Width = 19
            .height = 19
            .visible = True
            .PicNum = 24
        End With

    ' main - Select Hair Left
        With Buttons(38)
            .state = 0 'normal
            .x = 175
            .y = 141
            .Width = 19
            .height = 19
            .visible = True
            .PicNum = 23
        End With

    ' main - Select Gender Right
        With Buttons(39)
            .state = 0 'normal
            .x = 211
            .y = 141
            .Width = 19
            .height = 19
            .visible = True
            .PicNum = 24
        End With

In modInput
Find the MainMenu_MouseDown sub

This will check if we’re clicking a button.

Add this at the bottom before “End Sub”

For i = 36 To 39
        x = GUIWindow(GUI_MAINMENU).x + Buttons(i).x
        y = GUIWindow(GUI_MAINMENU).y + Buttons(i).y
        ' check if we're on the button
        If (GlobalX >= x And GlobalX <= x + Buttons(i).Width) And (GlobalY >= y And GlobalY <= y + Buttons(i).height) Then
            Buttons(i).state = 2 ' clicked
        End If
    Next

Find the MainMenu_MouseUp sub

This will check if we clicked a button and what the button will do once we’ve clicked it.

Find:

' reset buttons
    resetClickedButtons

Above it, add this:

    ' Character Customization Buttons
    ' find out which button we're clicking
    For i = 36 To 39
        x = GUIWindow(GUI_MAINMENU).x + Buttons(i).x
        y = GUIWindow(GUI_MAINMENU).y + Buttons(i).y
        ' check if we're on the button
        If (GlobalX >= x And GlobalX <= x + Buttons(i).Width) And (GlobalY >= y And GlobalY <= y + Buttons(i).height) Then
            If Buttons(i).state = 2 Then
                ' do stuffs
                Select Case i
                    Case 36
                        If curMenu = MENU_NEWCHAR Then
                            ' do eet
                            ChangeGender
                            RenderChar
                        End If
                    Case 37
                        If curMenu = MENU_NEWCHAR Then
                            ' do eet
                            ChangeGender
                            RenderChar
                        End If
                    Case 38
                        If curMenu = MENU_NEWCHAR Then
                            ' Select Hair
                            newCharHair = newCharHair - 1
                            If newCharHair < 0 Then
                                newCharHair = Count_Hair
                            End If
                        End If
                    Case 39
                        If curMenu = MENU_NEWCHAR Then
                            ' Cycle Hair
                            newCharHair = newCharHair + 1
                            If newCharHair > Count_Hair Then
                                newCharHair = 1
                            End If
                        End If
                End Select
                ' play sound
                Play_Sound Sound_ButtonClick
            End If
        End If
    Next

Non-Code Work:

1. In your graphics folder, create a new folder in your “characters” folder called “hair”
2. Add 26.png to your /graphics/gui/ folder and replace the old one
3. Add 23.png and 24.png to your graphics/gui/buttons/ folder.

I think that’s it but I’m not sure. I have a bad habit of adding in things then forgetting what all I did to get it working, if you have trouble with the tutorial, have any questions, or find I left something out, let me know. Congrats, you now have a basic character customization screen. I hope this helps you further your project, and I hope its understandable enough for you to add your own customization options. I’ll try to do my tutorials with a bit more explanation in the future.

Look awesome… i’ll try this after school
(IF THIS WORKS YOU’RE MY GOD)

In modConstants(client)
I had to change the max buttons from 35:
Public Const MAX_BUTTONS As Long = 39

When I go to make the exe file, it raises an error modHandleData, Sub HandlePlayerData:
Call SetPlayerHair(i, Buffer.ReadLong)
“Sub or Function not defined”

Edit: my mistake.  I did not add all of the modDatabase changes.

@Recoil:

In modConstants(client)
I had to change the max buttons from 35:
Public Const MAX_BUTTONS As Long = 39

Ah thank you for pointing that out, I forgot about that. Added to original post.

I have a question while I am at it.  I followed the tutorial exactly, + the minor change I suggested, compiles and works.  Now when I go to create a character I will select the class, I hit “Accept” then I get an error “Object variable or With block variable not set”.

So I went back through VB6, caught the error: “Subscript out of range”

modDirectX8 - DrawNewChar Sub = RenderTexture Tex_Hair(newCharHair), x + 265, y + 120, 32, 0, 32, 32, 32, 32

I did not do the first tutorial that was listed for the paper dolling.  Also, I do not have any images in the folders.  I was hoping that I could get pointed to an idea of what I am doing wrong?  Thanks

You will need some images in your hairs folder. Count_Hair, which is the number of hair graphics in the folder, needs to return a positive value. 0 and any negative value will be out of range.

In the Resources section, you will find some CS:DE paperdolling graphics packs for you to use with your project.

You do not need any prerequisite tutorial for this one to work, but it works nicely with the Paperdolling tut.

Okay, to make sure I am getting this correct, (since I went ahead and installed the other tutorial as well), All the body party go in the paperdoll folder, and the hair goes in the male/female folders depending on what it is, and I just have to rename all the images: 1.png, 2.png, 3.png, etc?

Alright, I am still having difficulty.  I have installed all hair and gender images from the graphics pack…they are all the same to make sure the names are the same for testing.

When I debug I keep having issues with this line:

RenderChar

    RenderTexture Tex_Hair(newCharHair), x + 265, y + 120, 32, 0, 32, 32, 32, 32

I have went back through all the tutorial and ensured that I followed the lines correctly.  Is anyone else running into this issue because I cannot seem to locate how to resolve it.

OMG, i love this ! This is working very well 🙂

Is it possible to post the working Public Sub DrawNewChar() from client-modDirectX8?

Nevermind.  I had the “hair” folder in the “graphics” folder.  This is supposed to go into “characters”(client\data files\graphics\characters\hair).

I thought I had it in there but had overlooked which directory I was creating my files in.  Sorry for all the trouble and unnecessary posts 😞

@Recoil:

Nevermind.  I had the “hair” folder in the “graphics” folder.  This is supposed to go into “characters”(client\data files\graphics\characters\hair).

I thought I had it in there but had overlooked which directory I was creating my files in.  Sorry for all the trouble and unnecessary posts 😞

Glad you got it figured out. 😉

Works like a charm, thank you!
I got one question though, if you could help me with it. When you make a new character, it starts with no hair, how and what do I change/add so you can’t make a char without hair?

whether this also applies to EO 2.0  :huh:

@Couture:

Works like a charm, thank you!
I got one question though, if you could help me with it. When you make a new character, it starts with no hair, how and what do I change/add so you can’t make a char without hair?

You can make newCharHair = 1 when the menu is drawn.

@GoldSide:

whether this also applies to EO 2.0  :huh:

No it does not, every tutorial I do in the future will also be for CS:DE. EO and CS:DE are very similar, so you could convert using many of the same procedures.

but whether this procedure can apply to EO?

@GoldSide:

but whether this procedure can apply to EO?

Not the exact procedures. You’ll have to make modifications.

okay 🙂

Fixed a bug, the original post was updated.

If you have already implemented the tutorial, replace your DrawHair sub with the one in the first post. Thanks.

Ohh, great code, If only I could convert it to EO xD

Log in to reply