Skip to content

Commit

Permalink
feat(adventure): enhance item picker
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jun 26, 2021
1 parent 1981c0c commit 4b01056
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 47 deletions.
30 changes: 13 additions & 17 deletions packages/plugin-adventure/src/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ namespace Event {

// Money

export const loseMoney = (value: Adventurer.Infer<number>): Event => ({ user }) => {
export const loseMoney = (value: Adventurer.Infer<number>): Visible => ({ user }) => {
const loss = Math.min(getValue(value, user), user.money)
user.money -= loss
return `$s 损失了 ${+loss.toFixed(1)}¥!`
}

export const gainMoney = (value: Adventurer.Infer<number>): Event => ({ user }) => {
export const gainMoney = (value: Adventurer.Infer<number>): Visible => ({ user }) => {
const gain = Math.min(getValue(value, user), user.money)
user.money += gain
user.wealth += gain
Expand Down Expand Up @@ -140,22 +140,20 @@ namespace Event {
if (result) output.push(result)
}
session.app.emit('adventure/gain', itemMap, session, output)
const result = Item.checkOverflow(session, Object.keys(itemMap))
if (result) output.push(result)
return output.join('\n')
}

const rarities = ['N', 'R', 'SR', 'SSR', 'EX'] as Item.Rarity[]

export const gainRandom = (count: Adventurer.Infer<number>, exclude: readonly string[] = []): Event => (session) => {
export const gainRandom = (count: Adventurer.Infer<number>, exclude: readonly string[] = []): Visible => (session) => {
const _count = getValue(count, session.user)
const gainListOriginal: string[] = []
const gainListFormatted: string[] = []
const itemMap: Record<string, number> = {}
const gainList: string[] = []

const data = {} as Record<Item.Rarity, string[]>
for (const rarity of rarities) {
data[rarity] = Item.data[rarity].filter(({ name, condition }) => {
return !exclude.includes(name) && (!condition || condition(session.user, false))
data[rarity] = Item.data[rarity].filter(({ name, beforePick }) => {
return !exclude.includes(name) && !beforePick?.(session)
}).map(({ name }) => name)
}

Expand All @@ -167,15 +165,13 @@ namespace Event {
session._item = item
const result = Item.gain(session, item)
if (result) output.push(result)
gainListOriginal.push(item)
gainListFormatted.push(session._item)
session._gains.add(item)
itemMap[item] = (itemMap[item] || 0) + 1
gainList.push(session._item)
}

output.unshift(`$s 获得了 ${_count} 件随机物品:${Item.format(gainListFormatted)}!`)
const itemMap = toItemMap(gainListOriginal)
output.unshift(`$s 获得了 ${_count} 件随机物品:${Item.format(gainList)}!`)
session.app.emit('adventure/gain', itemMap, session, output)
const result = Item.checkOverflow(session, Object.keys(itemMap))
if (result) output.push(result)
return output.join('\n')
}

Expand All @@ -192,7 +188,7 @@ namespace Event {
return output.join('\n')
}

export const loseRandom = (count: Adventurer.Infer<number>, exclude: readonly string[] = []): Event => (session) => {
export const loseRandom = (count: Adventurer.Infer<number>, exclude: readonly string[] = []): Visible => (session) => {
const lostList: string[] = []
let length = 0

Expand Down Expand Up @@ -222,7 +218,7 @@ namespace Event {
return output.join('\n')
}

export const loseRecent = (count: Adventurer.Infer<number>): Event => (session) => {
export const loseRecent = (count: Adventurer.Infer<number>): Visible => (session) => {
const _count = getValue(count, session.user)
const recent = session.user.recent.slice(0, _count)
if (!recent.length) return
Expand Down
19 changes: 15 additions & 4 deletions packages/plugin-adventure/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Context, User, isInteger } from 'koishi-core'
import { Show } from './utils'
import { Adventurer, Show } from './utils'
import Achievement from './achv'
import Affinity from './affinity'
import Buff from './buff'
Expand Down Expand Up @@ -56,12 +56,23 @@ export function apply(ctx: Context, config?: Config) {
ctx.plugin(Show)

ctx.command('user.add-item', '添加物品', { authority: 4 })
.userFields(['warehouse'])
.adminUser(({ target }, item, count = '1') => {
.option('effect', '-e 触发效果')
.userFields<Adventurer.Field>(({ options }, fields) => {
if (!options.effect) {
return fields.add('warehouse')
}
for (const field of Adventurer.fields) {
fields.add(field)
}
})
.adminUser(({ target, options, session }, item, count = '1') => {
if (!Item.data[item]) return `未找到物品“${item}”。`
const currentCount = target.warehouse[item] || 0
const nCount = Number(count)
if (!isInteger(nCount) || nCount <= 0) return '参数错误。'
if (options.effect) {
return Event.gain({ [item]: nCount })(session).replace(/\$s/g, session.username)
}
const currentCount = target.warehouse[item] || 0
target.warehouse[item] = currentCount + nCount
})

Expand Down
33 changes: 18 additions & 15 deletions packages/plugin-adventure/src/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Phase from './phase'
import Rank from './rank'

type Note = (user: Pick<ReadonlyUser, 'flag' | 'warehouse'>) => string
type Condition = (user: ReadonlyUser, isLast: boolean) => boolean
type BeforePick = (session: Adventurer.Session) => boolean

interface Item {
name: string
Expand All @@ -15,12 +15,12 @@ interface Item {
value?: number
bid?: number
onGain?: Event
onLose?: Event<'usage'>
onLose?: Event
beforePick?: BeforePick
lottery?: number
fishing?: number
plot?: boolean
note?: Note
condition?: Condition
}

namespace Item {
Expand Down Expand Up @@ -54,10 +54,6 @@ namespace Item {
data[name].note = note
}

export function condition(name: string, condition: Condition) {
data[name].condition = condition
}

export function onGain(name: string, event: Event) {
data[name].onGain = event
}
Expand All @@ -66,20 +62,27 @@ namespace Item {
data[name].onLose = event
}

export function beforePick(name: string, event: BeforePick) {
data[name].beforePick = event
}

export interface Config {
createBuyer?: (user: User.Observed<'timers'>) => (name: string) => number
createSeller?: (user: User.Observed<'timers'>) => (name: string) => number
}

export function pick(items: Item[], user: ReadonlyUser, isLast = false) {
const weightEntries = items.filter(({ lottery, condition }) => {
return lottery !== 0 && (!condition || condition(user, isLast))
}).map(({ name, lottery }) => [name, lottery ?? 1] as const)
const weight = Object.fromEntries(weightEntries)
return Item.data[Random.weightedPick(weight)]
type Keys<O, T = any> = { [K in keyof O]: O[K] extends T ? K : never }[keyof O]

export function pick(items: Item[], session: Adventurer.Session, key: Keys<Item, number>, fallback: number) {
const weightEntries = items.map<[string, number]>((item) => {
const probability = item[key] ?? fallback
if (!probability || item.beforePick?.(session)) return [item.name, 0]
return [item.name, probability]
})
return Item.data[Random.weightedPick(Object.fromEntries(weightEntries))]
}

export function lose(session: Session<'usage' | 'warehouse'>, name: string, count = 1) {
export function lose(session: Adventurer.Session, name: string, count = 1) {
if (session.user.warehouse[name]) {
session.user.warehouse[name] -= count
}
Expand Down Expand Up @@ -132,7 +135,7 @@ namespace Item {
}
}

export function checkOverflow(session: Adventurer.Session, names = Object.keys(session.user.warehouse)) {
export function checkOverflow(session: Adventurer.Session, names: Iterable<string> = session._gains) {
const itemMap: Record<string, number> = {}
for (const name of names) {
const { maxCount, value } = Item.data[name]
Expand Down
22 changes: 11 additions & 11 deletions packages/plugin-adventure/src/luck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,34 +88,34 @@ namespace Luck {
if (options.tenTimes) {
const output = [`恭喜 ${session.username} 获得了:`]
for (let index = 10; index > 0; index--) {
const prize = Item.pick(Item.data[Random.weightedPick(probabilities)], user)
const prize = Item.pick(Item.data[Random.weightedPick(probabilities)], session, 'lottery', 1)
output.push(`${prize.name}${prize.rarity})`)
}
return output.join('\n')
}

const affinity = Affinity.get(user)
const maxUsage = Math.floor(affinity / 30) + 5
let times = maxUsage - getUsage('lottery', user)
if (times <= 0) {
session._lotteryLast = maxUsage - getUsage('lottery', user)
if (session._lotteryLast <= 0) {
return '调用次数已达上限。'
}

const gainList: string[] = []
session._gains = new Set()
const output: string[] = []
function getPrize(output: string[]) {
times -= 1
session._lotteryLast -= 1
const weights = user.noSR >= 9 ? allowanceProbabilities : probabilities
const rarity = Luck.use(user).weightedPick(weights)
if (rarity === 'R' || rarity === 'N') {
user.noSR += 1
} else {
user.noSR = 0
}
const item = Item.pick(Item.data[rarity], user, !times)
const item = Item.pick(Item.data[rarity], session, 'lottery', 1)
const { name, description } = item
const isOld = item.name in user.warehouse
gainList.push(name)
session._gains.add(name)
session._item = name
const result = Item.gain(session, item.name)
if (options.simple && options.quick) {
Expand All @@ -131,18 +131,18 @@ namespace Luck {
getPrize(output)
} else {
if (options.simple) output.push(`恭喜 ${session.username} 获得了:`)
while (times && !checkTimer('$lottery', user)) {
while (session._lotteryLast && !checkTimer('$lottery', user)) {
getPrize(output)
}
}

const result = Item.checkOverflow(session, gainList)
const result = Item.checkOverflow(session)
if (result) output.push(result)
user.usage.lottery = maxUsage - times
user.usage.lottery = maxUsage - session._lotteryLast
session.app.emit('adventure/check', session, output)
await user._update()

if (!times) output.push('您本日的抽奖次数已用完,请明天再试吧~')
if (!session._lotteryLast) output.push('您本日的抽奖次数已用完,请明天再试吧~')
return output.join('\n').replace(/\$s/g, session.username)
})
}
Expand Down
8 changes: 8 additions & 0 deletions packages/plugin-adventure/src/phase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ declare module 'koishi-core' {
_canSkip?: boolean
/** 即将获得的道具名 */
_item: string
/** 当前获得的物品列表 */
_gains: Set<string>
/** 剩余抽卡次数 */
_lotteryLast: number
}
}

Expand Down Expand Up @@ -296,6 +300,8 @@ export namespace Phase {

/** handle events */
async function epilog(session: Adventurer.Session, events: Event[] = []) {
session._gains = new Set()

const hints: string[] = []
for (const event of events || []) {
const result = event(session)
Expand All @@ -306,6 +312,8 @@ export namespace Phase {
}
}

const result = Item.checkOverflow(session)
if (result) hints.push(result)
session.app.emit('adventure/check', session, hints)
await sendEscaped(session, hints.join('\n'))
}
Expand Down

0 comments on commit 4b01056

Please sign in to comment.