enum PacketFamily : byte
{
	 1 Connection
	 2 Account
	 3 Character
	 4 Login
	 5 Welcome
	 6 Walk
	 7 Face
	 8 Chair
	 9 Emote
	11 Attack
	12 Spell
	13 Shop
	14 Item
	16 StatSkill
	17 Global
	18 Talk
	19 Warp
	21 Jukebox
	22 Players
	23 Avatar
	24 Party
	25 Refresh
	26 NPC
	27 PlayerRange
	28 NPCRange
	29 Range
	30 Paperdoll
	31 Effect
	32 Trade
	33 Chest
	34 Door
	35 Message
	36 Bank
	37 Locker
	38 Barber
	39 Guild
	40 Music
	41 Sit
	42 Recover
	43 Board
	44 Cast
	45 Arena
	46 Priest
	47 Marriage
	48 AdminInteract
	49 Citizen
	50 Quest
	51 Book
	255 Init
}

enum PacketAction : byte
{
	 1 Request
	 2 Accept
	 3 Reply
	 4 Remove
	 5 Agree
	 6 Create
	 7 Add
	 8 Player
	 9 Take
	10 Use
	11 Buy
	12 Sell
	13 Open
	14 Close
	15 Msg
	16 Spec
	17 Admin
	18 List
	20 Tell
	21 Report
	22 Announce
	23 Server
	24 Drop
	25 Junk
	26 Obtain
	27 Get
	28 Kick
	29 Rank
	30 Target_Self
	31 Target_Other
	33 Target_Group
	34 Dialog
	240 Ping
	241 Pong
	242 Net3
	255 Init
}

// ---

enum AdminLevel : char
{
	0 Player
	1 Spy
	2 LightGuide
	3 Guardian
	4 GameMaster
	5 HighGameMaster
}

enum Direction : char
{
	0 Down
	1 Left
	2 Up
	3 Right
}

enum Emote : char
{
	 1 Happy
	 2 Depressed
	 3 Sad
	 4 Angry
	 5 Confused
	 6 Surprised
	 7 Hearts
	 8 Moon
	 9 Suicidal
	10 Embarrassed
	11 Drunk
	12 Trade
	13 LevelUp
	14 Playful
}

enum QuestPage : char
{
	1 Progress
	2 History
}

enum Gender : char
{
	0 Female
	1 Male
}

enum Skin : char
{
	0 White
	1 Tan
	2 Yellow
	3 Orc
	4 Skeleton
	5 Panda
	6 Fish
}

"Shows on paperdoll and online player list. Official server/client doesn't support Guide or Guardian icons"
enum PaperdollIcon : char
{
	 1 Player
	 4 GM
	 5 HGM
	 6 Party
	 9 GM_Party
	10 HGM_Party
}

enum AvatarSlot : char
{
	1 Clothes
	2 Hair
	3 HairColor
}

enum TalkReply : short
{
	1 NotFound
}

enum SitState : char
{
	0 Stand
	1 Chair
	2 Floor
}

enum SitAction : char
{
	1 Sit
	2 Stand
}

enum TrainType : char
{
	1 Stat
	2 Skill
}

enum BookIcon : short
{
	 3 Item
	 5 Talk
	 8 Kill
	10 Step
}

enum DialogEntryType : char
{
	1 Text
	2 Link
}

enum DialogReply : char
{
	1 OK
	2 Link
}

enum InitReply : byte
{
	  1 Out_Of_Date
	  2 OK
	  3 Banned
	  4 Warp_File_EMF
	  5 File_EMF
	  6 File_EIF
	  7 File_ENF
	  8 File_ESF
	  9 Players
	 10 Map_Mutation
	 11 Friend_List_Players
	 12 File_ECF
}

enum InitBanType : byte
{
	// The logic in the client is "permanent = ban_type >= 2", meaning 0 and 1 are both interpreted
	// as temporary bans.
	0 Temp0
	1 Temp
	2 Perm
}

enum FileType : char
{
	1 Map
	2 Item
	3 NPC
	4 Spell
	5 Class
}

enum GuildReply : short
{
	 1 Busy
	 2 Not_Approved
	 3 Already_Member
	 4 No_Candidates
	 5 Exists
	 6 Create_Begin
	 7 Create_Add_Confirm
	 8 Create_Add
	 9 Recruiter_Offline
	10 Recruiter_Not_Here
	11 Recruiter_Wrong_Guild
	12 Not_Recruiter
	13 Join_Request
	14 Not_Present
	15 Account_Low
	16 Accepted
	17 Not_Found
	18 Updated
	19 Ranks_Updated
	20 Remove_Leader
	21 Remove_Not_Member
	22 Removed
	23 Ranking_Leader
	24 Ranking_Not_Member
}

enum GuildInfoType : short
{
	1 Description
	2 Ranks
	3 Bank
}

enum MapEffect : char
{
	1 Quake
}

enum InnUnsubscribeReply : char
{
	0 Not_Citizen
	1 Unsubscribed
}

enum SkillMasterReply : char
{
	1 Remove_Items
	2 Wrong_Class
}

enum PartyRequestType : char
{
	0 Join
	1 Invite
}

enum CharacterReply : short
{
	1 Exists
	2 Full
	3 Full3
	4 Not_Approved
	5 OK
	6 Deleted
	_ SessionId
}

enum AccountReply : short
{
	1 Exists
	2 Not_Approved
	3 Created
	5 Change_Failed
	6 Changed
	7 RequestDenied
	8 Reserved8
	9 Reserved9
	_ SessionId
}

enum LoginReply : short
{
	1 Wrong_User
	2 Wrong_UserPass
	3 OK
	4 Banned
	5 LoggedIn
	6 Busy
}

enum WarpAnimation : char
{
	0 None
	1 Scroll
	2 Admin
}

enum WarpType : char
{
	1 Local
	2 MapSwitch
}

enum ItemType : char
{
	 0 Static
	 2 Money
	 3 Heal
	 4 Teleport
	 5 Spell
	 6 EXPReward
	 7 StatReward
	 8 SkillReward
     9 Key
	10 Weapon
	11 Shield
	12 Armor
	13 Hat
	14 Boots
	15 Gloves
	16 Accessory
	17 Belt
	18 Necklace
	19 Ring
	20 Armlet
	21 Bracer
	22 Beer
	23 EffectPotion
	24 HairDye
	25 CureCurse
}

enum PlayerKilledState : char
{
	1 Alive
	2 Dead
}

// ---

struct Paperdoll_BAHWS
{
	short boots
	short armor
	short hat
	short weapon
	short shield
}

struct Paperdoll_BAHSW
{
	short boots
	short armor
	short hat
	short shield
	short weapon
}

struct Paperdoll_B000A0HSW
{
	short boots
	short = 0
	short = 0
	short = 0
	short armor
	short = 0
	short hat
	short shield
	short weapon
}

struct Paperdoll_Full
{
	short boots
	short accessory
	short gloves
	short belt
	short armor
	short necklace
	short hat
	short shield
	short weapon
	short ring[2]
	short armlet[2]
	short bracer[2]
}

struct Coords
{
	char x
	char y
}

struct BigCoords
{
	short x
	short y
}

struct Weight
{
	char current
	char max
}

struct Item
{
	short id
	int amount
}

struct ReverseItem
{
	int amount
	short id
}

