User talk:Technomage

From Post-Apocalyptic RPG wiki

Jump to: navigation, search

Contents

Personal Task List

  • Rewrite the Programming/How_to_contribute to be more of a "road-map" for the department;
  • Add test XML scripts and screenshots for PyChan bug reports;

Draft Proposals

Continuous Integration Toolset

Published Code Metrics

I think that it would be instructive to have a set of code metrics published to the web that are updated with each code commit. Some useful metrics might include:

  • Unit test success/failure rates
  • Test coverage and code complexity (McCabe complexity metric)
  • Codebase size (e.g. number of lines, percent comments etc.)
  • Number of documented/undocumented functions and classes

In general, improving the scores of various metrics should (at least in theory) lead to better, more maintainable code. It would also give us a sense of which areas of the code need attention the most (e.g. a module might not have enough code documentation or not be thoroughly tested).

In terms of implementation pymetrics could be used to statically analyze our code and produce some of the metrics in a form that can be used on a website (it has the ability to write results to an SQL database). The test coverage and unit test success rate metrics would have to be generated by a more sophisticated tool (e.g. BuildBot). AFAIK there is no 3rd party tool that would display the results in a readable format, so we would have to program that part ourselves (which shouldn't be too hard).

Barra indicated that our codesion.com site does provide pre- and post-commit web hooks that could be used to automatically initiate code metric generation, so integration with our existing infrastructure should be fairly easy.

DialogEngine Evaluation

Dynamic Root DialogSection

TODO: add some content :P

Undoable DialogActions

After fixing a few bugs in the recent DialogEngine redesign it struck me that failing to execute a DialogAction could have serious repercussions that affect the rest of a dialog or a quest. For instance, consider a dialog response that causes the following actions to fire:

  1. Start quest "x"
  2. Give the player an item essential to completing quest "x"
  3. Modify a quest variable that allows the player to enter into dialog with NPCs related to the quest

In this example assume that the dialog response only appears when the player does not have quest "x" activated.

Now consider what happens if there is a bug in the code that prevents the NPC from giving the item to the player. The start_quest action fires and activates quest "x", but the player does 'not get the essential item and so cannot complete the quest. Depending upon the DialogEngine implementation the quest variable may or may not be modified (right now it does get modified - errors don't abort the evaluation of subsequent actions). Even if at a later date the bug is fixed any saved games will be useless because the player will be unable to reset the quest and get the item.

One possible solution would be to provide a method of undoing a DialogAction, just like the undo function of a word processor. In the event of an error, DialogAction parsing would immediately stop and then undo previously executed DialogActions within the same response. Most dialog actions should be easily undoable (e.g. if the action gives the player an item, simply give the item back to the NPC). It would probably have to be more sophisticated than that (e.g. what would happen if the player was in the middle of a lengthy dialog that is only accessible once?), but that's the basic idea.

Another possible solution would be to provide "buffer" objects for modifying the game state. It would work very similar to a frame buffer used to render graphics to the computer monitor: essentially the buffer objects would accept the state changes made by DialogActions, but those changes would not be applied until the buffer object updates the part of the game model it buffers. So, for instance, there would be a BufferPlayerCharacter object which would accept the DialogAction changes on behalf of the PlayerCharacter object, but in the event of an error the BufferPlayerCharacter object could simply discard those changes and no modifications would be made to the actual game state.

One-Shot and Conditional DialogActions

