// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../common/mmo.h"
#include "../common/socket.h"
#include "../common/db.h"
#include "../common/lock.h"
#include "../common/showmsg.h"
#include "char.h"
#include "inter.h"
#include "int_party.h"
char party_txt[1024] = "save/party.txt";
static struct dbt *party_db;
static int party_newid = 100;
int mapif_party_broken(int party_id, int flag);
int party_check_empty(struct party *p);
int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id);
// �p?�e�B�f?�^�̕�����ւ�?��
int inter_party_tostr(char *str, struct party *p) {
int i, len;
len = sprintf(str, "%d\t%s\t%d,%d\t", p->party_id, p->name, p->exp, p->item);
for(i = 0; i < MAX_PARTY; i++) {
struct party_member *m = &p->member[i];
len += sprintf(str + len, "%d,%d,%d\t%s\t", m->account_id, m->char_id, m->leader, ((m->account_id > 0) ? m->name : "NoMember"));
}
return 0;
}
// �p?�e�B�f?�^�̕������?��
int inter_party_fromstr(char *str, struct party *p) {
int i, j;
int tmp_int[16];
char tmp_str[256];
memset(p, 0, sizeof(struct party));
// printf("sscanf party main info\n");
if (sscanf(str, "%d\t%255[^\t]\t%d,%d\t", &tmp_int[0], tmp_str, &tmp_int[1], &tmp_int[2]) != 4)
return 1;
p->party_id = tmp_int[0];
memcpy(p->name, tmp_str, NAME_LENGTH-1);
p->exp = tmp_int[1]?1:0;
p->item = tmp_int[2];
// printf("%d [%s] %d %d\n", tmp_int[0], tmp_str[0], tmp_int[1], tmp_int[2]);
for(j = 0; j < 3 && str != NULL; j++)
str = strchr(str + 1, '\t');
for(i = 0; i < MAX_PARTY; i++) {
struct party_member *m = &p->member[i];
if (str == NULL)
return 1;
// printf("sscanf party member info %d\n", i);
if (sscanf(str + 1, "%d,%d,%d\t%255[^\t]\t", &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str) != 4)
return 1;
m->account_id = tmp_int[0];
m->char_id = tmp_int[1];
m->leader = tmp_int[2]?1:0;
memcpy(m->name, tmp_str, NAME_LENGTH-1);
// printf(" %d %d [%s]\n", tmp_int[0], tmp_int[1], tmp_str);
for(j = 0; j < 2 && str != NULL; j++)
str = strchr(str + 1, '\t');
}
return 0;
}
// �p?�e�B�f?�^�̃�?�h
int inter_party_init() {
char line[8192];
struct party *p;
FILE *fp;
int c = 0;
int i, j;
party_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
if ((fp = fopen(party_txt, "r")) == NULL)
return 1;
while(fgets(line, sizeof(line) - 1, fp)) {
j = 0;
if (sscanf(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 && party_newid <= i) {
party_newid = i;
continue;
}
p = (struct party*)aCalloc(sizeof(struct party), 1);
if (p == NULL){
ShowFatalError("int_party: out of memory!\n");
exit(0);
}
memset(p, 0, sizeof(struct party));
if (inter_party_fromstr(line, p) == 0 && p->party_id > 0) {
if (p->party_id >= party_newid)
party_newid = p->party_id + 1;
idb_put(party_db, p->party_id, p);
party_check_empty(p);
} else {
ShowError("int_party: broken data [%s] line %d\n", party_txt, c + 1);
aFree(p);
}
c++;
}
fclose(fp);
return 0;
}
void inter_party_final()
{
party_db->destroy(party_db, NULL);
return;
}
// �p?�e�B?�f?�^�̃Z?�u�p
int inter_party_save_sub(DBKey key, void *data, va_list ap) {
char line[8192];
FILE *fp;
inter_party_tostr(line, (struct party *)data);
fp = va_arg(ap, FILE *);
fprintf(fp, "%s" RETCODE, line);
return 0;
}
// �p?�e�B?�f?�^�̃Z?�u
int inter_party_save() {
FILE *fp;
int lock;
if ((fp = lock_fopen(party_txt, &lock)) == NULL) {
ShowError("int_party: cant write [%s] !!! data is lost !!!\n", party_txt);
return 1;
}
party_db->foreach(party_db, inter_party_save_sub, fp);
lock_fclose(fp,party_txt, &lock);
return 0;
}
// �p?�e�B��?���p
int search_partyname_sub(DBKey key,void *data,va_list ap) {
struct party *p = (struct party *)data,**dst;
char *str;
str = va_arg(ap, char *);
dst = va_arg(ap, struct party **);
if (strncmpi(p->name, str, NAME_LENGTH) == 0)
*dst = p;
return 0;
}
// �p?�e�B��?��
struct party* search_partyname(char *str) {
struct party *p = NULL;
party_db->foreach(party_db, search_partyname_sub, str, &p);
return p;
}
// EXP�������z�ł��邩�`�F�b�N
int party_check_exp_share(struct party *p) {
int i, oi[MAX_PARTY], dudes=0;
int maxlv = 0, minlv = 0x7fffffff;
for(i = 0; i < MAX_PARTY; i++) {
int lv = p->member[i].lv;
if (p->member[i].online) {
if (lv < minlv)
minlv = lv;
if (maxlv < lv)
maxlv = lv;
if( lv >= 70 )
dudes+=1000;
oi[dudes%1000] = i;
dudes++;
}
}
if((dudes/1000 >= 2) && (dudes%1000 == 3) && maxlv-minlv>party_share_level) {
int pl1=0,pl2=0,pl3=0;
pl1=search_character_index(p->member[oi[0]].name);
pl2=search_character_index(p->member[oi[1]].name);
pl3=search_character_index(p->member[oi[2]].name);
ShowDebug("PARTY: group of 3 Id1 %d lv %d name %s Id2 %d lv %d name %s Id3 %d lv %d name %s\n",pl1,p->member[oi[0]].lv,p->member[oi[0]].name,pl2,p->member[oi[1]].lv,p->member[oi[1]].name,pl3,p->member[oi[2]].lv,p->member[oi[2]].name);
if (char_married(pl1,pl2) && char_child(pl1,pl3))
return 1;
if (char_married(pl1,pl3) && char_child(pl1,pl2))
return 1;
if (char_married(pl2,pl3) && char_child(pl2,pl1))
return 1;
}
return (maxlv==0 || maxlv-minlv<=party_share_level);
}
// �p?�e�B���ǂ����`�F�b�N
int party_check_empty(struct party *p) {
int i;
for(i = 0; i < MAX_PARTY; i++) {
if (p->member[i].account_id > 0) {
return 0;
}
}
mapif_party_broken(p->party_id, 0);
idb_remove(party_db, p->party_id);
return 1;
}
// �L�����̋������Ȃ����`�F�b�N�p
int party_check_conflict_sub(DBKey key, void *data, va_list ap) {
struct party *p = (struct party *)data;
int party_id, account_id, char_id, i;
party_id=va_arg(ap, int);
account_id=va_arg(ap, int);
char_id=va_arg(ap, int);
if (p->party_id == party_id) //No conflict to check
return 0;
for(i = 0; i < MAX_PARTY; i++) {
if (p->member[i].account_id == account_id && p->member[i].char_id == char_id) {
ShowWarning("int_party: party conflict! %d %d %d\n", account_id, party_id, p->party_id);
mapif_parse_PartyLeave(-1, p->party_id, account_id, char_id);
}
}
return 0;
}
// �L�����̋������Ȃ����`�F�b�N
int party_check_conflict(int party_id, int account_id, int char_id) {
party_db->foreach(party_db, party_check_conflict_sub, party_id, account_id, char_id);
return 0;
}
//-------------------------------------------------------------------
// map server�ւ̒ʐM
// �p?�e�B�쐬��
int mapif_party_created(int fd,int account_id, int char_id, struct party *p) {
WFIFOHEAD(fd, 39);
WFIFOW(fd,0) = 0x3820;
WFIFOL(fd,2) = account_id;
WFIFOL(fd,6) = char_id;
if (p != NULL) {
WFIFOB(fd,10) = 0;
WFIFOL(fd,11) = p->party_id;
memcpy(WFIFOP(fd,15), p->name, NAME_LENGTH);
ShowInfo("Created party (%d - %s)\n", p->party_id, p->name);
} else {
WFIFOB(fd,10) = 1;
WFIFOL(fd,11) = 0;
memset(WFIFOP(fd,15), 0, NAME_LENGTH);
}
WFIFOSET(fd,39);
return 0;
}
// �p?�e�B����炸
int mapif_party_noinfo(int fd, int party_id) {
WFIFOHEAD(fd, 8);
WFIFOW(fd,0) = 0x3821;
WFIFOW(fd,2) = 8;
WFIFOL(fd,4) = party_id;
WFIFOSET(fd,8);
ShowWarning("int_party: info not found %d\n", party_id);
return 0;
}
// �p?�e�B���܂Ƃߑ���
int mapif_party_info(int fd, struct party *p) {
unsigned char buf[2048];
WBUFW(buf,0) = 0x3821;
memcpy(buf + 4, p, sizeof(struct party));
WBUFW(buf,2) = 4 + sizeof(struct party);
if (fd < 0)
mapif_sendall(buf, WBUFW(buf,2));
else
mapif_send(fd, buf, WBUFW(buf,2));
return 0;
}
// �p?�e�B�����o�lj���
int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) {
WFIFOHEAD(fd, 15);
WFIFOW(fd,0) = 0x3822;
WFIFOL(fd,2) = party_id;
WFIFOL(fd,6) = account_id;
WFIFOL(fd,10) = char_id;
WFIFOB(fd,14) = flag;
WFIFOSET(fd,15);
return 0;
}
// �p?�e�B�ݒ�?�X�ʒm
int mapif_party_optionchanged(int fd,struct party *p, int account_id, int flag) {
unsigned char buf[15];
WBUFW(buf,0) = 0x3823;
WBUFL(buf,2) = p->party_id;
WBUFL(buf,6) = account_id;
WBUFW(buf,10) = p->exp;
WBUFW(buf,12) = p->item;
WBUFB(buf,14) = flag;
if (flag == 0)
mapif_sendall(buf, 15);
else
mapif_send(fd, buf, 15);
return 0;
}
//Checks whether the even-share setting of a party is broken when a character logs in. [Skotlex]
int inter_party_logged(int party_id, int account_id, int char_id)
{
struct party *p;
int i;
if (!party_id)
return 0;
p = idb_get(party_db, party_id);
if(p==NULL)
return 0;
for (i = 0; i < MAX_PARTY; i++)
if (p->member[i].account_id == account_id && p->member[i].char_id == char_id)
{
p->member[i].online = 1;
break;
}
if(p->exp && !party_check_exp_share(p))
{
p->exp=0;
mapif_party_optionchanged(0,p,0,0);
return 1;
}
return 0;
}
// �p?�e�B?�ޒʒm
int mapif_party_leaved(int party_id,int account_id, int char_id) {
unsigned char buf[16];
WBUFW(buf,0) = 0x3824;
WBUFL(buf,2) = party_id;
WBUFL(buf,6) = account_id;
WBUFL(buf,10) = char_id;
mapif_sendall(buf, 14);
return 0;
}
// �p?�e�B�}�b�v�X�V�ʒm
int mapif_party_membermoved(struct party *p, int idx) {
unsigned char buf[20];
WBUFW(buf,0) = 0x3825;
WBUFL(buf,2) = p->party_id;
WBUFL(buf,6) = p->member[idx].account_id;
WBUFL(buf,10) = p->member[idx].char_id;
WBUFW(buf,14) = p->member[idx].map;
WBUFB(buf,16) = p->member[idx].online;
WBUFW(buf,17) = p->member[idx].lv;
mapif_sendall(buf, 19);
return 0;
}
// �p?�e�B���U�ʒm
int mapif_party_broken(int party_id, int flag) {
unsigned char buf[7];
WBUFW(buf,0) = 0x3826;
WBUFL(buf,2) = party_id;
WBUFB(buf,6) = flag;
mapif_sendall(buf, 7);
ShowInfo("Party broken (%d)\n", party_id);
return 0;
}
// �p?�e�B??��
int mapif_party_message(int party_id, int account_id, char *mes, int len, int sfd) {
unsigned char buf[2048];
WBUFW(buf,0) = 0x3827;
WBUFW(buf,2) = len + 12;
WBUFL(buf,4) = party_id;
WBUFL(buf,8) = account_id;
memcpy(WBUFP(buf,12), mes, len);
mapif_sendallwos(sfd, buf,len + 12);
return 0;
}
//-------------------------------------------------------------------
// map server����̒ʐM
// �p?�e�B
int mapif_parse_CreateParty(int fd, int account_id, int char_id, char *name, char *nick, unsigned short map, int lv, int item, int item2) {
struct party *p;
int i;
for(i = 0; i < NAME_LENGTH && name[i]; i++) {
if (!(name[i] & 0xe0) || name[i] == 0x7f) {
ShowInfo("int_party: illegal party name [%s]\n", name);
mapif_party_created(fd, account_id, char_id, NULL);
return 0;
}
}
if ((p = search_partyname(name)) != NULL) {
ShowInfo("int_party: same name party exists [%s]\n", name);
mapif_party_created(fd, account_id, char_id, NULL);
return 0;
}
p = (struct party *) aCalloc(sizeof(struct party), 1);
if (p == NULL) {
ShowFatalError("int_party: out of memory !\n");
mapif_party_created(fd,account_id,char_id,NULL);
return 0;
}
p->party_id = party_newid++;
memcpy(p->name, name, NAME_LENGTH);
p->exp = 0;
p->item=(item?1:0)|(item2?2:0);
p->member[0].account_id = account_id;
p->member[0].char_id = char_id;
memcpy(p->member[0].name, nick, NAME_LENGTH);
p->member[0].map = map;
p->member[0].leader = 1;
p->member[0].online = 1;
p->member[0].lv = lv;
idb_put(party_db, p->party_id, p);
mapif_party_created(fd, account_id, char_id, p);
mapif_party_info(fd, p);
return 0;
}
// �p?�e�B���v��
int mapif_parse_PartyInfo(int fd, int party_id) {
struct party *p;
p = idb_get(party_db, party_id);
if (p != NULL)
mapif_party_info(fd, p);
else
mapif_party_noinfo(fd, party_id);
return 0;
}
// �p?�e�B�lj��v��
int mapif_parse_PartyAddMember(int fd, int party_id, int account_id, int char_id, char *nick, unsigned short map, int lv) {
struct party *p;
int i;
p = idb_get(party_db, party_id);
if (p == NULL) {
mapif_party_memberadded(fd, party_id, account_id, char_id, 1);
return 0;
}
for(i = 0; i < MAX_PARTY; i++) {
if (p->member[i].account_id == 0) {
int flag = 0;
p->member[i].account_id = account_id;
p->member[i].char_id = char_id;
memcpy(p->member[i].name, nick, NAME_LENGTH);
p->member[i].map = map;
p->member[i].leader = 0;
p->member[i].online = 1;
p->member[i].lv = lv;
mapif_party_memberadded(fd, party_id, account_id, char_id, 0);
mapif_party_info(-1, p);
if (p->exp && !party_check_exp_share(p)) {
p->exp = 0;
flag = 0x01;
}
if (flag)
mapif_party_optionchanged(fd, p, 0, 0);
return 0;
}
}
mapif_party_memberadded(fd, party_id, account_id, char_id, 1);
return 0;
}
// �p?�e�B?�ݒ�?�X�v��
int mapif_parse_PartyChangeOption(int fd, int party_id, int account_id, int exp, int flag) {
struct party *p;
//NOTE: No clue what that flag is about, in all observations so far it always comes as 0. [Skotlex]
flag = 0;
p = idb_get(party_db, party_id);
if (p == NULL)
return 0;
p->exp = exp;
if (exp>0 && !party_check_exp_share(p)) {
flag |= 0x01;
p->exp = 0;
}
mapif_party_optionchanged(fd, p, account_id, flag);
return 0;
}
// �p?�e�B?�ޗv��
int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) {
struct party *p;
int i;
p = idb_get(party_db, party_id);
if (p != NULL) {
for(i = 0; i < MAX_PARTY; i++) {
if (p->member[i].account_id == account_id && p->member[i].char_id == char_id)
{
mapif_party_leaved(party_id, account_id, char_id);
memset(&p->member[i], 0, sizeof(struct party_member));
//Normally unneeded except when a family is even-sharing
//and one of the three leaves the party.
if(p->exp && !party_check_exp_share(p)){
p->exp=0;
mapif_party_optionchanged(fd,p,0,0);
}
if (party_check_empty(p) == 0)
mapif_party_info(-1, p);// �܂��l������̂Ńf?�^���M
return 0;
}
}
}
return 0;
}
// �p?�e�B�}�b�v�X�V�v��
int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, int lv) {
struct party *p;
int i;
p = idb_get(party_db, party_id);
if (p == NULL)
return 0;
for(i = 0; i < MAX_PARTY; i++) {
if (p->member[i].account_id == account_id && p->member[i].char_id == char_id)
{
p->member[i].map = map;
p->member[i].online = online;
if (p->member[i].lv != lv) {
p->member[i].lv = lv;
if (p->exp && !party_check_exp_share(p)) {
p->exp = 0;
mapif_party_optionchanged(fd, p, 0, 0);
}
}
mapif_party_membermoved(p, i);
break;
}
}
return 0;
}
// �p?�e�B���U�v��
int mapif_parse_BreakParty(int fd, int party_id) {
struct party *p;
p = idb_get(party_db, party_id);
if (p == NULL)
return 0;
idb_remove(party_db, party_id);
mapif_party_broken(fd, party_id);
return 0;
}
// �p?�e�B���b�Z?�W���M
int mapif_parse_PartyMessage(int fd, int party_id, int account_id, char *mes, int len) {
return mapif_party_message(party_id, account_id, mes, len, fd);
}
// �p?�e�B�`�F�b�N�v��
int mapif_parse_PartyCheck(int fd, int party_id, int account_id, int char_id) {
return party_check_conflict(party_id, account_id, char_id);
}
int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id)
{
struct party *p;
int i;
p = idb_get(party_db, party_id);
if (p == NULL)
return 0;
for (i = 0; i < MAX_PARTY; i++)
{
if(p->member[i].leader)
p->member[i].leader = 0;
if(p->member[i].account_id == account_id && p->member[i].char_id == char_id)
p->member[i].leader = 1;
}
return 1;
}
// map server ����̒ʐM
// ?�P�p�P�b�g�̂݉�͂��邱��
// ?�p�P�b�g���f?�^��inter.c�ɃZ�b�g���Ă�������
// ?�p�P�b�g���`�F�b�N��ARFIFOSKIP�͌Ăяo�����ōs����̂ōs���Ă͂Ȃ�Ȃ�
// ?�G��?�Ȃ�0(false)�A�����łȂ��Ȃ�1(true)���������Ȃ���Ȃ�Ȃ�
int inter_party_parse_frommap(int fd) {
RFIFOHEAD(fd);
switch(RFIFOW(fd,0)) {
case 0x3020: mapif_parse_CreateParty(fd, RFIFOL(fd,2), RFIFOL(fd,6),(char*)RFIFOP(fd,10), (char*)RFIFOP(fd,34), RFIFOW(fd,58), RFIFOW(fd,60), RFIFOB(fd,62), RFIFOB(fd,63)); break;
case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break;
case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), (char*)RFIFOP(fd,14), RFIFOW(fd,38), RFIFOW(fd,40)); break;
case 0x3023: mapif_parse_PartyChangeOption(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOW(fd,10), RFIFOW(fd,12)); break;
case 0x3024: mapif_parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
case 0x3025: mapif_parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOW(fd,14), RFIFOB(fd,16), RFIFOW(fd,17)); break;
case 0x3026: mapif_parse_BreakParty(fd, RFIFOL(fd,2)); break;
case 0x3027: mapif_parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break;
case 0x3028: mapif_parse_PartyCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
case 0x3029: mapif_parse_PartyLeaderChange(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
default:
return 0;
}
return 1;
}
// �T?�o?����?�ޗv���i�L�����폜�p�j
int inter_party_leave(int party_id, int account_id, int char_id) {
return mapif_parse_PartyLeave(-1, party_id, account_id, char_id);
}