Cats update 1 (One Game a Month, March)

Published on 2018-03-14.

The Project

It’s about the halfway mark for OGAM March 2018. Now is a good opportunity to check progress and recalibrate efforts around the second half of the month.

The March One Game a Month entry takes an idea that came from a brainstorming session with my friend and roommate M. a few months ago. The idea: feed cats to the benefit or detriment of their health.

There are many virtual pet games, and my experience with them minimal. I had the original Dogz, but was too old for Tamagotchi by a few years. I have the impression the genre as a whole focuses on fantastical elements to keep the user interested, and there may be many parts of ordinary pet interactions yet to be explored in a game.

Here’s the list I jotted after the OGAM March theme of PERMANENCE was announced:

  • Satiation. Cats may be more satisfied by some foods than others.

  • Nutrition. Different foods augment cats’ attributes in different ways.

  • Food preference. Cats prefer some foods over others.

  • Breeding. Cats have the opportunity to breed with other cats and produce offspring with attributes from both parents.

  • Religion. Cats may have religions that affect their demeanor and their relationships with other cats.

  • Weight management. Overfeeding cats will increase weight, but diminish the other benefits of food. Weight too low/too high causes death or other health problems.

  • Permadeath. When a cat dies, a headstone is created in the yard for it. Passed cats may deliver the occasional boon or burden to the player from beyond the grave.

  • Equipment upgrades to incorporate idle game elements. e.g. auto-feeder.

Here’s the plan of record, which I created around the same time as the previous list and have been working from daily. Going forward, I’ll probably just use diffs in these blog posts.

Feature planned for future/maybe
================================

- Human interaction with cats.
- A friendly tutorial.
- Breed with neighborhood cats.

Features planned for OGAM deadline
==================================

- Cat feeding and care.
- Day and night cycle.
- Permanence: every character is permanent.
- Cat-racter generation.
- Religion/horoscopes.
- Two music tracks.

To do
=====

Future/maybe
------------

- Communicate changes to user with a popup or scrolling marquee.
- Cat is more/less likely to eat food based on preference.
- Food degrades/spoils over time?
- Some food is turned into waste and cats poop.

2018-03-28 milestone
--------------------

- Support metric units.

2018-03-21 milestone
--------------------

- Add save/load game state functionality.
- Add breeds.
- Bug: death animations are visible from other scenes.
- View legacy of passed cats.

2018-03-14 milestone
--------------------

- Add pause functionality.

Finished
========

2018-03-10
----------

- Add log view to show a full screen of log messages.
- Show last few messages of history log on main screen.
- Add a history log, so the user can review the events of the past.
- Add keyboard help screen.
- Hunting consumes energy and cat can only hunt with enough energy left.
- Sleep restores energy.

2018-03-09
----------

- Cats gain toxicity when eating foods that carry it.
- Cat can hunt to get its own food.
- Add inventory management of food; food is now a finite resource.
- Create new cat when existing one dies.
- Add messages on cause of death.
- Add cat age (months and years).
- Cat leaves headstone in yard after death.

2018-03-08
----------

- Cat gains weight when eating and loses weight when not eating.
- Cat dies outside of a healthy weight range.
- Randomly assign name, sex, and religion to new cats.

2018-03-06
----------

- Cat faces the correct left/right direction based on direction of movement.
- Support tile rotation/mirror for map.
- Cat stops moving with wall collision.
- Cat wanders around the room.
- Day and night transition.
- Draw yard floor tile.
- Draw grave head stone.
- Switch between house and yard views.
- Show cat statistics in a UI gadget.

2018-03-05
----------

- Add floor tiles to scene.

2018-03-04
----------

- Add food dish to scene.
- Present feed window when 'f' key is pressed.
- When user selects a food from the window, feed the cat and augment
  its statistics.
- Add a coffee table to the scene.
- Add a refrigerator to the scene.
- Add windows to the scene.

Here’s what the game looks like today. These are cats in a house.

../_images/cat_mahlzeit_2018-03-12_0.png

I turned up the challenge level and created a grim scene pretty quickly. These are the headstones of the deceased in the back yard.

../_images/2018-03-13_graveyard.png

What’s emerged feels like a cat hunting simulation with roguelike elements. I wanted this kind of twitch cat digestion management idea to work and be entertaining, but I started to stress about balancing it. I realized that, while hardcore cat survival sounds like it could be pretty fun, it’s probably not the kind of game I’m inspired to make and play right now. If I can focus on just a few interesting aspects of feline existence this month, hunting prey to keep that digestive machine moving is probably not near the top.

For the next few days, I’ll play around with the more cozy facets of day-to-day life for a cat: sleeping, eating too much and getting fat, getting into fights with other cats over food and toys, and manipulating humans.

The Tech

I introduced ng, the engine I’m writing for all OGAM 2018 efforts, in the previous post. Here’s a look at how ng changed to support the work over the last few days. Again, this is the entire plan file thus far and I’ll probably show diffs in future posts.

To do
=====

- Reusable logging configuration for each module.
- Support multiplicative color modification of entities.
- Add collision info to event system.
- Implement Python configuration DSL.
- Better classes around event system.
- Add/remove debug and performance statistics with 'z' key.
- Support center point (pivot point) for rotation.
- Simple IPython integration.
- Annotation-based profiler.
- Add copyright/licensing info to files on build.