There may be many situations in which we might want the player to be able to see a particular DialogSection more than once, but only want to execute the DialogActions associated with that DialogSection once (or have its execution contingent upon some conditional test). A prime example is the meet action: the player can only meet an NPC once (provided we don't implement some kind of "amnesia" status condition!), but in the current implementation the meet command is run every time the player sees the root DialogSection (so may be run multiple times within the same conversation!). This isn't a problem currently since the meet DialogAction simply does nothing if the player has already met the NPC, but it does illustrate the problems we would likely encounter when implementing new actions in the future.

We would have to introduce some new syntax to enable conditional testing for individual DialogActions, something like the following:

SECTIONS:
-   ID: main_dialog
    SAY: "Foo!"
    ACTIONS:
    -   give_stuff:
        ARGS:
        -   SuperAwesomeShotgunOfDestruction
        -   ShotgunAmmo
        CONDITION: not pc.hasItem('SuperAwesomeShotgunOfDestruction')
    RESPONSES:
    -    TEXT: "Thanks Mister!"
         ACTIONS:
         -    goto: end

Unfortunately because of the way YAML works the new 'ARGS' keyword would be mandatory when conditions are used (compared with "- give_item: SuperAwesomeShotgunOfDestruction") so it would make the ACTIONS syntax a little bit more complicated. On the other hand, this new syntax is much more explicit (see #ACTIONS Syntax Enhancements) and so may be a desirable change.

My Workflow

I've been reviewing the various wiki articles about contributing code and the workflow that's been established for PARPG. Having had some real experience contributing to PARPG I think its time I solidified my own workflow, for my own sanity and for the sanity of everyone else that has to work with me! For the most part this should adhere to PARPG's established workflow.

0. Pick a Project

Check Trac and the wiki, the discussion boards, IRC, and my brain, preferably in that order. Discuss in IRC/discussion boards to make sure the project is worthwhile and somebody else isn't doing the same thing.

Once an acceptable project is chosen, update the appropriate sections of the wiki (ToDo, Active Code Proposals).

1. Discussion and Brainstorming

Jump on IRC and/or the discussion boards and start a discussion about the project, how it should be implemented and its implications. Post brainstorming and ideas to talk page

2. Design and Proposal Drafting

At this point I should have a good idea of where the project is going, so I should be able to draft a basic proposal, make a basic UML/flowchart diagram etc. and post them on my talk page. Solicit comments on IRC and discussion board if and when appropriate.

Some prototyping may be in order, but actual coding should be kept to a minimal at this stage.

3. Prototyping and Redesign

Now comes the fun part: prototyping the design. During prototyping it is almost a certainty that some elements of the original design draft will have to be rethought and revisited, so it is important to take some time to regularly update the proposal draft and seek feedback via IRC and discussion boards. This is an iterative design strategy, and it will likely take several cycles of prototyping and redesign to come up with something that works.

Code should be documented as it is written, but some sloppiness is to be expected at this stage of development. No unit tests or functional tests should be written at this point - I've tried some test-driven design approaches in the past and found that the interface changes far too rapidly to make them efficient. Instead, there will be lots and lots of ugly and disorganized debugging spaghetti code!

4. Publish Proposal

Once a working prototype is created, the design proposal should be cleaned up and moved to the relevant Active Code Proposal wiki page. Final comments should be solicited via IRC and/or discussion boards.

At this stage the design should be pretty solid, but if there are a lot of comments or new project requirements are realized it may be worthwhile to go back to stage 3 and redesign.

If the prototype is stable and pretty well fleshed out it should be committed to SVN so that others can test the new code and see if it meets the project requirements. Whether the prototype is commit-ready will depend upon how extensive the changes are and whether core subsystems are affected.

5. Document Code, Write Tests, Write User Documentation

Clean up existing code documentation and write documentation for all undocumented modules, functions, classes and methods.

Write unit tests for all public methods and functions, and write one or more functional tests depending upon the project.

Finally, if the project involved a major redesign of the public API some user document should be written or updated as appropriate.

6. Commit Code

Synchronize with SVN, deal with conflicts if necessary, grab an iced-tea and hit the big red button.

Be on hand for the next few days in case something goes wrong and I have to fix it.

Random Thoughts

Player Actions and Game Dialog

I've been reading some articles about game dialog and how it should be implemented (e.g. [[1]]). Probably a dangerous thing to do in the middle of polishing up the current DialogEngine implementation, since new ideas have begun to form.

It strikes me that the goal of dialog should be twofold in a game like PARPG:

  1. reveal the main plot and its subplots;
  2. immerse the player in the game world by allowing them to form dynamic relationships with the individuals that inhabit it, making the player responsible for his/her actions;

The current DialogEngine is essentially a "Hub-and-Spokes" type dialog system with some branching elements. The player can interrogate an NPC along pretty much every line of inquiry from the start and always has the option to start over back at the root dialog section. The branching comes in with response conditionals, which can open up new lines of inquiry and close others according to the game state and (presumably) the actions of the player. If used correctly this system can provide the player with a sense of responsibility for his/her actions by opening up new avenues of dialog, but ultimately it is still a "Hub-and-Spokes" type dialog system that constrains the type of responses an NPC can have toward a player's actions.

Consider this scenario: a player steals an apple from a vender and is caught. One of three things can happen with the current DialogEngine:

  1. the vender takes justice into his/her own hands and attacks the player, resulting in the death of either the vender or the player
  2. the player looses face with the vender, and will either be blocked from initiating further dialog with the vender or loose some lines of inquiry as a result
  3. nothing - the vender laughs about the situation and pretends that it never happened (and forgot about the other 12 times you tried to steal the apple!)

None of these options seem particularly satisfying to me. Option 1 definitely has permanent consequences for the player, but obviously not all NPCs should immediately attack the player for offending them! Option 2 is less extreme but lacks any real moral feedback - all of a sudden some dialog responses are no longer available to the player, not a particularly devastating realization. Option 3 is just plain silly.

An alternative approach would be to dynamically select the "hub" or root of the conversation based upon the player's actions. In the example above, the player would no longer be greeted enthusiastically by the vender but would instead be subjected to some rather harsh language with a limited set of responses. Dialog roots could be selected based upon the player's faction alignments, previous actions and quest decisions, or even if they are a little tipsy!

Of course defining multiple dialog roots/hubs means more writing investment, but ultimately I think the investment is worth the reward. Not every dialog would have to define multiple root sections, so it would be possible to focus on the core storyline NPCs.

But now its late and I'm simply ranting :p

Personal tools