Categories
tech

Designing Random Encounters for my Vue RPG

So I’m nonetheless piddling away at my RPG in Vue.js. To be transparent, I’m by no means going to complete it. But you’ll examine it right here (Testing Vue.js Application Files That Aren’t Components) and right here (Making Monsters with JavaScript). Over the previous few months I’ve been slowly studying a fantastic ebook for RPG fanatics, The CRPG Book. It’s a assessment of over 400 video games during the last 40 years of laptop role-playing.

While studying the ebook, I’m finding some cool options that older video games had and that I ignored whilst rising up. (Or in all probability simply do not bear in mind.) A couple of video games have discussed the usage of encounters with easy “Choose Your Own Adventure” good judgment. Basically, you’re introduced with one thing and given a number of choices. No battle, simply easy possible choices.

The extra I considered this the extra I believed it might be a captivating a part of my sport. If you believe that there is a random likelihood of battle as you stroll round (a part of the core gameplay I need to have), then there generally is a smaller likelihood of a easy stumble upon. I’d believe those going down perhaps a few times consistent with gameplay so rather uncommon, however they’d be a wreck from the standard battle stumble upon.

I designed my machine with the next options:

  • Encounters are in an array, randomly selected.
  • Each stumble upon has exhausting coded textual content and is static.
  • Each enouncter has exhausting coded choices.
  • However, every possibility has random effects.
  • Results can have an effect on participant stats, for instance, draining well being or giving your gold.
  • Finally, every stumble upon has an not obligatory “prereq”. This is a ‘protection’ test to make issues slightly more straightforward for effects. So if a outcome drains 10 gold, I do not need to have to fret about unfavorable balances. Ditto for dying. While I’m nice with an stumble upon harming you, I did not need it to kill you. I will be able to say this level is one I’m reconsidering and would possibly roll again. For gold, I may just merely let it take your entire gold and depart you at 0, and for damaging encounters, it can be kinda amusing if some may just in truth kill you.

My information construction than looks as if so:

  • prereq: If handed, a string this is evaluated towards participant information, like “hp>10”. If false, this stumble upon can not occur.
  • textual content: The textual content of the stumble upon.
  • choices: An array of choices the place:
    • textual content: The textual content of the choice
    • effects: An array of effects in response to this feature the place one is randomly decided on. Each outcome has:
      • textual content: The textual content describing the outcome.
      • impact: An impact, if any, at the participant, additionally a string this is evaluated, like gold+=10.

Here’s an instance:

{ prereq:'gold>0 && hp>0', textual content:'You meet a beggar who asks for assist. He seems to be determined.', choices: [ { text: 'Give a gold coin to him.', results:[ { text: 'The beggar thanks you!', effect:'gold--' }, { text: 'The beggar thanks you, winks, and dissappears.', effect:'gold += 300'}, // it was a god or whatever in disguise { text: 'The beggar smirks and punches you!', effect:'hp--' } ] }, { textual content: 'Walk away.', effects:[ { text: 'The beggar spits at you!', effect:'' }, // no effect { text: 'The beggar growls and punshes you!', effect:'hp--' } ] }, ]
},

The JavaScript software has two primary strategies. The first returns a random stumble upon that is filtered via prereqs. A participant object is handed in (I’m now not the usage of TypeScript so what I in point of fact imply is a “easy object illustration” of the participant). The subsequent way takes a participant object, an stumble upon, and a decided on possibility. It figures out the random outcome and applies the impact. Here’s all the software.

import { misc } from './misc' const information = [ { prereq:'gold>0 && hp>0', textual content:'You meet a beggar who asks for assist. He seems to be determined.', choices: [ { text: 'Give a gold coin to him.', results:[ { text: 'The beggar thanks you!', effect:'gold--' }, { text: 'The beggar thanks you, winks, and dissappears.', effect:'gold += 300'}, // it was a god or whatever in disguise { text: 'The beggar smirks and punches you!', effect:'hp--' } ] }, { textual content: 'Walk away.', effects:[ { text: 'The beggar spits at you!', effect:'' }, // no effect { text: 'The beggar growls and punshes you!', effect:'hp--' } ] }, ] }, { prereq:'hp>0', textual content:'You pay attention a growl from at the back of you.', choices: [ { text: 'Put on a brave face.', results:[ { text: 'You seem to have scared off whatever was stalking you.', effect:'exp+=100' } ] }, { textual content: 'Run away', effects:[ { text: 'You run until your out of breath.' , effect:'' }, // no effect { text: 'You run, but trip and sprain your ankle!', effect:'hp--' } ] }, ] }
]
export const encounterMaker = { // given a participant ob, to find an stumble upon they may be able to do make a selection(participant) { let imaginableEncounters = information.clear out(d => { if(!d.prereq) go back true; let prereq = fixEvalString(d.prereq); go back eval(prereq); }); if(imaginableEncounters.duration === 0) go back null; go back imaginableEncounters[misc.getRandomIntInclusive(0, possibleEncounters.length-1)]; }, get to the bottom of(participant, stumble upon, selection) { if(selection >= stumble upon.choices.duration) selection = 0; let decided on = stumble upon.choices[choice]; let outcome = decided on.effects[misc.getRandomIntInclusive(0, selected.results.length-1)]; console.log('outcome for '+selection, outcome); if(outcome.impact != '') { console.log(participant); eval(fixEvalString(outcome.impact)); console.log(participant); } go back participant; } } // software serve as to mend eval string to incorporate participant
serve as fixEvalString(str) { str = str.substitute(/gold/g, 'participant.gold'); str = str.substitute(/hp/g, 'participant.hp'); str = str.substitute(/exp/g, 'participant.exp'); go back str;
}

The two strategies I described above are outlined as make a selection and get to the bottom of. Notice that I wrote a serve as, fixEvalString, that can be utilized via my prereqs and results to switch the participant. This seems like unhealthy code. I imply, eval is unhealthy on the whole. Given that I do know the “form” of my participant information I may just transfer to in a different way of doing this, however I’ll concern about that once I end the sport, which is, you already know, by no means.

I did construct a software to assist check this, and here is what it looks as if:

/*
Ray, run with: node -r esm check.js
*/ import { encounterMaker } from '../src/utils/encounterMaker' console.log('fundamental participant');
console.log(encounterMaker.make a selection({ gold:10, hp:10
})); console.log('deficient participant');
console.log(encounterMaker.make a selection({ gold:0, hp:10
})); console.log('useless participant');
console.log(encounterMaker.make a selection({ gold:10, hp:0
}));
console.log('---------------------------------');
console.log('fundamental participant get to the bottom of');
let participant = { gold:10, hp: 10, exp:200
};
let enc = encounterMaker.make a selection(participant);
console.log('selected enc', enc);
participant = encounterMaker.get to the bottom of(participant, enc, 0);
console.log('Player at finish', participant);
participant = encounterMaker.get to the bottom of(participant, enc, 1);
console.log('Player at end2', participant);

As you’ll see, I’ve were given a couple of make a selection calls and a couple of get to the bottom of ones. The output looks as if so:

fundamental participant
{ prereq: 'hp>0', textual content: 'You pay attention a growl from at the back of you.', choices: [ { text: 'Put on a brave face.', results: [Array] }, { textual content: 'Run away', effects: [Array] } ]
}
deficient participant
{ prereq: 'hp>0', textual content: 'You pay attention a growl from at the back of you.', choices: [ { text: 'Put on a brave face.', results: [Array] }, { textual content: 'Run away', effects: [Array] } ]
}
useless participant
null
---------------------------------
fundamental participant get to the bottom of
selected enc { prereq: 'gold>0 && hp>0', textual content: 'You meet a beggar who asks for assist. He seems to be determined.', choices: [ { textual content: 'Give a gold coin to him.', effects: [Array] }, { textual content: 'Walk away.', effects: [Array] } ]
}
outcome for 0 { textual content: 'The beggar thank you you!', impact: 'gold--' }
{ gold: 10, hp: 10, exp: 200 }
{ gold: 9, hp: 10, exp: 200 }
Player at finish { gold: 9, hp: 10, exp: 200 }
outcome for 1 { textual content: 'The beggar spits at you!', impact: '' }
Player at end2 { gold: 9, hp: 10, exp: 200 }

You can to find the entire repo at https://github.com/iandroid.eu/vue-demos/tree/grasp/grpg. I feel subsequent I’m going to take a stab and making a map. I’ve been hashing round some concepts for a couple of weeks now and I feel I’m able to place pen to paper as a way to talk.

Photo via Tommy Tang on Unsplash