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,140 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
"longheng.io/server/internal/modules/dnf/dnfenum"
|
||||
"longheng.io/server/internal/modules/dnf/dungeoncmd"
|
||||
"longheng.io/server/internal/modules/dnf/worldmap"
|
||||
)
|
||||
|
||||
const (
|
||||
currentFruzStationDungeonID = int64(102)
|
||||
currentFruzStationNarkeQuestID = int64(3733)
|
||||
currentFruzStationNarkeActorID = int64(11526)
|
||||
currentFruzStationObstacleSmall = int64(16109)
|
||||
currentFruzStationObstacleLarge = int64(16110)
|
||||
)
|
||||
|
||||
func currentFruzStationNarkeObstacleMap(mapID int64) bool {
|
||||
switch mapID {
|
||||
case 76503, 76504, 76506:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func currentFruzStationNarkeActorInRoom(room *runtimeDungeonRoom, objectKey uint32) bool {
|
||||
if room == nil || objectKey == 0 {
|
||||
return false
|
||||
}
|
||||
for _, actor := range room.Snapshot().ExtendedActors {
|
||||
if actor.ObjectKey != objectKey || actor.Kind != runtimeDungeonActorAICharacter ||
|
||||
actor.State != runtimeDungeonMonsterAnnounced || actor.HostileReference != nil ||
|
||||
actor.Packet.Code != uint32(currentFruzStationNarkeActorID) || actor.AICharacter == nil {
|
||||
continue
|
||||
}
|
||||
return actor.AICharacter.Code == currentFruzStationNarkeActorID &&
|
||||
normalizeDungeonPVFSymbol(actor.AICharacter.Faction) == "character" &&
|
||||
normalizeDungeonPVFSymbol(actor.AICharacter.AIType) == "normal"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Narke follows the player from the first room while keeping the actor key
|
||||
// announced there. Check both the live room and cached visits, but accept only
|
||||
// the exact friendly APC definition owned by quest 3733.
|
||||
func currentFruzStationRuntimeContainsNarke(runtime *runtimeDungeonState, objectKey uint32) bool {
|
||||
if runtime == nil || objectKey == 0 {
|
||||
return false
|
||||
}
|
||||
if currentFruzStationNarkeActorInRoom(runtime.Room, objectKey) {
|
||||
return true
|
||||
}
|
||||
for _, visit := range runtime.Rooms {
|
||||
if visit == nil || visit.Room == nil || visit.Room == runtime.Room {
|
||||
continue
|
||||
}
|
||||
if currentFruzStationNarkeActorInRoom(visit.Room, objectKey) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// handleCurrentFruzStationNarkeObstacleDeathLocked owns only the native
|
||||
// variable/count-zero op39 emitted after Narke's grenade hits quest rubble.
|
||||
// The obstacles are client-side passive objects and are not ordinary runtime
|
||||
// actors, so they need an op38 receipt without monster, reward, quest or room
|
||||
// clear mutation. Missing this receipt leaves Narke waiting forever after the
|
||||
// room's monsters are dead.
|
||||
func (s *Service) handleCurrentFruzStationNarkeObstacleDeathLocked(
|
||||
session *gameSession,
|
||||
runtime *runtimeDungeonState,
|
||||
scene worldmap.DungeonRoomScene,
|
||||
request dungeoncmd.DieMonsterRequest,
|
||||
) (bool, error) {
|
||||
if s == nil || session == nil || runtime == nil || runtime.Session == nil || runtime.Room == nil ||
|
||||
session.dungeon.runtime != runtime || !dungeonRuntimeOwnsCharacter(runtime, session.selectedCharacterID) ||
|
||||
runtime.Dungeon.ID != currentFruzStationDungeonID ||
|
||||
currentDungeonStoryQuestID(runtime) != currentFruzStationNarkeQuestID ||
|
||||
!currentFruzStationNarkeObstacleMap(scene.Map.Map.ID) ||
|
||||
request.Layout != dungeoncmd.DieMonsterRequestLayoutVariableCombat ||
|
||||
request.CombatEntryCount != 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
objectID := int64(request.RuntimeObjectKey)
|
||||
if objectID != currentFruzStationObstacleSmall && objectID != currentFruzStationObstacleLarge {
|
||||
return false, nil
|
||||
}
|
||||
available := currentDungeonScenePassiveObjectCount(scene, objectID)
|
||||
if available == 0 {
|
||||
return false, nil
|
||||
}
|
||||
ownerIsNarke := request.OwnerObjectKey != ^uint16(0) &&
|
||||
currentFruzStationRuntimeContainsNarke(runtime, uint32(request.OwnerObjectKey))
|
||||
if request.OwnerObjectKey != ^uint16(0) && !ownerIsNarke {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
visit, err := runtime.currentDungeonRoomVisit(scene)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
responseBody, err := buildCurrentDungeonDeathNotificationBody(
|
||||
request.RuntimeObjectKey,
|
||||
currentDungeonDeathResponseAICharacter,
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := s.sendGameUpperRawClass(
|
||||
session,
|
||||
uint16(dnfenum.CmdPacketNotifyDieMonster),
|
||||
responseBody,
|
||||
0,
|
||||
); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
retired := visit.QuestPassiveObjectDeaths[objectID]
|
||||
if retired < available {
|
||||
if visit.QuestPassiveObjectDeaths == nil {
|
||||
visit.QuestPassiveObjectDeaths = make(map[int64]uint32)
|
||||
}
|
||||
retired++
|
||||
visit.QuestPassiveObjectDeaths[objectID] = retired
|
||||
}
|
||||
s.logGameEvent(session, "game-dungeon-fruz-narke-obstacle-death-accepted",
|
||||
"dungeon_id", runtime.Dungeon.ID,
|
||||
"quest_id", currentFruzStationNarkeQuestID,
|
||||
"maze_index", runtime.MazeIndex,
|
||||
"map_id", scene.Map.Map.ID,
|
||||
"object_key", request.RuntimeObjectKey,
|
||||
"related_actor_object_key", request.OwnerObjectKey,
|
||||
"owner_is_narke", ownerIsNarke,
|
||||
"retired", retired,
|
||||
"available", available,
|
||||
"response_opcode", uint16(dnfenum.CmdPacketNotifyDieMonster))
|
||||
return true, nil
|
||||
}
|
||||
Reference in new issue
Block a user