talkgroup

maiki does an RPG Game Kata

Continuing the discussion from Practice a bunch of random katas:

Here’s the deal: I don’t “code”, per se. But I certainly hack things together. So rather than choosing a language, I’m choosing WordPress, as a web game. I feel it gets across the same wisdom. :slight_smile:

Mirror’d at mirror/kata-catalog - katas/RPG Combat.md at master - kata-catalog - allthe.codes.

Background

This is a fun kata that has the programmer building simple combat rules, as for a role-playing game (RPG). It is implemented as a sequence of iterations. The domain doesn’t include a map or any other character skills apart from their ability to damage and heal one another.

Instructions

  1. Complete each iteration before reading the next one.
  2. It’s recommended you perform this kata with a pairing partner and while writing tests.

Writing tests… ha!

Easier to copy from the raw file at https://allthe.codes/mirror/kata-catalog/raw/branch/master/katas/RPG%20Combat.md

Iteration One

  1. All Characters, when created, have:

    • Health, starting at 1000
    • Level, starting at 1
    • May be Alive or Dead, starting Alive (Alive may be a true/false)
  2. Characters can Deal Damage to Characters.

    • Damage is subtracted from Health
    • When damage received exceeds current Health, Health becomes 0 and the character dies
  3. A Character can Heal a Character.

    • Dead characters cannot be healed
    • Healing cannot raise health above 1000

Oh yeah, I need to spin up a staging site, to build these out. But I can describe my ideas here for when I get to that point.

Sounds like a post type. That means the other points will be fields of some type.

Health is a number field, as is level. Alive or Dead, rather than a binary toggle, I’m gonna add as a limited taxonomy. Makes it easier to lean on WordPress to generate views to look at characters. :slight_smile:

Okay, so we need some kind of function to allow one character to deal damage. The requisites:

Sans any other context, I’ll just create a form that targets a post, and runs a function to subtract an amount from the health field. Will have to think how I want to check for 0 health; probably check if result is 0 or less, and if so switch tags and set to 0.

Basically the same function as damage, but adding.

Aside: I once read an RPG engine that used a damage algorithm, and to heal a target you fed it a negative number. :rofl:

Make it “dead” tagged posts can’t be heal-functioned. And do a check for health on the upper end, as well. :thinking:

I’ve created a character post type, with a health field and taxonomy terms as statuses, such as “alive” or “dead”.

It took me a bit to figure out how I wanted to update/apply damage to a character, but I’m making some headway there. :slight_smile:

I’m onto the command, so I’m studying “command pattern” and other design patterns.

1 Like

It’s maybe my favorite pattern.

1 Like

Creating a form that offers two options: attack or heal. I decided either would roll a d100 and apply the number depending on the choice, subtracting for attack, and adding to heal.

I wrote a small function to populate a hidden field with the d100:

add_filter( 'gform_field_value_d100', 'roll_d100' );
function roll_d100( $value ) {
    $d100 = wp_rand( 1, 100 );
    return $d100;
}

References:

Next step is checking health state and setting up transitions. In my case because I’m lazy and using WordPress catagories (so I can auto-view all the characters by status without doing anything), I’ll check it health is 0 or below and switch out terms (alive and dead). If health is more than 1000, set it 1000. I’ll hook that into the form submission.

Okay, now I’m getting somewhere. I’m very pleased with the progress I’m making, though I haven’t quite figured out how to get change health I’m close!

Here’s my deal: I want to populate a list with characters to hit. I create a query to only show the category for “alive” (since those are the characters to attack or heal).

add_filter( 'gform_pre_render_2', 'populate_posts' );
add_filter( 'gform_pre_validation_2', 'populate_posts' );
add_filter( 'gform_pre_submission_filter_2', 'populate_posts' );
add_filter( 'gform_admin_pre_render_2', 'populate_posts' );
function populate_posts( $form ) {
 
    foreach ( $form['fields'] as &$field ) {
 
        if ( $field->type != 'select' || strpos( $field->cssClass, 'populate-posts' ) === false ) {
            continue;
        }
 
        // you can add additional parameters here to alter the posts that are retrieved
        // more info: http://codex.wordpress.org/Template_Tags/get_posts
        $posts = get_posts( 'numberposts=-1&post_status=publish&post_type=character&cat=2' );
 
        $choices = array();
 
        foreach ( $posts as $post ) {
            $choices[] = array( 'text' => $post->post_title . " HP:" . $post->health, 'value' => $post->ID );
        }
 
        // update 'Select a Post' to whatever you'd like the instructive option to be
        $field->placeholder = 'Select a Character';
        $field->choices = $choices;
 
    }
 
    return $form;
}

Having a difficult time taking a screen shot of the results, but it list their name and their current HP; very cool! It also gives us the post_ID of the character chosen as a value, which I want to use later.

add_action( 'gform_after_submission_2', 'set_post_content', 10, 2 );
function set_post_content( $entry, $form ) {
 
    //getting post
    $post = get_post( maybe_unserialize( rgar( $entry, '3') ) );
 
    //changing post content
    $post->post_content = 'success: ' . maybe_unserialize( rgar( $entry, '3') );
	
    //updating post
    wp_update_post( $post );
}

