Chapter 17. Creating the Item Editor

This chapter focuses on the development of an item editor to make creating and managing game items (such as swords, armor pieces, rings, and other gear found in a typical RPG) easier for a designer. If you have ever tried to create an RPG without an editor like this, I’m sure you ran into the same roadblock that I have when it comes to working with items. Just giving the player a sword tends to be a hard-coded, manual process, with less than favorable results. Even just storing items in a text file and reading them in is better than manually creating arrays of items in code, unless your game is on the extremely simple side with just “hack & slash” gameplay without much depth. Since our goal is to send the player on quests before we finish this book, having items that satisfy the quests is essential! For an example of how the item data is used, we’ll put that on hold until the next chapter and just focus on editing in this one.

Here’s what we’ll cover in this chapter:

Item Editor Design

The item editor is a bit different from the character editor that we developed back in Chapter 14. While characters are stored one per .char file, many items are stored in a .item file. The editor has a File menu, which allows you to start a new item database, load an existing file, or save the current file to a new file. The item editor is shown in Figure 17.1.

The item editor works with an entire list of items at a time.

Figure 17.1. The item editor works with an entire list of items at a time.

The editor was designed to work with groups of items in a file. The example shown here contains some weapons and armor items, but it would be better to organize the items into separate groups (weapons, armor, rings, etc.) so that it’s easier to manage and search for specific items. Another approach is to just store everything in a single item database, which is perfectly okay but it’s just harder to find and edit items when there are so many in a single file.

This editor does make it possible for players to cheat on your game. By giving the filenames a different extension besides .xml, we can at least hide the data from casual wanna-be hackers, but anyone with bare minimum skills will try opening the .item file in a text editor and immediately see that it is an editable text file containing .xml tables. There are ways to get around this problem, but that subject is beyond the scope of this book. Experience has shown me that for players who want to hack a game file, whether it’s a core database or a saved game, no effort short of hard encryption will prevent them from doing so. If someone wants to hack your game that badly, take it as a compliment.

Item Images

The item editor works with just single-frame bitmaps, with support for two versions for each item: a drop image and an inventory image. The drop image is usually smaller and oriented with the ground tiles, while the inventory image is often oriented straight-on so it looks better in an inventory screen (the player’s equipment). Neither image is suitable for both purposes. If you try to use the inventory image as a drop item, it will look funny on the ground at the wrong angle. However, one alternative is to bring up a “loot window” showing the contents of an item rather than dropping items directly onto the ground. A loot window does eliminate quite a bit of work since we do not need to keep track of two different images in addition to the added code to allow the player to pick up items on the ground.

An advantage to the ground image is that you can place individual items anywhere in the game world for the player to find. But, due to the extra work required, I think most designers would rather have their artists working on new gameplay art rather than extra drop images. This is why in most games you’ll most often find crates and boxes rather than usable items in the game world. Since we have both versions of the artwork for every item from Reiner, we can use them with the item editor. Just note that both are not absolutely necessary and it’s acceptable to just use the inventory version, as long as you have a loot window come up in the game whenever the player opens a container. I kind of like the realism added when items drop to the ground when an NPC falls in combat (maybe even add the dramatic effect of having the items scatter around the body randomly). Since it’s fairly common to “loot the corpse” in most computer RPGs, use whichever method you want in your own game since the artwork is available.

What if we were to add multiple image frame support to the item editor, so that batches of item artwork could be stored on a sprite sheet? That’s a distinct possibility. At the very least, we could store both the drop and inventory image together in a two-frame image. The problem is, the artwork is not all uniform in size, with the drop items being somewhat smaller (in Reiner’s). Sure, you could enlarge the images to a fixed size all around, but will that save time in the long run versus just adding both image filenames into the item editor fields?

There is the additional problem of having literally hundreds of asset files in the game’s folder. The limited set of item images used so far already accounts for a large number of files cluttering the game’s main folder. A definite improvement would be to store the images in a sub-folder like .assets under the main folder, and then prefix all of the image filenames stored in the item editor database with .assets. So, a filename field such as “drop plate 1.png” would become “.assetsdrop plate 1.png.” You can do any such manipulation in the game code while loading these assets.

Looking Up Items

I wanted to use an auto-increment identifier for each item in the item editor database and then use the ID in quests and in the player’s inventory. But, though an identifier-based database is required for a professional project, it’s not the best choice for an amateur game with multi-purpose tools like what we have for Celtic Crusader. Instead of using an ID, the Item.Name property will be used to look up the data for an item. All that is required to make this work effectively is to ensure that your items each have a unique name. If you want to have three rings called “Magic Ring,” be sure to add a qualifier to the name like “Magic Ring +1” or something to uniquely identify each item. Since the name is the lookup field, the first item matching the name will be used in a lookup.

