Arbitary Combat System

// A sample (GS)script for an arbitary combat system.
// The attacker selects a target to attack, and the target gets two choices
// They can try evade damage entirely by using an AGILITY buffed roll
// Or they can BLOCK which will subtract their block value from the damage
// (minimum damage 1).  If the attacker rolls a 20 and lands damage
// (of any kind) it is doubled as a critical hit.  We report this as a
// "mighty swing" in the output

// have the caller select a target
Character target=gsSelectCharacter(CALLER,"Select a target to attack");

// have the target select a defence type
String defencetype=gsGetChoice(target,CALLER+" is attacking you, pick a defence type!",["Dodge","Block"]);

// do some attack rolls ; the attacker gets their "ATTACK" attribute added
// just because otherwise this code biases the defender who gets their bonus

Integer naturalattackroll=gsRand(1,20);
// note we force this KV into an Integer, naturally it's a string and that
// will ruin the maths later.
Integer attackvalue=gsGetKV(CALLER,"Characters.Attack");
Integer attackroll=naturalattackroll+attackvalue;
Integer defenceroll=gsRand(1,20);
// only gets a bonus if "dodge" mode
if (defencetype=="Dodge") {
  Integer naturalagility=gsGetKV(CALLER,"Characters.Agility");
  defenceroll=defenceroll+naturalagility;
}

// and calculate the attack
String output="";
Integer originaldamage=gsRand(1,6); // 1-6 damage
Integer damage=originaldamage; // for debugging, so we can see the original
if (defencetype=="Dodge") {
  // all or nothing attack
  if (defenceroll>attackroll) {
    // miss
    output=" swings at "+target+", but misses!";
    damage=0;
  } else {
    // hit...
    // is it a crit?
    if (naturalattackroll==20) {
      output=" swings mightily at "+target;
      damage=damage*2;
    } else { output=" swings at "+target; }
  }
}
if (defencetype=="Block") {
  // defence roll does nothing, we just take damage.  maybe double.
  // double it before the block value.  you may wish to do it after
  // which would make the minimum crit damage blockable to 2 rather than 1
  output=" strikes against "+target+"'s shield";
  if (naturalattackroll==20) {
    damage=damage*2;
    output=" strikes mightily against "+target+"'s shield";
  }
  Integer blockvalue=gsGetKV(CALLER,"Characters.Block");
  damage=damage-blockvalue;
  if (damage<1) { damage=1; } // cap the reduction to 1 damage
}

// add damage to the message, if any was done
if (damage>0) {
  output=output+", dealing "+damage+" points of damage";
}

Integer discard=gsSayAsChar(CALLER,"/me "+output);
// and inflict the damage.  Need an API call for this :P
// this ought to work for now
discard=gsAPIX(CALLER,"Roller.rollDamageAgainst",[target+"","1","1","1","1","1","0",damage+"",CALLER+output]);