This was tough to figure out, but now I know what get_post, maybe_unserialize, rgar, and wp_update_post do now…

This was a tough nut to crack, because I missed this from the docs (Entry Object - Gravity Forms Documentation):

List Field

The List field type due to its complex setup compared to other field types (it has many rows and columns of data) it’s stored in serialized format, so you need to unserialize it first to access the data.

maybe_unserialize( rgar( $entry , '3' ) ); // unserialize values associated with list field 3

And of course it works! Currently, it just changed the post_content to read “success:” and the post ID.

So I think I can include all the math in this function, and just use the form to collect the IDs of both characters. I’ll generate a number, and depending on “attack” or “heal” I’ll apply the number to the targets current health, and save it to the character. Then I’ll add in checks for 0 or 1,000+ health. :thinking:

Woke up thinking: I can’t update health because that’s a meta field, so I need to call the appropriate function. Will try that later. :slight_smile:

Hey, that worked! Now I have damage working!

add_action( 'gform_after_submission_2', 'set_post_content', 10, 2 );
function set_post_content( $entry, $form ) {
 
    //getting post
    $post = get_post( maybe_unserialize( rgar( $entry, '3') ) );
    $health = $post->health;

    //modify health
  	$d100 = wp_rand( 1, 100 );
  	$health -= $d100;
  	$post->health = $health;
	
    //updating post
    update_post_meta( $post->ID, 'health', $health );
}

@tim, I was jazzed to discover -= to create a negative number. At this point “attack” and “heal” are a difference in negative/positive number.

I figure if/else for attack/heal, but was looking for pointers on a common pattern to handle this. Also, is there a feature in PHP that will number range check, for when checking health 0 or 1,000+?

add_action( 'gform_after_submission_2', 'set_post_content', 10, 2 );
function set_post_content( $entry, $form ) {
 
    //getting post and entry data
    $post = get_post( maybe_unserialize( rgar( $entry, '3') ) );
 	$health = $post->health;
	$action = rgar( $entry, '2' );
	
	//modify health
	$d100 = wp_rand( 1, 100 );
	if ( $action == "Heal" ) {
		$health += $d100;
	} else {
		$health -= $d100;
	}
	$post->health = $health;
	
    //updating post
    update_post_meta( $post->ID, 'health', $health );
}

Sorry, I’m editing this in an online IDE inside WordPress, so the tabs/spaces don’t convert well.

Ahem.

I got heal and attack working! Okay, this isn’t bad. I also realized something else: elsewhere in the WordPress system I’ve configured a field validation check in Pods, which checks the health field and never let’s it get above 1,000. That handles one portion of that check (for this iteration), so the last is a function to check for 0 health and “tag” a character as dead…

At this point my system has no checks on it: a character can just do damage over and over again. I’m not overly worried about it, but it is apparent I’ll need to address that eventually. Thinking on it, I don’t think I want the targeting component to have the function that can “dead” characters. But that will probably get moved into a component in a future iteration.

So I check if $health is 0 or less, if so set to 0, then… wp_update_post.

Haha, I’m really pleased I figured this out. Because I appreciate a lot more of how WordPress works, and feel I’ll be able to do more. Check this out!

add_action( 'gform_after_submission_2', 'char_interact', 10, 2 );
function char_interact( $entry, $form ) {
 
    //getting post and entry data
    $post = get_post( maybe_unserialize( rgar( $entry, '3') ) );
 	$health = $post->health;
	$action = rgar( $entry, '2' );
	
	//modify health
	$d100 = wp_rand( 1, 100 );
	if ( $action == "Heal" ) {
		$health += $d100;
	} else {
		$health -= $d100;
	}
	$post->health = $health;
	
	//check health and livability
	if ( $health <= '0') {
		$health = '0';
		$death = array(
		'ID' => $post->ID,
		'post_category' => array( 3 ),
		'post_content'  => 'So dead!',
		);
		wp_update_post( $death );
	}
	
    //updating health
    update_post_meta( $post->ID, 'health', $health );
}

The form currently has two fields:

  1. One asking to attack or heal
  2. A drop-down list of alive characters

The above code checks the action and adjust the d100 rolls accordingly, and then checks if health is 0 or below, sets it to 0, and updates the character post, the important part being the post_category array, in this case having “3”, which is “dead”.

Okay, I’ve handled this iteration… except I didn’t actually make “characters” do anything. But I’m most of the way there: I just need to include “interactor” fields to the forms, and then, um, report it. I’ve been giving some thought to that, how to show the player something is happening with a character. With WordPress we ought to have a cool revision or comment system to track what’s happening where. I’m sure at some point I’ll need to build in a small messaging system that creates a standard object I can pass around to different components of the game. For now I just need a battle log… :thinking:

I misunderstood what was happening before, and attributed it to Pods, but this functionality is validation on the edit post form. I have it set to not allow numbers larger than 1,000, but of course I can override that with a function, which I did when I was showing it off to Clover. :slight_smile:

Anyhow:

	if ( $health >= '1000') {
		$health = '1000';
	}

:sunglasses:

I’m just getting started! This is so much fun!

add_action( 'gform_after_submission_2', 'char_interact', 10, 2 );
function char_interact( $entry, $form ) {
 
    //getting post and entry data
    $post = get_post( maybe_unserialize( rgar( $entry, '3') ) );
 	$health = $post->health;
	$action = rgar( $entry, '2' );
	
	//modify health
	$d100 = wp_rand( 1, 100 );
	if ( $action == "Heal" ) {
		$health += $d100;
	} else {
		$health -= $d100;
	}
	$post->health = $health;
	
	//check health and livability
	if ( $health <= '0') {
		$health = '0';
		$death = array(
		'ID' => $post->ID,
		'post_category' => array( 3 ),
		);
		wp_update_post( $death );
		$death_comment = array(
		'comment_post_ID' => $post->ID,
		'comment_content' => 'This character has died!',
		'comment_author'       => 'Battlelog',
    	'comment_author_email' => 'battlelog@maiki.xyz',
		);
	}
	if ( $health >= '1000') {
		$health = '1000';
		$overheal_comment = array(
		'comment_post_ID' => $post->ID,
		'comment_content' => 'This character was overhealed!',
		'comment_author'       => 'Battlelog',
    	'comment_author_email' => 'battlelog@maiki.xyz',
		);
		wp_new_comment( $overheal_comment );
	}
	
    //updating health
    update_post_meta( $post->ID, 'health', $health );
}

I’m already figuring it out, how I want to build a commenting function that can collect a bunch of data from the transition, and compile a message; that way I can send one message to each object and it will do the right thing.

For now I just got to incorporate the actor into this interaction.

Okay, I did a thing where I also embed the form into the character pages, pull in the embedded post_id, and then send a comment to both target and source of each interaction. However, I’m obviously not going to keep it that way, I’d much rather have a full custom post type that I can query and use to generate neat narrative descriptions of combat. So…

I’m marking this interation complete! Time to move on to the second one!

Iteration Two

  1. A Character cannot Deal Damage to itself.

  2. A Character can only Heal itself.

  3. When dealing damage:

    • If the target is 5 or more Levels above the attacker, Damage is reduced by 50%
    • If the target is 5 or more levels below the attacker, Damage is increased by 50%

Okay, now I’m gonna think about breaking up my mega function into distinct blocks, and really nail down how I want to handle permissions (such as which object get to attack which objects). :thinking:

After chatting with @tim about event-driven design versus game loop design, I’ve decided to approach this differently. Which is to say, I’m not really trying to learn how to code an idea into the language of my choice, but I am going for something similar: achieving an idea with the tools I have/use. And when I read the katas I think of these complete ideas that I’d like to build.

One issue with this kata:

So I’m just gonna throw those out the window. Because the way I build apps is by holding a complete scope in mind, and then finding the sculpture in the stone.

New plan: I’m gonna read the full scope, and then describe how I’d build this as a slower, web-based game. :slight_smile:

When I look at what the characters will look like by iteration 5, it isn’t actually that complex, as a database object.

For characters we’ll need fields for:

  • health
  • level
  • status (alive, dead)
  • range based on weapon (melee, ranged)
  • faction

Factions don’t have any real metadata, they are just a permission for actions, so I might just make it a string or tag. If I tied factions to tags in WordPress, I’d be able to add them a lot easier, and let WP do the heavy lifting.

Things are non-people characters, the example given is a tree with 2000 HP (which is only twice as much as a person, so what does that say about HP…). Since they can’t otherwise interact aside from “taking damage”, they need a health field, and a status that can say “destroyed”.

:thinking:

That being the entirety of the kata, I’m definitely making factions and status custom taxonomies. health and level can be numbers. Not sure about range, since I might want to add weapons as toggable options (a character can switch weapons).

There’s one more object we need to deal with: messaging. In web games, that is basically the part that gets shown to the player. And because I prefer perfect knowledge strategy games with external random elements, I’m gonna make a “battle log” object that gets broadcast publicly (meaning everyone can see it).

Depending on how gameplay emerges, I might make another object to hold games together. :thinking:

When I first started this I thought I’d use Gravity Forms to hold all the math logic, because it’s pretty good at that. But when I look at all the objects, I realize I really only need a form to assist with targeting. That is to say, to assist the player in choosing what to do to which objects.

That isn’t to say GF isn’t doing a lot of lifting! I don’t have to design any forms, and there is a lot of validation and hooks Gravity Forms supplies I’ll be using.

Considering game mechanics, I wondered if it made sense to have two-way engagements. If it were time-based (meaning each person got to act every so often, instead of global turns), it would be easy to gang up on one character. But it might be neat to make combat an engagement, where initiating combat means both/all combatants take swings at each other. Maybe that makes ranged more interesting, it’s the only way to really “gank” someone. :thinking: