Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411196 Posts in 69314 Topics- by 58380 Members - Latest Member: feakk

March 18, 2024, 06:38:44 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsCrea
Pages: [1] 2 3 ... 8
Print
Author Topic: Crea  (Read 32217 times)
jmcmorris
Level 1
*



View Profile WWW
« on: May 17, 2012, 02:29:11 PM »


Crea is a 2D sandbox RPG with a focus on modding and the community. The game has been in development since Feb 2012. Aside from modding, Crea strives to strike a balance between building, exploring and fighting in a high fantasy setting.

We ran a successful kickstarter back in July/August 2012 and since then I (Jasson) have been working on the game fulltime. My wife is handling the artwork part-time and we have 3 other people working on the music, design and UI on the side.

The game engine is component based and is being written in C++. All of the game content is defined in python scripts and is freely modifiable. With the content being in python files it allows the content to contain game logic to do things such as supply unique effects to equipment.

Links:
Development Blog
Project Plan
Successful Kickstarter
Livestreams



Screens:

« Last Edit: March 20, 2013, 01:07:23 PM by jmcmorris » Logged

IndieGamesDig
Level 0
***


asdfjkl


View Profile WWW
« Reply #1 on: May 17, 2012, 05:09:18 PM »

That's a nice lama. I'm a fan of the pallet and art style  Smiley
Logged
jmcmorris
Level 1
*



View Profile WWW
« Reply #2 on: May 18, 2012, 10:24:10 AM »

Thanks! I'm a big fan too.

Since this devlog is starting in the middle of the project, I figured I would do my best to get it caught up to speed. Here are a list of features we have implemented in order that they were implemented.


And here are some we plan on implementing in the near future.

  • Leveling and Character Stats
  • Equipment
  • Lighting
  • Plant life
  • Monsters
  • NPCs
  • and more to come

Several of these have been covered in detail in the blog post links I provided. Still, if you have any questions then feel free to ask.
Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #3 on: May 30, 2012, 10:30:29 AM »

Hey, I missed last week's update. Things have been busy for me! Lots of work, real life events and Diablo 3 have all been keeping me occupied. Fortunately all of these are wrapping up for me - just one last wedding this upcoming weekend. I still have managed to get some work on Crea done and soon much more.

Tiles changed from 24x24 to 16x16. This is a fairly large gameplay change but it is a good one as it will give players more creative freedom. Along with this change, we've remade and cleaned up the various tile types. Before too long we'll put up a screenshot of them in game. For now this small sample will have to do!



In other news, we have been refining the character customizations and ensuring that they are implemented 100%. This includes things like adding armor to the character and what not. We still have some work left but it is coming together.

Our artist has also been working on some animations. Here is the walking animation. These are the base graphics we use for the character. Everything in game can be swapped out for other images and/or recolored.



The character and clothing are about 20 small images put together using Spriter. We also have a running, walking, jumping, falling, and swinging animations. All of which you will be seeing in good time.

Our UI master has been putting together a mockup of the final UI. Nothing about it has been finalized yet but just the mockup gives the game a much more polished look. Once we get something a little more final then I will be sure to post it.

Things are starting to come together!


Original Post
Logged

zede05
Level 2
**



View Profile WWW
« Reply #4 on: May 31, 2012, 06:45:27 AM »

That looks really nice, following!
Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #5 on: June 01, 2012, 06:51:29 PM »

Thanks zede!

Today I implemented a cool feature that I have dubbed "composite animations". Basically a given entity can have any number of different groups of animations playing at one time. This is in conjunction with the modular animations that I mentioned in my last update. So my engine technically supports modular composite animations - fancy!

This is being used for the character's front arm so that it can do its own thing such as swing a pickaxe while the character is running or jumping.

This is all exposed to the content scripts so it is possible to do anything with this. Such as a giant monster with multiple eyes blinking independently while the monster attempts to stomp on you. Why you would want to do that? Because it's awesome.

I imagine I'll be posting a video before too long showing off this feature along with many others. Smiley
Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #6 on: June 04, 2012, 09:03:02 AM »

I finally got around to putting together a quick video showing some features in Crea. Keep in mind that everything is still very much a work in progress. I'm interested to hear what you think.

In this video I show the modular composite animations, crafting, placing items down and a few other basic things.



Click image to go to the youtube video.
« Last Edit: June 04, 2012, 10:06:03 AM by jmcmorris » Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #7 on: June 04, 2012, 04:44:28 PM »

Logged

eigenbom
Level 10
*****


@eigenbom


View Profile WWW
« Reply #8 on: June 04, 2012, 05:52:23 PM »