"Used for shops, lockers, and various item transfers"
struct ShortItem
{
	short id
	three amount
}

"Used for craft ingredients"
struct VeryShortItem
{
	short id
	char amount
}

struct Spell
{
	short id
	short level
}

struct CharacterMapInfo
{
	string name
	short id
	short map_id
	struct BigCoords coords
	Direction direction
	char class_id
	raw_string(3) guild_tag
	char level
	Gender gender
	char hairstyle
	char haircolor
	Skin skin_id
	short max_hp
	short hp
	short max_tp
	short tp
	struct Paperdoll_B000A0HSW paperdoll
	SitState sit_state
	char invisible
	optional WarpAnimation animation
	break
}

struct NPCMapInfo
{
	char index
	short id
	struct Coords coords
	Direction direction
}

struct ItemMapInfo
{
	short uid
	short id
	struct Coords coords
	three amount
}

struct AvatarChange
{
	short player_id
	AvatarSlot slot
	char sound

	union(slot)
	{
		Clothes: clothes
		{
			struct Paperdoll_BAHWS paperdoll
		}

		Hair: hair
		{
			char style
			char color
		}

		HairColor: hair_color
		{
			char color
		}
	}
}

struct NearbyInfo
{
	char num_characters
	break
	struct CharacterMapInfo characters[num_characters]
	struct NPCMapInfo npcs[]
	break
	struct ItemMapInfo items[]
}

// --- Init

struct OnlinePlayers
{
	string name
	string title
	char = 0
	PaperdollIcon icon
	char class_id
	raw_string(3) guild_tag
	break
}

"Initialization request"
client_packet(Init, Init)
{
	three challenge
	char version[3]
	char protocol
	prefix_string hdid
}

"Requests a list of online players. Was used by WatchDog.exe to generate website pages"
client_packet(Message, List)
{
	break
}

"Initialization reply"
server_packet(Init, Init)
{
	InitReply reply_code

	union(reply_code)
	{
		Out_Of_Date: out_of_date
		{
			char version[3]
		}

		OK: ok
		{
			byte seq_bytes[2]
			byte encode_multiple
			byte decode_multiple
			short player_id
			three response
		}

		Banned: banned
		{
			InitBanType ban_type

			union(ban_type)
			{
				Temp0: ban_temp0
				{
					byte mins_remaining
				}

				Temp: ban_temp
				{
					byte mins_remaining
				}
			}
		}

		Warp_File_EMF: warp_file_emf
		{
			byte content[]
		}

		File_EMF: file_emf
		{
			byte content[]
		}

		File_EIF: file_eif
		{
			char file_id
			byte content[]
		}

		File_ENF: file_enf
		{
			char file_id
			byte content[]
		}

		File_ESF: file_esf
		{
			char file_id
			byte content[]
		}

		// TODO: represent MESSAGE_LIST response here. It uses the same reply code
		// 255, 255, 12, 2, 254, 255, 97, 112, 111, 108, 108, 111, 255, 255, 3, 55, 8, 254, 254, 2, 5, 255
		// [short:num_characters] {string:name,string:title,char:level,int:experience,Gender:gender,AdminLevel:admin,break}
		File_ECF: file_ecf
		{
			char file_id
			byte content[]
		}

		Map_Mutation: map_mutation
		{
			byte content[]
		}

		Players: players
		{
			short num_online
			break
			struct OnlinePlayers list[]
		}

		Friend_List_Players: friends
		{
			// Should probably be number of friends but what do I know
			short num_online
			break
			// EOSERV is generous enough to send each player name twice
			string list[]
		}
	}
}


// --- Connection

"Confirm initialization data"
client_packet(Connection, Accept)
{
	short decode_multiple
	short encode_multiple
	short player_id
}

"Ping reply"
client_packet(Connection, Ping)
{
	raw_string k // "k"
}

"Ping request"
server_packet(Connection, Player)
{
	short seq1
	char seq2
}


// --- Account

"Request creating an account"
client_packet(Account, Request)
{
	raw_string username
}

"Confirm creating an account"
client_packet(Account, Create)
{
	short session_id
	break
	string username
	string password
	string fullname
	string location
	string email
	string computer
	string hdid
}

"Change password"
client_packet(Account, Agree)
{
	string username
	string oldpassword
	string newpassword
}

"Reply to client Account-family packets"
server_packet(Account, Reply)
{
	AccountReply reply_code

	union(reply_code)
	{
		Exists: exists
		{
			raw_string no
		}

		Not_Approved: not_approved
		{
			raw_string no
		}

		Change_Failed: change_failed
		{
			raw_string no
		}

		Changed: changed
		{
			raw_string ok
		}

		Created: created
		{
			raw_string go
		}

		SessionId: session_id
		{
			char sequence_start
			raw_string ok
		}
	}
}


// --- Character

"Character selection screen character info"
struct Character_Info
{
	string name
	int id
	char level
	Gender gender
	char hairstyle
	char haircolor
	Skin skin
	AdminLevel admin
	struct Paperdoll_BAHSW paperdoll
	break
}

"Character selection screen character info"
struct Character_List
{
	char num_characters
	char = 1
	break
	struct Character_Info characters[num_characters]
}

"Request to create a character"
client_packet(Character, Request)
{
	string new // NEW
}

"Confirm creating a character"
client_packet(Character, Create)
{
	short session_id
	Gender:short gender
	short hairstyle
	short haircolor
	Skin:short skin
	break
	string name
}

"Confirm deleting character from an account"
client_packet(Character, Remove)
{
	short session_id
	int character_id
}

"Request to delete a character from an account"
client_packet(Character, Take)
{
	int character_id
}

"Reply to client Character-family packets"
server_packet(Character, Reply)
{
	CharacterReply reply_code

	union(reply_code)
	{
		Full: full
		{
			raw_string no
		}

		Full3: full3
		{
			raw_string no
		}

		Not_Approved: not_approved
		{
			raw_string no
		}

		Exists: exists
		{
			raw_string no
		}

		SessionId: session_id
		{
			raw_string ok
		}

		OK: ok
		{
			struct Character_List character_list
		}

		Deleted: deleted
		{
			struct Character_List character_list
		}
	}
}

"Reply to client Character_Take"
server_packet(Character, Player)
{
	short session_id
	int character_id
}


// --- Login

"Login request"
client_packet(Login, Request)
{
	string username
	string password
}

"Login reply"
server_packet(Login, Reply)
{
	LoginReply reply_code

	union(reply_code)
	{
		OK: ok
		{
			struct Character_List character_list
		}

		Wrong_UserPass: wrong_user_pass
		{
			raw_string no
		}

		Wrong_User: wrong_user
		{
			raw_string no
		}

		Banned: banned
		{
			raw_string no
		}

		LoggedIn: logged_in
		{
			raw_string no
		}

		Busy: busy
		{
			raw_string no
		}
	}
}


// --- Welcome

struct ServerSettings
{
	short jail_map
	short rescue_map
	char rescue_x
	char rescue_y
	short spy_and_light_guide_flood_rate
	short guardian_flood_rate
	short game_master_flood_rate
	short high_game_master_flood_rate
}

enum WelcomeReply : short
{
	1 SelectCharacter
	2 EnterGame
	3 ServerBusy
	4 LoggedIn
}

"Selected a character"
client_packet(Welcome, Request)
{
	int character_id
}

