Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[EO2.0]Player movement animations.


Boynaar44
 Share

Recommended Posts

Hi guys,

This tutorial will show you how to make a sprite-animation for different actions the player practices, like running, biking or even swimming. The tutorial is out of different parts, you will read them below.

@Content:

> Content
>
> 1\. Needed resources.
>
> 2\. The plan.
>
> 3\. Create a work space.
>
> 4\. New equipment-type == new item-type #1.
>
> 5\. New equipment-type == new item-type #2.
>
> 6\. Player <> NPC.
>
> 7\. Sprite action.
>
> 8\. Paperdoll action.
>
> 9\. Download.
>
> 10\. Questions and Answers.

@NeededResources:

> 1\. Needed resources.
>
> We are going to use those resources, so save them on a place and keep them ready for use.
>
> (I don't mind if anyone want's to use them for their own or for whatever else.)
>
> The sprites.
>
> You can choose if you want to use the sized or the unsized versions of the sprites in the tutorial.
>
> **[Picture 1]** [Sized female sprite](http://www.touchofdeathforums.com/community/index.php?/page/index.html/gallery.html/_/other/sprite-sized-female-1-r146).
>
> **[Picture 2]** [Sized male sprite](http://www.touchofdeathforums.com/community/index.php?/page/index.html/gallery.html/_/other/sprite-sized-male-1-r147).
>
> **[Picture 3]** [Unsized female sprite](http://www.touchofdeathforums.com/community/index.php?/page/index.html/gallery.html/_/other/sprite-unsized-female-1-r148).
>
> **[Picture 4]** [Unsized male sprite](http://www.touchofdeathforums.com/community/index.php?/page/index.html/gallery.html/_/other/sprite-unsized-male-1-r149).
>
> Character window.
>
> In this character window you see a new equipment slot, which will be the keyitem.
>
> **[Picture 5]** [Character window replacement](http://www.touchofdeathforums.com/community/index.php?/page/index.html/gallery.html/_/gui/character-window-r152).
>
> Items.
>
> A shoes and a bike, 2 items, 2 different speeds, 2 different sprite sheets.
>
> **[Picture 6]** [Shoes](http://www.touchofdeathforums.com/community/index.php?/page/index.html/gallery.html/_/item/item-shoes-r150).
>
> **[Picture 7]** [Bike](http://www.touchofdeathforums.com/community/index.php?/page/index.html/gallery.html/_/item/item-bike-r151).

@ThePlan:

> 2\. The plan.
>
> I will try to explain things carefully, since tutorials may be a good coding experience improvement for some starter coders.
>
> How I will do that? Is by placing a spoiler at each part to explain what we did there.
>
> Some constants, form editing in the frmEditor_Item and placing the needed resources in the client data folder will be done first.
>
> We add a new equipment slot for the bike and the shoes. This requires us to make the item types shoes and bike too.
>
> After this tutorial, players and npc's will be no longer the same. Since player characters can then perform more body actions.
>
> We will be then making the real work, the player animation.
>
> Then we make the paperdolls work proper with the new sprites.

@CreateAWorkSpace:

> 3\. Create a work space.
>
> Here we start the coding.
>
> But first **create** the folder "**players**" inside your "**\EO2.0\client\data files\graphics\**" folder.
>
> Place there any or all of of the pictures **[Picture 1]**, **[Picture 2]**, **[Picture 3]** or **[Picture 4]**.
>
> Go to your "**\EO2.0\client\data files\graphics\items\**" folder and place there the pictures **[Picture 6]** and **[Picture 7]**.
>
> Now replace your actual **chataracter.jpg** with the one on **[Picture 5]**.
>
>
> >! I will not go too deep into this.
> >! If you already have more equipments installed, that means you know how to install 1 more equipment slot.
> >! Well then install one more equipment slot and use "**ITEM_TYPE_KEYITEM**" as the constant for the new item type wich will be equipped on that slot.
> >! In your **equipments enumeration** use "**Keyitem**" for the new equipment slot enumeration.
> >! If you have done that, you can skip part 4 of this tutorial.
>
> Now in your **client** source, open **modConstants** and add the following constants at the **bottom**.
>
> (So it's Client -> modConstants -> bottom) *Lol Dora The Explorer.
>
> ```
>
> ' Keyitem type constants '
>
> Public Const KEYITEM_TYPE_SHOES As Byte = 0 ' This is the index for the shoes, to check if its equipped. '
>
> Public Const KEYITEM_TYPE_BIKE As Byte = 1 ' This is the index for the bike, to check if its equipped. '
>
> ' Player animation '
>
> Public Const SPRITE_TYPE_NPC As Byte = 1 ' This is to tell its an NPC sprite. '
>
> Public Const SPRITE_TYPE_PLAYER As Byte = 2 ' This is to tell its a player sprite. '
>
> Public Const SPRITE_FRAMES As Byte = 20 ' The count of the total frames of a sprite. '
>
> Public Const DIR_UP_LINE As Byte = 0 ' The index number of the UP sprite-line direction of the player sprite. '
>
> Public Const DIR_DOWN_LINE As Byte = 1 ' The index number of the DOWN sprite-line direction of the player sprite. '
>
> Public Const DIR_LEFT_LINE As Byte = 2 ' The index number of the LEFT sprite-line direction of the player sprite. '
>
> Public Const DIR_RIGHT_LINE As Byte = 3 ' The index number of the RIGHT sprite-line direction of the player sprite. '
>
> Public Const WALKING_STOP_FRAME_START As Byte = 0 ' The index number of the first WALKING stop sprite-frame. '
>
> Public Const WALKING_STOP_FRAME_END As Byte = 2 ' The index number of the second WALKING stop sprite-frame. '
>
> Public Const RUNNING_STOP_FRAME_START As Byte = 4 ' The index number of the first RUNNING stop sprite-frame. '
>
> Public Const RUNNING_STOP_FRAME_END As Byte = 6 ' The index number of the second RUNNING stop sprite-frame. '
>
> Public Const BIKING_STOP_FRAME_START As Byte = 8 ' The index number of the first BIKING stop sprite-frame. '
>
> Public Const BIKING_STOP_FRAME_END As Byte = 10 ' The index number of the second BIKING stop sprite-frame. '
>
> ```
>
> Now **change** the **values** of the following constants inside your **modConstants**.
>
> ```
>
> Public Const WALK_SPEED As Byte = 6
>
> Public Const RUN_SPEED As Byte = 7
>
> ```
>
> Now we go **Server** side, open your **modConstants** and add the following constants somewhere at the **bottom**.
>
> ```
>
> ' Keyitem type constants '
>
> Public Const KEYITEM_TYPE_SHOES As Byte = 0
>
> Public Const KEYITEM_TYPE_BIKE As Byte = 1
>
> ```
>
>
> >! We need constants to indicate to something in the code. So that's why we define the constants first, we are gonna use them during the progress.
> >! Explanation about the constants we added will be told later in the tutorial.
> >! For now we changed the walking and running speed, that's because the equipped item will be able to effect the movement speed.
> >! There is the "SPRITE_TYPE_NPC" and the "SPRITE_TYPE_PLAYER" we added as constants, that's because we need to say if it is an NPC or a player what will be blitted in the game, as I said NPC's and Players will no longer be the same.

@EquipmentItem1:

> 4\. New equipment-type == new item-type #1
>
> Now we will create a new equipment type.
>
> First we will add the constants we are gonna use in this part.
>
> **Client and server** side, open **modConstants** and **find** there the following comment.
>
> ```
>
> ' Item constants
>
> ```
>
> There will be a list under the comment with constants numbered with values from small to large.
>
> **Add** in the **client and server** to that list the following **constant** with the next large number as value, like in my case the list ends with "ITEM_TYPE_SPELL" wich has a value of "8". That's why in my case I have to change the "##" into 9, because the last large value for me is "8".
>
> ```
>
> Public Const ITEM_TYPE_KEYITEM As Byte = ## ' Change the ## to the next large number in the items constants list.
>
> ```
>
> Now in your **client**, still in **modConstants** find and **change** the values of the following **constants**.
>
> ```
>
> Public Const EqLeft As Long = 4
>
> Public Const EqOffsetX As Long = 6
>
> Public Const EqColumns As Long = 5
>
> ```
>
> Now the enumerations.
>
> In both the client and server, open modEnumerations and find the following comment.
>
> ```
>
> ' Equipment used by Players
>
> ```
>
> In the enumeration list "**Equipment**" in the **client and server**, add the following enumeration on a new line right **after** "**Shield**".
>
> ```
>
> Keyitem
>
> ```
>
> In your **Server find** the following **3 subs**, they should be right after each other.
>
> ```
>
> SendWornEquipment
>
> SendMapEquipment
>
> SendMapEquipmentTo
>
> ```
>
> Add to the subs "**SendWornEquipment**" and "**SendMapEquipment**" the following code-line, add to both of them the same code.
>
> Add it under "**Buffer.WriteLong GetPlayerEquipment(index, Shield)**".
>
> ```
>
> Buffer.WriteLong GetPlayerEquipment(index, Keyitem)
>
> ```
>
> Now do the **same** with the sub "**SendMapEquipmentTo**", but note there is "index" replaced with "**PlayerNum**", do it also under the shield.
>
> ```
>
> Buffer.WriteLong GetPlayerEquipment(PlayerNum, Keyitem)
>
> ```
>
> Now the server sends the equipment slot value of the "Keyitem", we need to handle it in the client.
>
> In your client **search** for the handlers of the "**SPlayerWornEq**" and the "**SMapWornEq**" messages.
>
> You should do that by looking into your **modHandleData** on the sub **InitMessages**, there you should see where the messages get handled.
>
> In the handler of the "**SPlayerWornEq**" add this code-line, just the same way as you did server side.
>
> ```
>
> Call SetPlayerEquipment(MyIndex, Buffer.ReadLong, Keyitem)
>
> ```
>
> Now on the handler of "**SMapWornEq**", do the same, but note you have to change "**MyIndex**" there…
>
> ```
>
> Call SetPlayerEquipment(playerNum, Buffer.ReadLong, Keyitem)
>
> ```
>
> Now we are almost done with our new equipment slot.
>
> We just need something to be able to equip it on our new slot, a new item type.
>
> This will require us to do some form editing.
>
> Open **frmEditor_Item** and search for the combobox **cmbType**, select it and in the properties window search for "**List**".
>
> **Add at the end of that list** the following item.
>
> ```
>
> Keyitem
>
> ```
>
> Well, the Keyitem equipment-type we just added doesn't have any paperdoll, but we still need to put it in the paperdoll ordering to avoid errors.
>
> So find the sub called "**Main**" and at the **bottom** of that sub, there is a list for ordering the paperdolls.
>
> Add to that list a **new ordering place** for the "**Keyitem**".
>
> ```
>
> PaperdollOrder(5) = Equipment.Keyitem
>
> ```
>
> Now we just need to make sure we can equip the new itemtype "Keyitem" on the Keyitem equipment slot.
>
> We equip things by double-clicking the picInventory in frmMain.
>
> If you take a look at "picInventory_DblClick" sub, you will see if we are not in trade, bank or shop, it will call the sub "SendUseItem".
>
> If you then take a look at that sub, you will see the message what will be sent to the server is "CUseItem".
>
> Now go to the **server** and find the handler sub of "**CUseItem**", just like you did before on client side with SPlayerWornEq and SMapWornEq.
>
> You will finally end up on the sub "**UseItem**".
>
> Now there is a case-statement for each itemtype.
>
> So we just need to add a new case for the "Keyitem" itemtype.
>
> There are some if-statements in some cases to see if the user of the item meets the requirements.
>
> We need those if-statements also.
>
> Add a **new case** into the case-statement with the if-statements we need.
>
> Then add the actual code to equip the Keyitem.
>
> You will then end up having added this case to the case-statement.
>
> ```
>
> Case ITEM_TYPE_KEYITEM
>
> ' stat requirements '
>
> For i = 1 To Stats.Stat_Count - 1
>
> If GetPlayerRawStat(index, i) < Item(itemnum).Stat_Req(i) Then
>
> PlayerMsg index, "You do not meet the stat requirements to equip this item.", BrightRed
>
> Exit Sub
>
> End If
>
> Next
>
> ' level requirement '
>
> If GetPlayerLevel(index) < Item(itemnum).LevelReq Then
>
> PlayerMsg index, "You do not meet the level requirement to equip this item.", BrightRed
>
> Exit Sub
>
> End If
>
> ' class requirement '
>
> If Item(itemnum).ClassReq > 0 Then
>
> If Not GetPlayerClass(index) = Item(itemnum).ClassReq Then
>
> PlayerMsg index, "You do not meet the class requirement to equip this item.", BrightRed
>
> Exit Sub
>
> End If
>
> End If
>
> ' access requirement '
>
> If Not GetPlayerAccess(index) >= Item(itemnum).AccessReq Then
>
> PlayerMsg index, "You do not meet the access requirement to equip this item.", BrightRed
>
> Exit Sub
>
> End If
>
> If GetPlayerEquipment(index, Keyitem) > 0 Then
>
> tempItem = GetPlayerEquipment(index, Keyitem)
>
> End If
>
> SetPlayerEquipment index, itemnum, Keyitem
>
> PlayerMsg index, "You equip " & CheckGrammar(Item(itemnum).Name), BrightGreen
>
> TakeInvItem index, itemnum, 1
>
> If tempItem > 0 Then
>
> GiveInvItem index, tempItem, 0 ' give back the stored item '
>
> tempItem = 0
>
> End If
>
> Call SendWornEquipment(index)
>
> Call SendMapEquipment(index)
>
> ' send the sound '
>
> SendPlayerSound index, GetPlayerX(index), GetPlayerY(index), SoundEntity.seItem, itemnum
>
> ```
>
> We are done with part 1 of adding a new equipment.
>
> And guess what, we are even completely done server side with this tutorial.
>
> Now we will be only working client side.
>
> Here is some explanation about somethings we have met during this part.
>
>
> >! Changing the values of EqLeft, EqOffsetX and [background=rgb(248, 248, 248)]EqColumns[/background] in modConstants was necessary since we had a new character.jpg.
> >! The EqLeft is to indicate where the first equipment slot is from the left to the right.
> >! The EqOffsetX is to say what is the distance in the width between each slot.
> >! And the [background=rgb(248, 248, 248)]EqColumns[/background] is to say how many slots we have.
> >! In the cmbType in frmEditor_Item we had to put "Keyitem" at the end of the list.
> >! The items of a combobox list are index based, what means the first item has an index of "0", the next is "1" and so on.
> >! In modConstants we have put the value for [background=rgb(248, 248, 248)]ITEM_TYPE_KEYITEM[/background] as the largest number of the item types constants.
> >! The [background=rgb(248, 248, 248)]ITEM_TYPE_KEYITEM[/background] must have the same value as the index value of "Keyitem" inside the cmbType.
> >! We have also put a new ordering for the paperdoll of the keyitem and actually it doesn't support paperdolls.
> >! I said already we did that to avoid errors.
> >! The standard value of each PaperdollOrder index is "0", since it's a long value.
> >! There is no equipment index "0", you can see that in the equipments enumeration, it starts with "1".
> >! So when it will try to get the value of the equipment type "0", we will get an error because there is not equipment type "0".
> >! We can also avoid this error by ignoring indexes under 1 or above the max equipments, but sometimes we need errors.
>
> If you compile and test now, you will see you are now able to equip your new item type.

@EquipmentItem2:

> 5\. New equipment-type == new item-type #2
>
> So now we have a new equipment type and a new item type to equip.
>
> As I mentioned before, we have no more server side coding, everything will be client side now.
>
> In this part we are gonna make sure that the equipped keyitem can effect the player speed.
>
> But first we are gonna divide the keyitem type into 2 another types, the shoes and the bike.
>
> We need some form editing in **frmEditor_Item**.
>
> Make sure you add the following objects into your frmEditor_Item
>
> Frame inside frmEditor_Item : **fraKeyitem**
>
> Combobox inside fraKeyitem : **cmbKeyitem** –> Change property value of "**Style**" to "**2 - Dropdown List**".
>
> Frame inside fraKeyitem : **fraKeyitemFrame** –> Change property value of "**index**" to "**0**"
>
> HScrollBar inside fraKeyitemFrame(0) : **scrlKeyitemSpeed** –> Change property value of "**Index**" to "**0**", "**Min**" to "**1**" and the value of "**Max**" to "**100**".
>
> Frame inside fraKeyitem : **fraKeyitemFrame** –> Change property value of "**index**" to "**1**"
>
> HScrollBar inside fraKeyitemFrame(1) : **scrlKeyitemSpeed** –> Change property value of "**Index**" to "**1**", "**Min**" to "**1**" and the value of "**Max**" to "**100**".
>
> If you did it right, you would end up having this inside the spoiler.
>
> >! ![](http://img841.imageshack.us/img841/2849/examplet.jpg)
>
> Now change the property value of "**Visible**" for the following objects to "**False**".
>
> fraKeyitem, fraKeyitemFrame(0) and fraKeyitemFrame(1).
>
> Add the following items to the **cmbKeyitem** **List** property.
>
> Make sure they are ordered the same.
>
> ```
>
> Shoes
>
> Bike
>
> ```
>
> We have done form editing, now let's go back to coding.
>
> Double-click the **cmbType** inside your **frmEditor_Item**.
>
> You will come at the sub "**cmbType_Click**".
>
> Add there inbetween all the if-statements the following if-statement.
>
> ```
>
> If (cmbType.ListIndex = ITEM_TYPE_KEYITEM) Then
>
> fraKeyitem.Visible = True
>
> Else
>
> fraKeyitem.Visible = False
>
> End If
>
> ```
>
> We need this little sub to **hide** the **fraKeyItemFrame(0)** and **fraKeyItemFrame(1)**.
>
> Add it anywhere to your project, it would suit placing at the **bottom** in your **modGeneral**.
>
> Note if you place it in any other place, edit the error handler.
>
> ```
>
> Public Sub HideKeyitemFrames() ' Player animation '
>
> Dim i As Integer
>
> ' If debug mode, handle error then exit out '
>
> If Options.Debug = 1 Then On Error GoTo errorhandler ' I place an error handler instead of ''On Error Resume Next'', since this is really an error catcher sub. '
>
> ' On Error Resume Next <-- I have commented out this line, because it is a very big mistake. It is necessary to have all indexes ordered without skipping any index. '
>
> For i = 0 To frmEditor_Item.fraKeyitemFrame.UBound
>
> frmEditor_Item.fraKeyitemFrame(i).Visible = False
>
> Next
>
> ' Error handler '
>
> Exit Sub
>
> errorhandler:
>
> HandleError "HideKeyitemFrames", "modGeneral", Err.Number, Err.Description, Err.Source, Err.HelpContext
>
> Err.Clear
>
> Exit Sub
>
> End Sub
>
> ```
>
> In your **frmEditor_Item** source, browse to the **cmbKeyitem Click** handler.
>
> That will be the sub "**cmbKeyitem_Click**".
>
> Make there an error handler if you want, just like all the other subs.
>
> Place the following code in cmbKeyitem_Click.
>
> ```
>
> If EditorIndex = 0 Or EditorIndex > MAX_ITEMS Then Exit Sub
>
> HideKeyitemFrames
>
> fraKeyitemFrame(cmbKeyitem.ListIndex).Visible = True
>
> Item(EditorIndex).Data1 = cmbKeyitem.ListIndex ' Since the item type "Data1" is not used, we will be using it to store what keyitem type we have. '
>
> ```
>
> Now double-click any of the **scrlKeyitemSpeed** and make sure you are on the sub "**scrlKeyitemSpeed_Change(Index As Integer)**".
>
> Make there an error handler if you want.
>
> Add the following then to the sub "scrlKeyitemSpeed_Change".
>
> ```
>
> If EditorIndex = 0 Or EditorIndex > MAX_ITEMS Then Exit Sub
>
> fraKeyitemFrame(Index).Caption = "Speed: " & scrlKeyitemSpeed(Index).Value
>
> Item(EditorIndex).Data2 = scrlKeyitemSpeed(Index).Value ' Since the item type "Data2" is not used, we will be using it to store the movement speed the item gives. '
>
> ```
>
> Find the sub "**ItemEditorInit**", inside the **with-statement** place the following inbetween all the other **if-statements**.
>
> ```
>
> If (frmEditor_Item.cmbType.ListIndex = ITEM_TYPE_KEYITEM) Then
>
> frmEditor_Item.fraKeyitem.Visible = True
>
> frmEditor_Item.cmbKeyitem.ListIndex = .Data1
>
> HideKeyitemFrames
>
> frmEditor_Item.fraKeyitemFrame(frmEditor_Item.cmbKeyitem.ListIndex).Visible = True
>
> frmEditor_Item.scrlKeyitemSpeed(frmEditor_Item.cmbKeyitem.ListIndex).Value = .Data2
>
> Else
>
> frmEditor_Item.fraKeyitem.Visible = False
>
> End If
>
> ```
>
> You can now create a shoes and a bike. With values of speed.
>
> Now we just need to make the speed values actually change the player movement speed.
>
> This will be only working with the sub "**ProcessMovement**".
>
> **Dim** "**KeyItemNum**" as a **Long** value in the sub "ProcessMovement".
>
> ```
>
> Dim KeyItemNum As Long
>
> ```
>
> **Add** this code line **under** "**If Options.Debug = 1 Then On Error GoTo errorhandler**".
>
> ```
>
> If GetPlayerEquipment(Index, Keyitem) > 0 And GetPlayerEquipment(Index, Keyitem) <= MAX_ITEMS Then KeyItemNum = GetPlayerEquipment(Index, Keyitem) Else KeyItemNum = 0
>
> ```
>
> **Remove** the following **case-statement** from the sub "**ProcessMovement**".
>
> ```
>
> Select Case Player(Index).Moving
>
> Case MOVING_WALKING: MovementSpeed = ((ElapsedTime / 1000) * (RUN_SPEED * SIZE_X))
>
> Case MOVING_RUNNING: MovementSpeed = ((ElapsedTime / 1000) * (WALK_SPEED * SIZE_X))
>
> Case Else: Exit Sub
>
> End Select
>
> ```
>
> Now **instead** of it **place** this case-statement.
>
> ```
>
> Select Case Player(Index).Moving
>
> Case MOVING_WALKING ' When walking '
>
> If KeyItemNum = 0 Then
>
> MovementSpeed = ((ElapsedTime / 1000) * (WALK_SPEED * SIZE_X)) ' If no keyitem equipped, move with normal speed when walking. '
>
> Else
>
> Select Case Item(KeyItemNum).Data1
>
> Case KEYITEM_TYPE_SHOES
>
> MovementSpeed = ((ElapsedTime / 1000) * (WALK_SPEED * SIZE_X)) ' If shoes, move normally when walking. '
>
> Case KEYITEM_TYPE_BIKE
>
> MovementSpeed = ((ElapsedTime / 1000) * (Item(KeyItemNum).Data2 * SIZE_X)) 'If on bike, move with the bike speed even if walking. '
>
> Case Else
>
> MovementSpeed = ((ElapsedTime / 1000) * (WALK_SPEED * SIZE_X)) ' Anything else, move with normal speed wehn walking. '
>
> End Select
>
> End If
>
> Case MOVING_RUNNING ' When running '
>
> If KeyItemNum = 0 Then
>
> MovementSpeed = ((ElapsedTime / 1000) * (RUN_SPEED * SIZE_X)) ' If no keyitem equipped, move with normal running speed when running. '
>
> Else
>
> Select Case Item(KeyItemNum).Data1
>
> Case KEYITEM_TYPE_SHOES
>
> MovementSpeed = ((ElapsedTime / 1000) * (Item(KeyItemNum).Data2 * SIZE_X)) ' If shoes, move with shoes speed when running. '
>
> Case KEYITEM_TYPE_BIKE
>
> MovementSpeed = ((ElapsedTime / 1000) * (Item(KeyItemNum).Data2 * SIZE_X)) 'If on bike, move with the bike speed even when running. '
>
> Case Else
>
> MovementSpeed = ((ElapsedTime / 1000) * (RUN_SPEED * SIZE_X)) ' Anything else, move with normal speed wehn running. '
>
> End Select
>
> End If
>
> Case Else: Exit Sub
>
> End Select
>
> ```
>
> Well done, now if you compile it and test it you will see the shoes makes you run with it's speed when holding shift. The bike makes you have high speed with or without shift being down.

@PlayersNPCNotSame:

> 6\. Player <> NPC.
>
> You know the idea of humans and animals are the same?
>
> Well I personally think they are not.
>
> This will now be the case on your project, players no longer are like NPC's.
>
> Find the following comment inside your **modDirectDraw7**.
>
> ```
>
> ' gfx buffers
>
> ```
>
> In the list under it, add the following line.
>
> ```
>
> Public DDS_PlayerSprites() As DirectDrawSurface7
>
> ```
>
> Now scroll down a little to the list of the following comment.
>
> ```
>
> ' descriptions
>
> ```
>
> Add to that list the following line.
>
> ```
>
> Public DDSD_PlayerSprites() As DDSURFACEDESC2
>
> ```
>
> Now scroll one more town down to the list of the timers.
>
> Add this line there.
>
> ```
>
> Public PlayerSpriteTimer() As Long
>
> ```
>
> And in the list of "Number of graphic files" add the following line.
>
> ```
>
> Public NumPlayerSprites As Long
>
> ```
>
> Now we have created the workspace to seperate players from NPC's, add the following sub at the **bottom** of your **modDatabase**.
>
> ```
>
> Public Sub CheckPlayerSprites()
>
> Dim i As Long
>
> ' If debug mode, handle error then exit out '
>
> If Options.Debug = 1 Then On Error GoTo errorhandler
>
> i = 1
>
> While FileExist(GFX_PATH & "players\" & i & GFX_EXT)
>
> NumPlayerSprites = NumPlayerSprites + 1
>
> i = i + 1
>
> Wend
>
> If NumPlayerSprites = 0 Then Exit Sub
>
> ReDim DDS_PlayerSprites(1 To NumPlayerSprites)
>
> ReDim DDSD_PlayerSprites(1 To NumPlayerSprites)
>
> ReDim PlayerSpriteTimer(1 To NumPlayerSprites)
>
> ' Error handler '
>
> Exit Sub
>
> errorhandler:
>
> HandleError "CheckPlayerSprites", "modDatabase", Err.Number, Err.Description, Err.Source, Err.HelpContext
>
> Err.Clear
>
> Exit Sub
>
> End Sub
>
> ```
>
> All right, we have the sub, now the caller.
>
> Find the following comment inside your **modGeneral**.
>
> ```
>
> ' DX7 Master Object is already created, early binding
>
> ```
>
> Add to that list the following line.
>
> ```
>
> Call CheckPlayerSprites
>
> ```
>
> Now find the following comment inside your **modDirectDraw7**.
>
> ```
>
> ' Unload DirectDraw
>
> ```
>
> Add there in the list the following code.
>
> ```
>
> For i = 1 To NumPlayerSprites
>
> Set DDS_PlayerSprites(i) = Nothing
>
> ZeroMemory ByVal VarPtr(DDSD_PlayerSprites(i)), LenB(DDSD_PlayerSprites(i))
>
> Next
>
> ```
>
> Find the following comment inside your **modDirectDraw7**
>
> ```
>
> ' Y-based render. Renders Players, Npcs and Resources based on Y-axis.
>
> ```
>
> **Remove** the following code from there.
>
> ```
>
> If NumCharacters > 0 Then
>
> ' Players '
>
> For i = 1 To Player_HighIndex
>
> If IsPlaying(i) And GetPlayerMap(i) = GetPlayerMap(MyIndex) Then
>
> If Player(i).y = y Then
>
> Call BltPlayer(i)
>
> End If
>
> End If
>
> Next
>
> ' Npcs '
>
> For i = 1 To Npc_HighIndex
>
> If MapNpc(i).y = y Then
>
> Call BltNpc(i)
>
> End If
>
> Next
>
> End If
>
> ```
>
> Instead of it put the following code.
>
> ```
>
> If NumCharacters > 0 Then
>
> ' Npcs '
>
> For i = 1 To Npc_HighIndex
>
> If MapNpc(i).y = y Then
>
> Call BltNpc(i)
>
> End If
>
> Next
>
> End If
>
> If NumPlayerSprites > 0 Then
>
> ' Players '
>
> For i = 1 To Player_HighIndex
>
> If IsPlaying(i) And GetPlayerMap(i) = GetPlayerMap(MyIndex) Then
>
> If Player(i).y = y Then
>
> Call BltPlayer(i)
>
> End If
>
> End If
>
> Next
>
> End If
>
> ```
>
> Now find the following comment inside your **modGameLogic** and add the following code between the other codes like it.
>
> ```
>
> If NumPlayerSprites > 0 Then
>
> For i = 1 To NumPlayerSprites 'Check to unload surfaces '
>
> If PlayerSpriteTimer(i) > 0 Then 'Only update surfaces in use '
>
> If PlayerSpriteTimer(i) < Tick Then 'Unload the surface '
>
> Call ZeroMemory(ByVal VarPtr(DDSD_PlayerSprites(i)), LenB(DDSD_PlayerSprites(i)))
>
> Set DDS_PlayerSprites(i) = Nothing
>
> PlayerSpriteTimer(i) = 0
>
> End If
>
> End If
>
> Next
>
> End If
>
> ```
>
> Now find the sub **BltSprite** and add to it an optional variable "SPRITE_TYPE" as a byte which holds a standard value "SPRITE_TYPE_NPC".
>
> Just change the head of the sub BltSprite to this one.
>
> ```
>
> Private Sub BltSprite(ByVal Sprite As Long, ByVal x2 As Long, y2 As Long, rec As DxVBLib.RECT, Optional ByVal SPRITE_TYPE As Byte = SPRITE_TYPE_NPC)
>
> ```
>
> Now still in the same sub **BltSprite**, remove the following line.
>
> ```
>
> If Sprite < 1 Or Sprite > NumCharacters Then Exit Sub
>
> ```
>
> Now place instead of it the following code.
>
> ```
>
> If SPRITE_TYPE = SPRITE_TYPE_NPC Then
>
> If Sprite < 1 Or Sprite > NumCharacters Then Exit Sub
>
> ElseIf SPRITE_TYPE = SPRITE_TYPE_PLAYER Then
>
> If Sprite < 1 Or Sprite > NumPlayerSprites Then Exit Sub
>
> End If
>
> ```
>
> Now at the end of the sub **BltSprite**, find and remove the following code.
>
> ```
>
> Call Engine_BltFast(x, y, DDS_Character(Sprite), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
>
> ```
>
> Place instead of it, this code.
>
> ```
>
> If SPRITE_TYPE = SPRITE_TYPE_NPC Then
>
> Call Engine_BltFast(x, y, DDS_Character(Sprite), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
>
> ElseIf SPRITE_TYPE = SPRITE_TYPE_PLAYER Then
>
> Call Engine_BltFast(x, y, DDS_PlayerSprites(Sprite), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
>
> End If
>
> ```
>
> Now find the sub **NewCharacterBltSprite**, replace the entire sub with this.
>
> ```
>
> Public Sub NewCharacterBltSprite()
>
> Dim Sprite As Long
>
> Dim sRECT As DxVBLib.RECT
>
> Dim dRECT As DxVBLib.RECT
>
> Dim width As Long, height As Long
>
> ' If debug mode, handle error then exit out
>
> If Options.Debug = 1 Then On Error GoTo errorhandler
>
> If frmMenu.cmbClass.ListIndex = -1 Then Exit Sub
>
> If frmMenu.optMale.Value = True Then
>
> Sprite = Class(frmMenu.cmbClass.ListIndex + 1).MaleSprite(newCharSprite)
>
> Else
>
> Sprite = Class(frmMenu.cmbClass.ListIndex + 1).FemaleSprite(newCharSprite)
>
> End If
>
> If Sprite < 1 Or Sprite > NumPlayerSprites Then
>
> frmMenu.picSprite.Cls
>
> Exit Sub
>
> End If
>
> PlayerSpriteTimer(Sprite) = GetTickCount + SurfaceTimerMax
>
> If DDS_PlayerSprites(Sprite) Is Nothing Then
>
> Call InitDDSurf("players\" & Sprite, DDSD_PlayerSprites(Sprite), DDS_PlayerSprites(Sprite))
>
> End If
>
> width = DDSD_PlayerSprites(Sprite).lWidth / SPRITE_FRAMES
>
> height = DDSD_PlayerSprites(Sprite).lHeight / 4
>
> frmMenu.picSprite.width = width
>
> frmMenu.picSprite.height = height
>
> sRECT.top = DIR_DOWN_LINE * height
>
> sRECT.Bottom = sRECT.top + height
>
> sRECT.Left = 0
>
> sRECT.Right = sRECT.Left + width
>
> dRECT.top = 0
>
> dRECT.Bottom = height
>
> dRECT.Left = 0
>
> dRECT.Right = width
>
> Call Engine_BltToDC(DDS_PlayerSprites(Sprite), sRECT, dRECT, frmMenu.picSprite)
>
> ' Error handler
>
> Exit Sub
>
> errorhandler:
>
> HandleError "NewCharacterBltSprite", "modDirectDraw7", Err.Number, Err.Description, Err.Source, Err.HelpContext
>
> Err.Clear
>
> Exit Sub
>
> End Sub
>
> ```
>
> Now find the sub **DrawPlayerName** and **remove** the following if-statement.
>
> ```
>
> If GetPlayerSprite(Index) < 1 Or GetPlayerSprite(Index) > NumCharacters Then
>
> TextY = ConvertMapY(GetPlayerY(Index) * PIC_Y) + Player(Index).YOffset - 16
>
> Else
>
> ' Determine location for text
>
> TextY = ConvertMapY(GetPlayerY(Index) * PIC_Y) + Player(Index).YOffset - (DDSD_Character(GetPlayerSprite(Index)).lHeight / 4) + 16
>
> End If
>
> ```
>
> Now place instead of it the following one.
>
> ```
>
> If GetPlayerSprite(Index) < 1 Or GetPlayerSprite(Index) > NumPlayerSprites Then ' Player animation
>
> TextY = ConvertMapY(GetPlayerY(Index) * PIC_Y) + Player(Index).YOffset - 16
>
> Else
>
> ' Determine location for text
>
> TextY = ConvertMapY(GetPlayerY(Index) * PIC_Y) + Player(Index).YOffset - (DDSD_PlayerSprites(GetPlayerSprite(Index)).lHeight / 4) + 16 ' Player animation
>
> End If
>
> ```
>
> Now find the sub **BltPlayer**, inside that sub you are gonna find the following variables and change them with the variables next to them.
>
> **NumCharacters** –-> **NumPlayerSprites**
>
> **DDS_Character** –-> **DDS_PlayerSprites**
>
> **CharacterTimer** –-> **PlayerSpriteTimer**
>
> **DDSD_Character** –-> **DDSD_PlayerSprites**
>
> Now find the following code inside the **BltPlayer** sub and remove it.
>
> ```
>
> ' render the actual sprite
>
> Call BltSprite(Sprite, x, y, rec)
>
> ```
>
> Instead of it, place the following.
>
> ```
>
> ' render the actual sprite
>
> Call BltSprite(Sprite, x, y, rec, SPRITE_TYPE_PLAYER)
>
> ```
>
> I was in a hurry making this, I hope I didn't forget anything.
>
> If I didn't forget anything, now your game should load the player sprites from the "players" folder.
>
> But it still reads it as a 4 framed sprite, so it will look pretty weird with the resources I gave you to place there.

@SpriteAction:

> 7\. Sprite action.
>
> Here we will be doing the real thing.
>
> Find and **remove** the following from the sub **DrawPlayerName**.
>
> ```
>
> If GetPlayerSprite(Index) < 1 Or GetPlayerSprite(Index) > NumCharacters Then
>
> TextY = ConvertMapY(GetPlayerY(Index) * PIC_Y) + Player(Index).YOffset - 16
>
> Else
>
> ' Determine location for text
>
> TextY = ConvertMapY(GetPlayerY(Index) * PIC_Y) + Player(Index).YOffset - (DDSD_Character(GetPlayerSprite(Index)).lHeight / 4) + 16
>
> End If
>
> ```
>
> And place instead of it the following.
>
> ```
>
> If GetPlayerSprite(Index) < 1 Or GetPlayerSprite(Index) > NumPlayerSprites Then ' Player animation
>
> TextY = ConvertMapY(GetPlayerY(Index) * PIC_Y) + Player(Index).YOffset - 16
>
> Else
>
> ' Determine location for text
>
> TextY = ConvertMapY(GetPlayerY(Index) * PIC_Y) + Player(Index).YOffset - (DDSD_PlayerSprites(GetPlayerSprite(Index)).lHeight / 4) + 16 ' Player animation
>
> End If
>
> ```
>
> Now find the sub [b/BltPlayer[/b] and dim the following variables there.
>
> ```
>
> Dim KeyItemNum As Long
>
> Dim AnimPlus As Long
>
> ```
>
> Now find and **remove** the following from **BltPlayer**.
>
> ```
>
> ' Reset frame
>
> If Player(Index).Step = 3 Then
>
> Anim = 0
>
> ElseIf Player(Index).Step = 1 Then
>
> Anim = 2
>
> End If
>
> ```
>
> Place instead of it, the following.
>
> ```
>
> KeyItemNum = GetPlayerEquipment(Index, Keyitem) ' Player animation
>
> If Player(Index).Moving = MOVING_RUNNING Then ' Player animation
>
> If KeyItemNum > 0 And KeyItemNum <= MAX_ITEMS Then
>
> If Item(KeyItemNum).Type = ITEM_TYPE_KEYITEM Then
>
> Select Case Item(KeyItemNum).Data1
>
> Case KEYITEM_TYPE_SHOES
>
> ' Reset frame
>
> If Player(Index).Step = 3 Then
>
> Anim = RUNNING_STOP_FRAME_START
>
> ElseIf Player(Index).Step = 1 Then
>
> Anim = RUNNING_STOP_FRAME_END
>
> End If
>
> Case KEYITEM_TYPE_BIKE
>
> If Player(Index).Step = 3 Then
>
> Anim = BIKING_STOP_FRAME_START
>
> ElseIf Player(Index).Step = 1 Then
>
> Anim = BIKING_STOP_FRAME_END
>
> End If
>
> End Select
>
> End If
>
> Else ' If doesn't equip any keyitem, walk normally.
>
> If Player(Index).Step = 3 Then
>
> Anim = WALKING_STOP_FRAME_START
>
> ElseIf Player(Index).Step = 1 Then
>
> Anim = WALKING_STOP_FRAME_END
>
> End If
>
> End If
>
> Else ' If not running then we use the walking animation
>
> If KeyItemNum > 0 And KeyItemNum <= MAX_ITEMS Then
>
> If Item(KeyItemNum).Type = ITEM_TYPE_KEYITEM Then
>
> Select Case Item(KeyItemNum).Data1
>
> Case KEYITEM_TYPE_SHOES
>
> ' Reset frame
>
> If Player(Index).Step = 3 Then
>
> Anim = WALKING_STOP_FRAME_START
>
> ElseIf Player(Index).Step = 1 Then
>
> Anim = WALKING_STOP_FRAME_END
>
> End If
>
> Case KEYITEM_TYPE_BIKE
>
> If Player(Index).Step = 3 Then
>
> Anim = BIKING_STOP_FRAME_START
>
> ElseIf Player(Index).Step = 1 Then
>
> Anim = BIKING_STOP_FRAME_END
>
> End If
>
> End Select
>
> End If
>
> Else
>
> If Player(Index).Step = 3 Then
>
> Anim = WALKING_STOP_FRAME_START
>
> ElseIf Player(Index).Step = 1 Then
>
> Anim = WALKING_STOP_FRAME_END
>
> End If
>
> End If
>
> End If
>
> ```
>
> Now find and **remove** the following from the sub **BltPlayer**.
>
> ```
>
> ' Check for attacking animation
>
> If Player(Index).AttackTimer + (attackspeed / 2) > GetTickCount Then
>
> If Player(Index).Attacking = 1 Then
>
> Anim = 3
>
> End If
>
> Else
>
> ' If not attacking, walk normally
>
> Select Case GetPlayerDir(Index)
>
> Case DIR_UP
>
> If (Player(Index).YOffset > 8) Then Anim = Player(Index).Step
>
> Case DIR_DOWN
>
> If (Player(Index).YOffset < -8) Then Anim = Player(Index).Step
>
> Case DIR_LEFT
>
> If (Player(Index).XOffset > 8) Then Anim = Player(Index).Step
>
> Case DIR_RIGHT
>
> If (Player(Index).XOffset < -8) Then Anim = Player(Index).Step
>
> End Select
>
> End If
>
> ```
>
> Place instead of it, the following.
>
> ```
>
> ' Check for attacking animation
>
> If Player(Index).AttackTimer + (attackspeed / 2) > GetTickCount Then
>
> If Player(Index).Attacking = 1 Then
>
> Anim = Anim + 1
>
> End If
>
> Else
>
> ' If not attacking, walk normally
>
> If KeyItemNum > 0 And KeyItemNum <= MAX_ITEMS Then
>
> If Item(KeyItemNum).Type = ITEM_TYPE_KEYITEM Then
>
> Select Case Item(KeyItemNum).Data1
>
> Case KEYITEM_TYPE_SHOES
>
> If Player(Index).Moving = MOVING_RUNNING Then
>
> AnimPlus = RUNNING_STOP_FRAME_START
>
> ElseIf Player(Index).Moving = MOVING_WALKING Then
>
> AnimPlus = WALKING_STOP_FRAME_START
>
> End If
>
> Case KEYITEM_TYPE_BIKE
>
> If Player(Index).Moving = MOVING_RUNNING Then
>
> AnimPlus = BIKING_STOP_FRAME_START
>
> ElseIf Player(Index).Moving = MOVING_WALKING Then
>
> AnimPlus = BIKING_STOP_FRAME_START
>
> End If
>
> End Select
>
> End If
>
> End If
>
> Select Case GetPlayerDir(Index)
>
> Case DIR_UP
>
> If (Player(Index).YOffset > 8) Then Anim = Player(Index).Step + AnimPlus
>
> Case DIR_DOWN
>
> If (Player(Index).YOffset < -8) Then Anim = Player(Index).Step + AnimPlus
>
> Case DIR_LEFT
>
> If (Player(Index).XOffset > 8) Then Anim = Player(Index).Step + AnimPlus
>
> Case DIR_RIGHT
>
> If (Player(Index).XOffset < -8) Then Anim = Player(Index).Step + AnimPlus
>
> End Select
>
> End If
>
> ```
>
> Now find and **remove** the following from the sub **BltPlayer**.
>
> ```
>
> ' Set the left
>
> Select Case GetPlayerDir(Index)
>
> Case DIR_UP
>
> spritetop = 3
>
> Case DIR_RIGHT
>
> spritetop = 2
>
> Case DIR_DOWN
>
> spritetop = 0
>
> Case DIR_LEFT
>
> spritetop = 1
>
> End Select
>
> ```
>
> Place instead of it, the following.
>
> ```
>
> ' Set the left
>
> Select Case GetPlayerDir(Index)
>
> Case DIR_UP
>
> spritetop = DIR_UP_LINE
>
> Case DIR_RIGHT
>
> spritetop = DIR_RIGHT_LINE
>
> Case DIR_DOWN
>
> spritetop = DIR_DOWN_LINE
>
> Case DIR_LEFT
>
> spritetop = DIR_LEFT_LINE
>
> End Select
>
> ```
>
> Now find and **remove** the following from the sub **BltPlayer**.
>
> ```
>
> With rec
>
> .top = spritetop * (DDSD_PlayerSprites(Sprite).lHeight / 4)
>
> .Bottom = .top + (DDSD_PlayerSprites(Sprite).lHeight / 4)
>
> .Left = Anim * (DDSD_PlayerSprites(Sprite).lWidth / 4)
>
> .Right = .Left + (DDSD_PlayerSprites(Sprite).lWidth / 4)
>
> End With
>
> ```
>
> And place instead of it, the following.
>
> ```
>
> With rec
>
> .top = spritetop * (DDSD_PlayerSprites(Sprite).lHeight / 4)
>
> .Bottom = .top + (DDSD_PlayerSprites(Sprite).lHeight / 4)
>
> .Left = Anim * (DDSD_PlayerSprites(Sprite).lWidth / SPRITE_FRAMES)
>
> .Right = .Left + (DDSD_PlayerSprites(Sprite).lWidth / SPRITE_FRAMES)
>
> End With
>
> ```
>
> Now find and **remove** the following from the sub **BltPlayer**.
>
> ```
>
> ' Calculate the X
>
> x = GetPlayerX(Index) * PIC_X + Player(Index).XOffset - ((DDSD_PlayerSprites(Sprite).lWidth / 4 - 32) / 2)
>
> ```
>
> Place instead of it, the following.
>
> ```
>
> ' Calculate the X
>
> x = GetPlayerX(Index) * PIC_X + Player(Index).XOffset - ((DDSD_PlayerSprites(Sprite).lWidth / SPRITE_FRAMES - 32) / 2)
>
> ```
>
> Now find and **remove** the following from the sub **BltPlayer**.
>
> ```
>
> ' Is the player's height more than 32..?
>
> If (DDSD_PlayerSprites(Sprite).lHeight) > 32 Then
>
> ' Create a 32 pixel offset for larger sprites
>
> y = GetPlayerY(Index) * PIC_Y + Player(Index).YOffset - ((DDSD_PlayerSprites(Sprite).lHeight / 4) - 32)
>
> Else
>
> ' Proceed as normal
>
> y = GetPlayerY(Index) * PIC_Y + Player(Index).YOffset
>
> End If
>
> ```
>
> Place instead of it, the following.
>
> ```
>
> ' Is the player's height more than 32..?
>
> If (DDSD_PlayerSprites(Sprite).lHeight) > 32 Then ' Player animation
>
> ' Create a 32 pixel offset for larger sprites
>
> y = GetPlayerY(Index) * PIC_Y + Player(Index).YOffset - ((DDSD_PlayerSprites(Sprite).lHeight / 4) - 32) ' Player animation
>
> Else
>
> ' Proceed as normal
>
> y = GetPlayerY(Index) * PIC_Y + Player(Index).YOffset
>
> End If
>
> ```
>
> Done.

@PaperdollAction:

> 8\. Paperdoll action.
>
> Now we make the paperdoll work. I did not test this, but I trust that's all what has to be done about it.
>
> Find and **remove** the following from **BltPaperdoll**.
>
> ```
>
> With rec
>
> .top = spritetop * (DDSD_Paperdoll(Sprite).lHeight / 4)
>
> .Bottom = .top + (DDSD_Paperdoll(Sprite).lHeight / 4)
>
> .Left = Anim * (DDSD_Paperdoll(Sprite).lWidth / 4)
>
> .Right = .Left + (DDSD_Paperdoll(Sprite).lWidth / 4)
>
> End With
>
> ```
>
> Place instead of it, the following.
>
> ```
>
> With rec
>
> .top = spritetop * (DDSD_Paperdoll(Sprite).lHeight / 4)
>
> .Bottom = .top + (DDSD_Paperdoll(Sprite).lHeight / 4)
>
> .Left = Anim * (DDSD_Paperdoll(Sprite).lWidth / SPRITE_FRAMES)
>
> .Right = .Left + (DDSD_Paperdoll(Sprite).lWidth / SPRITE_FRAMES)
>
> End With
>
> ```
>
> That's all I believe.

@Download:

> 9\. Download
>
> Here you can download a vanilla version of EO2.0 with the player animations installed on.
>
> In the source code I have put the following comment on each place I have changed something or added something.
>
> ```
>
> ' Player animation
>
> ```
>
> You can download it from this forum or Mediafire, also from the attached file with this topic.
>
> [![](http://img831.imageshack.us/img831/4153/mediafireicon.png)](http://www.mediafire.com/download.php?g1us4e0o82ikzw2) [![](http://imageshack.us/a/img29/2193/ebutton3.png)](http://www.freemmorpgmaker.com/files/imagehost/pics/69fceb51df822c18b9c261d34b7595b8.rar)

10\. Questions and answers

**1\. Would this also work for attacking animations?**

With this you can only make movement animations, an attacking animation is not moving, so no.

**2\. Would these animations be global? Like if I pressed the "running" button, would everyone see me running?**

Yes.

________________________
Link to comment
Share on other sites

  • 2 weeks later...
> Yes.
>
> Btw, if you have a problem with your attack animation not being showed to other players, you can fix it with this.
>
> [http://www.touchofde…es/#entry849195](http://www.touchofdeathforums.com/community/index.php?/topic/129156-eoevent-system-3-some-bug-fixes/#entry849195)

Well that's good to see. I will definitely be using this, thanks!

Also, that's hilarious! I literally just searched on how to fix that. lol
Link to comment
Share on other sites

  • 2 years later...
> Yes.
>
> Btw, if you have a problem with your attack animation not being showed to other players, you can fix it with this.
>
> [http://www.touchofdeathforums.com/community/index.php?/topic/129156-eoevent-system-3-some-bug-fixes/#entry849195](http://www.touchofdeathforums.com/community/index.php?/topic/129156-eoevent-system-3-some-bug-fixes/#entry849195)

Sorry about the necropost, but the new "charset" model will only render the "walk", "run" and "bike", right? What's the point in the "fishing" and the pokeball sprites? Couldn't it be used for the attacking frame on EO2.0? Would be very more useful. And can I have a paperdoll to bike? It would be nice for multiple color bikes. Just clean the sprite to make the bike invisible and add a paperdoll option.
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...