lib/vhyl-rpg-lite-traits.t

Reference index ยท View on GitHub

   1  modify RpgLiteProfile
   2      lastCheck = nil
   3      lastCheckDraw = -1
   4      griefDays = 0
   5      phobias = []
   6  ;
   7  
   8  /** Immediate trusted Luck hook for a raw dice total; return the best of three and start play-time cooldown. */
   9  rpgLiteReroll(profile, original, dice, higherIsBetter)
  10  {
  11      local interval = profile.level('ridiculous-luck') > 0 ? 600 : profile.level('extraordinary-luck') > 0 ? 1800
  12          : profile.level('luck') > 0 ? 3600 : 0;
  13      if (interval == 0 || profile.playSeconds < profile.luckReady) throw new RpgError();
  14      rpgInteger(dice, 1, 100);
  15      local chosen = original;
  16      for (local attempt = 0; attempt < 2; attempt += 1)
  17      {
  18          local roll = 0;
  19          for (local index = 0; index < dice; index += 1) roll += rpgRandom.die();
  20          if (higherIsBetter ? roll > chosen : roll < chosen) chosen = roll;
  21      }
  22      profile.luckReady = profile.playSeconds + interval; profile.lastCheck = nil;
  23      return chosen;
  24  }
  25  
  26  /** Reroll the character's most recent success check before any other random draw. */
  27  rpgLiteLuck(profile, original, higherIsBetter)
  28  {
  29      if (original != profile.lastCheck || profile.lastCheckDraw != rpgRandom.draws) throw new RpgError();
  30      return rpgCheck(original.score, rpgLiteReroll(profile, original.roll, 3, higherIsBetter));
  31  }
  32  
  33  /** Consume the once-per-session authored misfortune trigger. */
  34  rpgLiteUnluckiness(profile, sessionId)
  35  {
  36      if (profile.level('unluckiness') == 0 || profile.unluckySession == sessionId) return nil;
  37      profile.unluckySession = sessionId; return true;
  38  }
  39  
  40  /** Resolve secret human or animal empathy; the author supplies true or misleading impressions. */
  41  rpgLiteEmpathy(profile, animal)
  42  {
  43      if (profile.level(animal ? 'animal-empathy' : 'empathy') == 0) throw new RpgError();
  44      return rpgLiteAttributeCheck(profile, rpgIQ, 'empathy', 0);
  45  }
  46  
  47  /** Resolve sensory detection, optional comprehension and Danger Sense fallback. */
  48  rpgLiteNotice(profile, context, comprehension, danger)
  49  {
  50      local noticed = rpgLiteCheck(profile, context);
  51      if (noticed.achieved && comprehension != nil) noticed = rpgLiteCheck(profile, comprehension);
  52      if (!noticed.achieved && danger && profile.level('danger-sense') > 0)
  53          noticed = rpgLiteAttributeCheck(profile, rpgPerception, 'danger-sense', 0);
  54      return noticed;
  55  }
  56  
  57  /** Triggered behavioral obligation with self-control and author-defined action satisfaction. */
  58  class RpgLiteObligation: object
  59      profile = nil
  60      trait = nil
  61      trigger = nil
  62      active = nil
  63      check = nil
  64      begin(modifier, giveIn)
  65      {
  66          if (!holds(self.profile.traits, self.trait)) throw new RpgError();
  67          self.check = self.trait.controlled ? rpgLiteSelfControl(self.profile, self.trait, modifier, giveIn) : nil;
  68          self.active = self.check == nil || !self.check.achieved;
  69          return self.active;
  70      }
  71      permits(action) { return !self.active || action.satisfies(self); }
  72  ;
  73  
  74  /** Persistent phobic exposure, initial self-control and ten-minute repeated checks. */
  75  class RpgLitePhobia: object
  76      profile = nil
  77      trait = nil
  78      present = nil
  79      elapsed = 0
  80      panicking = nil
  81      enter(threatOnly, modifier)
  82      {
  83          self.present = true; self.elapsed = 0;
  84          self.panicking = !rpgLiteSelfControl(self.profile, self.trait, modifier + (threatOnly ? 4 : 0), nil).achieved;
  85          return self.panicking;
  86      }
  87      advance(seconds)
  88      {
  89          rpgInteger(seconds, 0, 86400);
  90          if (!self.present) return nil;
  91          self.elapsed += seconds;
  92          while (self.elapsed >= 600)
  93          { self.elapsed -= 600; self.panicking = !rpgLiteSelfControl(self.profile, self.trait, 0, nil).achieved; }
  94          return self.panicking;
  95      }
  96      leave() { self.present = nil; self.panicking = nil; self.elapsed = 0; return nil; }
  97  ;
  98  
  99  /** Time/world travel with concentration, IQ, fatigue and authored safe-destination hooks. */
 100  class RpgLiteJump: object
 101      profile = nil
 102      kind = 'world'
 103      destination = nil
 104      seconds = 10
 105      fatigueCost = 1
 106      safe = true
 107      available() { return nil; }
 108      arrived(destination, wrong) { return nil; }
 109      resolve()
 110      {
 111          rpgInteger(self.seconds, 1, 10); rpgInteger(self.fatigueCost, 1, 10);
 112          if (!holds(['time','world'], self.kind) || self.profile.level(self.kind == 'time' ? 'jumper-time' : 'jumper-world') == 0)
 113              throw new RpgError();
 114          if (self.available() != nil || !rpgLiteCanAct(self.profile, nil)) return nil;
 115          local result = rpgLiteAttributeCheck(self.profile, rpgIQ, 'jump', self.seconds - 10);
 116          rpgSpendTime(self.seconds); rpgLiteFatigue(self.profile, self.fatigueCost, 'jump');
 117          if (result.roll >= 14 || !self.safe) { result.achieved = nil; result.outcome = 'setback'; }
 118          if (result.achieved) self.arrived(self.destination, nil);
 119          else if (result.critical == 'failure' && self.safe) self.arrived(self.destination, true);
 120          return result;
 121      }
 122  ;
 123  
 124  /** Spend earned points during approved downtime to remove an owned disadvantage. */
 125  rpgLiteBuyOff(profile, trait)
 126  {
 127      if (!profile.developmentOpen || !holds(profile.traits, trait) || trait.points >= 0) return nil;
 128      local cost = -rpgLiteTraitCost(trait);
 129      if (profile.unspentPoints < cost) return nil;
 130      local kept = [];
 131      foreach (local owned in profile.traits) if (owned != trait) kept = kept + [owned];
 132      profile.unspentPoints -= cost; profile.traits = kept; return true;
 133  }
 134  
 135  /** Spend the incremental investment on an owned or new skill, respecting TL learning restrictions. */
 136  rpgLiteLearn(profile, skill, points, teacher)
 137  {
 138      rpgInteger(points, 1, 10000);
 139      if (!profile.developmentOpen) return nil;
 140      if (skill.techLevel != nil && skill.techLevel > profile.techLevel
 141          && (skill.attribute == rpgIQ || !teacher)) return nil;
 142      local training = nil;
 143      foreach (local candidate in profile.skills) if (candidate.skill == skill) training = candidate;
 144      local cost = points - (training == nil ? 0 : training.points);
 145      if (cost <= 0 || cost > profile.unspentPoints) return nil;
 146      if (training == nil)
 147      { training = new RpgTraining(); training.skill = skill; profile.skills = profile.skills + [training]; }
 148      profile.unspentPoints -= cost; training.points = points; return true;
 149  }