"Entering game"
client_packet(Welcome, Msg)
{
	three session_id
	int character_id
}

"Requesting a file"
client_packet(Welcome, Agree)
{
	FileType file_type
	short session_id
	union (file_type)
	{
		Map: map
		{
			short character_id
		}
		Item: item
		{
			char file_id
		}
		NPC: npc
		{
			char file_id
		}
		Spell: spell
		{
			char file_id
		}
		Class: class
		{
			char file_id
		}
	}
}

"Reply to selecting a character / entering game"
server_packet(Welcome, Reply)
{
	WelcomeReply reply_code

	union(reply_code)
	{
		SelectCharacter: select_character
		{
			short session_id
			int character_id
			short map_id
			short map_rid[2]
			three map_filesize
			short eif_rid[2]
			short eif_length
			short enf_rid[2]
			short enf_length
			short esf_rid[2]
			short esf_length
			short ecf_rid[2]
			short ecf_length
			string name
			string title
			string guild_name
			string guild_rank_name
			char class_id
			raw_string(3) guild_tag
			AdminLevel admin
			char level
			int experience
			int usage
			struct CharacterStats2 stats
			struct Paperdoll_Full paperdoll
			char guild_rank
			struct ServerSettings settings
			char login_message
			break
		}

		EnterGame: enter_game
		{
			break
			string news[9]
			struct Weight weight
			struct Item items[]
			break
			struct Spell spells[]
			break
			struct NearbyInfo nearby
		}
	}
}


// --- AdminInteract

enum AdminMessageType : char
{
	1 Message
	2 Report
}

struct CharacterStats4
{
	short hp
	short max_hp
	short tp
	short max_tp
	struct CharacterBaseStats base
	struct CharacterSecondaryStats secondary
}

"Talk to admin"
client_packet(AdminInteract, Tell)
{
	raw_string message
}

"Report character"
client_packet(AdminInteract, Report)
{
	string reportee
	raw_string message
}

"Incoming admin report"
server_packet(AdminInteract, Reply)
{
	AdminMessageType message_type
	break

	union(message_type)
	{
		Message: message
		{
			string player_name
			string message
		}

		Report: report
		{
			string player_name
			string message
			string reportee_name
		}
	}
}

"Nearby player disappearing (admin hide)"
server_packet(AdminInteract, Remove)
{
	short player_id
}

"Nearby player appearing (admin un-hide)"
server_packet(AdminInteract, Agree)
{
	short player_id
}

"Admin character inventory popup"
server_packet(AdminInteract, List)
{
	string name
	int usage
	break
	int gold_bank
	break
	struct Item inventory[]
	break
	struct ShortItem bank[]
}

"Admin character info popup"
server_packet(AdminInteract, Tell)
{
	string name
	int usage
	break
	break
	int exp
	char level
	short map_id
	struct BigCoords map_coords
	struct CharacterStats4 stats
	short light
	short dark
	short fire
	short water
	short earth
	short wind
	struct Weight weight
}

// --- Global

"Enable whispers"
client_packet(Global, Remove)
{
	char = 'n'
}

"Disable whispers"
client_packet(Global, Player)
{
	char = 'y'
}

"Opened global tab"
client_packet(Global, Open)
{
	char = 'y'
}

"Closed global tab"
client_packet(Global, Close)
{
	char = 'n'
}


// --- Talk

struct GlobalBackfillMessage
{
	string player_name
	string message
}

"Guild chat message"
client_packet(Talk, Request)
{
	raw_string message
}

"Party chat message"
client_packet(Talk, Open)
{
	raw_string message
}

"Global chat message"
client_packet(Talk, Msg)
{
	raw_string message
}

"Private chat message"
client_packet(Talk, Tell)
{
	string name
	raw_string message
}

"Public chat message"
client_packet(Talk, Report)
{
	raw_string message
}

"Admin chat message"
client_packet(Talk, Admin)
{
	raw_string message
}

"Admin announcement"
client_packet(Talk, Announce)
{
	raw_string message
}

"Guild chat message"
server_packet(Talk, Request)
{
	string player_name
	string message
}

"Party chat message"
server_packet(Talk, Open)
{
	short player_id
	string message
}

"Global chat message"
server_packet(Talk, Msg)
{
	string player_name
	string message
}

"Private chat message"
server_packet(Talk, Tell)
{
	string player_name
	string message
}

"Public chat message"
server_packet(Talk, Player)
{
	short player_id
	raw_string message
}

"Admin chat message"
server_packet(Talk, Admin)
{
	string player_name
	string message
}

"Admin announcement"
server_packet(Talk, Announce)
{
	string player_name
	string message
}

"Server message"
server_packet(Talk, Server)
{
	raw_string message
}

"Reply to trying to send a private message"
server_packet(Talk, Reply)
{
	TalkReply reply_code
	raw_string name
}

"Global chat backfill"
server_packet(Talk, List)
{
	struct GlobalBackfillMessage messages[]
}

"Status bar message"
server_packet(Message, Open)
{
	raw_string message
}

"Large message box"
server_packet(Message, Accept)
{
	string messages[4]
	break
}

"Temporary mute applied"
server_packet(Talk, Spec)
{
	string admin_name
}


// --- Attack

"Attacking"
client_packet(Attack, Use)
{
	Direction direction
	three timestamp
}

"Nearby player attacking"
server_packet(Attack, Player)
{
	short player_id
	Direction direction
}

"Nearby player hit by another player"
server_packet(Avatar, Reply)
{
	short player_id
	short victim_id
	three damage
	Direction direction
	char hp_percentage
	char died
}


// --- Chair

"Sitting on a chair"
client_packet(Chair, Request)
{
	SitAction sit_action
	union(sit_action)
	{
		Sit: sit
		{
			struct Coords coords
		}
	}
}

"Nearby player sitting on a chair"
server_packet(Chair, Player)
{
	short player_id
	struct Coords coords
	Direction direction
	char = 0
}

"Your character sitting on a chair"
server_packet(Chair, Reply)
{
	short player_id
	struct Coords coords
	Direction direction
	char = 0
}

"Your character standing up from a chair"
server_packet(Chair, Close)
{
	short player_id
	struct Coords coords
}


// --- Sit

"Floor sit/stand request"
client_packet(Sit, Request)
{
	SitAction sit_action
}

"Nearby player sitting down"
server_packet(Sit, Player)
{
	short player_id
	struct Coords coords
	Direction direction
	char = 0
}

"Your character standing up"
server_packet(Sit, Close)
{
	short player_id
	struct Coords coords
}

"Nearby player standing up"
server_packet(Sit, Remove)
{
	short player_id
	struct Coords coords
}

"Your character sitting down"
server_packet(Sit, Reply)
{
	short player_id
	struct Coords coords
	Direction direction
	char = 0
}


// --- Emote

"Doing an emote"
client_packet(Emote, Report)
{
	Emote emote
}

"Nearby player doing an emote"
server_packet(Emote, Player)
{
	short player_id
	Emote emote
}

"Nearby player doing an effect"
server_packet(Effect, Player)
{
	short player_id
	three effect_id
}


// --- Face

"Facing a direction"
client_packet(Face, Player)
{
	Direction direction
}

"Nearby player facing a direction"
server_packet(Face, Player)
{
	short player_id
	Direction direction
}


// --- Walk

