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 }