Item Class

As with the previous level editor and character editor, the new item editor includes a class (called Item) that makes the data available to our game. The Item class is meant to handle a single item from the editor database (which is stored in an XML file). In our game code, we will need to load the .item file with additional code and then use the Item class for each item that is loaded. In other words, there is no overall “Items” class that loads an entire .item file, is saved by the editor, and makes those items available to the game. Perhaps there should be?

Public Class Item
    Private p_name As String
    Private p_desc As String
    Private p_dropfile As String
    Private p_invfile As String
    Private p_category As String
    Private p_weight As Single
    Private p_value As Single
    Private p_attacknumdice As Integer
    Private p_attackdie As Integer
    Private p_defense As Integer
    Private p_buffStr As Integer
    Private p_buffDex As Integer
    Private p_buffSta As Integer
    Private p_buffInt As Integer
    Private p_buffCha As Integer

    Public Sub New()
        p_name = "new item"
        p_desc = ""
        p_dropfile = ""
        p_invfile = ""
        p_category = ""
        p_weight = 0.0
        p_value = 0.0
        p_attacknumdice = 0
        p_attackdie = 0
        p_defense = 0
        p_buffStr = 0
        p_buffDex = 0
        p_buffSta = 0
        p_buffInt = 0
        p_buffCha = 0
    End Sub

    Public Property Name() As String
        Get
            Return p_name

    End Get
    Set(ByVal value As String)
        p_name = value
    End Set
End Property

Public Property Description() As String
    Get
        Return p_desc
    End Get
    Set(ByVal value As String)
        p_desc = value
    End Set
End Property

Public Property DropImageFilename() As String
    Get
        Return p_dropfile
    End Get
    Set(ByVal value As String)
        p_dropfile = value
    End Set
End Property

Public Property InvImageFilename() As String
    Get
        Return p_invfile
    End Get
    Set(ByVal value As String)
        p_invfile = value
    End Set
End Property

Public Property Category() As String
    Get
        Return p_category
    End Get
    Set(ByVal value As String)
        p_category = value
    End Set
End Property

Public Property Weight() As Single
    Get
        Return p_weight
    End Get
    Set(ByVal value As Single)
        p_weight = value
    End Set
End Property

Public Property Value() As Single
    Get
        Return p_value
    End Get
    Set(ByVal value As Single)
        p_value = value
    End Set
End Property

Public Property AttackNumDice() As Integer
    Get
        Return p_attacknumdice
    End Get
    Set(ByVal value As Integer)
        p_attacknumdice = value
    End Set
End Property

Public Property AttackDie() As Integer
    Get
        Return p_attackdie
    End Get
    Set(ByVal value As Integer)
        p_attackdie = value
    End Set
End Property

Public Property Defense() As Integer
    Get

        Return p_defense
    End Get
    Set(ByVal value As Integer)
        p_defense = value
    End Set
End Property

Public Property STR() As Integer
    Get
        Return p_buffStr
    End Get
    Set(ByVal value As Integer)
        p_buffStr = value
    End Set
End Property

Public Property DEX() As Integer
    Get
        Return p_buffDex
    End Get
    Set(ByVal value As Integer)
        p_buffDex = value
    End Set
End Property

Public Property STA() As Integer
    Get
        Return p_buffSta
    End Get
    Set(ByVal value As Integer)
        p_buffSta = value
    End Set
End Property

Public Property INT() As Integer
    Get
        Return p_buffInt
    End Get
    Set(ByVal value As Integer)
        p_buffInt = value

    End Set
End Property

Public Property CHA() As Integer
    Get
        Return p_buffCha
    End Get
    Set(ByVal value As Integer)
        p_buffCha = value
    End Set
End Property