struct WalkCommon
{
	Direction direction
	three timestamp
	struct Coords coords
}

"Walking with #nowall"
client_packet(Walk, Admin)
{
	struct WalkCommon walk
}

"Walking through a player"
client_packet(Walk, Spec)
{
	struct WalkCommon walk
}

"Walking"
client_packet(Walk, Player)
{
	struct WalkCommon walk
}

"Nearby player has disappeared from view"
server_packet(Avatar, Remove)
{
	short player_id
	optional WarpAnimation animation
}

"Player has appeared in nearby view"
server_packet(Players, Agree)
{
	struct NearbyInfo nearby
}

"Nearby player has walked"
server_packet(Walk, Player)
{
	short player_id
	Direction direction
	struct Coords coords
}

"Players, NPCs, and Items appearing in nearby view"
server_packet(Walk, Reply)
{
	short player_ids[]
	break
	char npc_indexes[]
	break
	struct ItemMapInfo items[]
}

"Your character has been frozen"
server_packet(Walk, Close)
{
	char = 'S'
}

"Your character has been unfrozen"
server_packet(Walk, Open)
{
	char = 'S'
}


// --- Bank

"Talked to a banker NPC"
client_packet(Bank, Open)
{
	short npc_index
}

"Depositing gold"
client_packet(Bank, Add)
{
	int amount
}

"Withdrawing gold"
client_packet(Bank, Take)
{
	int amount
}

"Open banker NPC interface"
server_packet(Bank, Open)
{
	int gold_bank
	three session_token
	char locker_upgrades
}

"Update gold counts after deposit/withdraw"
server_packet(Bank, Reply)
{
	int gold_inventory
	int gold_bank
}

// --- Locker

"Placing an item in a bank locker"
client_packet(Locker, Add)
{
	struct Coords locker_coords
	struct ShortItem deposit_item
}

"Taking an item from a bank locker"
client_packet(Locker, Take)
{
	struct Coords locker_coords
	short take_item_id
}

"Opening a bank locker"
client_packet(Locker, Open)
{
	struct Coords locker_coords
}

"Buying a locker space upgrade from a banker NPC"
client_packet(Locker, Buy)
{
	char = 1
}

"Response to adding an item to a bank locker"
server_packet(Locker, Reply)
{
	struct Item deposited_item
	struct Weight weight
	struct ShortItem locker_items[]
}

"Response to taking an item from a bank locker"
server_packet(Locker, Get)
{
	struct ShortItem taken_item
	struct Weight weight
	struct ShortItem locker_items[]
}

"Opening a bank locker"
server_packet(Locker, Open)
{
	struct Coords locker_coords
	struct ShortItem locker_items[]
}

"Response to buying a locker space upgrade from a banker NPC"
server_packet(Locker, Buy)
{
	int gold_amount
	char locker_upgrades
}

"Reply to trying to add an item to a full locker"
server_packet(Locker, Spec)
{
	char locker_max_items
}


// --- Citizen

"Sleeping at an inn"
client_packet(Citizen, Request)
{
	short session_id
	short vendor_id
}

"Confirm sleeping at an inn"
client_packet(Citizen, Accept)
{
	short session_id
	short vendor_id
}

"Subscribing to a town"
client_packet(Citizen, Reply)
{
	short session_id
	break
	short vendor_id
	break
	string answer1
	string answer2
	raw_string answer3
}

"Giving up citizenship of a town"
client_packet(Citizen, Remove)
{
	short session_id
}

"Talking to a citizenship NPC"
client_packet(Citizen, Open)
{
	short npc_index
}

"Response to subscribing to a town"
server_packet(Citizen, Reply)
{
	char questions_wrong
}

"Response to giving up citizenship of a town"
server_packet(Citizen, Remove)
{
	InnUnsubscribeReply reply_code
}

"Response from talking to a citizenship NPC"
server_packet(Citizen, Open)
{
	three vendor_id
	char current_home_id
	short session_id
	break
	string question1
	string question2
	raw_string question3
}

"Sleeping at an inn"
server_packet(Citizen, Accept)
{
	int gold_amount
}

"Reply to requesting sleeping at an inn"
server_packet(Citizen, Request)
{
	int cost
}


// --- Shop

struct ShopTradeItem
{
	short item_id
	three buy_price
	three sell_price
	char max_buy_amount
}

struct ShopCraftItem
{
	short item_id
	struct VeryShortItem ingredients[4]
}

"Crafting an item from a shop"
client_packet(Shop, Create)
{
	short craft_item_id
	int session_id
}

"Purchasing an item from a shop"
client_packet(Shop, Buy)
{
	struct Item buy_item
	int session_id
}

"Selling an item to a shop"
client_packet(Shop, Sell)
{
	struct Item sell_item
	int session_id
}

"Talking to a shop NPC"
client_packet(Shop, Open)
{
	short npc_index
}

"Response to crafting an item from a shop"
server_packet(Shop, Create)
{
	short craft_item_id
	struct Weight weight
	struct Item ingredients[4]
}

"Response to purchasing an item from a shop"
server_packet(Shop, Buy)
{
	int gold_amount
	struct Item bought_item
	struct Weight weight
}

"Response to selling an item to a shop"
server_packet(Shop, Sell)
{
	struct ReverseItem sold_item
	int gold_amount
	struct Weight weight
}

"Response from talking to a shop NPC"
server_packet(Shop, Open)
{
	short session_id
	string shop_name
	struct ShopTradeItem trade_items[]
	break
	struct ShopCraftItem craft_items[]
	break
}


// --- StatSkill

struct CharacterBaseStats
{
	short str
	short intl
	short wis
	short agi
	short con
	short cha
}

struct CharacterBaseStats2
{
	short str
	short wis
	short intl
	short agi
	short con
	short cha
}

struct CharacterSecondaryStats
{
	short mindam
	short maxdam
	short accuracy
	short evade
	short armor
}

struct CharacterStats1
{
	short stat_points
	short skill_points
	short hp
	short max_hp
	short tp
	short max_tp
	short max_sp
	struct CharacterBaseStats base
	struct CharacterSecondaryStats secondary
}

struct CharacterStats2
{
	short hp
	short max_hp
	short tp
	short max_tp
	short max_sp
	short stat_points
	short skill_points
	short karma
	struct CharacterSecondaryStats secondary
	struct CharacterBaseStats2 base
}

struct CharacterStats3
{
	struct CharacterBaseStats base
	short max_hp
	short max_tp
	short max_sp
	short max_weight
	struct CharacterSecondaryStats secondary
}

struct SkillLearn
{
	short id
	char level_req
	char class_req
	int cost
	short skill_req[4]
	struct CharacterBaseStats stat_req
}

"Talking to a skill master NPC"
client_packet(StatSkill, Open)
{
	short npc_index
}

"Learning a skill from a skill master"
client_packet(StatSkill, Take)
{
	int session_id
	short spell_id
}

"Forgetting a skill at a skill master"
client_packet(StatSkill, Remove)
{
	int session_id
	short spell_id
}

enum StatId : short
{
	1 Str
	2 Int
	3 Wis
	4 Agi
	5 Con
	6 Cha
}

enum MarriageRequestType : char
{
	1 MarriageApproval
	2 Divorce
}

enum MarriageReply : short
{
	1 AlreadyMarried
	2 NotMarried
	3 Success
	4 NotEnoughGold
	5 WrongName
	6 ServiceBusy
	7 DivorceNotification
}

