Merge pull request #4812 from HabitRPG/blade-rebalancing
Merge Rebalancing branch from habitrpg-shared and preserve commit history
This commit is contained in:
+7295
-7255
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ api = module.exports
|
||||
moment = require 'moment'
|
||||
i18n = require './i18n.coffee'
|
||||
t = (string, vars) ->
|
||||
func = (lang) ->
|
||||
func = (lang) ->
|
||||
vars ?= {a: 'a'}
|
||||
i18n.t(string, vars, lang)
|
||||
func.i18nLangFunc = true #Trick to recognize this type of function
|
||||
@@ -53,7 +53,7 @@ _.each api.mystery, (v,k)->v.key = k
|
||||
gear =
|
||||
weapon:
|
||||
base:
|
||||
0:
|
||||
0:
|
||||
text: t('weaponBase0Text'), notes: t('weaponBase0Notes'), value:0
|
||||
warrior:
|
||||
0: text: t('weaponWarrior0Text'), notes: t('weaponWarrior0Notes'), value:0
|
||||
@@ -370,7 +370,7 @@ gear =
|
||||
springWarrior: event: events.spring, specialClass: 'warrior', text: t('headAccessorySpecialSpringWarriorText'), notes: t('headAccessorySpecialSpringWarriorNotes'), value: 20
|
||||
springMage: event: events.spring, specialClass: 'wizard', text: t('headAccessorySpecialSpringMageText'), notes: t('headAccessorySpecialSpringMageNotes'), value: 20
|
||||
springHealer: event: events.spring, specialClass: 'healer', text: t('headAccessorySpecialSpringHealerText'), notes: t('headAccessorySpecialSpringHealerNotes'), value: 20
|
||||
|
||||
|
||||
mystery:
|
||||
201403: text: t('headAccessoryMystery201403Text'), notes: t('headAccessoryMystery201403Notes'), mystery:'201403', value: 0
|
||||
201404: text: t('headAccessoryMystery201404Text'), notes: t('headAccessoryMystery201404Notes'), mystery:'201404', value: 0
|
||||
@@ -479,6 +479,8 @@ api.gearTypes = gearTypes
|
||||
#
|
||||
diminishingReturns = (bonus, max, halfway=max/2) -> max*(bonus/(bonus+halfway))
|
||||
|
||||
calculateBonus = (value, stat, crit=1, stat_scale=0.5) -> (if value < 0 then 1 else value+1) + (stat * stat_scale * crit)
|
||||
|
||||
api.spells =
|
||||
|
||||
wizard:
|
||||
@@ -489,13 +491,11 @@ api.spells =
|
||||
target: 'task'
|
||||
notes: t('spellWizardFireballNotes')
|
||||
cast: (user, target) ->
|
||||
# I seriously have no idea what I'm doing here. I'm just mashing buttons until numbers seem right-ish. Anyone know math?
|
||||
bonus = user._statsComputed.int * user.fns.crit('per')
|
||||
target.value += diminishingReturns(bonus*.02, 4)
|
||||
bonus *= Math.ceil ((if target.value < 0 then 1 else target.value+1) *.075)
|
||||
#console.log {bonus, expBonus:bonus,upBonus:bonus*.1}
|
||||
user.stats.exp += diminishingReturns(bonus,75)
|
||||
user.party.quest.progress.up += diminishingReturns(bonus*.1,50,30)
|
||||
user.party.quest.progress.up ?= 0
|
||||
user.party.quest.progress.up += Math.ceil(user._statsComputed.int * .1)
|
||||
|
||||
mpheal:
|
||||
text: t('spellWizardMPHealText')
|
||||
@@ -505,9 +505,8 @@ api.spells =
|
||||
notes: t('spellWizardMPHealNotes'),
|
||||
cast: (user, target)->
|
||||
_.each target, (member) ->
|
||||
bonus = Math.ceil(user._statsComputed.int * .1)
|
||||
bonus = 25 if bonus > 25 #prevent ability to replenish own mp infinitely
|
||||
member.stats.mp += bonus
|
||||
bonus = user._statsComputed.int
|
||||
member.stats.mp += Math.ceil(diminishingReturns(bonus, 25, 125)) # maxes out at 25
|
||||
|
||||
earth:
|
||||
text: t('spellWizardEarthText')
|
||||
@@ -517,8 +516,9 @@ api.spells =
|
||||
notes: t('spellWizardEarthNotes'),
|
||||
cast: (user, target) ->
|
||||
_.each target, (member) ->
|
||||
bonus = user._statsComputed.int
|
||||
member.stats.buffs.int ?= 0
|
||||
member.stats.buffs.int += Math.ceil(user._statsComputed.int * .05)
|
||||
member.stats.buffs.int += Math.ceil(diminishingReturns(bonus, 30,200))
|
||||
|
||||
frost:
|
||||
text: t('spellWizardFrostText'),
|
||||
@@ -537,8 +537,11 @@ api.spells =
|
||||
target: 'task'
|
||||
notes: t('spellWarriorSmashNotes')
|
||||
cast: (user, target) ->
|
||||
target.value += 2.5 * (user._statsComputed.str / (user._statsComputed.str + 50)) * user.fns.crit('con')
|
||||
user.party.quest.progress.up += Math.ceil(user._statsComputed.str * .2)
|
||||
bonus = user._statsComputed.str * user.fns.crit('con')
|
||||
target.value += diminishingReturns(bonus, 2.5, 35)
|
||||
user.party.quest.progress.up ?= 0
|
||||
user.party.quest.progress.up += diminishingReturns(bonus, 55, 70)
|
||||
|
||||
defensiveStance:
|
||||
text: t('spellWarriorDefensiveStanceText')
|
||||
mana: 25
|
||||
@@ -546,8 +549,10 @@ api.spells =
|
||||
target: 'self'
|
||||
notes: t('spellWarriorDefensiveStanceNotes')
|
||||
cast: (user, target) ->
|
||||
bonus = user._statsComputed.con
|
||||
user.stats.buffs.con ?= 0
|
||||
user.stats.buffs.con += Math.ceil(user._statsComputed.con * .05)
|
||||
user.stats.buffs.con += Math.ceil(diminishingReturns(bonus, 40, 200))
|
||||
|
||||
valorousPresence:
|
||||
text: t('spellWarriorValorousPresenceText')
|
||||
mana: 20
|
||||
@@ -556,8 +561,10 @@ api.spells =
|
||||
notes: t('spellWarriorValorousPresenceNotes')
|
||||
cast: (user, target) ->
|
||||
_.each target, (member) ->
|
||||
bonus = user._statsComputed.str
|
||||
member.stats.buffs.str ?= 0
|
||||
member.stats.buffs.str += Math.ceil(user._statsComputed.str * .05)
|
||||
member.stats.buffs.str += Math.ceil(diminishingReturns(bonus, 20, 200))
|
||||
|
||||
intimidate:
|
||||
text: t('spellWarriorIntimidateText')
|
||||
mana: 15
|
||||
@@ -566,8 +573,9 @@ api.spells =
|
||||
notes: t('spellWarriorIntimidateNotes')
|
||||
cast: (user, target) ->
|
||||
_.each target, (member) ->
|
||||
bonus = user._statsComputed.con
|
||||
member.stats.buffs.con ?= 0
|
||||
member.stats.buffs.con += Math.ceil(user._statsComputed.con * .03)
|
||||
member.stats.buffs.con += Math.ceil(diminishingReturns(bonus,24,200))
|
||||
|
||||
rogue:
|
||||
pickPocket:
|
||||
@@ -577,8 +585,9 @@ api.spells =
|
||||
target: 'task'
|
||||
notes: t('spellRoguePickPocketNotes')
|
||||
cast: (user, target) ->
|
||||
bonus = (if target.value < 0 then 1 else target.value+2) + (user._statsComputed.per * 0.5)
|
||||
user.stats.gp += 25 * (bonus / (bonus + 75))
|
||||
bonus = calculateBonus(target.value, user._statsComputed.per)
|
||||
user.stats.gp += diminishingReturns(bonus, 25, 75)
|
||||
|
||||
backStab:
|
||||
text: t('spellRogueBackStabText')
|
||||
mana: 15
|
||||
@@ -587,11 +596,10 @@ api.spells =
|
||||
notes: t('spellRogueBackStabNotes')
|
||||
cast: (user, target) ->
|
||||
_crit = user.fns.crit('str', .3)
|
||||
target.value += _crit * .03
|
||||
bonus = (if target.value < 0 then 1 else target.value+1) * _crit
|
||||
user.stats.exp += bonus
|
||||
user.stats.gp += bonus
|
||||
# user.party.quest.progress.up += bonus if user.party.quest.key # remove hurting bosses for rogues, seems OP for now
|
||||
bonus = calculateBonus(target.value, user._statsComputed.str, _crit)
|
||||
user.stats.exp += diminishingReturns(bonus, 75, 50)
|
||||
user.stats.gp += diminishingReturns(bonus, 18, 75)
|
||||
|
||||
toolsOfTrade:
|
||||
text: t('spellRogueToolsOfTradeText')
|
||||
mana: 25
|
||||
@@ -599,10 +607,11 @@ api.spells =
|
||||
target: 'party'
|
||||
notes: t('spellRogueToolsOfTradeNotes')
|
||||
cast: (user, target) ->
|
||||
## lasts 24 hours ##
|
||||
_.each target, (member) ->
|
||||
bonus = user._statsComputed.per
|
||||
member.stats.buffs.per ?= 0
|
||||
member.stats.buffs.per += Math.ceil(user._statsComputed.per * .03)
|
||||
member.stats.buffs.per += Math.ceil(diminishingReturns(bonus, 400, 100))
|
||||
|
||||
stealth:
|
||||
text: t('spellRogueStealthText')
|
||||
mana: 45
|
||||
@@ -633,7 +642,7 @@ api.spells =
|
||||
cast: (user, target) ->
|
||||
_.each user.tasks, (target) ->
|
||||
return if target.type is 'reward'
|
||||
target.value += 1.5 * (user._statsComputed.int / (user._statsComputed.int + 40))
|
||||
target.value += 4 * (user._statsComputed.int / (user._statsComputed.int + 40))
|
||||
protectAura:
|
||||
text: t('spellHealerProtectAuraText')
|
||||
mana: 30
|
||||
@@ -641,10 +650,10 @@ api.spells =
|
||||
target: 'party'
|
||||
notes: t('spellHealerProtectAuraNotes')
|
||||
cast: (user, target) ->
|
||||
## lasts 24 hours ##
|
||||
_.each target, (member) ->
|
||||
bonus = user._statsComputed.con
|
||||
member.stats.buffs.con ?= 0
|
||||
member.stats.buffs.con += Math.ceil(user._statsComputed.con * .15)
|
||||
member.stats.buffs.con += Math.ceil(diminishingReturns(bonus, 200, 200))
|
||||
heallAll:
|
||||
text: t('spellHealerHealAllText')
|
||||
mana: 25
|
||||
@@ -825,11 +834,11 @@ api.specialPets =
|
||||
'Mammoth-Base': 'mammoth'
|
||||
|
||||
api.specialMounts =
|
||||
'BearCub-Polar': 'polarBear'
|
||||
'LionCub-Ethereal': 'etherealLion'
|
||||
'MantisShrimp-Base': 'mantisShrimp'
|
||||
'Turkey-Base': 'turkey'
|
||||
'Mammoth-Base': 'mammoth'
|
||||
'BearCub-Polar': 'polarBear'
|
||||
'LionCub-Ethereal': 'etherealLion'
|
||||
'MantisShrimp-Base': 'mantisShrimp'
|
||||
'Turkey-Base': 'turkey'
|
||||
'Mammoth-Base': 'mammoth'
|
||||
|
||||
api.hatchingPotions =
|
||||
Base: value: 2, text: t('hatchingPotionBase')
|
||||
@@ -1043,7 +1052,7 @@ api.quests =
|
||||
]
|
||||
gp: 25
|
||||
exp: 125
|
||||
|
||||
|
||||
hedgehog:
|
||||
text: t('questHedgehogText')
|
||||
notes: t('questHedgehogNotes')
|
||||
|
||||
+40
-17
@@ -1067,12 +1067,10 @@ api.wrap = (user, main=true) ->
|
||||
nextDelta = if not options.cron and direction is 'down' then calculateReverseDelta() else calculateDelta()
|
||||
unless task.type is 'reward'
|
||||
if (user.preferences.automaticAllocation is true and user.preferences.allocationMode is 'taskbased' and !(task.type is 'todo' and direction is 'down')) then user.stats.training[task.attribute] += nextDelta
|
||||
# ===== STRENGTH =====
|
||||
# (Only for up-scoring, ignore up-onlies and rewards)
|
||||
# Note, we create a new val (adjustAmt) to add to task.value, since delta will be used in Exp & GP calculations - we don't want STR to bonus that
|
||||
if direction is 'up' and !(task.type is 'habit' and !task.down)
|
||||
if direction is 'up' # Make progress on quest based on STR
|
||||
user.party.quest.progress.up = user.party.quest.progress.up || 0;
|
||||
user.party.quest.progress.up += (nextDelta * (1 + (user._statsComputed.str / 200))) if task.type in ['daily','todo']
|
||||
user.party.quest.progress.up += (nextDelta * (0.5 + (user._statsComputed.str / 400))) if task.type is 'habit'
|
||||
task.value += nextDelta
|
||||
delta += nextDelta
|
||||
|
||||
@@ -1113,9 +1111,17 @@ api.wrap = (user, main=true) ->
|
||||
hpMod = delta * conBonus * task.priority * 2 # constant 2 multiplier for better results
|
||||
stats.hp += Math.round(hpMod * 10) / 10 # round to 1dp
|
||||
|
||||
gainMP = (delta) ->
|
||||
delta *= user._tmp.crit or 1
|
||||
user.stats.mp += delta
|
||||
user.stats.mp = user._statsComputed.maxMP if user.stats.mp >= user._statsComputed.maxMP
|
||||
user.stats.mp = 0 if user.stats.mp < 0
|
||||
|
||||
# ===== starting to actually do stuff, most of above was definitions =====
|
||||
switch task.type
|
||||
when 'habit'
|
||||
changeTaskValue()
|
||||
gainMP(_.max([0.25, (.0025 * user._statsComputed.maxMP)]) * if direction is 'down' then -1 else 1)
|
||||
# Add habit value to habit-history (if different)
|
||||
if (delta > 0) then addPoints() else subtractPoints()
|
||||
|
||||
@@ -1134,6 +1140,7 @@ api.wrap = (user, main=true) ->
|
||||
task.streak = 0 unless user.stats.buffs.streaks
|
||||
else
|
||||
changeTaskValue()
|
||||
gainMP(_.max([1, (.01 * user._statsComputed.maxMP)]) * if direction is 'down' then -1 else 1)
|
||||
if direction is 'down'
|
||||
delta = calculateDelta() # recalculate delta for unchecking so the gp and exp come out correctly
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
@@ -1160,12 +1167,7 @@ api.wrap = (user, main=true) ->
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
# MP++ per checklist item in ToDo, bonus per CLI
|
||||
multiplier = _.max([(_.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),1)),1])
|
||||
mpDelta = _.max([(multiplier), (.01 * user._statsComputed.maxMP * multiplier)])
|
||||
mpDelta *= user._tmp.crit or 1
|
||||
mpDelta *= -1 if direction is 'down' # unticking a todo
|
||||
user.stats.mp += mpDelta
|
||||
user.stats.mp = user._statsComputed.maxMP if user.stats.mp >= user._statsComputed.maxMP
|
||||
user.stats.mp = 0 if user.stats.mp < 0 # BUT DO WE WANT THIS? SEE COMMIT DESCRIPTION
|
||||
gainMP(_.max([(multiplier), (.01 * user._statsComputed.maxMP * multiplier)]) * if direction is 'down' then -1 else 1)
|
||||
|
||||
when 'reward'
|
||||
# Don't adjust values for rewards
|
||||
@@ -1222,7 +1224,9 @@ api.wrap = (user, main=true) ->
|
||||
|
||||
crit: (stat='str', chance=.03) ->
|
||||
#console.log("Crit Chance:"+chance*(1+user._statsComputed[stat]/100))
|
||||
if user.fns.predictableRandom() <= chance*(1+user._statsComputed[stat]/100) then 1.5 + (.02*user._statsComputed[stat])
|
||||
s = user._statsComputed[stat]
|
||||
if user.fns.predictableRandom() <= chance*(1 + s/100)
|
||||
1.5 + 4*s/(s + 200)
|
||||
else 1
|
||||
|
||||
###
|
||||
@@ -1487,17 +1491,22 @@ api.wrap = (user, main=true) ->
|
||||
|
||||
# Tally each task
|
||||
todoTally = 0
|
||||
dailyChecked = 0 # how many dailies were checked?
|
||||
dailyDueUnchecked = 0 # how many dailies were due but not checked?
|
||||
user.party.quest.progress.down ?= 0
|
||||
user.todos.concat(user.dailys).forEach (task) ->
|
||||
return unless task
|
||||
|
||||
{id, type, completed, repeat} = task
|
||||
|
||||
return if (type is 'daily') && !completed && user.stats.buffs.stealth && user.stats.buffs.stealth-- # User "evades" a certain number of uncompleted dailies
|
||||
return if (type is 'daily') && !completed && user.stats.buffs.stealth && user.stats.buffs.stealth-- # User "evades" a certain number of uncompleted dailies (includes uncompletd GREY dailies - TODO fix that?)
|
||||
# All processing of Dailies is calculated AFTER stealth -- stealthed Dailies are treated as just not being there
|
||||
|
||||
|
||||
# Deduct experience for missed Daily tasks, but not for Todos (just increase todo's value)
|
||||
unless completed
|
||||
# Deduct points for missed Daily tasks, but not for Todos (just increase todo's value)
|
||||
if completed
|
||||
if type is 'daily'
|
||||
dailyChecked += 1
|
||||
else
|
||||
scheduleMisses = daysMissed
|
||||
# for dailys which have repeat dates, need to calculate how many they've missed according to their own schedule
|
||||
if (type is 'daily') and repeat
|
||||
@@ -1506,9 +1515,17 @@ api.wrap = (user, main=true) ->
|
||||
thatDay = moment(now).subtract({days: n + 1})
|
||||
scheduleMisses++ if api.shouldDo(thatDay, repeat, user.preferences)
|
||||
if scheduleMisses > 0
|
||||
perfect = false if type is 'daily'
|
||||
if type is 'daily'
|
||||
perfect = false
|
||||
if task.checklist?.length > 0 # Partially completed checklists dock fewer mana points
|
||||
fractionChecked = _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0) / task.checklist.length
|
||||
dailyDueUnchecked += (1 - fractionChecked)
|
||||
dailyChecked += fractionChecked
|
||||
else
|
||||
dailyDueUnchecked += 1
|
||||
delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:scheduleMisses, cron:true}});
|
||||
user.party.quest.progress.down += delta if type is 'daily'
|
||||
|
||||
|
||||
switch type
|
||||
when 'daily'
|
||||
@@ -1557,7 +1574,13 @@ api.wrap = (user, main=true) ->
|
||||
else clearBuffs
|
||||
|
||||
# Add 10 MP, or 10% of max MP if that'd be more. Perform this after Perfect Day for maximum benefit
|
||||
user.stats.mp += _.max([10,.1 * user._statsComputed.maxMP])
|
||||
# Adjust for fraction of dailies completed
|
||||
#console.log("Prior MP: " + user.stats.mp)
|
||||
dailyChecked=1 if dailyDueUnchecked is 0 and dailyChecked is 0
|
||||
#console.log("DueUnchecked: " + dailyDueUnchecked)
|
||||
#console.log("Checked: " + dailyChecked)
|
||||
user.stats.mp += _.max([10,.1 * user._statsComputed.maxMP]) * dailyChecked / (dailyDueUnchecked + dailyChecked)
|
||||
#console.log("After MP: " +user.stats.mp)
|
||||
user.stats.mp = user._statsComputed.maxMP if user.stats.mp > user._statsComputed.maxMP
|
||||
|
||||
# After all is said and done, progress up user's effect on quest, return those values & reset the user's
|
||||
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user