Tag Archives: modding

Feature – Equipment

Overview

All equipment in Crea is randomly generated to some degree. Each piece of equipment is made up of attributes. These attributes can be either positive or negative effects such as stat increase/decrease, increased health regeneration, or vampiric hit. The human race has a large assortment of equipment slots: Head, Body, Hands, Legs, Feet, Ring, and Neck. All but the last two will change your character’s appearance.

The majority of equipment found in Crea will have some constant attributes as well as some random ones. Such as all Copper Greaves will always have a DEF +5 attribute and will randomly be assigned 0-2 of the following attributes: ATK +(1 to 2), MDEF +(2 to 4), and AGI +1. (This is just an example).

Modding

All aspects of equipment are completely moddable. Not only can you create new pieces of equipment, it is very possible to create entirely new attributes and even add new equipment slots.

Creating a new piece of equipment is reasonably simple. In addition to creating a normal item entity (Render, Item, Craft components), add an equipment component to an item. An Equipment component has a list of image substitutions and a function (onCreate) called when a new piece of that equipment is created. Inside of the onCreate function is where attributes are added to the equipment component.

equipment = Equipment('chest')
#These are image substitutions similar to what is discussed in Character Customization
equipment.addSub('customizations/shirt_front.png', 'mods/base/item/armor/chest/silver_breastplate/torso.png', Vectorf(2, 0))
equipment.addSub('customizations/sleeve_lower_0.png', 'mods/base/item/armor/chest/silver_breastplate/arm_lower_0.png')
equipment.addSub('customizations/sleeve_upper_0.png', 'mods/base/item/armor/chest/silver_breastplate/arm_upper_0.png')
#... and more substitutions

def createSilverChestplate(component):
    component.addAttribute(ChangeStatAttribute('HP', 5))
    component.addAttribute(ChangeStatAttribute('Attack', 3))

equipment.onCreate = createSilverChestplate
add(equipment)

Creating a new type of attribute involves a new python class that implements an interface. Here is an example with a brief description is provided with each method.

class ChangeStatAttribute(object):
    name = "ChangeStat"
    def __init__(self, stat='', value=0):
        self.stat = stat
        self.value = value
        self.changed = 0

    def alignment(self):
        #Called to determine if this is a positive or negative attribute
        return self.value >= 0

    def quality(self):
        #Reports the quality of this attribute. The quality of an equipment piece is the sum of its attributes.
        return self.value * statWeights[self.stat]

    def onEquip(self, args):
        #Called when this attribute is being equipped
        player = args['player']
        stat = player.stats.get(self.stat)
        if stat.hasMax():
            self.changed = stat.adjustMax(self.value)
        else:
            self.changed = stat.adjust(self.value)

    def onUnequip(self, args):
        #Called when this attribute is being unequipped
        player = args['player']
        stat = player.stats.get(self.stat)
        if stat.hasMax():
            stat.adjustMax(-self.changed)
        else:
            stat.adjust(-self.changed)
        self.changed = 0

    def save(self, stream):
        stream.saveString(self.stat)
        stream.saveInt32(self.value)
        stream.saveInt32(self.changed)

    def load(self, stream):
        self.stat = stream.loadString()
        self.value = stream.loadInt32()
        self.changed = stream.loadInt32()

When creating a new piece of equipment you must specify which equipment slot the new item is compatible with. In order to create a new equipment slot it only requires adding the slot to the character body script (see Character Customization). After this is done any new equipment can use the new equipment slot.

#Define which gear slots this body supports
#These show up in the character equipment UI in game
body.gear = Gear()
body.gear.slots.append(GearSlot("head", "mods/base/body/hm/gear/head.png"))
body.gear.slots.append(GearSlot("chest", "mods/base/body/hm/gear/chest.png"))
body.gear.slots.append(GearSlot("hand", "mods/base/body/hm/gear/hand.png"))
body.gear.slots.append(GearSlot("leg", "mods/base/body/hm/gear/leg.png"))
body.gear.slots.append(GearSlot("feet", "mods/base/body/hm/gear/feet.png"))

Links

Feature – Character Customization

Overview

The character creation provides a great amount of customization. The first and most important choice the player has is choosing the type of body to use. The body type defines the majority of the aspects for the character – appearance, customizations, equipment, stats, and so on. A body type can be “human male”, “human female” or even some completely different race such as a Robot. Body customizations are a part of the rendered body that can be changed such as hair, shirt, pants, shoes, and accessories.

Aside from customizations, a body type also defines which equipment slots the character will have, the base stats for the character, and the proficiencies. Each of these will be expanded upon in their own respective feature posts. With these character aspects being associated to the body type means that entire unique characters can be created. Such as a Robot race that has its own unique pieces of equipment, customizations, stats, and proficiencies.

Modding

Every aspect of character customization is moddable: body types, customizations, equipment slots, stats, and proficiencies. I will cover all of this by going over the human male body script, which is quite lengthy. Some explanation is embedded in the code – Look for lines starting with ‘#’.

name = "Human Male"
body = Body('hm')

body.render = ModularRender('mods/base/body/hm/human.scml')

#Character's are rendered with a ModularRender.
#This means that the character is broken up into small sprites
#such as head, arms, hands, torso, legs, clothing, hair and so on.
#Each of these sprites can be replaced with another sprite.
#These mappings below associate a name with a sprite or group of sprites.
#This makes it possible to do things like recolor all body sprites or hide them.

body.render.addMapping('body', 'arm/arm_lower_0.png')
body.render.addMapping('body', 'arm/arm_upper_0.png')
body.render.addMapping('body', 'arm/hand_0.png')
body.render.addMapping('body', 'leg/leg_lower_0.png')
body.render.addMapping('body', 'leg/leg_upper_0.png')
body.render.addMapping('body', 'leg/foot.png')
body.render.addMapping('body', 'head/head.png')

#Lots more mapping removed for brevity...

#Sets the animation render ordering.
body.render.setOrder(['arm_back', '', 'arm_front'])

#Set up the body animations
body.animation = Animation("idle")
body.animation.bind('arm_back', 'run_arm_back', 'run')
body.animation.bind('arm_back', 'walk_arm_back', 'walk')
body.animation.bind('arm_back', 'idle_arm_back', 'idle')
body.animation.bind('arm_back', 'jump_arm_back', 'jump')
body.animation.bind('arm_back', 'fall_arm_back', 'fall')
body.animation.bind('arm_back', 'use_arm_back', 'use')
body.animation.bind('arm_front', 'run_arm_front', 'run')
body.animation.bind('arm_front', 'walk_arm_front', 'walk')
body.animation.bind('arm_front', 'idle_arm_front', 'idle')
body.animation.bind('arm_front', 'jump_arm_front', 'jump')
body.animation.bind('arm_front', 'fall_arm_front', 'fall')
body.animation.bind('arm_front', 'use_arm_front', 'use')
body.animation.setLooping('idle_arm_front', False)
body.animation.setLooping('jump_arm_front', False)
body.animation.setLooping('fall_arm_front', False)
body.animation.setLooping('use_arm_front', False)
body.animation.setLooping('use', False)
body.animation.setLooping("death", False)

#Here we add the customizations this body supports
#These show up in the Character Creation UI when this body is selected
hair = Customization('Hair', True)
hair.isOptional = True
body.addCustom(hair)

accessory1 = Customization('Accessory 1', True)
accessory1.isOptional = True
body.addCustom(accessory1)

accessory2 = Customization('Accessory 2', True)
accessory2.isOptional = True
body.addCustom(accessory2)

body.addCustom(Customization('Shirt', True))
body.addCustom(Customization('Pants', True))
body.addCustom(Customization('Shoes', True))

#Import this file and make substitions to body
#body.addSub('torso/torso.png', 'mods/base/body/hf/torso/torso.png')

body.hide('customizations/gloves.png')
body.hide('customizations/helmet.png')

#Define which gear slots this body supports
#These show up in the character equipment UI in game
body.gear = Gear()
body.gear.slots.append(GearSlot("head", "mods/base/body/hm/gear/head.png"))
body.gear.slots.append(GearSlot("chest", "mods/base/body/hm/gear/chest.png"))
body.gear.slots.append(GearSlot("hand", "mods/base/body/hm/gear/hand.png"))
body.gear.slots.append(GearSlot("leg", "mods/base/body/hm/gear/leg.png"))
body.gear.slots.append(GearSlot("feet", "mods/base/body/hm/gear/feet.png"))

#Define which stats characters with this body get
body.stats = Stats()
body.stats.stats.append(Stat('HP', 'HP', 300, 9999, True, True))
body.stats.stats.append(Stat('AP', 'AP', 100, 9999, True, True))
body.stats.stats.append(Stat('ATK', 'Attack', 100, 999, False, True))
body.stats.stats.append(Stat('DEF', 'Defense', 100, 999, False, True))
body.stats.stats.append(Stat('MATK', 'M Attack', 100, 999, False, True))
body.stats.stats.append(Stat('MDEF', 'M Defense', 100, 999, False, True))
body.stats.stats.append(Stat('AGI', 'Agility', 100, 999, False, True))
body.stats.stats.append(Stat('DEX', 'Dexterity', 100, 999, False, True))

#Define which proficiencies characters with this body get
execfile('mods/base/proficiency/craft/craft.py')
execfile('mods/base/proficiency/explore/explore.py')
execfile('mods/base/proficiency/fight/fight.py')
execfile('mods/base/proficiency/gather/gather.py')
body.proficiencies = Proficiencies()
body.proficiencies.proficiencies.append(craftProficiency)
body.proficiencies.proficiencies.append(exploreProficiency)
body.proficiencies.proficiencies.append(fightProficiency)
body.proficiencies.proficiencies.append(gatherProficiency)

add(body)

We are using Spriter for our character animations. Consequently, in order to create new animations or an entirely new body type then you must use Spriter. You can easily setup new sprite mappings, mentioned above at the top of the code.

A body script, such as the one above, defines the supported customizations; however, the actual customizations are defined elsewhere in their own scripts. Creating new customizations is reasonably straightforward. A customization is a very simple entity. It contains a “Custom” component which takes two parameters. The first is the name of the customization this is for and the second is if it supports recoloring or not. After that a Custom component can define any number of sprite substitutions that take place if this customization is used. Here we change what the top hair looks like and hide the bottom hair since it is unused.

name = "Guy Hair"
custom = Custom("Hair", True)
custom.addSub('head/hair_top.png', 'mods/base/custom/hair/hm_hair.png')
custom.hide('head/hair_bottom.png')
add(custom)

As you can see, creating new hair styles, clothing and any other customizations is very easy!

Links

Advanced Modding Video

Before getting to the video, someone has created a page on reddit for Crea. Help us spread the word. Thanks! http://redd.it/x22mt

Moving on, this is my follow up video to the modding video I posted last week. This is some advanced modding and consequently is much more involved but don’t worry about that. We will be writing lots of documentation and tutorials which will explain everything in full detail. Enjoy!

Modding Video and Development Update

I forgot to post the modding video I put together the other day. It only covers some very simple modding but it really shows just how easy it is to mod Crea. I have another video planned that I will be putting together within the next few days (maybe even today!). This follow up video will have showcase some advanced modding.

Aside from putting together videos and keeping up with the Kickstarter campaign, I have been doing my best to keep pushing forward with development. Lately I have been implementing the real inventory system. Once I finish it and get some screenshots I will go over the details. I will say that it will be a lot less like Terraria and Minecraft and more like Torchlight.

Terraforming in Crea

Randomly generated content can be great and sometimes not so much. If the input is too constrained, then the result is something that is different and yet all too familiar. I think this can be seen in the Terraria world generation. Terraria has a fairly limited number of biomes and how they are used is restricted. Such as a large world will always have 6 floating islands and there is always one dungeon. After a few worlds patterns quickly become apparent. Don’t get me wrong though. Terraria can provide dozens of hours of exciting exploration, but the novelty does eventually wear off.

World images created with MoreTerra

When I set out designing the world generation for Crea, I knew I wanted to enable players to be able to have unique experiences with every world. The answer was quite obvious – moddable biomes. If players could easily create their own biomes or play with other player’s biomes then the possibilities are endless. Here is an example of what a biome content looks like.

biome.name = "Plains"
#How often this biome should be used
biome.frequency = 50

#The minimum and maximum dimensions of the biome
biome.minWidth = 80
biome.maxWidth = 300
biome.minHeight = 80
biome.maxHeight = 300

#Specifies that this biome only occurs on the surface
biome.surface = True

#The elevation of the biome
biome.elevation = 100

#The amount of elevation changes (hills) in the biome
biome.hilly = 10.0

#How much the lowest and highest elevation can differ
biome.relief = 2.0

#A list of biomes that cannot neighbor this biome
biome.blacklist = ['Volcano']

#List of actions to apply to the world
#This part is still being worked on.
biome.placeTiles('stone', frequency=20, pattern)
biome.placeTiles('mud', frequency=10, pattern)
biome.plant('grass', frequency=40)
biome.plant('tree', frequency=5)

Some of these details will change but this gives a good idea of what a biome looks like. Most of the contents are explained. You can do simple things like increase the ‘hilly’ and ‘relief’ properties and have mountains. For the real power we have the actions section at the bottom.

After the basic terrain has been generated, each biome has it’s list of actions applied to it. In this case all Plains biomes will have stone randomly placed, then some mud, then lots of grass will be planted, and finally a few trees. I plan to include several basic actions that players can use to make creating biomes as simple as possible, but what if you want to do something completely different? Lets say you want to build a crazy dungeon. For advanced modders, I have added a special action that takes a callback function which provides you ample power to twist the world to your desires.

Now that we know how biomes are made, the next logical question is “How are they used”? Players will have the option to create a world with simple or advanced options. With simple the player chooses a name and the world size and is good to go. From the advanced options the player is also able to adjust the frequency of biomes. Want a world consisting purely of bunny warrens? Kelley, our artist, does!

Not only will players never have to see the same world twice, but they will be able to explore the worlds that they want.