enum PriestReply : short
{
	1 NotDressed
	2 LowLevel
	3 PartnerNotPresent
	4 PartnerNotDressed
	5 Busy
	6 DoYou
	7 PartnerAlreadyMarried
	8 NoPermission
}

"Spending a stat point on a stat or skill"
client_packet(StatSkill, Add)
{
	TrainType action_type

	union(action_type)
	{
		Stat: stat
		{
			StatId stat_id
		}

		Skill: skill
		{
			short spell_id
		}
	}
}

"Resetting stats at a skill master"
client_packet(StatSkill, Junk)
{
	int session_id
}

"Response from talking to a skill master NPC"
server_packet(StatSkill, Open)
{
	short session_id
	string shop_name
	struct SkillLearn skills[]
}

"Response from unsuccessful attempt to learn a skill from a skill master"
server_packet(StatSkill, Reply)
{
	SkillMasterReply reply_code

	union(reply_code)
	{
		Remove_Items: remove_items
		{
			// TODO: EOSERV doesn't implement - is this really empty?
		}

		Wrong_Class: wrong_class
		{
			short class_id
		}
	}
}

"Response from learning a skill from a skill master"
server_packet(StatSkill, Take)
{
	short spell_id
	int gold_amount
}

"Response to forgetting a skill at a skill master"
server_packet(StatSkill, Remove)
{
	short spell_id
}

"Response to spending stat points"
server_packet(StatSkill, Player)
{
	short stat_points
	struct CharacterStats3 stats
}

"Response to spending skill points"
server_packet(StatSkill, Accept)
{
	short skill_points
	struct Spell spell
}

"Response to resetting character"
server_packet(StatSkill, Junk)
{
	struct CharacterStats1 stats
}



// --- Item

struct ItemCharacterStats
{
	short max_hp
	short max_tp
	struct CharacterBaseStats base
	struct CharacterSecondaryStats secondary
}

"Using an item"
client_packet(Item, Use)
{
	short use_item_id
}

"Dropping items on the ground"
client_packet(Item, Drop)
{
	struct ShortItem drop_item
	struct Coords coords
}

"Junking items"
client_packet(Item, Junk)
{
	struct Item junk_item
}

"Taking items from the ground"
client_packet(Item, Get)
{
	short take_item_index
}

"Reply to using an item"
server_packet(Item, Reply)
{
	ItemType used_item_type
	struct Item used_item
	struct Weight weight

	union(used_item_type)
	{
		Heal: heal
		{
			int hp_gain
			short hp
			short tp
		}

		HairDye: hair_dye
		{
			char hair_color
		}

		EffectPotion: effect_potion
		{
			short effect_id
		}

		CureCurse: cure_curse
		{
			struct ItemCharacterStats stats
		}

		EXPReward: exp_reward
		{
			int experience
			"A value greater than 0 is 'new level' and indicates the player leveled up."
			char level_up
			short stat_points
			short skill_points
			short max_hp
			short max_tp
			short max_sp
		}
	}
}

"Reply to dropping items on the ground"
server_packet(Item, Drop)
{
	short item_id
	three amount_dropped
	int amount_remaining
	short item_index
	struct Coords coords
	struct Weight weight
}

"Item appeared on the ground"
server_packet(Item, Add)
{
	short item_id
	short item_index
	three item_amount
	struct Coords coords
}

"Item disappeared from the ground"
server_packet(Item, Remove)
{
	short item_index
}

"Reply to junking items"
server_packet(Item, Junk)
{
	struct ShortItem junked_item
	int junked_item_amount
	struct Weight weight
}

"Reply to taking items from the ground"
server_packet(Item, Get)
{
	short taken_item_index
	struct ShortItem taken_item
	struct Weight weight
}

"Receive item (from quest)"
server_packet(Item, Obtain)
{
	struct ShortItem item
	char cur_weight
}

"Lose item (from quest)"
server_packet(Item, Kick)
{
	struct Item item
	char cur_weight
}

"Reply to using an item that you don't have"
server_packet(Item, Agree)
{
	short item_id
}

"Reply to trying to take a protected item from the ground"
server_packet(Item, Spec)
{
	short = 2
}


// --- Barber

"Talking to barber NPC"
client_packet(Barber, Open)
{
	short npc_index
}

"Purchasing a hair-style"
client_packet(Barber, Buy)
{
	char style
	char color
	int session_id
}

"Reply from talking to barber NPC"
server_packet(Barber, Open)
{
	int session_id
}

"Reply from purchasing a hair-style"
server_packet(Barber, Agree)
{
	int gold_amount
	struct AvatarChange change
}


// --- Board

struct BoardPostListing
{
	short post_id
	break
	string author
	string subject
}

"Removing a post from a town board"
client_packet(Board, Remove)
{
	short board_id
	short post_id
}

"Posting a new message to a town board"
client_packet(Board, Create)
{
	short board_id
	break
	string post_subject
	string post_body
}

"Reading a post on a town board"
client_packet(Board, Take)
{
	short board_id
	short post_id
}

"Opening a a town board"
client_packet(Board, Open)
{
	short board_id
}

"Reply to reading a post on a town board"
server_packet(Board, Player)
{
	short post_id
	string post_body
}

"Reply to opening a town board"
server_packet(Board, Open)
{
	char board_id
	char num_posts

	struct BoardPostListing posts[num_posts]
}


// --- Jukebox

"Opening the jukebox listing"
client_packet(Jukebox, Open)
{
	struct Coords coords
}

"Requesting a song on a jukebox"
client_packet(Jukebox, Msg)
{
	break
	break
	short track_id
}

"Playing a note with the bard skill"
client_packet(Jukebox, Use)
{
	char instrument_id
	char note_id
}

"Reply to successfully requesting a song"
server_packet(Jukebox, Agree)
{
	int gold_amount
}

"Reply to unsuccessfully requesting a song"
server_packet(Jukebox, Reply)
{
	short = 1
}

"Reply to opening the jukebox listing"
server_packet(Jukebox, Open)
{
	short map_id
	string jukebox_player
}

"Someone playing a note with the bard skill nearby"
server_packet(Jukebox, Msg)
{
	short player_id
	Direction direction
	char instrument_id
	char note_id
}

"Play jukebox music"
server_packet(Jukebox, Player)
{
        char mfx_id
}

"Play background music"
server_packet(Jukebox, Use)
{
        char mfx_id
}


// --- Warp

"Accept a warp request from the server"
client_packet(Warp, Accept)
{
	short map_id
	short session_id
}

"Request to download a copy of the map"
client_packet(Warp, Take)
{
	short map_id
	short session_id
}

"Warp request from server"
server_packet(Warp, Request)
{
	WarpType warp_type
	short map_id

	union(warp_type)
	{
		MapSwitch: map_switch
		{
			short map_rid[2]
			three map_filesize
		}
	}

	short session_id
}

"Reply after accepting a warp"
server_packet(Warp, Agree)
{
	WarpType warp_type
	union(warp_type)
	{
		MapSwitch: map_switch
		{
			short map_id
			WarpAnimation warp_anim
		}
	}
	struct NearbyInfo nearby
}


// --- Paperdoll

struct Paperdoll_Info
{
	string name
	string home
	string partner
	string title
	string guild
	string guild_rank
	short player_id
	char class_id
	Gender gender
	char = 0
}

"Request for a player's paperdoll"
client_packet(Paperdoll, Request)
{
	short player_id
}

"Unequipping an item"
client_packet(Paperdoll, Remove)
{
	short item_id
	char sub_loc
}

"Equipping an item"
client_packet(Paperdoll, Add)
{
	short item_id
	char sub_loc
}

"Reply to requesting a paperdoll"
server_packet(Paperdoll, Reply)
{
	struct Paperdoll_Info info
	struct Paperdoll_Full paperdoll
	PaperdollIcon paperdoll_icon
}

"Reply to unequipping an item"
server_packet(Paperdoll, Remove)
{
	struct AvatarChange change
	short item_id
	char sub_loc
	struct ItemCharacterStats stats
}

"Reply to equipping an item"
server_packet(Paperdoll, Agree)
{
	struct AvatarChange change
	short item_id
	three item_amount_remaining
	char sub_loc
	struct ItemCharacterStats stats
}

"Failed to equip an item due to being the incorrect class"
// There is no visible effect in the client, it simply re-assigns the player's class
server_packet(Paperdoll, Ping)
{
	"The player's current class ID -- not the item's required class ID"
	char class_id
}

"Nearby player changed clothes"
server_packet(Avatar, Agree)
{
	struct AvatarChange change
}


// --- Book

"Request for a player's book"
client_packet(Book, Request)
{
	short player_id
}

"Reply to requesting a book"
server_packet(Book, Reply)
{
	struct Paperdoll_Info info
	PaperdollIcon paperdoll_icon
	break
	string quest_name[]
}


// --- Message

"#ping command request"
client_packet(Message, Ping)
{
	short = 2
}

"#ping command reply"
server_packet(Message, Pong)
{
	short = 2
}

"Server is rebooting"
server_packet(Message, Close)
{
	char = 'r'
}


// --- Players

"#find command request"
client_packet(Players, Accept)
{
	raw_string name
}

"Requesting a list of online players"
client_packet(Players, Request)
{
	break
}

"Requesting a list of online friends"
client_packet(Players, List)
{
	break
}

"Nearby player has logged out"
server_packet(Players, Remove)
{
	short player_id
}

"#find command reply - offline"
server_packet(Players, Ping)
{
	raw_string name
}

"#find command reply - same map"
server_packet(Players, Pong)
{
	raw_string name
}

"#find command reply - different map"
server_packet(Players, Net3)
{
	raw_string name
}


// --- Door

"Opening a door"
client_packet(Door, Open)
{
	struct Coords coords
}

"Door nearby opening"
server_packet(Door, Open)
{
	struct Coords coords
	char = 0
}

"Reply to trying to open a locked door"
server_packet(Door, Close)
{
	char key
}


// --- Chest

"Opening a chest"
client_packet(Chest, Open)
{
	struct Coords coords
}

"Placing an item in to a chest"
client_packet(Chest, Add)
{
	struct Coords coords
	struct ShortItem add_item
}

"Taking an item from a chest"
client_packet(Chest, Take)
{
	struct Coords coords
	short take_item_id
}

"Reply to opening a chest"
server_packet(Chest, Open)
{
	struct Coords coords
	struct ShortItem items[]
}

"Reply to placing an item in to a chest"
server_packet(Chest, Reply)
{
	struct Item added_item
	struct Weight weight
	struct ShortItem items[]
}

"Reply to removing an item from a chest"
server_packet(Chest, Get)
{
	struct ShortItem taken_item
	struct Weight weight
	struct ShortItem items[]
}

"Chest contents updating"
server_packet(Chest, Agree)
{
	struct ShortItem items[]
}

"Reply to trying to add an item to a full chest"
server_packet(Chest, Spec)
{
	byte = 0
}


// --- Refresh

"Requesting new info about nearby objects"
client_packet(Refresh, Request)
{
	break
}


server_packet(Refresh, Reply)
{
	struct NearbyInfo nearby
}


// --- Range
"Requesting info about nearby players and/or npcs"
client_packet(Range, Request)
{
	short player_ids[]
	break
	char npc_indexes[]
}

"Sent when unknown player has walked into view"
client_packet(PlayerRange, Request)
{
	short player_ids[]
}

"Sent when unknown npc has walked into view"
client_packet(NPCRange, Request)
{
	char num_npcs
	break
	char npc_indexes[]
}

"MapInfo for characters and/or npcs"
server_packet(Range, Reply)
{
	struct NearbyInfo nearby
}


// --- Party

struct PartyMember
{
	short player_id
	char leader
	char level
	char hp_percentage
	string name
}

struct PartyExpShare
{
	short player_id
	int experience
	"A value greater than 0 is 'new level' and indicates the player leveled up."
	char level_up
}

enum PartyReply : char
{
	0 AlreadyInAnotherParty
	1 AlreadyInYourParty
	2 PartyIsFull
}

"Send party invite / join request"
client_packet(Party, Request)
{
	PartyRequestType request_type
	short player_id
}

"Accept party invite / join request"
client_packet(Party, Accept)
{
	PartyRequestType request_type
	short player_id
}

"Remove player from a party"
client_packet(Party, Remove)
{
	short player_id
}

"Request updated party info"
client_packet(Party, Take)
{
	char num_members
}

"Received party invite / join request"
server_packet(Party, Request)
{
	PartyRequestType request_type
	short player_id
	raw_string player_name
}

"Member list received when party is first joined"
server_packet(Party, Create)
{
	struct PartyMember members[]
}

"New player joined the party"
server_packet(Party, Add)
{
	struct PartyMember member
}

"Player left the party"
server_packet(Party, Remove)
{
	short player_id
}

"Left / disbanded a party"
server_packet(Party, Close)
{
	break
}

"Party member list update"
server_packet(Party, List)
{
	struct PartyMember members[]
}

"Update party member's HP"
server_packet(Party, Agree)
{
	short player_id
	char hp_percentage
}

"Updated experience and level-ups from party experience"
server_packet(Party, Target_Group)
{
	struct PartyExpShare gains[]
}

"Failed party invite / join request"
server_packet(Party, Reply)
{
	PartyReply reply_code
	union (reply_code)
	{
		AlreadyInAnotherParty: already_in_another_party
		{
			string player_name
		}

		AlreadyInYourParty: already_in_your_party
		{
			string player_name
		}
	}
}


// --- Guild

struct GuildStaff
{
	char rank
	break
	string name
}

struct GuildMember
{
	char rank
	break
	string name
	string rank_name
}

"Requested to create a guild"
client_packet(Guild, Request)
{
	int session_id
	break
	string guild_tag
	string guild_name
}

"Accept pending guild creation invite"
client_packet(Guild, Accept)
{
	int = 20202
	short player_id
}

"Leave guild"
client_packet(Guild, Remove)
{
	int session_id
}

"Update the guild description or rank list"
client_packet(Guild, Agree)
{
	int session_id
	GuildInfoType info_type

	union(info_type)
	{
		Description: description
		{
			raw_string description
		}

		Ranks: ranks
		{
			string ranks[9]
		}
	}
}

"Final confirm creating a guild"
client_packet(Guild, Create)
{
	int session_id
	break
	string guild_tag
	string guild_name
	string description
}

"Request to join a guild"
client_packet(Guild, Player)
{
	int sesssion_id
	break
	string guild_tag
	string recruiter_name
}

"Request guild bank / ranks / description"
client_packet(Guild, Take)
{
	int session_id
	GuildInfoType info_type
}

"Accepted a join request"
client_packet(Guild, Use)
{
	short player_id
}

"Deposit gold in to the guild bank"
client_packet(Guild, Buy)
{
	int session_id
	int gold_amount
}

"Talking to a guild master NPC"
client_packet(Guild, Open)
{
	short npc_index
}

"Requested member list of a guild"
client_packet(Guild, Tell)
{
	int session_id
	raw_string guild_identity
}

"Requested general information of a guild"
client_packet(Guild, Report)
{
	int session_id
	raw_string guild_identity
}

"Disband guild"
client_packet(Guild, Junk)
{
	int session_id
}

"Kick member from guild"
client_packet(Guild, Kick)
{
	int session_id
	raw_string member_name
}

"Update a member's rank"
client_packet(Guild, Rank)
{
	int sesson_id
	char rank
	raw_string member_name
}

"Generic guild reply messages"
server_packet(Guild, Reply)
{
	GuildReply reply_code

	union(reply_code)
	{
		Create_Add: create_add
		{
			raw_string name
		}

		Create_Add_Confirm: create_add_confirm
		{
			raw_string name
		}

		Join_Request: join_request
		{
			short player_id
			raw_string name
		}
	}
}

"Guild create request"
server_packet(Guild, Request)
{
	short player_id
	raw_string guild_identity
}

"Guild created"
server_packet(Guild, Create)
{
	short leader_id
	break
	string guild_tag
	string guild_name
	string rank_name
	int gold_amount
}

"Get guild description reply"
server_packet(Guild, Take)
{
	string description
}

"Get guild rank list reply"
server_packet(Guild, Rank)
{
	string ranks[9]
}

"Get guild bank reply"
server_packet(Guild, Sell)
{
	int gold_amount
}

"Deposit guild bank reply"
server_packet(Guild, Buy)
{
	int gold_amount
}

"Talk to guild master NPC reply"
server_packet(Guild, Open)
{
	three session_id
}

"Get guild member list reply"
server_packet(Guild, Tell)
{
	short num_members
	break
	struct GuildMember members[num_members]
}

"Get guild info reply"
server_packet(Guild, Report)
{
	string name
	string tag
	string create_date
	string description
	string wealth
	string ranks[9]
	short num_staff
	break
	struct GuildStaff staff[num_staff]
}

"Joined guild info"
server_packet(Guild, Agree)
{
	short recruiter_id
	break
	string guild_tag
	string guild_name
	string rank_name
}

"Update guild rank"
server_packet(Guild, Accept)
{
	char rank
}

"Left the guild"
// The official server never sends this packet, so the dummy byte is unknown.
// This packet is only known as a result of decompiling the official client.
server_packet(Guild, Kick)
{
	break
}


// --- Spell

enum SpellTargetType : char
{
	1 Player
	2 NPC
}

"Begin spell chanting"
client_packet(Spell, Request)
{
	short spell_id
	three timestamp
}

"Self-targeted spell cast"
client_packet(Spell, Target_Self)
{
	Direction direction
	short spell_id
	three timestamp
}

"Targeted spell cast"
client_packet(Spell, Target_Other)
{
	SpellTargetType target_type
	three previous_timestamp
	short spell_id
  short victim_id
	three timestamp
}

"Group spell cast"
client_packet(Spell, Target_Group)
{
	short spell_id
	three timestamp
}

"Raise arm to cast a spell (vestigial)"
client_packet(Spell, Use)
{
	Direction direction
}

"Nearby player chanting a spell"
server_packet(Spell, Request)
{
	short player_id
	short spell_id
}

"Nearby player self-casted a spell"
server_packet(Spell, Target_Self)
{
	short player_id
	short spell_id
	int spell_heal_hp
	char hp_percentage

	// TODO: Represent optionally present data
	short hp
	short tp
}

"Nearby player hit by a damage spell from a player"
server_packet(Avatar, Admin)
{
	short caster_id
	short victim_id
	char caster_direction
	three damage
	char hp_percentage
	char victim_died
	short spell_id
}

"Nearby player raising their arm to cast a spell (vestigial)"
server_packet(Spell, Player)
{
	short player_id
	Direction direction
}

struct GroupHealTargetPlayer
{
	short player_id
	char hp_percentage
	short hp
}

"Nearby player(s) hit by a group heal spell from a player"
server_packet(Spell, Target_Group)
{
	short spell_id
	short caster_id
	short caster_tp
	short spell_heal_hp

	struct GroupHealTargetPlayer players[]
}

"Nearby player hit by a heal spell from a player"
server_packet(Spell, Target_Other)
{
	short victim_id
	short caster_id
	Direction caster_direction
	short spell_id
	int spell_heal_hp
	char hp_percentage
	short hp
}


// --- Trade

struct TradeItemData
{
	short player1_id
	struct Item player1_items[]
	break
	short player2_id
	struct Item player2_items[]
	break
}

"Requesting a trade with another player"
client_packet(Trade, Request)
{
	char unk1
	short player_id
}

"Accepting a trade request"
client_packet(Trade, Accept)
{
	char unk1
	short player_id
}

"Remove an item from the trade screen"
client_packet(Trade, Remove)
{
	short item_id
}

"Mark trade as agreed"
client_packet(Trade, Agree)
{
	char agree_state
}

"Add an item to the trade screen"
client_packet(Trade, Add)
{
	struct Item add_item
}

"Cancel the trade"
client_packet(Trade, Close)
{
	char unk1
}

"Trade request from another player"
server_packet(Trade, Request)
{
	char unk1
	short player_id
	raw_string player_name
}

"Trade window opens"
server_packet(Trade, Open)
{
	short player_id
	string player_name
	short your_player_id
	string your_name
}

"Trade updated (items changed)"
server_packet(Trade, Reply)
{
	struct TradeItemData trade_data
}

"Trade updated (items changed while trade was acceptedd)"
server_packet(Trade, Admin)
{
	struct TradeItemData trade_data
}

"Trade items updated"
server_packet(Trade, Use)
{
	struct TradeItemData trade_data
}

"Own agree state updated"
server_packet(Trade, Spec)
{
	char agree_state
}

"Partner agree state updated"
server_packet(Trade, Agree)
{
	short player_id
	char agree_state
}

"Partner closed trade window"
server_packet(Trade, Close)
{
	short player_id
}


// --- NPC activity

struct LevelUpStats
{
	char level
	short stat_points
	short skill_points
	short max_hp
	short max_tp
	short max_sp
}

// NPC/Cast_Reply/Accept could share data probably

"Nearby NPC hit by a player"
server_packet(NPC, Reply)
{
	short player_id
	char player_direction
	short npc_index
	three damage
	short hp_percentage
	// This is probably something
	char = 1
}

"Nearby NPC hit by a spell from a player"
server_packet(Cast, Reply)
{
	short spell_id
	short player_id
	char player_direction
	short npc_index
	three damage
	short hp_percentage
	short caster_tp
}

"Nearby NPC killed by player"
server_packet(NPC, Spec)
{
	short killer_id
	char killer_direction
	short npc_index
	short drop_index
	short drop_id
	struct Coords drop_coords
	int drop_amount
	three damage
	int experience
}

