Giter Site home page Giter Site logo

timondavis / cm-check Goto Github PK

View Code? Open in Web Editor NEW
1.0 2.0 0.0 499 KB

A flexible, dynamic way to do 'checks' like you'd have in a classic RPG. Subscribe to events that let you affect the outcome of every check from any perspective.

License: Other

TypeScript 99.73% Dockerfile 0.27%

cm-check's Introduction

CM-Check

Check is a suite of lightweight, customizable tools for game developers. The project was developed to serve the simple purpose of making it easy to execute Ability Checks, much like you might find in most modern Role Playing Games (RPGs), but offer the extensibility and customization developers should expect.

Running a basic check

In essence, the concept is this: Dice are rolled against a target number and compared with the result, and the success of each 'check' is then determined.

Benny the Barbarian is going to need to knock down that wooden door before she can escape the evil wizard's cave. She needs to pass a Strength check with a target of 14. If she succeeds, the door will be knocked off of its hinges and she will be free!

Creating and Executing a Basic Check

According to the classic d20 rules, characters making a check need to roll 1d20 ( one 20-sided die ) and the result needs to be higher than, or equal to 14. Lets express this in code.

// Invoke the CheckExecutor class, the brain center of the system.
var CheckExecutor = require( 'cm-check' );

// Create a new check;
var strengthTest = CheckExecutor.generateCheck();

strengthTest.setTarget( 14 ); // Check Target # = 14
strengthTest.addDie( 1, 20 ); // Check Die Rolled = 1 die with 20 sides 

// Execute the Check
CheckExecutor.execute( strengthTest ); // Roll dice, do other processes...

// Get Result and print to console
if ( strengthTest.isPass() ) { console.log( 'Pass!' ); }
else { console.log( 'Fail :(' ); }

Default Checks

The following checks can be generated out of the box:

  • SimpleCheck - 'simple' : A simple check with no configuration
  • D20AttributeCheck - 'd20-attribute' : A check which handles standard D20 SRD attribute checks. Automatically applies the appropriate roll modifier for the given attribute score. Automatically registers 1d20 to the roll.

To create a new check:

	var CheckExecutor = require( 'cm-check' );
	var simpleCheck = CheckExecutor.generateCheck( 'simple' );	 // Simple Check (also works with no string provided)
	var d20AttributeCheck = CheckExecutor.generateCheck( 'd20-attribute' ); // D20 Attribute Check

Modifiers

Right of the box, you can generate Modifiers for the check. You can add or subtract values from Check Results, Check Targets, as well as add or remove die from the check itself.

By adding modifiers to a check, you can account for factors that affect the outcome of the check. Consider the following scenario:

After rolling a 12 on their Strength Check when attempting to knock down the door, failing to hit the target of 14, Benny the Barbarian still needs to get the door open somehow. She drinks a potion of extra strength, which will add 3 points to her next Strength Check ( Strength Check +3 ).

Applying a Modifier

To make a second check, we can simply add a modifier to the same check and run it again.

var CheckExecutor = require( 'cm-check' );
var strengthTest = CheckExecutor.generateCheck();
var strengthModifier = CheckExecutor.generateModifier( 'result' );

strengthTest.setTarget( 14 );
strengthTest.addDie( 1, 20 );

// Configure strength modifier
strengthModifier.setName( 'Potion of Strength' );
strengthModifier.setValue( 3 ); // 

// Apply modifier
strengthTest.addModifier( strengthModifier);

// Execute the check
CheckExecutor.execute( strengthTest );

// Get result
if ( strengthTest.isPass() ) { console.log( 'PASS' ); }
else { console.log( 'FAIL' ); }

If you prefer daisy-chaining and consolidating your code a bit, The same scenerio can be expressed as

var CheckExecutor= require( 'cm-check' );

var strengthCheck = 
	CheckExecutor.generateCheck()
    .setTarget( 14 )
    .addDie( 1, 20 )
    .addModifier( 
        CheckExecutor.generateModifier( 'result' )
        .setName( 'Potion of Strength' )
        .setValue( 3 ));  // You can supply number | string (numeric) | number[] or string (numeric)[] for value

Check.execute( strengthCheck );

if ( strengthTest.isPass() ) { console.log( 'PASS' ); }
else { console.log( 'FAIL' ); }

Default Modifiers

The following default modifiers are available:

  • ResultModifier - 'result' : Affects the result generated by the Check roll
  • TargetModifier - 'target' : Affects the target of the Check
  • DieModifier - 'die' : Affects the die used to execute the roll, or the die after the Check is rolled.

To create a modifier:

var CheckExecutor = require( 'cm-check' );

var ResultModifier = CheckExecutor.generateModifier( 'result' ); // Result Modifier
var TargetModifier = CheckExecutor.generateModifier( 'target' ); // Target Modifier
var DieModifier = CheckExecutor.generateModifier( 'die' );		 // Die modifier