Public ReadOnly Property Summary() As String
    Get
        Dim text As String
        text = "This '" + p_name + "', "

        Dim weight As String = ""
        Select Case p_weight
            Case Is > 50 : weight = "a very heavy "
            Case Is > 25 : weight = "a heavy "
            Case Is > 15 : weight = "a "
            Case Is > 7 : weight = "a light "
            Case Is > 0 : weight = "a very light "
        End Select
        text += weight

        Select Case p_category
            Case "Weapon" : text += "weapon"
            Case "Armor" : text += "armor item"
            Case "Necklace" : text += "necklace"
            Case "Ring" : text += "ring"
            Case Else : text += p_category.ToLower() + " item"
        End Select

        If p_attacknumdice <> 0 Then
            text += ", attacks at " + p_attacknumdice.ToString() _
                + "D" + p_attackdie.ToString() _
                + " (" + p_attacknumdice.ToString() + " - " _

                   + (p_attackdie * p_attacknumdice).ToString() _
                   + " damage)"
            End If

            If p_defense <> 0 Then
                text += ", adds " + p_defense.ToString() + " armor points"
            End If

            Dim fmt As String = "+#;-#"
            If p_buffStr <> 0 Then
                text += ", " + p_buffStr.ToString(fmt) + " STR"
            End If
            If p_buffDex <> 0 Then
                text += ", " + p_buffDex.ToString(fmt) + " DEX"
            End If
            If p_buffSta <> 0 Then
                text += ", " + p_buffSta.ToString(fmt) + " STA"
            End If
            If p_buffInt <> 0 Then
                text += ", " + p_buffInt.ToString(fmt) + " INT"
            End If
            If p_buffCha <> 0 Then
                text += ", " + p_buffCha.ToString(fmt) + " CHA"
            End If

            Return text + "."
        End Get
    End Property

    Public Overrides Function ToString() As String
        Return p_name
    End Function
End Class

Item Editor Source Code