"Reply to request for information about nearby NPCs"
server_packet(NPC, Agree)
{
	char num_npcs
	struct NPCMapInfo npcs[num_npcs]
}

"Nearby NPC killed by player and you levelled up"
server_packet(NPC, Accept)
{
	short killer_id
	char killer_direction
	short npc_index
	short drop_index
	short drop_id
	struct Coords drop_coords
	int drop_amount
	three damage
	int experience
	struct LevelUpStats level_up
}

"Nearby NPC killed by player spell"
server_packet(Cast, Spec)
{
	short spell_id
	short killer_id
	char killer_direction
	short npc_index
	short drop_index
	short drop_id
	struct Coords drop_coords
	int drop_amount
	three damage
	short caster_tp
	int experience
}

"Nearby NPC killed by player spell and you levelled up"
server_packet(Cast, Accept)
{
	short spell_id
	short killer_id
	char killer_direction
	short npc_index
	short drop_index
	short drop_id
	struct Coords drop_coords
	int drop_amount
	three damage
	short caster_tp
	int experience
	struct LevelUpStats level_up
}

"Killing all boss children"
server_packet(NPC, Junk)
{
	short npc_id
}

"NPC chat message"
server_packet(NPC, Dialog)
{
	short npc_index
	string message
}

"NPC chat messages"
server_packet(Quest, Report)
{
	short npc_id
	string messages[]
}

struct NPCUpdatePos
{
	char npc_index
	struct Coords coords
	Direction direction
}

struct NPCUpdateAttack
{
	char npc_index
	PlayerKilledState killed_state
	Direction direction
	short player_id
	three damage
	char hp_percentage
}

struct NPCUpdateChat
{
	char npc_index
	char message_length
	raw_string(message_length) message
}

"Main NPC update message"
server_packet(NPC, Player)
{
	struct NPCUpdatePos pos[]
	break
	struct NPCUpdateAttack attack[]
	break
	struct NPCUpdateChat chat[]
	break
}

// --- Quest

struct QuestProgressEntry
{
	string name
	string description
	short icon
	short progress
	short target
	break
}

struct DialogQuestEntry
{
	short quest_id
	string quest_name
}

struct DialogEntry
{
	DialogEntryType entry_type

	// Allowing a union here would require a lot of extra implementation work
	// link_id will just go unused when entry_type != Link
	short link_id
	string line
}

"Talking to a quest NPC"
client_packet(Quest, Use)
{
	short npc_index

	// Quest id is 0 unless manually selected on the dialog
	short quest_id
}

"Response to a quest NPC dialog"
client_packet(Quest, Accept)
{
	short session_id
	short dialog_id
	short quest_id
	short npc_index
	DialogReply reply_type

	union(reply_type)
	{
		Link: link
		{
			char action
		}
	}
}

"Quest history / progress request"
client_packet(Quest, List)
{
	QuestPage page
}

"Quest selection dialog"
server_packet(Quest, Dialog)
{
	char quest_count
	short vendor_id
	short quest_id
	short session_id
	short dialog_id
	break

	struct DialogQuestEntry quest_entries[quest_count]
	struct DialogEntry entries[]
}

"Quest history / progress reply"
server_packet(Quest, List)
{
	QuestPage page
	// EOSERV sends total number of quests which seems wrong
	short num_quests

	union(page)
	{
		Progress: progress
		{
			struct QuestProgressEntry quests[]
		}

		History: history
		{
			string quests[]
		}
	}
}

// This is probably wrong
"Nearby player levelled up from quest"
server_packet(Item, Accept)
{
	short player_id
}


// --- Arena

"Arena is blocked message"
server_packet(Arena, Drop)
{
	char = 'N'
}

"Arena start message"
server_packet(Arena, Use)
{
	char num_players
}

"Arena kill message"
server_packet(Arena, Spec)
{
	short player_id
	break
	Direction direction
	break
	int num_kills
	break
	string killer_name
	string victim_name
}

"Arena win message"
server_packet(Arena, Accept)
{
	string winner_name
	int num_kills
	break
	string killer_name
	string victim_name
}


// --- Marriage

"Talking to a law NPC"
client_packet(Marriage, Open)
{
	short npc_index
}

"Requesting marriage approval"
client_packet(Marriage, Request)
{
	MarriageRequestType request_type
	int session_id
	break
	raw_string name
}

"Response from talking to a law NPC"
server_packet(Marriage, Open)
{
	three session_id
}

"Reply to client Marriage-family packets"
server_packet(Marriage, Reply)
{
	MarriageReply reply_code
	union(reply_code)
	{
		Success: success
		{
			int gold_amount
		}
	}
}


// --- Priest

"Accepting a marriage request"
client_packet(Priest, Accept)
{
	short session_id
}

"Talking to a priest NPC"
client_packet(Priest, Open)
{
	int npc_index
}

"Requesting marriage at a priest"
client_packet(Priest, Request)
{
	int session_id
	break
	string name
}

"Saying 'I do' at a wedding"
client_packet(Priest, Use)
{
	int session_id
}

"Response from talking to a priest NPC"
server_packet(Priest, Open)
{
	int session_id
}

"Reply to client Priest-family packets"
server_packet(Priest, Reply)
{
	PriestReply reply_code
}

"Wedding request"
server_packet(Priest, Request)
{
	short session_id
	string partner_name
}


// --- Misc.

enum MapDamageType : char
{
	1 TPDrain
	2 Spikes
}

struct MapDrainDamageOther
{
	short player_id
	char hp_percentage
	short damage
}

"HP/TP update"
server_packet(Recover, Player)
{
	short hp
	short tp
	short sp
}

"Stats update"
server_packet(Recover, List)
{
	short class_id
	struct CharacterStats3 stats
}

"Karma/experience update"
server_packet(Recover, Reply)
{
	int experience
	short karma
	"A value greater than 0 is 'new level' and indicates the player leveled up."
	char level_up

	// Only if level_up != 0
	short stat_points
	short skill_points
}

"Updated stats when levelling up from party experience"
server_packet(Recover, TargetGroup)
{
	short stat_points
	short skill_points
	short max_hp
	short max_tp
	short max_sp
}

"Nearby player gained HP"
server_packet(Recover, Agree)
{
	short player_id
	int heal_hp
	char hp_percentage
}

"Map effect"
server_packet(Effect, Use)
{
	MapEffect effect
	char param
}

"Map spike timer"
server_packet(Effect, Report)
{
	char = 'S'
}

"Map spell effect"
server_packet(Effect, Agree)
{
	struct Coords coords
	short effect_id
}

"Taking spike or tp drain damage"
server_packet(Effect, Spec)
{
	MapDamageType map_damage_type

	union(map_damage_type)
	{
		TPDrain: tp_drain
		{
			short tp_damage
			short tp
			short max_tp
		}

		Spikes: spikes
		{
			short damage
			short hp
			short max_hp
		}
	}
}

"Map drain damage"
server_packet(Effect, Target_Other)
{
	short damage
	short hp
	short max_hp
	struct MapDrainDamageOther others[]
}

"Nearby character taking spike damage"
server_packet(Effect, Admin)
{
	short player_id
	char hp_percentage
	char died
	three damage
}

"Sound effect"
server_packet(Music, Player)
{
	char sound_id
}
