Merge branch 'develop' into hairlessbear-cron_dailies_fix

This commit is contained in:
Blade Barringer
2015-05-02 22:10:58 -05:00
179 changed files with 5348 additions and 4975 deletions
+65 -34
View File
@@ -47,6 +47,7 @@ api.mystery =
201501: {start:'2015-01-26',end:'2015-02-02', text:'Starry Knight Set'}
201502: {start:'2015-02-24',end:'2015-03-02', text:'Winged Enchanter Set'}
201503: {start:'2015-03-25',end:'2015-04-02', text:'Aquamarine Set'}
201504: {start:'2015-04-24',end:'2015-05-02', text:'Busy Bee Set'}
301404: {start:'3014-03-24',end:'3014-04-02', text:'Steampunk Standard Set'}
301405: {start:'3014-04-24',end:'3014-05-02', text:'Steampunk Accessories Set'}
wondercon: {start:'2014-03-24',end:'2014-04-01'} # not really, but the mechanic works
@@ -219,6 +220,7 @@ gear =
201412: text: t('armorMystery201412Text'), notes: t('armorMystery201412Notes'), mystery:'201412', value: 0
201501: text: t('armorMystery201501Text'), notes: t('armorMystery201501Notes'), mystery:'201501', value: 0
201503: text: t('armorMystery201503Text'), notes: t('armorMystery201503Notes'), mystery:'201503', value: 0
201504: text: t('armorMystery201504Text'), notes: t('armorMystery201504Notes'), mystery:'201504', value: 0
301404: text: t('armorMystery301404Text'), notes: t('armorMystery301404Notes'), mystery:'301404', value: 0
head:
@@ -368,6 +370,7 @@ gear =
201402: text: t('backMystery201402Text'), notes: t('backMystery201402Notes'), mystery:'201402', value: 0
201404: text: t('backMystery201404Text'), notes: t('backMystery201404Notes'), mystery:'201404', value: 0
201410: text: t('backMystery201410Text'), notes: t('backMystery201410Notes'), mystery:'201410', value: 0
201504: text: t('backMystery201504Text'), notes: t('backMystery201504Notes'), mystery:'201504', value: 0
special:
wondercon_red: text: t('backSpecialWonderconRedText'), notes: t('backSpecialWonderconRedNotes'), value: 0, mystery:'wondercon'
wondercon_black: text: t('backSpecialWonderconBlackText'), notes: t('backSpecialWonderconBlackNotes'), value: 0, mystery:'wondercon'
@@ -507,28 +510,30 @@ 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:
fireball:
# Burst of Flames
text: t('spellWizardFireballText')
mana: 10
lvl: 11
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)
#Sync the user stats to see if we level the user
user.party.quest.progress.up ?= 0
user.party.quest.progress.up += Math.ceil(user._statsComputed.int * .1)
# Sync the user stats to see if we level the user
req = { language: user.preferences.language }
user.fns.updateStats( user.stats , req )
mpheal:
# Ethereal Surge
text: t('spellWizardMPHealText')
mana: 30
lvl: 12
@@ -536,11 +541,11 @@ 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:
# Earthquake
text: t('spellWizardEarthText')
mana: 35
lvl: 13
@@ -548,10 +553,12 @@ api.spells =
notes: t('spellWizardEarthNotes'),
cast: (user, target) ->
_.each target, (member) ->
bonus = user._statsComputed.int - user.stats.buffs.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:
# Chilling Frost
text: t('spellWizardFrostText'),
mana: 40
lvl: 14
@@ -562,24 +569,32 @@ api.spells =
warrior:
smash:
# Brutal Smash
text: t('spellWarriorSmashText')
mana: 10
lvl: 11
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:
# Defensive Stance
text: t('spellWarriorDefensiveStanceText')
mana: 25
lvl: 12
target: 'self'
notes: t('spellWarriorDefensiveStanceNotes')
cast: (user, target) ->
bonus = user._statsComputed.con - user.stats.buffs.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:
# Valorous Presence
text: t('spellWarriorValorousPresenceText')
mana: 20
lvl: 13
@@ -587,9 +602,12 @@ api.spells =
notes: t('spellWarriorValorousPresenceNotes')
cast: (user, target) ->
_.each target, (member) ->
bonus = user._statsComputed.str - user.stats.buffs.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:
# Intimidating Gaze
text: t('spellWarriorIntimidateText')
mana: 15
lvl: 14
@@ -597,20 +615,24 @@ api.spells =
notes: t('spellWarriorIntimidateNotes')
cast: (user, target) ->
_.each target, (member) ->
bonus = user._statsComputed.con - user.stats.buffs.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:
# Pickpocket
text: t('spellRoguePickPocketText')
mana: 10
lvl: 11
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:
# Backstab
text: t('spellRogueBackStabText')
mana: 15
lvl: 12
@@ -618,26 +640,28 @@ 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
#Sync the user stats to see if we level the user
bonus = calculateBonus(target.value, user._statsComputed.str, _crit)
user.stats.exp += diminishingReturns(bonus, 75, 50)
user.stats.gp += diminishingReturns(bonus, 18, 75)
# Sync the user stats to see if we level the user
req = { language: user.preferences.language }
user.fns.updateStats( user.stats , req )
toolsOfTrade:
# Tools of the Trade
text: t('spellRogueToolsOfTradeText')
mana: 25
lvl: 13
target: 'party'
notes: t('spellRogueToolsOfTradeNotes')
cast: (user, target) ->
## lasts 24 hours ##
_.each target, (member) ->
bonus = user._statsComputed.per - user.stats.buffs.per
member.stats.buffs.per ?= 0
member.stats.buffs.per += Math.ceil(user._statsComputed.per * .03)
member.stats.buffs.per += Math.ceil(diminishingReturns(bonus, 100, 50))
stealth:
# Stealth
text: t('spellRogueStealthText')
mana: 45
lvl: 14
@@ -645,11 +669,12 @@ api.spells =
notes: t('spellRogueStealthNotes')
cast: (user, target) ->
user.stats.buffs.stealth ?= 0
## scales to user's # of dailies; maxes out at 100% at 100 per ##
user.stats.buffs.stealth += Math.ceil(user.dailys.length * user._statsComputed.per / 100)
## scales to user's # of dailies; Diminishing Returns, maxes out at 64%, halfway point at 55 PER##
user.stats.buffs.stealth += Math.ceil( diminishingReturns(user._statsComputed.per, user.dailys.length*0.64,55))
healer:
heal:
# Healing Light
text: t('spellHealerHealText')
mana: 15
lvl: 11
@@ -658,7 +683,9 @@ api.spells =
cast: (user, target) ->
user.stats.hp += (user._statsComputed.con + user._statsComputed.int + 5) * .075
user.stats.hp = 50 if user.stats.hp > 50
brightness:
# Searing Brightness
text: t('spellHealerBrightnessText')
mana: 15
lvl: 12
@@ -667,19 +694,23 @@ 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:
# Protective Aura
text: t('spellHealerProtectAuraText')
mana: 30
lvl: 13
target: 'party'
notes: t('spellHealerProtectAuraNotes')
cast: (user, target) ->
## lasts 24 hours ##
_.each target, (member) ->
bonus = user._statsComputed.con - user.stats.buffs.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:
# Blessing
text: t('spellHealerHealAllText')
mana: 25
lvl: 14
@@ -890,11 +921,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')
+71 -43
View File
@@ -80,6 +80,17 @@ api.shouldDo = (day, repeat, options={}) ->
selected = repeat[api.dayMapping[api.startOfDay(_.defaults {now:day}, o).day()]]
return selected
###
------------------------------------------------------
Level cap
------------------------------------------------------
###
api.maxLevel = 100
api.capByLevel = (lvl) ->
if lvl > api.maxLevel then api.maxLevel else lvl
###
------------------------------------------------------
Scoring
@@ -476,16 +487,13 @@ api.wrap = (user, main=true) ->
rebirth: (req, cb, ga) ->
# Cost is 8 Gems ($2)
if (user.balance < 2 && user.stats.lvl < 100)
if (user.balance < 2 && user.stats.lvl < api.maxLevel)
return cb? {code:401,message: i18n.t('notEnoughGems', req.language)}
# only charge people if they are under level 100 - ryan
if user.stats.lvl < 100
# only charge people if they are under the max level - ryan
if user.stats.lvl < api.maxLevel
user.balance -= 2
# Save off user's level, for calculating achievement eligibility later
if user.stats.lvl < 100
lvl = user.stats.lvl
else
lvl = 100
lvl = api.capByLevel(user.stats.lvl)
# Turn tasks yellow, zero out streaks
_.each user.tasks, (task) ->
unless task.type is 'reward'
@@ -935,7 +943,7 @@ api.wrap = (user, main=true) ->
else
return cb?({code:401,message:i18n.t('notEnoughGems', req.language)}) unless user.balance >= .75
user.balance -= .75
_.merge user.stats, {str: 0, con: 0, per: 0, int: 0, points: user.stats.lvl}
_.merge user.stats, {str: 0, con: 0, per: 0, int: 0, points: api.capByLevel(user.stats.lvl)}
user.flags.classSelected = false
ga?.event('behavior', 'gems', 'changeClass').send()
#'stats.points': this is handled on the server
@@ -946,7 +954,7 @@ api.wrap = (user, main=true) ->
user.flags.classSelected = true
user.preferences.disableClasses = true
user.preferences.autoAllocate = true
user.stats.str = user.stats.lvl
user.stats.str = api.capByLevel(user.stats.lvl)
user.stats.points = 0
cb? null, _.pick(user,$w 'stats flags preferences')
@@ -1068,11 +1076,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)
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 +1120,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 +1149,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 +1176,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 +1233,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
###
@@ -1342,7 +1355,8 @@ api.wrap = (user, main=true) ->
_.invert(stats)[_.min stats]
when "classbased"
# Attributes get 3:2:1:1 per 7 levels.
ideal = [(user.stats.lvl / 7 * 3), (user.stats.lvl / 7 * 2), (user.stats.lvl / 7), (user.stats.lvl / 7)]
lvlDiv7 = user.stats.lvl / 7
ideal = [(lvlDiv7 * 3), (lvlDiv7 * 2), lvlDiv7, lvlDiv7]
# Primary, secondary etc. attributes aren't explicitly defined, so hardcode them. In order as above
preference = switch user.stats.class
when "wizard" then ["int", "per", "con", "str"]
@@ -1378,6 +1392,10 @@ api.wrap = (user, main=true) ->
user.stats.lvl++
tnl = api.tnl(user.stats.lvl)
user.stats.hp = 50
continue if user.stats.lvl > api.maxLevel
# Auto-allocate a point, or give them a new manual point
if user.preferences.automaticAllocation
user.fns.autoAllocate()
@@ -1388,8 +1406,6 @@ api.wrap = (user, main=true) ->
user.stats.points = 0
# This happens after dropping level with Fix Character Values and perhaps from other causes.
# TODO: Subtract points from attributes in the same manner as on death.
user.stats.hp = 50
user.stats.exp = stats.exp
# Set flags when they unlock features
@@ -1417,7 +1433,7 @@ api.wrap = (user, main=true) ->
dialog: i18n.t('messageFoundQuest', {questText: content.quests[k].text(req.language)}, req.language)
if !user.flags.rebirthEnabled and (user.stats.lvl >= 50 or user.achievements.ultimateGear or user.achievements.beastMaster)
user.flags.rebirthEnabled = true
if user.stats.lvl >= 100 and !user.flags.freeRebirth
if user.stats.lvl >= api.maxLevel and !user.flags.freeRebirth
user.flags.freeRebirth = true
###
@@ -1493,34 +1509,50 @@ 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
# Deduct experience for missed Daily tasks, but not for Todos (just increase todo's value)
unless completed
scheduleMisses = daysMissed
# Deduct points for missed Daily tasks, but not for Todos (just increase todo's value)
EvadeTask = 0
scheduleMisses = daysMissed
if completed
if type is 'daily'
dailyChecked += 1
else
# 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
scheduleMisses = 0
_.times daysMissed, (n) ->
thatDay = moment(now).subtract({days: n + 1})
scheduleMisses++ if api.shouldDo(thatDay, repeat, user.preferences)
if scheduleMisses > 0
perfect = false if type is 'daily'
delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:scheduleMisses, cron:true}});
if api.shouldDo(thatDay, repeat, user.preferences)
scheduleMisses++
if user.stats.buffs.stealth
user.stats.buffs.stealth--
EvadeTask++
if scheduleMisses > EvadeTask
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-EvadeTask), cron:true}}); # this line occurs for todos or dailys
user.party.quest.progress.down += delta if type is 'daily'
switch type
when 'daily'
# This occurs whether or not the task is completed
(task.history ?= []).push({ date: +new Date, value: task.value })
task.completed = false
_.each task.checklist, ((i)->i.completed=false;true)
if completed || (scheduleMisses > 0)
_.each task.checklist, ((i)->i.completed=false;true) # this should not happen for grey tasks unless they are completed
when 'todo'
#get updated value
absVal = if (completed) then Math.abs(task.value) else task.value
@@ -1555,15 +1587,14 @@ api.wrap = (user, main=true) ->
if perfect
user.achievements.perfect ?= 0
user.achievements.perfect++
if user.stats.lvl < 100
lvlDiv2 = Math.ceil(user.stats.lvl/2)
else
lvlDiv2 = 50
lvlDiv2 = Math.ceil(api.capByLevel(user.stats.lvl) / 2)
{str:lvlDiv2,int:lvlDiv2,per:lvlDiv2,con:lvlDiv2,stealth:0,streaks:false}
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
dailyChecked=1 if dailyDueUnchecked is 0 and dailyChecked is 0
user.stats.mp += _.max([10,.1 * user._statsComputed.maxMP]) * dailyChecked / (dailyDueUnchecked + dailyChecked)
user.stats.mp = user._statsComputed.maxMP if user.stats.mp > user._statsComputed.maxMP
# Analytics
@@ -1643,10 +1674,7 @@ api.wrap = (user, main=true) ->
else
+val[stat] or 0
, 0
if user.stats.lvl < 100
m[stat] += (user.stats.lvl - 1) / 2
else
m[stat] += 50
m[stat] += Math.floor(api.capByLevel(user.stats.lvl) / 2)
m
, {}
computed.maxMP = computed.int*2 + 30