DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
This commit is contained in:
commit
645b64fe21
4388 files changed
+1405457
No files matched your search
@@ -0,0 +1,85 @@
|
||||
package dnfbridge
|
||||
|
||||
// currentEpicWishHit* scale the Epic wish preference branch. The branch runs
|
||||
// only AFTER the Hell table has already selected rarity 4, so it never changes
|
||||
// the Epic rarity roll itself -- it only decides which Epic is handed out.
|
||||
//
|
||||
// The numerator is a product dial, not a PVF value. Measured against the live
|
||||
// runtime Script.pvf on 2026-09-23: etc/epicpieceinfo.etc holds 966 entries
|
||||
// across six level buckets (65/70/75/80/85/90), so the level-90 band [85,90]
|
||||
// carries roughly 264 candidates with a total creation-rate weight of roughly
|
||||
// 30,900; one average Epic is therefore only ~0.38% of the band and the
|
||||
// heaviest observed weight (250) is ~0.81%.
|
||||
//
|
||||
// Because the miss branch REMOVES every wished entry from the pool, a valid
|
||||
// wish does not add to its item's natural share -- it REPLACES it with
|
||||
// numerator/denominator/N (N = filled wish slots, 1..5). The former 25/100
|
||||
// therefore pinned a single wish at 25% of all Epic drops, about 66x its
|
||||
// natural share. 2/100 keeps a single wish a 2.5x..20x gain while a fully
|
||||
// filled five-slot list lands at 0.4% per slot, i.e. near -- and for the
|
||||
// heavier entries slightly below -- the natural share.
|
||||
const (
|
||||
currentEpicWishHitNumerator = 2
|
||||
currentEpicWishHitDenominator = 100
|
||||
)
|
||||
|
||||
func selectCurrentTimeRiftEpicCandidate(
|
||||
rng *currentDungeonDropLCG,
|
||||
pools map[int][]currentDungeonHellPartyFragmentCandidate,
|
||||
monsterLevel int,
|
||||
wishItems []uint32,
|
||||
) (currentDungeonHellPartyFragmentCandidate, bool, bool) {
|
||||
if rng == nil {
|
||||
return currentDungeonHellPartyFragmentCandidate{}, false, false
|
||||
}
|
||||
candidates := make([]currentDungeonHellPartyFragmentCandidate, 0)
|
||||
byEquipment := make(map[uint32]currentDungeonHellPartyFragmentCandidate)
|
||||
for _, level := range currentHellPartyEpicLevels(monsterLevel) {
|
||||
for _, candidate := range pools[level] {
|
||||
candidates = append(candidates, candidate)
|
||||
if candidate.EquipmentID != 0 {
|
||||
byEquipment[candidate.EquipmentID] = candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
return currentDungeonHellPartyFragmentCandidate{}, false, false
|
||||
}
|
||||
|
||||
validWish := make([]currentDungeonHellPartyFragmentCandidate, 0, len(wishItems))
|
||||
wished := make(map[uint32]struct{}, len(wishItems))
|
||||
for _, itemID := range wishItems {
|
||||
candidate, found := byEquipment[itemID]
|
||||
if itemID == 0 || !found {
|
||||
continue
|
||||
}
|
||||
if _, duplicate := wished[itemID]; duplicate {
|
||||
continue
|
||||
}
|
||||
wished[itemID] = struct{}{}
|
||||
validWish = append(validWish, candidate)
|
||||
}
|
||||
if len(validWish) != 0 &&
|
||||
rng.Next(currentEpicWishHitDenominator) < currentEpicWishHitNumerator {
|
||||
return validWish[rng.Next(len(validWish))], true, true
|
||||
}
|
||||
|
||||
normal := make([]currentDungeonHellPartyFragmentCandidate, 0, len(candidates))
|
||||
for _, candidate := range candidates {
|
||||
if _, excluded := wished[candidate.EquipmentID]; !excluded {
|
||||
normal = append(normal, candidate)
|
||||
}
|
||||
}
|
||||
if candidate, found := selectCurrentHellPartyFragmentCandidateFromPool(rng, normal); found {
|
||||
return candidate, true, false
|
||||
}
|
||||
|
||||
// If every eligible Epic is wished, the archived miss branch had an empty
|
||||
// pool and silently erased an already-successful rarity-4 result. Preserve
|
||||
// that Epic roll by falling back to the complete weighted pool.
|
||||
candidate, found := selectCurrentHellPartyFragmentCandidateFromPool(rng, candidates)
|
||||
if !found {
|
||||
return currentDungeonHellPartyFragmentCandidate{}, false, false
|
||||
}
|
||||
return candidate, true, false
|
||||
}
|
||||
Reference in new issue
Block a user