Note also that you can get all modifiers applied to a check with the .getModifiers() method on the check.

Advanced Usage

We've been over how to execute a simple check, but Check is easily extensible to handle more customized scenarios as well. Let's review these features below.

Hooks and Phases

When executing a check, 4 event hooks are fired, giving any other participating javascript a chance to interject and affect the check before it has finished execution.

The hooks are as follows:

  • '{checkTypeName}_modifier' : This gives you an opportunity to affect things right before the modifiers are applied. There is a _modifier hook before and after the roll, you can use phases to define which hook you mean to target (default phase is 'after' )
  • '{checkTypeName}_roll' : This hook triggers its listeners to fire after the modifiers have been applied, but just before the die on the check are rolled.
  • '{checkTypeName)_finish' : This hook fires and invokes subscribers after the final wave of modifiers has been applied after the roll. It is the last step in the check evaluation before accounting for changes in the die.

Why use custom hooks?

Custom hooks allow you to make global changes to some or all checks at the global check execution level. For instance, lets consider the following scenario:

Suddenly, the entire mansion is filled with horrific shrieks of maniacal laughter! The sound is so distracting that all members of the party - and monsters! - will take a -4 penalty to all combat checks until the laughter subsides.

To express this idea in code, we will register a new event reaction method, or Hook, that will be fired before any other modifiers are applied to any check of a given type that will be executed.

var CheckExecutor = require( 'cm-check' ); 

var checkType = 'd20-attribute'; // We're targeting d20 attribute checks specifically, so we'll want to include the typename.  You can also get the typename by making a new check instance and using the .getType() method.

// Now let's register a new callback function for the _finish hook
CheckExecutor.on( checkType + '_finish', function( check, phase ) {
    
    check.addModifier( CheckExecutor.generateModifier( 'Maniacal Laughter Penalty', -4 ) );
});

That's it! When we run any type of D20 check through the CheckExecutor, this modifier will now be automatically applied.

#####Check Hook Callback Signature

Note in the function above that the callback function for each of these interrupts has two parameters. The 0th parameter is the check object itself, which you may modify as you see fit. The 1st parameter is the phase string, which tells you which phase is currently being executed (a string).

The function need not return any values, they will get lost in the pipeline and not used.

Phases

As each hook is fired and it calls all of the registered callback functions, it passes along the phase to each callback. Also, each modifier registers itself as belonging to a certain phase 'before' or 'after' at the time of this writing.

Customization and Extension in Typescript

This library is built in typescript and transpiled into node.js - If you would like to use typescript to extend any part of this library, you'll find you should be able to do so.

With typescript, you will be able to register new types of Checks and Modifiers with whatever properties and behaviors you wish. The system is designed to make extension very simple using classic OOP architecture to enforce core rules and allow you freedom as much as possible.

You can get access to all of the classes provided on the CheckExecutor.Class object.

WTFPL

cm-check's People

Contributors

timondavis avatar

Stargazers

 avatar

Watchers

James Cloos avatar  avatar

cm-check's Issues

DieBag could use some more features

  • Push/Pop new die instances onto stacks?
  • There's no way to add a die as an object to the bag. Perhaps there should be?
  • How can the management of die in a collection be more simple, managable and/or intuitive?

V0.2.0 refactor

There are a few funny placements in the code and a few docblocks are missing above public/protected functions. Refactor before publishing v1.0.0

Add capability to sort die bags and search algorithmically

Right now die bags are unsorted other than by the # of sides they contain. Create a sorting algorithm/strategy which will efficiently keep die ordered as needed, or sort by other means if more than one means makes sense.

Once sorts are in place, implement rational searches on die that do not depend on brute force to find their targets.

Add namespacing

There is no namespacing on the project right now. The project needs namespacing that corresponds to the file directory structure

Publish product to NPM

Find out what is necessary to implement a library to NPM and execute. Note that the output must play nice with both typescript and standard node.js usage.

Consider encapsulation of CheckExecutor into check

It seems a little unintuitive to have to first make a CheckExecutor in order to process a Check, especially given the existance of the check.check() method. Consider enapsulating the CheckExecutor somehow, in a way that still works as intuitively (or better) than the system does now.

Refactor of Modifiers

Modifiers derive from the same parent class and are, at the time of this writing, generated by factories. But the interface for working with these items is different, and each type of Modifier is stored in its own bucket.

There's no point in implementing a generic interface if some instances need to be stretched beyond simplicity in order to use is implementation. Refactor so that all modifiers are stored and processed from the same bucket, and each modifier can be invoked by providing a string value or an array of string values.

Note that the Die Modifier, in order to use strings as values, needs some sort of a string interpreter in order to convert coded strings to result in die being added or removed from the check's die bag.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.