Looking good mate, looks like we've got a lot of overlap with our projects, both terraria inspired, both using c++/sfml, both moddable (lua vs python), etc. Good luck with the project. Smiley
Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #9 on: June 04, 2012, 06:28:17 PM »

Yeah, I noticed that too. We should chat sometime - I'm sure it would prove to be interesting. Thanks.
Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #10 on: June 07, 2012, 03:51:45 PM »

While I work on implementing plants into the game our artist has been working on some trees. Here are some of them with the seed, sapling and fully grown stages.

Normal Tree


Marshmallow Tree


Zebra Tree


There are two more mentioned over at our blog. Smiley
Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #11 on: June 18, 2012, 02:47:02 PM »

Got a large update today. The last week I have been in the zone and have gotten a great deal done. First off, we reworked our website a little. In my last update I said I was working on implementing plants. At that point I was still in the designing phase for them. Since then I have finished implementing them and much more.

For every feature my mantra is easily moddable but lots of control. All of the supported content up to this point has had one form of communication - engine asking for the entities components which are returned in component definition form. I struggled quite a bit while attempting to fit the plants into this format. I finally came to my senses and realized what I had to do.

All of the plant logic resides inside of python. How the plant grows, how it visually looks, how it is harvested, and even how it is planted. With this solution, any modder has unlimited control. As for the “easily” part of my mantra, I am wrapping the core plant concepts up into nice little python classes which are extremely simple to use. I have included the code below for those interested in its awesomeness.

Because of this approach I have had to go through my entire game engine and expose it to python. This is something I have been putting off because I wasn’t sure of the requirements, boost python is a little scary and I knew it was going to be quite an undertaking. With this beast finally slain, creating new content will be much easier and have even more freedom.

Here are some screenshots featuring the plants that can grow overtime and be interacted with. There is also an early version of the grass. Right now the grass just grows on soil and is destroyed when the tile underneath it is removed. Later today I plan to make grass occasionally spread to nearby areas as if seeds were dispersed.



You can harvest the trees too!



This is the Zebra tree script that most people will see and have to worry about. I hope most of this is self explanatory. We are adding the different components to the zebra tree entity and the ZebraTree class is used to override the default properties of the tree (which can be seen below). We can add bases, trunks crowns and saplings images and they will be randomly used in the appropriate places. We can also override many other properties such as the seed it produces how tall it can grow, and how fast it can grow.

Syntax Highlighted Source
Code:
name = "Zebra Tree"

add(ModularRender())

interactable = Interactable()
add(interactable)

class ZebraTree(Tree):
    trunks = [TreeImage("mods/base/plant/tree/zebra/trunk1.png", 2, 28), \
            TreeImage("mods/base/plant/tree/zebra/trunk2.png", 2, 28), \
            TreeImage("mods/base/plant/tree/zebra/trunk3.png", 23, 28)]
    crowns = [TreeImage("mods/base/plant/tree/zebra/crown1.png", 34, 101), \
              TreeImage("mods/base/plant/tree/zebra/crown2.png", 31, 98)]
    saplings = [TreeImage("mods/base/plant/tree/zebra/sapling.png", 8, 33)]
    seed = "mods/base/plant/tree/zebra/seed.ce"

    def __init__(self, entity, organic):
        super(ZebraTree, self).__init__(entity, organic)


def createZebraTree(entity, organic):
    return ZebraTree(entity, organic)

add(Organic(createZebraTree))



Here is the Zebra seed script which is really simple. As with all content entity scripts we add the needed components, this time a Render, Item and Interactable components. The important part here is that we add an interaction “use” and provide the “useSeed” function which is defined in the tree script just below this one. To make this function reusable the plant the seed creates needs to be bound to the function provided - in this case we provide the path to the zebra tree.

Syntax Highlighted Source
Code:
import functools

name = 'Zebra Seed'
render = Render('mods/base/plant/tree/zebra/seed.png')
add(render)
item = Item()
item.stack = 999
item.delay = 200
add(item)

interactable = Interactable()
interactable.add("use", functools.partial(useSeed, "mods/base/plant/tree/zebra/zebra.ce"))
add(interactable)



Now for the meat of the tree logic. Here is the tree script which contains the Tree class and useSeed function that are used in the above scripts. As with everything this is still in the works but is most of the way there. I’m not going to go into detail in this now since there is so much going on but if you understand it then awesome. Once things start becoming finalized we’ll write some official documentation on these sorts of things.

Syntax Highlighted Source
Code:
import random

class TreeImage:
    def __init__(self, path, center, height):
        self.path = path
        self.center = center
        self.height = height