Finished
========

2018-03-12
----------

- x/y entity scaling.
- Frame animation support.

2018-03-11
----------

- Support getting mouse cursor position within window.

2018-03-10
----------

- Bug: Fix child gadget id assignment problem.
- Get gadgets by name.
- Add and delete timers by name (named timers).
- Focus new nui text area controls and dismiss any keypress.

2018-03-09
----------

- Add support for playing sound effects in WAV format.
- Add support for z coordinate so user can control rendering order (2d depth).
- Add "visible" attribute of entity to determine whether or not it is rendered.

2018-03-07
----------

- Cleanly shutdown SDL on exit.
- Abstract SDL_RenderClear, etc. operations.

2018-03-06
----------

- Implement background color for nui gadgets.
- Support entity horizontal/vertical flip.
- Support entity rotation.
- Stop objects that collide with solid objects.

Since ng is a new engine, most of the current work is on essential features that you would find in any 2D game.

Here are a few highlights of the new features and how to use them with the ng API. You’ll see references to self._ng, which is an instance created outside of the code example. I may add more comprehensive examples in future posts.

Collision detection

Having game state react in some way to two entities touching one another is ubiquitous, and I had to add this so cats can wander around without walking through walls. Here’s how this functionality looks in isolation. When any of the moving blocks touch the center stationery block, they stop.

../_images/2018-03-13_collision.gif

Here’s the code fragment needed to set up this little test.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
block1 = self._ng.entity_create(name='block1', position=(250, 250),
    size=(128, 128), texture={'name': 'block', 'rect': (0, 0, 128, 128)},
    collides=True)
block2 = self._ng.entity_create(name='block2', position=(1, 250),
    size=(128, 128), texture={'name': 'block', 'rect': (0, 0, 128, 128)},
    collides=True, color_mod=(255, 100, 100))
block3 = self._ng.entity_create(name='block3', position=(250, 1),
    size=(128, 128), texture={'name': 'block', 'rect': (0, 0, 128, 128)},
    collides=True, color_mod=(255, 100, 100))
block4 = self._ng.entity_create(name='block4', position=(600, 250),
    size=(128, 128), texture={'name': 'block', 'rect': (0, 0, 128, 128)},
    collides=True, color_mod=(255, 100, 100))
block5 = self._ng.entity_create(name='block5', position=(250, 600),
    size=(128, 128), texture={'name': 'block', 'rect': (0, 0, 128, 128)},
    collides=True, color_mod=(255, 100, 100))

block2.velocity.x = 1
block3.velocity.y = 1
block4.velocity.x = -1
block5.velocity.y = -1

Setting the collides attribute on an entity adds it to collision processing. The collision processing in ng is currently very primitive. Each frame, each entity’s position is updated according to its velocity. After this is done, entities with collides are all bounding box tested against one another, and a list of collided entities is created. After all tests are complete, the collided entities are moved back to their position at the beginning of the frame.

This is enough physics to finish Cats. I’m not sure ng should resolve entity positions after collision, though. After Cats is finished, I may change it to fire an event with collision details and let the programmer decide what to do with them.

Frame animation

Frame animation is an essential 2D art technique, and I had to add it to bring some cats to life. Here’s a simple animation test.

../_images/2018-03-12_animation.gif

Here’s what’s necessary to do that.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Definitions

ANIM_FRAMES_CAT_WALK = {'name': 'walk', 'frames': 4}
ANIM_FRAMES_CAT_STOP = {'name': 'stop', 'frames': 1, 'first': (2, 0)}

ANIM_INFO_CAT = {'name': 'a_i_cat', 'texture': 'cat_walk',
    'texture_origin': (0, 0),
    'frame_size': (32, 16), 'animation_frames': ['walk', 'stop']}

ANIM_CAT = {'name': 'a_cat', 'info': 'a_i_cat', 'frame_duration': 0.15}

CAT = {'name': 'cat', 'position': (200, 300, -1), 'size': (128, 64),
    'texture': {'name': 'sheet', 'rect': (192, 0, 128, 64)},
    'velocity': (-1, 0), 'collides': True, 'animation': 'a_cat'}

# Code to create objects

self._ng.anim_frames_create(**config.ANIM_FRAMES_CAT_WALK)
self._ng.anim_frames_create(**config.ANIM_FRAMES_CAT_STOP)
self._ng.anim_info_create(**config.ANIM_INFO_CAT)
self._ng.anim_create(**config.ANIM_CAT)

The definitions section specifies all the data needed at runtime. The code section shows all the calls needed to create the animated entities. The first three create calls are one-time setup steps, and the fourth could be called multiple times to create any number of animated entities.

This syntax is slightly different than in the previous example. Here, we create dicts to hold the necessary data, and then send them as keyword arguments to the creation methods. This pattern separates executable code from data; in Cats, I have configuration data in a separate configuration file for easy modification.

I’m pretty happy with this syntax, but I plan to add a cascading entity creation feature to make it briefer. The entity_create call will take care of all the supporting data structures, and that will make the first three creation calls unnecessary.

Summary

Thanks for reading. I’ll post another update next week.