23 Commits

Author SHA1 Message Date
wish
fd1df19980 Merge pull request #139 from omenemomenemo/patch-1
Added more campaign_rewards entries
2025-05-04 16:38:58 +10:00
omenemomenemo
1bf9cbd11b Added more campaign_rewards entries
Based on Seleszi's research, I've further populated the list with rewards based on available campaign ids
2025-05-04 02:29:52 -04:00
wish
2ce130f4bf Merge branch 'refs/heads/main' into feature/event-tent 2025-04-01 20:39:19 +11:00
wish
9b07624444 fix TransferItem 2024-07-28 22:57:29 +10:00
wish
ba729aba80 temporarily override the minimum stamp value 2024-07-28 22:47:16 +10:00
wish
43c2917a00 temporarily override the minimum stamp value 2024-07-28 22:26:17 +10:00
wish
b6a963b131 major rewrite 2024-07-28 22:15:14 +10:00
wish
80e83f656e Merge branch 'refs/heads/main' into feature/event-tent 2024-07-27 16:22:24 +10:00
stratic-dev
aaf2af6d1d Removed remaining unks 2024-05-08 20:28:09 +01:00
stratic-dev
2c4675ef66 Fix comment 2024-04-23 23:38:04 +01:00
stratic-dev
ebe15bac1f found stamp multiplier, how to control recieve type and hide from menu 2024-04-23 23:28:08 +01:00
stratic-dev
b199187ca4 Merge branch 'main' into feature/event-tent 2024-04-22 22:17:38 +01:00
stratic-dev
5e5a6d9bb9 Merge branch 'main' into feature/event-tent 2024-03-11 20:16:20 +00:00
stratic-dev
83cb605fd7 multi stamps and deadlines 2024-02-27 22:41:24 +00:00
stratic-dev
4278846a68 Fixes for sql and select for stamp_amount 2024-02-27 00:18:18 +00:00
stratic-dev
d195e667ae Added code 2024-02-26 04:19:28 +00:00
stratic-dev
f5ee81b8d1 Added back fail 2024-02-26 03:34:51 +00:00
stratic-dev
50178f7aa0 added Patapatamellaloo 2024-02-26 03:30:01 +00:00
stratic-dev
a87c23629e Working example 2024-02-26 03:07:03 +00:00
stratic-dev
746b4e8a25 Create 22-campaign.sql 2024-02-25 14:30:50 +00:00
stratic-dev
c1e64ca0ad Merge branch 'main' into feature/event-tent 2024-02-25 14:30:31 +00:00
straticspaff
25d7e7745c Merge branch 'main' of https://github.com/ZeruLight/Erupe into feautre/event-tent 2023-11-18 05:36:28 +00:00
straticspaff
6604fa16c5 initial databasing 2023-11-18 05:34:56 +00:00
11 changed files with 5463 additions and 112 deletions

View File