TreePart = enum(BASE=1, TRUNK=2, CROWN=3, SAPLING=4)

class TreeSprite:
    def __init__(self, part, index):
        self.index = index
        self.part = part
        self.sprite = Sprite()

class Tree(object):
    bases = []
    trunks = []
    crowns = []
    saplings = []
    #Path to the seed content script
    seed = ""
    #The range of the number of seeds that will drop from the crown
    seedRange = (1, 3)
    wood = "mods/base/item/raw/wood.ce"
    growthTimeMin = 60 * 1000 #1 minute
    growthTimeMax = 5 * 60 * 1000 #5 minutes
    #How much time it takes to recover from a hit
    recoveryTime = 10000
    #How much life each part of the tree has - how many hits it can take
    partLife = 3
    trunkRange = (2, 6)

    def __init__(self, entity, organic):
        self.organic = organic

        #TODO: Is this reasonable to expect players to be network aware?
        if not Game.get().network.isHost():
            return

        entity.interactable.add("harvest", self.onHarvest)
        entity.interactable.add("removeSupport", self.onRemoveSupport)
        self.addGrowthTimer()

        #Start it off as a sapling
        self.stage = 0
        self.maxStage = random.randint(self.trunkRange[0], self.trunkRange[1])
        self.sprites = [TreeSprite(TreePart.SAPLING, random.randint(0, len(self.saplings) - 1))]
        self.construct()

    def addGrowthTimer(self):
        Game.get().timer.add(random.randint(self.growthTimeMin, self.growthTimeMax), self.grow)

    def grow(self):
        if self.stage == -1:
            return
        self.stage += 1

        if self.stage is 1:
            #Clear out the sapling and append the tree base and crown
            self.sprites = []
            if self.bases:
                self.sprites.append(TreeSprite(TreePart.BASE, random.randint(0, len(self.bases) -1)))
            if self.crowns:
                self.sprites.append(TreeSprite(TreePart.CROWN, random.randint(0, len(self.crowns) -1)))
        if self.stage >= 1:
            trunk = TreeSprite(TreePart.TRUNK, random.randint(0, len(self.trunks) - 1))
            if self.crowns:
                self.sprites.insert(-1, trunk)
            else:
                self.sprites.append(trunk)

        self.construct()
        if self.stage is not self.maxStage:
            self.addGrowthTimer()

    def getImage(self, sprite):
        if sprite.part is TreePart.BASE:
            return self.bases[sprite.index]
        elif sprite.part is TreePart.TRUNK:
            return self.trunks[sprite.index]
        elif sprite.part is TreePart.CROWN:
            return self.crowns[sprite.index]
        elif sprite.part is TreePart.SAPLING:
            return self.saplings[sprite.index]
        else:
            raise ValueError("TreeSprite contains unknown tree part: ", sprite.part)

    def construct(self):
        print "Building the tree at stage:", self.stage
        height = 0
        sprites = []
        for sprite in self.sprites:
            image = self.getImage(sprite)
            height -= image.height
            sprite.sprite.position.x = -image.center
            sprite.sprite.position.y = height
            self.organic.setImage(image.path, sprite.sprite)
            sprites.append(sprite.sprite)
        self.organic.onConstruct(sprites)

    def onHarvest(self, args):
        entity = args["entity"]
        sprite = entity.modularRender.getTopSprite(args["position"])
        index = 0
        for s in self.sprites:
            if s.sprite is sprite:
                break
            index += 1

        game = Game.get()
        parent = self.organic.getParent()
        if not parent:
            print "Could not find parent entity for the plant."
            return

        damageId = "%d-%d" % (parent.id, index)
        if not game.damage(damageId, 1, self.partLife, self.recoveryTime):
            #return if we haven't done enough damage to the targeted tree part
            return

        #ignore if at the top of the tree
        if index == len(self.sprites):
            return

        self.destroyAtIndex(index)


    def destroyAtIndex(self, index):
        #Create the dropped items in the world
        game = Game.get()
        woodId = game.mod.getContent(self.wood).id
        for s in self.sprites[index:]:
            image = self.getImage(s)
            centerY = image.height / 2
            position = s.sprite.getPosition()
            position.x += image.center
            position.y += centerY
            woodPosition = position
            woodPosition.x += random.randint(-image.center, image.center)
            woodPosition.y += random.randint(-centerY, -centerY)
            print "Creating dropped wood at position (%d, %d)" % (woodPosition.x, woodPosition.y)
            game.dropped.create(woodId, 1, woodPosition, Vectorf(0, 8))

            if s.part is TreePart.CROWN and self.seed:
                seedId = game.mod.getContent(self.seed).id
                for x in xrange(0, random.randint(self.seedRange[0], self.seedRange[1])):
                    seedPosition = position
                    seedPosition.x += random.randint(-image.center, image.center)
                    seedPosition.y += random.randint(-centerY, centerY)
                    print "Creating dropped seed at position (%d, %d)" % (seedPosition.x, seedPosition.y)
                    game.dropped.create(seedId, 1, seedPosition, Vectorf(0, 8))

        #remove the targeted sprite and all of the sprites above it
        self.sprites = self.sprites[:index]

        if self.sprites:
            #Reconstruct the graphics for the tree
            self.organic.onConstruct([sprite.sprite for sprite in self.sprites])
            #Set the stage to -1 signifying that it cannot grow anymore
            self.stage = -1
        else:
            #The entire tree was destroyed. Destroy the entity.
            parent = self.organic.getParent()
            if parent:
                parent.destroy()

    def onRemoveSupport(self, args):
        #First check to see if the base (or bottom trunk) is actually over the tile being changed
        sprite = self.sprites[0]
        spriteArea = sprite.sprite.getRect()
        if not spriteArea.intersects(args["area"]):
            #No overlap so this is a false alarm
            return

        if args["forced"]:
            #The support is being forcefully removed. Destroy the entire tree
            self.destroyAtIndex(0)
        else:
            #Cancel the request
            args["canceled"] = True

    def save_bad(self, stream):
        import pdb
        pdb.set_trace()
        stream << self.stage
        stream << len(self.sprites)
        for sprite in self.sprites:
            stream << self.part
            stream << self.index

    def load_bad(self, stream):
        stream >> self.stage
        spriteCount = 0
        stream >> spriteCount
        self.sprites = []
        for x in xrange(0, spriteCount):
            part = 0
            index = 0
            stream >> part
            stream >> index
            self.sprites.append(TreeSprite(part, index))
        self.construct()

    def pack_bad(self, packet):
        self.save(packet)

    def unpack_bad(self, packet):
        self.load(packet)

def useSeed(plant, args):
    #First calculate the position of the new tree and see if it is ok
    position = args["position"]
    ground = World.get().layer["ground"]

    #If the tile clicked on is solid as well as the tile above it then do not allow seed planting
    tilePos = ground.getTilePosition(position)
    if ground.getTile(tilePos) is not 0 and ground.getTile(Vector(tilePos.x, tilePos.y + 1)):
        return

    tilePos = ground.getTileInDirection(position, Vector(0, 1))
    verticalPos = tilePos.y * TileSystem.TILE_SIZE
    if verticalPos > (position.y + 2 * TileSystem.TILE_SIZE):
        return
    position.y = verticalPos

    game = Game.get()
    contentId = game.mod.getContent(plant).id
    tree = game.entity.create(contentId, True)

    tree.modularRender.setPosition(position)
    print "Created new tree at (%d, %d)" % (position.x, position.y)
    World.get().layer["active"].add(tree)
    args['decrement'] = 1



Lastly, I wanted to throw in the grass code which I’m happy about because I was able to write it without making any engine code changes. There is still in the works but I have the basics down. I will likely do the same for the grass that I did for the tree and have a base Grass class and allow for inheriting and changing the properties without having to worry about the logic.

Syntax Highlighted Source
Code:
name = "Grass"

class GrassImage(object):
    def __init__(self, path, height):
        self.path = path
        self.height = height

class GrassSprite(object):
    def __init__(self, index):
        self.index = index
        self.sprite = Sprite()

class Grass(object):
    images = [GrassImage("mods/base/plant/grass.png", 5)]
    sides = []
    growthTimeMin = 2 * 60 * 1000 #2 minutes
    growthTimeMax = 5 * 60 * 1000 #5 minutes
    spreadTimeMin = 8 * 60 * 1000 #8 minutes
    spreadTimeMax = 10 * 60 * 1000 #10 minutes

    def __init__(self, entity, organic):
        print "Creating grass."
        self.organic = organic
        if not Game.get().network.isHost():
            return
        entity.interactable.add("removeSupport", self.onRemoveSupport)
        grass = GrassSprite(random.randint(0, len(self.images) - 1))
        image = self.images[grass.index]
        grass.sprite.position.y = -image.height
        self.organic.setImage(image.path, grass.sprite)
        self.grass = [grass]
        self.construct()
        self.addGrowthTimer()

    def addGrowthTimer(self):
        Game.get().timer.add(random.randint(self.growthTimeMin, self.growthTimeMax), self.grow)

    def grow(self):
        self.addGrowthTimer()
        world = World.get()
        ground = world.layer["ground"]
        if len(self.grass) >= 30:
            #TODO: Check to see if the grass can spread and create a new grass entity
            return
        #The grass is store from left to right [leftmost:rightmost]
        #First check to see if the leftmost grass can spread more left
        self.growOutward(ground, 0, -1)
        self.growOutward(ground, len(self.grass) - 1, 1)
        self.construct()

    def growOutward(self, ground, index, direction):
        grassSprite = self.grass[index].sprite
        position = grassSprite.getPosition()
        position.x += TileSystem.TILE_SIZE * direction
        position.y += TileSystem.TILE_SIZE
        tilePos = ground.getTilePosition(position)
        if ground.getTile(tilePos):
            tile = ground.getTileComponent(tilePos)
            if 'soil' in tile.getGroups() and random.randint(0, 1):
                #Soil! We found it and passed the random test. Lets add it!
                grass = GrassSprite(random.randint(0, len(self.images) - 1))
                image = self.images[grass.index]
                grass.sprite.position.x = grassSprite.position.x + (TileSystem.TILE_SIZE * direction)
                grass.sprite.position.y = grassSprite.position.y
                self.organic.setImage(image.path, grass.sprite)
                if direction is -1:
                    self.grass.insert(0, grass)
                else:
                    self.grass.append(grass)

    def isTouchingTile(self, ground, tilePos):
        grassTile = ground.getTilePosition(grass.sprite.getPosition())
        return grassTile.x == tilePos.x and grassTile.y + 1 == tilePos.y

    def onRemoveSupport(self, args):
        tilePos = args["tilePos"]
        ground = World.get().layer["ground"]
        self.grass = [grass for grass in self.grass if not self.isTouchingTile(ground, tilePos, grass)]
        if self.grass:
            self.construct()
        else:
            #No more grass - go ahead and destroy this grass.
            parent = self.organic.getParent()
            if parent:
                parent.destroy()

    def construct(self):
        self.organic.onConstruct([grass.sprite for grass in self.grass])

def createGrass(entity, organic):
    return Grass(entity, organic)

add(ModularRender())
add(Interactable())
add(Organic(createGrass))



Thanks for reading through all of this! I know it is a lot. I’m interested to hear what you think of this.
Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #12 on: June 30, 2012, 09:39:47 PM »

Showing off parallaxing background in this newest video.



Also got a new mockup of the game which is actually almost all implemented. Just no monsters and lighting yet.

Logged

melos han-tani
Level 10
*****


View Profile WWW
« Reply #13 on: June 30, 2012, 10:07:03 PM »

i cant remember if ic ommented on this

im tired.

but i am following this because i think it's neat. the rpg aspect ++++


alright kee pgoing good luck good work!
Logged

Working on Anodyne 2! https://forums.tigsource.com/index.php?topic=65359.0 Made Anodyne, Even the Ocean, All Our Asias
Conker534
Guest
« Reply #14 on: June 30, 2012, 10:10:05 PM »

Beautiful BG
Logged
jmcmorris
Level 1
*



View Profile WWW
« Reply #15 on: July 01, 2012, 11:07:28 AM »

We are putting together a Kickstarter campaign right now. In order to build some momentum before launch and decided to try launchrock.

Visit this site to sign up and help spread the word.

We have some cool incentives going on - such as being able to get into early beta or have our artist draw an crea-style avatar for you. Such as this...




@Gabriel Thanks! As we wrap up functionality we are going to focus on bring more atmosphere into the worlds.

@seagaia Haha, you haven't commented before. Thanks for taking the time to. Smiley

@Conker Thanks!
Logged

ANtY
Level 10
*****


i accidentally did that on purpose


View Profile WWW
« Reply #16 on: July 02, 2012, 09:28:33 AM »

Corrupted llama forced me to subscribe to this thread  No No NO
Logged

jmcmorris
Level 1
*



View Profile WWW
« Reply #17 on: July 02, 2012, 09:32:10 AM »

Haha, that is what I employed him for. Cool
Logged

sb3dgraph
Level 2
**



View Profile
« Reply #18 on: July 02, 2012, 10:37:15 AM »

Beautiful trees. They look very clean and tidy.
Logged
SolarLune
Level 10
*****


It's been eons


View Profile WWW
« Reply #19 on: July 02, 2012, 04:53:32 PM »

Yeah, I like the art style, it looks nice and clean. Following!
Logged

Pages: [1] 2 3 ... 8
Print
Jump to:  

Theme orange-lt created by panic