initial festa build

This commit is contained in:
wish
2022-07-27 21:05:57 +10:00
parent 60d9d5b305
commit 9c772a09fc
5 changed files with 364 additions and 93 deletions

View File

@@ -11,7 +11,7 @@
"MaxHexdumpLength": 256, "MaxHexdumpLength": 256,
"Event": 0, "Event": 0,
"DivaEvent": 0, "DivaEvent": 0,
"FestaEvent": 0, "FestaEvent": -1,
"TournamentEvent": 0, "TournamentEvent": 0,
"MezFesEvent": true, "MezFesEvent": true,
"SaveDumps": { "SaveDumps": {

164
Erupe/festa.sql Normal file
View File

@@ -0,0 +1,164 @@
BEGIN;
CREATE TYPE event_type AS ENUM ('festa', 'diva', 'vs', 'mezfes');
DROP TABLE IF EXISTS public.event_week;
ALTER TABLE IF EXISTS public.guild_characters
ADD COLUMN IF NOT EXISTS souls int DEFAULT 0;
ALTER TABLE IF EXISTS public.guilds
DROP COLUMN IF EXISTS festival_colour;
CREATE TABLE IF NOT EXISTS public.events
(
id serial NOT NULL PRIMARY KEY,
event_type event_type NOT NULL,
start_time timestamp without time zone NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS public.festa_registrations
(
guild_id int NOT NULL,
team festival_colour NOT NULL
);
CREATE TABLE IF NOT EXISTS public.festa_trials
(
id serial NOT NULL PRIMARY KEY,
objective int NOT NULL,
goal_id int NOT NULL,
times_req int NOT NULL,
locale_req int NOT NULL DEFAULT 0,
reward int NOT NULL
);
-- Ripped trials
INSERT INTO public.festa_trials
(objective, goal_id, times_req, locale_req, reward)
VALUES
(1,27,1,0,1),
(5,53034,0,0,400),
(5,22042,0,0,89),
(5,23397,0,0,89),
(1,28,1,0,1),
(1,68,1,0,1),
(1,6,1,0,2),
(1,38,1,0,2),
(1,20,1,0,3),
(1,39,1,0,4),
(1,48,1,0,4),
(1,67,1,0,4),
(1,93,1,0,4),
(1,22,1,0,5),
(1,52,1,0,5),
(1,101,1,0,5),
(1,1,1,0,5),
(1,37,1,0,5),
(1,15,1,0,5),
(1,45,1,0,5),
(1,74,1,0,5),
(1,78,1,0,5),
(1,103,1,0,5),
(1,51,1,0,6),
(1,17,1,0,6),
(1,21,1,0,6),
(1,92,1,0,6),
(1,47,1,0,7),
(1,46,1,0,7),
(1,26,1,0,7),
(1,14,1,0,7),
(1,11,1,0,7),
(1,44,1,0,8),
(1,43,1,0,8),
(1,49,1,0,8),
(1,40,1,0,8),
(1,76,1,0,8),
(1,89,1,0,8),
(1,94,1,0,8),
(1,96,1,0,8),
(1,75,1,0,8),
(1,91,1,0,8),
(1,53,1,0,9),
(1,80,1,0,9),
(1,42,1,0,9),
(1,79,1,0,9),
(1,81,1,0,10),
(1,41,1,0,10),
(1,82,1,0,10),
(1,90,1,0,10),
(1,149,1,0,10),
(1,85,1,0,11),
(1,95,1,0,11),
(1,121,1,0,11),
(1,142,1,0,11),
(1,141,1,0,11),
(1,146,1,0,12),
(1,147,1,0,12),
(1,148,1,0,12),
(1,151,1,0,12),
(1,152,1,0,12),
(1,159,1,0,12),
(1,153,1,0,12),
(1,162,1,0,12),
(1,111,1,0,13),
(1,110,1,0,13),
(1,112,1,0,13),
(1,109,1,0,14),
(1,169,1,0,15),
(2,33,1,0,6),
(2,104,1,0,8),
(2,119,1,0,8),
(2,120,1,0,8),
(2,54,1,0,8),
(2,59,1,0,8),
(2,64,1,0,8),
(2,65,1,0,8),
(2,99,1,0,9),
(2,83,1,0,9),
(2,84,1,0,10),
(2,77,1,0,10),
(2,106,1,0,10),
(2,55,1,0,10),
(2,58,1,0,10),
(2,7,1,0,10),
(2,50,1,0,11),
(2,131,1,0,11),
(2,129,1,0,11),
(2,140,1,0,11),
(2,122,1,0,11),
(2,126,1,0,11),
(2,127,1,0,11),
(2,128,1,0,11),
(2,130,1,0,11),
(2,139,1,0,11),
(2,144,1,0,11),
(2,150,1,0,11),
(2,158,1,0,11),
(2,164,1,0,15),
(2,165,1,0,15),
(2,2,1,7,15),
(2,36,1,0,15),
(2,71,1,0,15),
(2,108,1,0,15),
(2,116,1,0,15),
(2,107,1,0,15),
(2,154,1,0,17),
(2,166,1,0,17),
(2,170,1,0,18),
(3,31,1,0,1),
(3,8,1,0,3),
(3,123,1,0,8),
(3,105,1,0,9),
(3,125,1,0,11),
(3,115,1,0,12),
(3,114,1,0,12),
(3,161,1,0,12),
(4,670,1,0,1),
(4,671,1,0,1),
(4,672,1,0,1),
(4,675,1,0,1),
(4,673,1,0,1),
(4,674,1,0,1);
END;

View File

@@ -2,12 +2,11 @@ package channelserver
import ( import (
"encoding/hex" "encoding/hex"
"math/rand"
"time"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring" ps "erupe-ce/common/pascalstring"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"math/rand"
"time"
) )
func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) {
@@ -69,65 +68,129 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func cleanupFesta(s *Session) {
s.server.db.Exec("DELETE FROM events WHERE event_type='festa'")
s.server.db.Exec("DELETE FROM festa_registrations")
s.server.db.Exec("UPDATE guild_characters SET souls=0")
}
func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
timestamps := make([]uint32, 5)
midnight := Time_Current_Midnight()
if debug && start <= 3 {
switch start {
case 1:
timestamps[0] = uint32(midnight.Unix())
timestamps[1] = uint32(midnight.Add(24 * 7 * time.Hour).Unix())
timestamps[2] = uint32(midnight.Add(24 * 14 * time.Hour).Unix())
timestamps[3] = uint32(midnight.Add(24*14*time.Hour + 150*time.Minute).Unix())
timestamps[4] = uint32(midnight.Add(24*28*time.Hour + 11*time.Hour).Unix())
case 2:
timestamps[0] = uint32(midnight.Add(-24 * 7 * time.Hour).Unix())
timestamps[1] = uint32(midnight.Unix())
timestamps[2] = uint32(midnight.Add(24 * 7 * time.Hour).Unix())
timestamps[3] = uint32(midnight.Add(24*7*time.Hour + 150*time.Minute).Unix())
timestamps[4] = uint32(midnight.Add(24 * 21 * time.Hour).Add(11 * time.Hour).Unix())
case 3:
timestamps[0] = uint32(midnight.Add(-24 * 14 * time.Hour).Unix())
timestamps[1] = uint32(midnight.Add(-24*7*time.Hour + 11*time.Hour).Unix())
timestamps[2] = uint32(midnight.Unix())
timestamps[3] = uint32(midnight.Add(150 * time.Minute).Unix())
timestamps[4] = uint32(midnight.Add(24*14*time.Hour + 11*time.Hour).Unix())
}
return timestamps
}
if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 {
cleanupFesta(s)
// Generate a new festa, starting midnight tomorrow
start = uint32(midnight.Add(24 * time.Hour).Unix())
s.server.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('festa', to_timestamp($1)::timestamp without time zone)", start)
}
timestamps[0] = start
timestamps[1] = timestamps[0] + 604800
timestamps[2] = timestamps[1] + 604800
timestamps[3] = timestamps[2] + 9000
timestamps[4] = timestamps[3] + 1240200
return timestamps
}
type Trial struct {
ID uint32 `db:"id"`
Objective uint8 `db:"objective"`
GoalID uint32 `db:"goal_id"`
TimesReq uint16 `db:"times_req"`
Locale uint16 `db:"locale_req"`
Reward uint16 `db:"reward"`
}
func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfInfoFesta) pkt := p.(*mhfpacket.MsgMhfInfoFesta)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
state := s.server.erupeConfig.DevModeOptions.FestaEvent
bf.WriteUint32(0xdeadbeef) // festaID id, start := uint32(0xDEADBEEF), uint32(0)
// Registration Week Start rows, _ := s.server.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='festa'")
// Introductory Week Start for rows.Next() {
// Totalling Time rows.Scan(&id, &start)
// Reward Festival Start (2.5hrs after totalling) }
// 2 weeks after RewardFes (next fes?)
midnight := Time_Current_Midnight() var timestamps []uint32
switch state { if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.FestaEvent >= 0 {
case 1: if s.server.erupeConfig.DevModeOptions.FestaEvent == 0 {
bf.WriteUint32(uint32(midnight.Unix())) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
bf.WriteUint32(uint32(midnight.Add(24 * 7 * time.Hour).Unix())) return
bf.WriteUint32(uint32(midnight.Add(24 * 14 * time.Hour).Unix())) }
bf.WriteUint32(uint32(midnight.Add(24*14*time.Hour + 150*time.Minute).Unix())) timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.FestaEvent), true)
bf.WriteUint32(uint32(midnight.Add(24*28*time.Hour + 11*time.Hour).Unix())) } else {
case 2: timestamps = generateFestaTimestamps(s, start, false)
bf.WriteUint32(uint32(midnight.Add(-24 * 7 * time.Hour).Unix())) }
bf.WriteUint32(uint32(midnight.Unix()))
bf.WriteUint32(uint32(midnight.Add(24 * 7 * time.Hour).Unix())) if timestamps[0] > uint32(Time_Current_Adjusted().Unix()) {
bf.WriteUint32(uint32(midnight.Add(24*7*time.Hour + 150*time.Minute).Unix()))
bf.WriteUint32(uint32(midnight.Add(24 * 21 * time.Hour).Add(11 * time.Hour).Unix()))
case 3:
bf.WriteUint32(uint32(midnight.Add(-24 * 14 * time.Hour).Unix()))
bf.WriteUint32(uint32(midnight.Add(-24*7*time.Hour + 11*time.Hour).Unix()))
bf.WriteUint32(uint32(midnight.Unix()))
bf.WriteUint32(uint32(midnight.Add(150 * time.Minute).Unix()))
bf.WriteUint32(uint32(midnight.Add(24*14*time.Hour + 11*time.Hour).Unix()))
default:
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return return
} }
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time
var blueSouls, redSouls uint32
s.server.db.QueryRow("SELECT SUM(gc.souls) FROM guild_characters gc INNER JOIN festa_registrations fr ON fr.guild_id = gc.guild_id WHERE fr.team = 'blue'").Scan(&blueSouls)
s.server.db.QueryRow("SELECT SUM(gc.souls) FROM guild_characters gc INNER JOIN festa_registrations fr ON fr.guild_id = gc.guild_id WHERE fr.team = 'red'").Scan(&redSouls)
bf.WriteUint32(id)
for _, timestamp := range timestamps {
bf.WriteUint32(timestamp)
}
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix()))
bf.WriteUint8(4) bf.WriteUint8(4)
ps.Uint8(bf, "", false) ps.Uint8(bf, "", false)
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint32(0) // Blue souls bf.WriteUint32(blueSouls)
bf.WriteUint32(0) // Red souls bf.WriteUint32(redSouls)
trials := 0 rows, _ = s.server.db.Queryx("SELECT * FROM festa_trials")
bf.WriteUint16(uint16(trials)) trialData := byteframe.NewByteFrame()
for i := 0; i < trials; i++ { var count uint16
bf.WriteUint32(uint32(i + 1)) // trialID for rows.Next() {
bf.WriteUint8(0xFF) // unk trial := &Trial{}
bf.WriteUint8(uint8(i)) // objective err := rows.StructScan(&trial)
bf.WriteUint32(0x1B) // monID, itemID if deliver if err != nil {
bf.WriteUint16(1) // huntsRemain? continue
bf.WriteUint16(0) // location }
bf.WriteUint16(1) // numSoulsReward count++
bf.WriteUint8(0xFF) // unk trialData.WriteUint32(trial.ID)
bf.WriteUint8(0xFF) // monopolised trialData.WriteUint8(0) // Unk
bf.WriteUint16(0) // unk trialData.WriteUint8(trial.Objective)
trialData.WriteUint32(trial.GoalID)
trialData.WriteUint16(trial.TimesReq)
trialData.WriteUint16(trial.Locale)
trialData.WriteUint16(trial.Reward)
trialData.WriteUint8(0xFF) // Unk
trialData.WriteUint8(0xFF) // MonopolyState
trialData.WriteUint16(0) // Unk
} }
bf.WriteUint16(count)
bf.WriteBytes(trialData.Data())
unk := 0 // static rewards? unk := uint16(0) // static rewards?
bf.WriteUint16(uint16(unk)) bf.WriteUint16(unk)
for i := 0; i < unk; i++ { for i := uint16(0); i < unk; i++ {
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint16(0) bf.WriteUint16(0)
bf.WriteUint16(0) bf.WriteUint16(0)
@@ -144,8 +207,15 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
// state festa (U)ser // state festa (U)ser
func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStateFestaU) pkt := p.(*mhfpacket.MsgMhfStateFestaU)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil || guild == nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
var souls uint32
s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint32(0) // souls bf.WriteUint32(souls)
bf.WriteUint32(0) // unk bf.WriteUint32(0) // unk
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
@@ -153,8 +223,13 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {
// state festa (G)uild // state festa (G)uild
func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStateFestaG) pkt := p.(*mhfpacket.MsgMhfStateFestaG)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil || guild == nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
resp := byteframe.NewByteFrame() resp := byteframe.NewByteFrame()
resp.WriteUint32(0) // souls resp.WriteUint32(guild.Souls)
resp.WriteUint32(1) // unk resp.WriteUint32(1) // unk
resp.WriteUint32(1) // unk resp.WriteUint32(1) // unk
resp.WriteUint32(1) // unk, rank? resp.WriteUint32(1) // unk, rank?
@@ -164,31 +239,54 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaMember) pkt := p.(*mhfpacket.MsgMhfEnumerateFestaMember)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil || guild == nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
members, err := GetGuildMembers(s, guild.ID, false)
if err != nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint16(0) // numMembers bf.WriteUint16(uint16(len(members)))
// uint16 unk for _, member := range members {
// uint32 charID bf.WriteUint16(0)
// uint32 souls bf.WriteUint32(member.CharID)
bf.WriteUint32(member.Souls)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfVoteFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfVoteFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEntryFesta) pkt := p.(*mhfpacket.MsgMhfVoteFesta)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEntryFesta) pkt := p.(*mhfpacket.MsgMhfEntryFesta)
bf := byteframe.NewByteFrame() guild, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil || guild == nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
bf.WriteUint32(uint32(rand.Intn(2))) team := uint32(rand.Intn(2))
// Update guild table switch team {
case 0:
s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID)
case 1:
s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'red')", guild.ID)
}
bf := byteframe.NewByteFrame()
bf.WriteUint32(team)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfChargeFesta) pkt := p.(*mhfpacket.MsgMhfChargeFesta)
// Update festa state table s.server.db.Exec("UPDATE guild_characters SET souls=souls+$1 WHERE character_id=$2", pkt.Souls, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }

View File

@@ -12,10 +12,10 @@ import (
"strings" "strings"
"time" "time"
"erupe-ce/common/byteframe"
"erupe-ce/common/bfutil" "erupe-ce/common/bfutil"
"erupe-ce/common/stringsupport" "erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring" ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"go.uber.org/zap" "go.uber.org/zap"
@@ -56,6 +56,7 @@ type Guild struct {
PugiName2 string `db:"pugi_name_2"` PugiName2 string `db:"pugi_name_2"`
PugiName3 string `db:"pugi_name_3"` PugiName3 string `db:"pugi_name_3"`
FestivalColour FestivalColour `db:"festival_colour"` FestivalColour FestivalColour `db:"festival_colour"`
Souls uint32 `db:"souls"`
Rank uint16 `db:"rank"` Rank uint16 `db:"rank"`
AllianceID uint32 `db:"alliance_id"` AllianceID uint32 `db:"alliance_id"`
Icon *GuildIcon `db:"icon"` Icon *GuildIcon `db:"icon"`
@@ -124,7 +125,14 @@ SELECT
pugi_name_1, pugi_name_1,
pugi_name_2, pugi_name_2,
pugi_name_3, pugi_name_3,
festival_colour, CASE WHEN (
SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id
) IS NULL THEN 'none' ELSE (
SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id
) END festival_colour,
(
SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id
) AS souls,
CASE CASE
WHEN rank_rp <= 48 THEN rank_rp/24 WHEN rank_rp <= 48 THEN rank_rp/24
WHEN rank_rp <= 288 THEN rank_rp/48+1 WHEN rank_rp <= 288 THEN rank_rp/48+1
@@ -138,14 +146,12 @@ SELECT
ga.parent_id = g.id OR ga.parent_id = g.id OR
ga.sub1_id = g.id OR ga.sub1_id = g.id OR
ga.sub2_id = g.id ga.sub2_id = g.id
) IS NULL THEN 0 ) IS NULL THEN 0 ELSE (
ELSE (
SELECT id FROM guild_alliances ga WHERE SELECT id FROM guild_alliances ga WHERE
ga.parent_id = g.id OR ga.parent_id = g.id OR
ga.sub1_id = g.id OR ga.sub1_id = g.id OR
ga.sub2_id = g.id ga.sub2_id = g.id
) ) END alliance_id,
END alliance_id,
icon, icon,
( (
SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id

View File

@@ -12,6 +12,7 @@ type GuildMember struct {
GuildID uint32 `db:"guild_id"` GuildID uint32 `db:"guild_id"`
CharID uint32 `db:"character_id"` CharID uint32 `db:"character_id"`
JoinedAt *time.Time `db:"joined_at"` JoinedAt *time.Time `db:"joined_at"`
Souls uint32 `db:"souls"`
Name string `db:"name"` Name string `db:"name"`
IsApplicant bool `db:"is_applicant"` IsApplicant bool `db:"is_applicant"`
OrderIndex uint8 `db:"order_index"` OrderIndex uint8 `db:"order_index"`
@@ -49,30 +50,32 @@ func (gm *GuildMember) IsRecruiter() bool {
} }
const guildMembersSelectSQL = ` const guildMembersSelectSQL = `
SELECT g.id as guild_id, SELECT
joined_at, g.id as guild_id,
c.name, joined_at,
character.character_id, souls,
coalesce(gc.order_index, 0) as order_index, c.name,
c.last_login, character.character_id,
coalesce(gc.avoid_leadership, false) as avoid_leadership, coalesce(gc.order_index, 0) as order_index,
c.hrp, c.last_login,
c.gr, coalesce(gc.avoid_leadership, false) as avoid_leadership,
c.weapon_id, c.hrp,
c.weapon_type, c.gr,
character.is_applicant, c.weapon_id,
CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader c.weapon_type,
FROM ( character.is_applicant,
SELECT character_id, true as is_applicant, guild_id CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader
FROM guild_applications ga FROM (
WHERE ga.application_type = 'applied' SELECT character_id, true as is_applicant, guild_id
UNION FROM guild_applications ga
SELECT character_id, false as is_applicant, guild_id WHERE ga.application_type = 'applied'
FROM guild_characters gc UNION
) character SELECT character_id, false as is_applicant, guild_id
JOIN characters c on character.character_id = c.id FROM guild_characters gc
LEFT JOIN guild_characters gc ON gc.character_id = character.character_id ) character
JOIN guilds g ON g.id = character.guild_id JOIN characters c on character.character_id = c.id
LEFT JOIN guild_characters gc ON gc.character_id = character.character_id
JOIN guilds g ON g.id = character.guild_id
` `
func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) { func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) {