I rewrote House_Request based on the code provided by undownding.

This commit is contained in:
Kei-Luna
2026-05-05 07:35:34 +09:00
parent 677e4027b9
commit 2d1079a80d
9 changed files with 763 additions and 72 deletions

View File

@@ -3,39 +3,26 @@ using System.Text.Json.Nodes;
namespace MikuSB.GameServer.Server.CallGS.Handlers.House;
// ArcadeGameEnterMainUI
// Returns all girl IDs (1-25) as unlocked, and syncs TeachMode + EndlessMode attrs.
[HouseFunc("ArcadeGameEnterMainUI")]
public class ArcadeGameEnterMainUI : IHouseFuncHandler
{
private const uint ArcadeGid = 101;
private const uint EndlessModeStateSid = 18000 + 5;
private const uint TeachModeConditionSid = 18000 + 36 + 8;
private static readonly int[] ArcadeUnlockedGirlList = [1, 5, 13, 23, 10];
public async Task Handle(Connection connection, string param)
{
var girlList = new JsonArray();
for (int i = 1; i <= 25; i++) girlList.Add(i);
var unlockGirls = new JsonArray();
foreach (var girlId in ArcadeUnlockedGirlList)
unlockGirls.Add(girlId);
var rsp = new JsonObject
{
["FuncName"] = "ArcadeGameEnterMainUI",
["tbUnlockGirlList"] = girlList
["tbUnlockGirlList"] = unlockGirls,
["FuncName"] = "ArcadeGameEnterMainUI"
};
var player = connection.Player!;
var sync = new NtfSyncPlayer();
sync.Custom[player.ToPackedAttrKey(ArcadeGid, TeachModeConditionSid)] = 1;
sync.Custom[player.ToShiftedAttrKey(ArcadeGid, TeachModeConditionSid)] = 1;
const uint endlessAllUnlocked = 0x3FFFFFE;
sync.Custom[player.ToPackedAttrKey(ArcadeGid, EndlessModeStateSid)] = endlessAllUnlocked;
sync.Custom[player.ToShiftedAttrKey(ArcadeGid, EndlessModeStateSid)] = endlessAllUnlocked;
await CallGSRouter.SendScript(connection, "House_Request", rsp.ToJsonString(), sync);
await CallGSRouter.SendScript(connection, "House_Request", HouseRequestScript.Success(rsp));
}
}
// ArcadeGameEnter — returns a random seed for level generation.
[HouseFunc("ArcadeGameEnter")]
public class ArcadeGameEnter : IHouseFuncHandler
{
@@ -48,50 +35,203 @@ public class ArcadeGameEnter : IHouseFuncHandler
["FuncName"] = "ArcadeGameEnter",
["nSeed"] = Random.Next(1, 1_000_000_000)
};
await CallGSRouter.SendScript(connection, "House_Request", rsp.ToJsonString());
await CallGSRouter.SendScript(connection, "House_Request", HouseRequestScript.Success(rsp));
}
}
// ArcadeGameSettlement — acknowledges round end; nAddExp=0 on private server.
[HouseFunc("ArcadeGameSettlement")]
public class ArcadeGameSettlement : IHouseFuncHandler
{
private const uint HouseArcadeInfoStart = 18000;
private const uint ArcadeAttrExpOffset = 2;
private const uint ArcadeAttrEndlessScoreOffset = 4;
private const uint ArcadeAttrGirlEndlessModeStateOffset = 5;
private const uint ArcadeAttrGirlNormalModeStateOffset = 10;
private const uint ArcadeAttrGirlNormalModeStateEndOffset = 35;
private const uint ArcadeAttrConditionValStartOffset = 36;
private const uint ArcadeAttrConditionValEndOffset = 55;
private const uint ArcadeAttrPropUseStartOffset = 56;
private const uint ArcadeAttrPropUseEndOffset = 250;
private const int ArcadeGameModeNormal = 1;
private const int ArcadeGameModeEndless = 2;
public async Task Handle(Connection connection, string param)
{
var rsp = new JsonObject { ["FuncName"] = "ArcadeGameSettlement", ["nAddExp"] = 0 };
await CallGSRouter.SendScript(connection, "House_Request", rsp.ToJsonString());
var root = HouseJson.ParseObject(param);
if (root == null) return;
var player = connection.Player!;
var sync = new NtfSyncPlayer();
var modeType = HouseJson.NumField(root, "nModeType");
var finishRound = Math.Max(0, HouseJson.NumField(root, "nFinishRound"));
var maxScore = Math.Max(0, HouseJson.NumField(root, "nMaxScore"));
if (root["tbConditionVal"] is JsonArray conditions)
{
foreach (var conditionNode in conditions)
{
if (conditionNode is not JsonArray condition || condition.Count < 2) continue;
var id = HouseJson.ToInt(condition[0]);
var value = Math.Max(0, HouseJson.ToInt(condition[1]));
if (id < 0) continue;
var sid = HouseArcadeInfoStart + ArcadeAttrConditionValStartOffset + (uint)id;
if (sid > HouseArcadeInfoStart + ArcadeAttrConditionValEndOffset) continue;
var prev = HouseAttr.Read(player, sid);
if ((uint)value > prev)
await HouseAttr.SetAsync(connection, sid, (uint)value, sync);
}
}
if (root["tbPropUse"] is JsonArray propUses)
{
var slotStart = HouseArcadeInfoStart + ArcadeAttrPropUseStartOffset;
var slotEnd = HouseArcadeInfoStart + ArcadeAttrPropUseEndOffset;
var slotState = new Dictionary<uint, ArcadePropUseSlot>();
var emptySlots = new Queue<uint>();
for (uint sid = slotStart; sid <= slotEnd; sid++)
{
var value = HouseAttr.Read(player, sid);
if (value == 0)
{
emptySlots.Enqueue(sid);
continue;
}
slotState[sid] = new ArcadePropUseSlot
{
Type = (byte)(value & 0xff),
Id = (byte)((value >> 8) & 0xff),
Count = (ushort)((value >> 16) & 0xffff)
};
}
foreach (var propUseNode in propUses)
{
if (propUseNode is not JsonArray propUse || propUse.Count < 3) continue;
var type = HouseJson.ToInt(propUse[0]) & 0xff;
var id = HouseJson.ToInt(propUse[1]) & 0xff;
var count = HouseJson.ToInt(propUse[2]);
if (count <= 0) continue;
uint? foundSid = null;
foreach (var (sid, slot) in slotState)
{
if (slot.Type == type && slot.Id == id)
{
foundSid = sid;
break;
}
}
if (foundSid != null)
{
var slot = slotState[foundSid.Value];
slot.Count = (ushort)Math.Min(0xffff, slot.Count + count);
slotState[foundSid.Value] = slot;
await HouseAttr.SetAsync(connection, foundSid.Value, HouseAttr.PackArcadePropUse(slot.Type, slot.Id, slot.Count), sync);
}
else if (emptySlots.Count > 0)
{
var sid = emptySlots.Dequeue();
var packedCount = (ushort)Math.Min(0xffff, count);
slotState[sid] = new ArcadePropUseSlot { Type = type, Id = id, Count = packedCount };
await HouseAttr.SetAsync(connection, sid, HouseAttr.PackArcadePropUse(type, id, packedCount), sync);
}
}
}
var girlList = root["tbGirlDataList"] as JsonArray;
var nAddExp = 0;
if (modeType == ArcadeGameModeNormal)
{
if (girlList != null)
{
foreach (var girlNode in girlList)
{
var girlId = HouseJson.ToInt(girlNode);
if (girlId <= 0) continue;
var sid = HouseArcadeInfoStart + ArcadeAttrGirlNormalModeStateOffset + (uint)girlId;
if (sid > HouseArcadeInfoStart + ArcadeAttrGirlNormalModeStateEndOffset) continue;
var prev = HouseAttr.Read(player, sid);
await HouseAttr.SetAsync(connection, sid, prev + (uint)finishRound, sync);
}
}
nAddExp = finishRound * 10;
}
else if (modeType == ArcadeGameModeEndless)
{
var scoreSid = HouseArcadeInfoStart + ArcadeAttrEndlessScoreOffset;
var prevScore = HouseAttr.Read(player, scoreSid);
if ((uint)maxScore > prevScore)
await HouseAttr.SetAsync(connection, scoreSid, (uint)maxScore, sync);
var bitsSid = HouseArcadeInfoStart + ArcadeAttrGirlEndlessModeStateOffset;
var bits = HouseAttr.Read(player, bitsSid);
var originalBits = bits;
if (girlList != null)
{
foreach (var girlNode in girlList)
{
var girlId = HouseJson.ToInt(girlNode);
if (girlId is > 0 and < 31)
bits |= 1u << girlId;
}
}
if (bits != originalBits)
await HouseAttr.SetAsync(connection, bitsSid, bits, sync);
nAddExp = maxScore / 10;
}
if (nAddExp > 0)
{
var expSid = HouseArcadeInfoStart + ArcadeAttrExpOffset;
var prevExp = HouseAttr.Read(player, expSid);
await HouseAttr.SetAsync(connection, expSid, prevExp + (uint)nAddExp, sync);
}
var rsp = new JsonObject
{
["nAddExp"] = nAddExp,
["FuncName"] = "ArcadeGameSettlement"
};
await CallGSRouter.SendScript(connection, "House_Request", HouseRequestScript.Success(rsp), sync);
}
private struct ArcadePropUseSlot
{
public int Type { get; set; }
public int Id { get; set; }
public ushort Count { get; set; }
}
}
// ArcadeGameLogSettlement — acknowledges log upload (no client data required).
[HouseFunc("ArcadeGameLogSettlement")]
public class ArcadeGameLogSettlement : IHouseFuncHandler
{
public async Task Handle(Connection connection, string param)
{
var rsp = new JsonObject { ["FuncName"] = "ArcadeGameLogSettlement" };
await CallGSRouter.SendScript(connection, "House_Request", rsp.ToJsonString());
await CallGSRouter.SendScript(connection, "House_Request", HouseRequestScript.Success(rsp));
}
}
// ArcadeGameGetLevelReward — UI refresh only on client side.
[HouseFunc("ArcadeGameGetLevelReward")]
public class ArcadeGameGetLevelReward : IHouseFuncHandler
{
public async Task Handle(Connection connection, string param)
{
var rsp = new JsonObject { ["FuncName"] = "ArcadeGameGetLevelReward" };
await CallGSRouter.SendScript(connection, "House_Request", rsp.ToJsonString());
await CallGSRouter.SendScript(connection, "House_Request", HouseRequestScript.Success(rsp));
}
}
// ArcadeGameGetAchReward — UI refresh only on client side.
[HouseFunc("ArcadeGameGetAchReward")]
public class ArcadeGameGetAchReward : IHouseFuncHandler
{
public async Task Handle(Connection connection, string param)
{
var rsp = new JsonObject { ["FuncName"] = "ArcadeGameGetAchReward" };
await CallGSRouter.SendScript(connection, "House_Request", rsp.ToJsonString());
await CallGSRouter.SendScript(connection, "House_Request", HouseRequestScript.Success(rsp));
}
}