@@ -11,9 +11,7 @@ import (
// MsgMhfAcquireItem represents the MSG_MHF_ACQUIRE_ITEM
type MsgMhfAcquireItem struct {
AckHandle uint32
Unk0 uint16
Length uint16
Unk1 []uint32
RewardIDs []uint32
}
// Opcode returns the ID associated with this packet type.
@@ -24,10 +22,10 @@ func (m *MsgMhfAcquireItem) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfAcquireItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Length = bf.ReadUint16()
for i := 0; i < int(m.Length); i++ {
m.Unk1 = append(m.Unk1, bf.ReadUint32())
bf.ReadUint16() // Zeroed
ids := bf.ReadUint16()
for i := uint16(0); i < ids; i++ {
m.RewardIDs = append(m.RewardIDs, bf.ReadUint32())
}
return nil
}

View File

@@ -2,6 +2,7 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/bfutil"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
@@ -9,10 +10,9 @@ import (
// MsgMhfApplyCampaign represents the MSG_MHF_APPLY_CAMPAIGN
type MsgMhfApplyCampaign struct {
AckHandle uint32
Unk0 uint32
Unk1 uint16
Unk2 []byte
AckHandle uint32
CampaignID uint32
Code string
}
// Opcode returns the ID associated with this packet type.
@@ -23,9 +23,9 @@ func (m *MsgMhfApplyCampaign) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfApplyCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint16()
m.Unk2 = bf.ReadBytes(16)
m.CampaignID = bf.ReadUint32()
bf.ReadUint16() // Zeroed
m.Code = string(bfutil.UpToNull(bf.ReadBytes(16)))
return nil
}

View File

@@ -8,9 +8,9 @@ import (
// MsgMhfEnumerateCampaign represents the MSG_MHF_ENUMERATE_CAMPAIGN
type MsgMhfEnumerateCampaign struct {
AckHandle uint32
Unk0 uint16
Unk1 uint16
AckHandle uint32
NullPadding1 uint16 // 0 in z2
NullPadding2 uint16 // 0 in z2
}
// Opcode returns the ID associated with this packet type.
@@ -21,15 +21,15 @@ func (m *MsgMhfEnumerateCampaign) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfEnumerateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint16()
m.NullPadding1 = bf.ReadUint16()
m.NullPadding2 = bf.ReadUint16()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfEnumerateCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint16(m.Unk0)
bf.WriteUint16(m.Unk1)
bf.WriteUint16(m.NullPadding1)
bf.WriteUint16(m.NullPadding2)
return nil
}

View File

@@ -11,8 +11,6 @@ import (
// MsgMhfEnumerateItem represents the MSG_MHF_ENUMERATE_ITEM
type MsgMhfEnumerateItem struct {
AckHandle uint32
Unk0 uint16
Unk1 uint16
CampaignID uint32
}
@@ -24,8 +22,8 @@ func (m *MsgMhfEnumerateItem) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfEnumerateItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint16()
bf.ReadUint16() // Zeroed
bf.ReadUint16() // Always 2
m.CampaignID = bf.ReadUint32()
return nil
}

View File

@@ -10,9 +10,9 @@ import (
// MsgMhfStateCampaign represents the MSG_MHF_STATE_CAMPAIGN
type MsgMhfStateCampaign struct {
AckHandle uint32
CampaignID uint32
Unk1 uint16
AckHandle uint32
CampaignID uint32
NullPadding uint16
}
// Opcode returns the ID associated with this packet type.
@@ -24,7 +24,7 @@ func (m *MsgMhfStateCampaign) Opcode() network.PacketID {
func (m *MsgMhfStateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.CampaignID = bf.ReadUint32()
m.Unk1 = bf.ReadUint16()
m.NullPadding = bf.ReadUint16() //0 in Z2
return nil
}

View File

@@ -11,12 +11,9 @@ import (
// MsgMhfTransferItem represents the MSG_MHF_TRANSFER_ITEM
type MsgMhfTransferItem struct {
AckHandle uint32
// looking at packets, these were static across sessions and did not actually
// correlate with any item IDs that would make sense to get after quests so
// I have no idea what this actually does
Unk0 uint32
Unk1 uint8
Unk2 uint16
QuestID uint32
ItemType uint8
Quantity uint16
}
// Opcode returns the ID associated with this packet type.
@@ -27,10 +24,10 @@ func (m *MsgMhfTransferItem) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfTransferItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint8()
m.QuestID = bf.ReadUint32()
m.ItemType = bf.ReadUint8()
bf.ReadUint8() // Zeroed
m.Unk2 = bf.ReadUint16()
m.Quantity = bf.ReadUint16()
return nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
BEGIN;
CREATE TABLE IF NOT EXISTS public.campaigns (
id SERIAL PRIMARY KEY,
min_hr INTEGER,
max_hr INTEGER,
min_sr INTEGER,
max_sr INTEGER,
min_gr INTEGER,
max_gr INTEGER,
reward_type INTEGER,
stamps INTEGER,
unk INTEGER,
background_id INTEGER,
start_time TIMESTAMP WITH TIME ZONE,
end_time TIMESTAMP WITH TIME ZONE,
title TEXT,
reward TEXT,
link TEXT,
code_prefix TEXT
);
CREATE TABLE IF NOT EXISTS public.campaign_categories (
id SERIAL PRIMARY KEY,
type INTEGER,
title TEXT,
description TEXT
);
CREATE TABLE IF NOT EXISTS public.campaign_category_links (
id SERIAL PRIMARY KEY,
campaign_id INTEGER,
category_id INTEGER
);
CREATE TABLE IF NOT EXISTS public.campaign_rewards (
id SERIAL PRIMARY KEY,
campaign_id INTEGER,
item_type INTEGER,
quantity INTEGER,
item_id INTEGER
);
CREATE TABLE IF NOT EXISTS public.campaign_rewards_claimed (
character_id INTEGER,
reward_id INTEGER
);
CREATE TABLE IF NOT EXISTS public.campaign_state (
id SERIAL PRIMARY KEY,
campaign_id INTEGER,
character_id INTEGER,
code TEXT
);
CREATE TABLE IF NOT EXISTS public.campaign_codes (
code TEXT,
multi BOOLEAN
);
CREATE TABLE IF NOT EXISTS public.campaign_quest (
campaign_id INTEGER,
character_id INTEGER
);
END;

View File

@@ -640,11 +640,6 @@ func handleMsgSysInfokyserver(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetCaUniqueID(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfTransferItem)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumeratePrice)
bf := byteframe.NewByteFrame()

View File

@@ -10,51 +10,68 @@ import (
)
type CampaignEvent struct {
ID uint32
Unk0 uint32
MinHR int16
MaxHR int16
MinSR int16
MaxSR int16
MinGR int16
MaxGR int16
Unk1 uint16
Unk2 uint8
Unk3 uint8
Unk4 uint16
Unk5 uint16
Start time.Time
End time.Time
Unk6 uint8
String0 string
String1 string
String2 string
String3 string
Link string
Prefix string
Categories []uint16
ID uint32 `db:"id"`
MinHR int16 `db:"min_hr"`
MaxHR int16 `db:"max_hr"`
MinSR int16 `db:"min_sr"`
MaxSR int16 `db:"max_sr"`
MinGR int16 `db:"min_gr"`
MaxGR int16 `db:"max_gr"`
RewardType uint16 `db:"reward_type"`
Stamps uint8 `db:"stamps"`
Unk uint8 `db:"unk"`
BackgroundID uint16 `db:"background_id"`
Start time.Time `db:"start_time"`
End time.Time `db:"end_time"`
Title string `db:"title"`
Reward string `db:"reward"`
Link string `db:"link"`
Prefix string `db:"code_prefix"`
}
type CampaignCategory struct {
ID uint16
Type uint8
Title string
Description string
ID uint16 `db:"id"`
Type uint8 `db:"type"`
Title string `db:"title"`
Description string `db:"description"`
}
type CampaignLink struct {
CategoryID uint16
CampaignID uint32
CategoryID uint16 `db:"category_id"`
CampaignID uint32 `db:"campaign_id"`
}
type CampaignReward struct {
ID uint32 `db:"id"`
ItemType uint16 `db:"item_type"`
Quantity uint16 `db:"quantity"`
ItemID uint16 `db:"item_id"`
Deadline time.Time `db:"deadline"`
}
func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateCampaign)
bf := byteframe.NewByteFrame()
events := []CampaignEvent{}
categories := []CampaignCategory{}
var events []CampaignEvent
var categories []CampaignCategory
var campaignLinks []CampaignLink
err := s.server.db.Select(&events, "SELECT id,min_hr,max_hr,min_sr,max_sr,min_gr,max_gr,reward_type,stamps,unk,background_id,start_time,end_time,title,reward,link,code_prefix FROM campaigns")
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
err = s.server.db.Select(&categories, "SELECT id, type, title, description FROM campaign_categories")
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
err = s.server.db.Select(&campaignLinks, "SELECT campaign_id, category_id FROM campaign_category_links")
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
if len(events) > 255 {
bf.WriteUint8(255)
bf.WriteUint16(uint16(len(events)))
@@ -63,7 +80,7 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
}
for _, event := range events {
bf.WriteUint32(event.ID)
bf.WriteUint32(event.Unk0)
bf.WriteUint32(0)
bf.WriteInt16(event.MinHR)
bf.WriteInt16(event.MaxHR)
bf.WriteInt16(event.MinSR)
@@ -72,22 +89,23 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt16(event.MinGR)
bf.WriteInt16(event.MaxGR)
}
bf.WriteUint16(event.Unk1)
bf.WriteUint8(event.Unk2)
bf.WriteUint8(event.Unk3)
bf.WriteUint16(event.Unk4)
bf.WriteUint16(event.Unk5)
bf.WriteUint16(event.RewardType)
bf.WriteUint8(event.Stamps)
bf.WriteUint8(event.Unk) // Related to stamp count
bf.WriteUint16(event.BackgroundID)
bf.WriteUint16(0)
bf.WriteUint32(uint32(event.Start.Unix()))
bf.WriteUint32(uint32(event.End.Unix()))
bf.WriteUint8(event.Unk6)
ps.Uint8(bf, event.String0, true)
ps.Uint8(bf, event.String1, true)
ps.Uint8(bf, event.String2, true)
ps.Uint8(bf, event.String3, true)
ps.Uint8(bf, event.Link, true)
for i := range event.Categories {
campaignLinks = append(campaignLinks, CampaignLink{event.Categories[i], event.ID})
if event.End.After(time.Now()) {
bf.WriteBool(true)
} else {
bf.WriteBool(false)
}
ps.Uint8(bf, event.Title, true)
ps.Uint8(bf, event.Reward, true)
ps.Uint8(bf, "", false)
ps.Uint8(bf, "", false)
ps.Uint8(bf, event.Link, true)
}
if len(events) > 255 {
@@ -98,7 +116,7 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
}
for _, event := range events {
bf.WriteUint32(event.ID)
bf.WriteUint8(1) // Always 1?
bf.WriteUint8(1) // Related to stamp count
bf.WriteBytes([]byte(event.Prefix))
}
@@ -135,42 +153,156 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfStateCampaign(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStateCampaign)
bf := byteframe.NewByteFrame()
bf.WriteUint16(1)
bf.WriteUint16(0)
var required int
var deadline time.Time
var stamps []uint32
err := s.server.db.Select(&stamps, "SELECT id FROM campaign_state WHERE campaign_id = $1 AND character_id = $2", pkt.CampaignID, s.charID)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
err = s.server.db.QueryRow(`SELECT stamps, end_time FROM campaigns WHERE id = $1`, pkt.CampaignID).Scan(&required, &deadline)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
bf.WriteUint16(uint16(len(stamps) + 1))
if required == 0 {
required = 1 // TODO: I don't understand how this is supposed to work
}
if len(stamps) < required {
bf.WriteUint16(0)
} else if len(stamps) >= required || deadline.After(time.Now()) {
bf.WriteUint16(2)
}
for _, v := range stamps {
bf.WriteUint32(v)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfApplyCampaign(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfApplyCampaign)
bf := byteframe.NewByteFrame()
bf.WriteUint32(1)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
// Check if the code exists and check if it's a multi-code
var multi bool
err := s.server.db.QueryRow(`SELECT multi FROM public.campaign_codes WHERE code = $1 GROUP BY multi`, pkt.Code).Scan(&multi)
if err != nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
// Check if the code is already used
var exists bool
if multi {
s.server.db.QueryRow(`SELECT COUNT(*) FROM public.campaign_state WHERE code = $1 AND character_id = $2`, pkt.Code, s.charID).Scan(&exists)
} else {
s.server.db.QueryRow(`SELECT COUNT(*) FROM public.campaign_state WHERE code = $1`, pkt.Code).Scan(&exists)
}
if exists {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
s.server.db.Exec(`INSERT INTO public.campaign_state (code, campaign_id, character_id) VALUES ($1, $2, $3)`, pkt.Code, pkt.CampaignID, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfEnumerateItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateItem)
items := []struct {
Unk0 uint32
Unk1 uint16
Unk2 uint16
Unk3 uint16
Unk4 uint32
Unk5 uint32
}{}
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(len(items)))
for _, item := range items {
bf.WriteUint32(item.Unk0)
bf.WriteUint16(item.Unk1)
bf.WriteUint16(item.Unk2)
bf.WriteUint16(item.Unk3)
bf.WriteUint32(item.Unk4)
bf.WriteUint32(item.Unk5)
var stamps, required, rewardType uint16
var deadline time.Time
err := s.server.db.QueryRow(`SELECT COUNT(*) FROM campaign_state WHERE campaign_id = $1 AND character_id = $2`, pkt.CampaignID, s.charID).Scan(&stamps)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
err = s.server.db.QueryRow(`SELECT stamps, reward_type, end_time FROM campaigns WHERE id = $1`, pkt.CampaignID).Scan(&required, &rewardType, &deadline)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
if required == 0 {
required = 1 // TODO: I don't understand how this is supposed to work
}
if stamps >= required {
var items []CampaignReward
if rewardType == 2 {
var exists int
s.server.db.QueryRow(`SELECT COUNT(*) FROM campaign_quest WHERE campaign_id = $1 AND character_id = $2`, pkt.CampaignID, s.charID).Scan(&exists)
if exists > 0 {
err = s.server.db.Select(&items, `
SELECT id, item_type, quantity, item_id, TO_TIMESTAMP(0) AS deadline FROM campaign_rewards
WHERE campaign_id = $1 AND item_type != 9
AND NOT EXISTS (SELECT 1 FROM campaign_rewards_claimed WHERE reward_id = campaign_rewards.id AND character_id = $2)
`, pkt.CampaignID, s.charID)
} else {
err = s.server.db.Select(&items, `
SELECT cr.id, cr.item_type, cr.quantity, cr.item_id, COALESCE(c.end_time, TO_TIMESTAMP(0)) AS deadline FROM campaign_rewards cr
JOIN campaigns c ON cr.campaign_id = c.id
WHERE campaign_id = $1 AND item_type = 9`, pkt.CampaignID)
}
} else {
err = s.server.db.Select(&items, `
SELECT id, item_type, quantity, item_id, TO_TIMESTAMP(0) AS deadline FROM campaign_rewards
WHERE campaign_id = $1
AND NOT EXISTS (SELECT 1 FROM campaign_rewards_claimed WHERE reward_id = campaign_rewards.id AND character_id = $2)
`, pkt.CampaignID, s.charID)
}
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
bf.WriteUint16(uint16(len(items)))
for _, item := range items {
bf.WriteUint32(item.ID)
bf.WriteUint16(item.ItemType)
bf.WriteUint16(item.Quantity)
bf.WriteUint16(item.ItemID) //HACK:placed quest id in this field to fit with Item No pattern. however it could be another field... possibly the other unks.
bf.WriteUint16(0) //Unk4, gets cast to uint8
bf.WriteUint32(0) //Unk5
bf.WriteUint32(uint32(deadline.Unix()))
}
if len(items) == 0 {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
} else {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfAcquireItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireItem)
for _, id := range pkt.RewardIDs {
s.server.db.Exec(`INSERT INTO campaign_rewards_claimed (reward_id, character_id) VALUES ($1, $2)`, id, s.charID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfTransferItem)
if pkt.ItemType == 9 {
var campaignID uint32
err := s.server.db.QueryRow(`
SELECT ce.campaign_id FROM campaign_rewards ce
JOIN event_quests eq ON ce.item_id = eq.quest_id
WHERE eq.id = $1
`, pkt.QuestID).Scan(&campaignID)
if err == nil {
s.server.db.Exec(`INSERT INTO campaign_quest (campaign_id, character_id) VALUES ($1, $2)`, campaignID, s.charID)
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -277,7 +277,33 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
}
bf.WriteUint8(questType)
if questType == 9 {
bf.WriteBool(false)
var stamps, required int
var deadline time.Time
err := s.server.db.QueryRow(`SELECT COUNT(*) FROM campaign_state WHERE campaign_id = (
SELECT campaign_id
FROM campaign_rewards
WHERE item_type = 9
AND item_id = $1
) AND character_id = $2`, questId, s.charID).Scan(&stamps)
err2 := s.server.db.QueryRow(`SELECT stamps, end_time
FROM campaigns
WHERE id = (
SELECT campaign_id
FROM campaign_rewards
WHERE item_type = 9
AND item_id = $1
)`, questId).Scan(&required, &deadline)
if required == 0 {
required = 1 // TODO: I don't understand how this is supposed to work
}
// Check if there are enough stamps to activate the quest, the deadline hasn't passed, and there are no errors
if stamps >= required && deadline.After(time.Now()) && err == nil && err2 == nil {
bf.WriteBool(true)
} else {
bf.WriteBool(false)
}
} else {
bf.WriteBool(true)
}