Like the character editor from a few chapters back, the item editor is all Visual Basic source code (the only editor written in C# is the level editor). The item editor is completely self-contained and it can create new item files from scratch as well as edit existing files. One nice feature is auto-save: while editing items, if you click on a different item in the list or close the editor, the current item is automatically saved. This takes out some of the tedium from editing a large number of items—just point, click, and edit, without concern for saving at every step.

Obviously, there is a form filled with controls that are not listed here, because the user interface is too complex to build from scratch (as in a tutorial-style walkthrough). The sources here are familiar because the XML code is similar to the code in the other editors. Some of the source code for the editor has been omitted to save space. To see the complete source code listing, please open the project (www.courseptr.com/downloads).

Public Class Form1
    Dim device As Graphics
    Dim surface As Bitmap
    Dim g_filename As String = "items.item"
    Dim currentIndex As Integer

    Private Sub Form1_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load
        surface = New Bitmap(Size.Width, Size.Height)
        picDrop.Image = surface
        device = Graphics.FromImage(surface)
        clearFields()
        loadFile(g_filename)
    End Sub

    Private Sub showItem(ByVal index As Integer)
        clearFields()
        Dim item As Item = lstItems.Items(index)
        txtName.Text = item.Name
        txtDesc.Text = item.Description
        txtDropImageFilename.Text = item.DropImageFilename
        txtInventoryImageFilename.Text = item.InvImageFilename
        cboCategory.Text = item.Category
        txtWeight.Text = item.Weight.ToString()
        txtValue.Text = item.Value.ToString()
        cboAttackNumDice.Text = item.AttackNumDice.ToString()
        cboAttackDie.Text = "D" + item.AttackDie.ToString()
        txtDefense.Text = item.Defense.ToString()
        txtSTR.Text = item.STR.ToString()

    txtDEX.Text = item.DEX.ToString()
    txtSTA.Text = item.STA.ToString()
    txtINT.Text = item.INT.ToString()
    txtCHA.Text = item.CHA.ToString()
    txtSummary.Text = item.Summary
End Sub

Private Function getElement(ByVal field As String, _
        ByRef element As XmlElement) As String
    Dim value As String = ""
    Try
        value = element.GetElementsByTagName(field)(0).InnerText
    Catch ex As Exception
        REM ignore error, just return empty
        Console.WriteLine(ex.Message)
    End Try
    Return value
End Function

Private Sub loadFile(ByVal filename As String)
    Try
        REM open the xml file
        Dim doc As New XmlDocument()
        doc.Load(filename)
        Dim list As XmlNodeList = doc.GetElementsByTagName("item")
        For Each node As XmlNode In list
            Dim element As XmlElement = node
            Dim item As New Item()
            item.Name = getElement("name", element)
            item.Description = getElement("description", element)
            item.DropImageFilename = getElement("dropimagefilename", _
                element)
            item.InvImageFilename = getElement("invimagefilename", _
                element)
            item.Category = getElement("category", element)
            item.Weight = Convert.ToSingle(getElement("weight", element))
            item.Value = Convert.ToSingle(getElement("value", element))
            item.AttackNumDice = Convert.ToInt32( _
                getElement("attacknumdice", element))

            item.AttackDie = Convert.ToInt32( _
                getElement("attackdie", element))
            item.Defense = Convert.ToInt32(getElement("defense", _
                element))
            item.STR = Convert.ToInt32(getElement("STR", element))
            item.DEX = Convert.ToInt32(getElement("DEX", element))
            item.STA = Convert.ToInt32(getElement("STA", element))
            item.INT = Convert.ToInt32(getElement("INT", element))
            item.CHA = Convert.ToInt32(getElement("CHA", element))
            lstItems.Items.Add(item)
        Next
    Catch ex As Exception
        MessageBox.Show(ex.Message)
        Return
    End Try
End Sub

Private Sub saveFile(ByVal filename As String)
    Try
        REM create data type templates
        Dim typeInt As System.Type
        Dim typeSingle As System.Type
        Dim typeStr As System.Type
        typeInt = System.Type.GetType("System.Int32")
        typeStr = System.Type.GetType("System.String")
        typeSingle = System.Type.GetType("System.Single")

        REM create xml schema
        Dim table As New DataTable("item")
        table.Columns.Add(New DataColumn("name", typeStr))
        table.Columns.Add(New DataColumn("description", typeStr))
        table.Columns.Add(New DataColumn("dropimagefilename", typeStr))
        table.Columns.Add(New DataColumn("invimagefilename", typeStr))
        table.Columns.Add(New DataColumn("category", typeStr))
        table.Columns.Add(New DataColumn("weight", typeSingle))
        table.Columns.Add(New DataColumn("value", typeSingle))
        table.Columns.Add(New DataColumn("attacknumdice", typeInt))
        table.Columns.Add(New DataColumn("attackdie", typeInt))
        table.Columns.Add(New DataColumn("defense", typeInt))
        table.Columns.Add(New DataColumn("STR", typeInt))

        table.Columns.Add(New DataColumn("DEX", typeInt))
        table.Columns.Add(New DataColumn("STA", typeInt))
        table.Columns.Add(New DataColumn("INT", typeInt))
        table.Columns.Add(New DataColumn("CHA", typeInt))

        REM copy character data into datatable
        For Each item As Item In lstItems.Items
            Dim row As DataRow = table.NewRow()
            row("name") = item.Name
            row("description") = item.Description
            row("dropimagefilename") = item.DropImageFilename
            row("invimagefilename") = item.InvImageFilename
            row("category") = item.Category
            row("weight") = item.Weight
            row("value") = item.Value
            row("attacknumdice") = item.AttackNumDice
            row("attackdie") = item.AttackDie
            row("defense") = item.Defense
            row("STR") = item.STR
            row("DEX") = item.DEX
            row("STA") = item.STA
            row("INT") = item.INT
            row("CHA") = item.CHA
            table.Rows.Add(row)
        Next

        REM save xml file
        table.WriteXml(filename)
        table.Dispose()

    Catch es As Exception
        MessageBox.Show(es.Message)
    End Try
End Sub

Private Sub saveCurrentItem()
    Dim item As Item
    If currentIndex < 0 Or txtName.Text = "" Then
        Return
    End If

    Try
        item = lstItems.Items(currentIndex)
        item.Name = txtName.Text
        item.Description = txtDesc.Text
        item.DropImageFilename = txtDropImageFilename.Text
        item.InvImageFilename = txtInventoryImageFilename.Text
        item.Category = cboCategory.Text
        item.Weight = Convert.ToSingle(txtWeight.Text)
        item.Value = Convert.ToSingle(txtValue.Text)
        item.AttackNumDice = Convert.ToInt32(cboAttackNumDice.Text)
        item.AttackDie = Convert.ToInt32(cboAttackDie.Text.Substring(1))
        item.Defense = Convert.ToInt32(txtDefense.Text)
        item.STR = Convert.ToInt32(txtSTR.Text)
        item.DEX = Convert.ToInt32(txtDEX.Text)
        item.STA = Convert.ToInt32(txtSTA.Text)
        item.INT = Convert.ToInt32(txtINT.Text)
        item.CHA = Convert.ToInt32(txtCHA.Text)
        lstItems.Items(currentIndex) = item
    Catch ex As Exception
        MessageBox.Show(ex.Message)
    End Try
End Sub

REM portions of this code have been omitted to conserve space

Level Up!

That concludes our work on the item editor. This editor has been developed with slightly different goals than the other editors we’ve seen so far, in that many items are stored in a single xml file rather than one item per file. This makes it quite easy to edit many items quickly by simply clicking each item in the list and making changes to it. Since the editor automatically saves changes made to items, this works quite well. Just be sure to save the file when you’re done editing.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.58.199