PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/src/map/battleground.c

https://gitlab.com/evol/hercules
C | 995 lines | 816 code | 126 blank | 53 comment | 244 complexity | 6fbe7ad54e14fee2b487ed25b270abdd MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.0
  1. /**
  2. * This file is part of Hercules.
  3. * http://herc.ws - http://github.com/HerculesWS/Hercules
  4. *
  5. * Copyright (C) 2012-2015 Hercules Dev Team
  6. * Copyright (C) Athena Dev Teams
  7. *
  8. * Hercules is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #define HERCULES_CORE
  22. #include "battleground.h"
  23. #include "map/battle.h"
  24. #include "map/clif.h"
  25. #include "map/guild.h"
  26. #include "map/homunculus.h"
  27. #include "map/map.h"
  28. #include "map/mapreg.h"
  29. #include "map/mercenary.h"
  30. #include "map/mob.h" // struct mob_data
  31. #include "map/npc.h"
  32. #include "map/party.h"
  33. #include "map/pc.h"
  34. #include "map/pet.h"
  35. #include "common/cbasetypes.h"
  36. #include "common/conf.h"
  37. #include "common/HPM.h"
  38. #include "common/memmgr.h"
  39. #include "common/nullpo.h"
  40. #include "common/showmsg.h"
  41. #include "common/socket.h"
  42. #include "common/strlib.h"
  43. #include "common/timer.h"
  44. #include <stdio.h>
  45. #include <string.h>
  46. struct battleground_interface bg_s;
  47. struct battleground_interface *bg;
  48. /// Search a BG Team using bg_id
  49. struct battleground_data* bg_team_search(int bg_id) {
  50. if( !bg_id ) return NULL;
  51. return (struct battleground_data *)idb_get(bg->team_db, bg_id);
  52. }
  53. struct map_session_data* bg_getavailablesd(struct battleground_data *bgd) {
  54. int i;
  55. nullpo_retr(NULL, bgd);
  56. ARR_FIND(0, MAX_BG_MEMBERS, i, bgd->members[i].sd != NULL);
  57. return( i < MAX_BG_MEMBERS ) ? bgd->members[i].sd : NULL;
  58. }
  59. /// Deletes BG Team from db
  60. bool bg_team_delete(int bg_id) {
  61. int i;
  62. struct battleground_data *bgd = bg->team_search(bg_id);
  63. if( bgd == NULL ) return false;
  64. for( i = 0; i < MAX_BG_MEMBERS; i++ ) {
  65. struct map_session_data *sd = bgd->members[i].sd;
  66. if (sd == NULL)
  67. continue;
  68. bg->send_dot_remove(sd);
  69. sd->bg_id = 0;
  70. }
  71. idb_remove(bg->team_db, bg_id);
  72. return true;
  73. }
  74. /// Warps a Team
  75. bool bg_team_warp(int bg_id, unsigned short map_index, short x, short y) {
  76. int i;
  77. struct battleground_data *bgd = bg->team_search(bg_id);
  78. if( bgd == NULL ) return false;
  79. for( i = 0; i < MAX_BG_MEMBERS; i++ )
  80. if( bgd->members[i].sd != NULL ) pc->setpos(bgd->members[i].sd, map_index, x, y, CLR_TELEPORT);
  81. return true;
  82. }
  83. void bg_send_dot_remove(struct map_session_data *sd) {
  84. if( sd && sd->bg_id )
  85. clif->bg_xy_remove(sd);
  86. }
  87. /// Player joins team
  88. bool bg_team_join(int bg_id, struct map_session_data *sd) {
  89. int i;
  90. struct battleground_data *bgd = bg->team_search(bg_id);
  91. if( bgd == NULL || sd == NULL || sd->bg_id ) return false;
  92. ARR_FIND(0, MAX_BG_MEMBERS, i, bgd->members[i].sd == NULL);
  93. if( i == MAX_BG_MEMBERS ) return false; // No free slots
  94. sd->bg_id = bg_id;
  95. bgd->members[i].sd = sd;
  96. bgd->members[i].x = sd->bl.x;
  97. bgd->members[i].y = sd->bl.y;
  98. /* populate 'where i came from' */
  99. if(map->list[sd->bl.m].flag.nosave || map->list[sd->bl.m].instance_id >= 0) {
  100. struct map_data *m=&map->list[sd->bl.m];
  101. if(m->save.map)
  102. memcpy(&bgd->members[i].source,&m->save,sizeof(struct point));
  103. else
  104. memcpy(&bgd->members[i].source,&sd->status.save_point,sizeof(struct point));
  105. } else
  106. memcpy(&bgd->members[i].source,&sd->status.last_point,sizeof(struct point));
  107. bgd->count++;
  108. guild->send_dot_remove(sd);
  109. for( i = 0; i < MAX_BG_MEMBERS; i++ ) {
  110. struct map_session_data *pl_sd = bgd->members[i].sd;
  111. if (pl_sd != NULL && pl_sd != sd)
  112. clif->hpmeter_single(sd->fd, pl_sd->bl.id, pl_sd->battle_status.hp, pl_sd->battle_status.max_hp);
  113. }
  114. clif->bg_hp(sd);
  115. clif->bg_xy(sd);
  116. return true;
  117. }
  118. /// Single Player leaves team
  119. int bg_team_leave(struct map_session_data *sd, enum bg_team_leave_type flag) {
  120. int i, bg_id;
  121. struct battleground_data *bgd;
  122. if( sd == NULL || !sd->bg_id )
  123. return 0;
  124. bg->send_dot_remove(sd);
  125. bg_id = sd->bg_id;
  126. sd->bg_id = 0;
  127. if( (bgd = bg->team_search(bg_id)) == NULL )
  128. return 0;
  129. ARR_FIND(0, MAX_BG_MEMBERS, i, bgd->members[i].sd == sd);
  130. if( i < MAX_BG_MEMBERS ) { // Removes member from BG
  131. if( sd->bg_queue.arena ) {
  132. bg->queue_pc_cleanup(sd);
  133. pc->setpos(sd,bgd->members[i].source.map, bgd->members[i].source.x, bgd->members[i].source.y, CLR_OUTSIGHT);
  134. }
  135. memset(&bgd->members[i], 0, sizeof(bgd->members[0]));
  136. }
  137. if (--bgd->count != 0) {
  138. char output[128];
  139. switch (flag) {
  140. default:
  141. case BGTL_QUIT:
  142. sprintf(output, "Server : %s has quit the game...", sd->status.name);
  143. break;
  144. case BGTL_LEFT:
  145. sprintf(output, "Server : %s is leaving the battlefield...", sd->status.name);
  146. break;
  147. case BGTL_AFK:
  148. sprintf(output, "Server : %s has been afk-kicked from the battlefield...", sd->status.name);
  149. break;
  150. }
  151. clif->bg_message(bgd, 0, "Server", output, strlen(output) + 1);
  152. }
  153. if( bgd->logout_event[0] && flag )
  154. npc->event(sd, bgd->logout_event, 0);
  155. if( sd->bg_queue.arena ) {
  156. bg->queue_pc_cleanup(sd);
  157. }
  158. return bgd->count;
  159. }
  160. /// Respawn after killed
  161. bool bg_member_respawn(struct map_session_data *sd) {
  162. struct battleground_data *bgd;
  163. if( sd == NULL || !pc_isdead(sd) || !sd->bg_id || (bgd = bg->team_search(sd->bg_id)) == NULL )
  164. return false;
  165. if( bgd->mapindex == 0 )
  166. return false; // Respawn not handled by Core
  167. pc->setpos(sd, bgd->mapindex, bgd->x, bgd->y, CLR_OUTSIGHT);
  168. status->revive(&sd->bl, 1, 100);
  169. return true; // Warped
  170. }
  171. int bg_create(unsigned short map_index, short rx, short ry, const char *ev, const char *dev) {
  172. struct battleground_data *bgd;
  173. bg->team_counter++;
  174. CREATE(bgd, struct battleground_data, 1);
  175. bgd->bg_id = bg->team_counter;
  176. bgd->count = 0;
  177. bgd->mapindex = map_index;
  178. bgd->x = rx;
  179. bgd->y = ry;
  180. safestrncpy(bgd->logout_event, ev, sizeof(bgd->logout_event));
  181. safestrncpy(bgd->die_event, dev, sizeof(bgd->die_event));
  182. memset(&bgd->members, 0, sizeof(bgd->members));
  183. idb_put(bg->team_db, bg->team_counter, bgd);
  184. return bgd->bg_id;
  185. }
  186. int bg_team_get_id(struct block_list *bl) {
  187. nullpo_ret(bl);
  188. switch( bl->type ) {
  189. case BL_PC:
  190. {
  191. const struct map_session_data *sd = BL_UCCAST(BL_PC, bl);
  192. return sd->bg_id;
  193. }
  194. case BL_PET:
  195. {
  196. const struct pet_data *pd = BL_UCCAST(BL_PET, bl);
  197. if (pd->msd != NULL)
  198. return pd->msd->bg_id;
  199. }
  200. break;
  201. case BL_MOB:
  202. {
  203. const struct mob_data *md = BL_UCCAST(BL_MOB, bl);
  204. const struct map_session_data *msd;
  205. if (md->special_state.ai != AI_NONE && (msd = map->id2sd(md->master_id)) != NULL)
  206. return msd->bg_id;
  207. return md->bg_id;
  208. }
  209. case BL_HOM:
  210. {
  211. const struct homun_data *hd = BL_UCCAST(BL_HOM, bl);
  212. if (hd->master != NULL)
  213. return hd->master->bg_id;
  214. }
  215. break;
  216. case BL_MER:
  217. {
  218. const struct mercenary_data *md = BL_UCCAST(BL_MER, bl);
  219. if (md->master != NULL)
  220. return md->master->bg_id;
  221. }
  222. break;
  223. case BL_SKILL:
  224. {
  225. const struct skill_unit *su = BL_UCCAST(BL_SKILL, bl);
  226. if (su->group != NULL)
  227. return su->group->bg_id;
  228. }
  229. break;
  230. }
  231. return 0;
  232. }
  233. bool bg_send_message(struct map_session_data *sd, const char *mes, int len) {
  234. struct battleground_data *bgd;
  235. nullpo_ret(sd);
  236. nullpo_ret(mes);
  237. if( sd->bg_id == 0 || (bgd = bg->team_search(sd->bg_id)) == NULL )
  238. return false; // Couldn't send message
  239. clif->bg_message(bgd, sd->bl.id, sd->status.name, mes, len);
  240. return true;
  241. }
  242. /**
  243. * @see DBApply
  244. */
  245. int bg_send_xy_timer_sub(DBKey key, DBData *data, va_list ap) {
  246. struct battleground_data *bgd = DB->data2ptr(data);
  247. struct map_session_data *sd;
  248. int i;
  249. nullpo_ret(bgd);
  250. for( i = 0; i < MAX_BG_MEMBERS; i++ ) {
  251. if( (sd = bgd->members[i].sd) == NULL )
  252. continue;
  253. if( sd->bl.x != bgd->members[i].x || sd->bl.y != bgd->members[i].y ) { // xy update
  254. bgd->members[i].x = sd->bl.x;
  255. bgd->members[i].y = sd->bl.y;
  256. clif->bg_xy(sd);
  257. }
  258. }
  259. return 0;
  260. }
  261. int bg_send_xy_timer(int tid, int64 tick, int id, intptr_t data) {
  262. bg->team_db->foreach(bg->team_db, bg->send_xy_timer_sub, tick);
  263. return 0;
  264. }
  265. enum bg_queue_types bg_str2teamtype (const char *str) {
  266. char temp[200], *parse;
  267. enum bg_queue_types type = BGQT_INVALID;
  268. nullpo_retr(type, str);
  269. safestrncpy(temp, str, 200);
  270. parse = strtok(temp,"|");
  271. while (parse != NULL) {
  272. normalize_name(parse," ");
  273. if( strcmpi(parse,"all") == 0 )
  274. type |= BGQT_INDIVIDUAL|BGQT_PARTY|BGQT_GUILD;
  275. else if( strcmpi(parse,"party") == 0 )
  276. type |= BGQT_PARTY;
  277. else if( strcmpi(parse,"guild") == 0 )
  278. type |= BGQT_GUILD;
  279. else if( strcmpi(parse,"solo") == 0 )
  280. type |= BGQT_INDIVIDUAL;
  281. else {
  282. ShowError("bg_str2teamtype: '%s' unknown type, skipping...\n",parse);
  283. }
  284. parse = strtok(NULL,"|");
  285. }
  286. return type;
  287. }
  288. void bg_config_read(void) {
  289. config_t bg_conf;
  290. config_setting_t *data = NULL;
  291. const char *config_filename = "conf/battlegrounds.conf"; // FIXME hardcoded name
  292. if (libconfig->read_file(&bg_conf, config_filename))
  293. return;
  294. data = libconfig->lookup(&bg_conf, "battlegrounds");
  295. if (data != NULL) {
  296. config_setting_t *settings = libconfig->setting_get_elem(data, 0);
  297. config_setting_t *arenas;
  298. const char *delay_var;
  299. int offline = 0;
  300. if( !libconfig->setting_lookup_string(settings, "global_delay_var", &delay_var) )
  301. delay_var = "BG_Delay_Tick";
  302. safestrncpy(bg->gdelay_var, delay_var, BG_DELAY_VAR_LENGTH);
  303. libconfig->setting_lookup_int(settings, "maximum_afk_seconds", &bg->mafksec);
  304. libconfig->setting_lookup_bool(settings, "feature_off", &offline);
  305. if( offline == 0 )
  306. bg->queue_on = true;
  307. if( (arenas = libconfig->setting_get_member(settings, "arenas")) != NULL ) {
  308. int i;
  309. int arena_count = libconfig->setting_length(arenas);
  310. CREATE( bg->arena, struct bg_arena *, arena_count );
  311. for(i = 0; i < arena_count; i++) {
  312. config_setting_t *arena = libconfig->setting_get_elem(arenas, i);
  313. config_setting_t *reward;
  314. const char *aName, *aEvent, *aDelayVar, *aTeamTypes;
  315. int minLevel = 0, maxLevel = 0;
  316. int prizeWin, prizeLoss, prizeDraw;
  317. int minPlayers, maxPlayers, minTeamPlayers;
  318. int maxDuration;
  319. int fillup_duration = 0, pregame_duration = 0;
  320. enum bg_queue_types allowedTypes;
  321. bg->arena[i] = NULL;
  322. if( !libconfig->setting_lookup_string(arena, "name", &aName) ) {
  323. ShowError("bg_config_read: failed to find 'name' for arena #%d\n",i);
  324. continue;
  325. }
  326. if( !libconfig->setting_lookup_string(arena, "event", &aEvent) ) {
  327. ShowError("bg_config_read: failed to find 'event' for arena #%d\n",i);
  328. continue;
  329. }
  330. libconfig->setting_lookup_int(arena, "minLevel", &minLevel);
  331. libconfig->setting_lookup_int(arena, "maxLevel", &maxLevel);
  332. if( minLevel < 0 ) {
  333. ShowWarning("bg_config_read: invalid %d value for arena '%s' minLevel\n",minLevel,aName);
  334. minLevel = 0;
  335. }
  336. if( maxLevel > MAX_LEVEL ) {
  337. ShowWarning("bg_config_read: invalid %d value for arena '%s' maxLevel\n",maxLevel,aName);
  338. maxLevel = MAX_LEVEL;
  339. }
  340. if( !(reward = libconfig->setting_get_member(arena, "reward")) ) {
  341. ShowError("bg_config_read: failed to find 'reward' for arena '%s'/#%d\n",aName,i);
  342. continue;
  343. }
  344. libconfig->setting_lookup_int(reward, "win", &prizeWin);
  345. libconfig->setting_lookup_int(reward, "loss", &prizeLoss);
  346. libconfig->setting_lookup_int(reward, "draw", &prizeDraw);
  347. if( prizeWin < 0 ) {
  348. ShowWarning("bg_config_read: invalid %d value for arena '%s' reward:win\n",prizeWin,aName);
  349. prizeWin = 0;
  350. }
  351. if( prizeLoss < 0 ) {
  352. ShowWarning("bg_config_read: invalid %d value for arena '%s' reward:loss\n",prizeLoss,aName);
  353. prizeLoss = 0;
  354. }
  355. if( prizeDraw < 0 ) {
  356. ShowWarning("bg_config_read: invalid %d value for arena '%s' reward:draw\n",prizeDraw,aName);
  357. prizeDraw = 0;
  358. }
  359. libconfig->setting_lookup_int(arena, "minPlayers", &minPlayers);
  360. libconfig->setting_lookup_int(arena, "maxPlayers", &maxPlayers);
  361. libconfig->setting_lookup_int(arena, "minTeamPlayers", &minTeamPlayers);
  362. if( minPlayers < 0 ) {
  363. ShowWarning("bg_config_read: invalid %d value for arena '%s' minPlayers\n",minPlayers,aName);
  364. minPlayers = 0;
  365. }
  366. if( maxPlayers > MAX_BG_MEMBERS * 2 ) {
  367. ShowWarning("bg_config_read: invalid %d value for arena '%s' maxPlayers, change #define MAX_BG_MEMBERS\n",maxPlayers,aName);
  368. maxPlayers = 0;
  369. }
  370. if( minTeamPlayers < 0 ) {
  371. ShowWarning("bg_config_read: invalid %d value for arena '%s' minTeamPlayers\n",minTeamPlayers,aName);
  372. minTeamPlayers = 0;
  373. }
  374. if( !libconfig->setting_lookup_string(arena, "delay_var", &aDelayVar) ) {
  375. ShowError("bg_config_read: failed to find 'delay_var' for arena '%s'/#%d\n",aName,i);
  376. continue;
  377. }
  378. if( !libconfig->setting_lookup_string(arena, "allowedTypes", &aTeamTypes) ) {
  379. ShowError("bg_config_read: failed to find 'allowedTypes' for arena '%s'/#%d\n",aName,i);
  380. continue;
  381. }
  382. libconfig->setting_lookup_int(arena, "maxDuration", &maxDuration);
  383. if( maxDuration < 0 ) {
  384. ShowWarning("bg_config_read: invalid %d value for arena '%s' maxDuration\n",maxDuration,aName);
  385. maxDuration = 30;
  386. }
  387. libconfig->setting_lookup_int(arena, "fillDuration", &fillup_duration);
  388. libconfig->setting_lookup_int(arena, "pGameDuration", &pregame_duration);
  389. if( fillup_duration < 20 ) {
  390. ShowWarning("bg_config_read: invalid %d value for arena '%s' fillDuration, minimum has to be 20, defaulting to 20.\n",fillup_duration,aName);
  391. fillup_duration = 20;
  392. }
  393. if( pregame_duration < 20 ) {
  394. ShowWarning("bg_config_read: invalid %d value for arena '%s' pGameDuration, minimum has to be 20, defaulting to 20.\n",pregame_duration,aName);
  395. pregame_duration = 20;
  396. }
  397. allowedTypes = bg->str2teamtype(aTeamTypes);
  398. CREATE( bg->arena[i], struct bg_arena, 1 );
  399. bg->arena[i]->id = i;
  400. safestrncpy(bg->arena[i]->name, aName, NAME_LENGTH);
  401. safestrncpy(bg->arena[i]->npc_event, aEvent, EVENT_NAME_LENGTH);
  402. bg->arena[i]->min_level = minLevel;
  403. bg->arena[i]->max_level = maxLevel;
  404. bg->arena[i]->prize_win = prizeWin;
  405. bg->arena[i]->prize_loss = prizeLoss;
  406. bg->arena[i]->prize_draw = prizeDraw;
  407. bg->arena[i]->min_players = minPlayers;
  408. bg->arena[i]->max_players = maxPlayers;
  409. bg->arena[i]->min_team_players = minTeamPlayers;
  410. safestrncpy(bg->arena[i]->delay_var, aDelayVar, NAME_LENGTH);
  411. bg->arena[i]->maxDuration = maxDuration;
  412. bg->arena[i]->queue_id = script->queue_create();
  413. bg->arena[i]->begin_timer = INVALID_TIMER;
  414. bg->arena[i]->fillup_timer = INVALID_TIMER;
  415. bg->arena[i]->pregame_duration = pregame_duration;
  416. bg->arena[i]->fillup_duration = fillup_duration;
  417. bg->arena[i]->ongoing = false;
  418. bg->arena[i]->allowed_types = allowedTypes;
  419. }
  420. bg->arenas = arena_count;
  421. }
  422. }
  423. libconfig->destroy(&bg_conf);
  424. }
  425. struct bg_arena *bg_name2arena (char *name) {
  426. int i;
  427. nullpo_retr(NULL, name);
  428. for(i = 0; i < bg->arenas; i++) {
  429. if( strcmpi(bg->arena[i]->name,name) == 0 )
  430. return bg->arena[i];
  431. }
  432. return NULL;
  433. }
  434. /**
  435. * Returns a character's position in a battleground's queue.
  436. *
  437. * @param queue_id The ID of the battleground's queue.
  438. * @param account_id The character's account ID.
  439. *
  440. * @return the position (starting at 1).
  441. * @retval 0 if the queue doesn't exist or the given account ID isn't present in it.
  442. */
  443. int bg_id2pos(int queue_id, int account_id)
  444. {
  445. struct script_queue *queue = script->queue(queue_id);
  446. if (queue) {
  447. int i;
  448. ARR_FIND(0, VECTOR_LENGTH(queue->entries), i, VECTOR_INDEX(queue->entries, i) == account_id);
  449. if (i != VECTOR_LENGTH(queue->entries)) {
  450. return i+1;
  451. }
  452. }
  453. return 0;
  454. }
  455. void bg_queue_ready_ack(struct bg_arena *arena, struct map_session_data *sd, bool response)
  456. {
  457. nullpo_retv(arena);
  458. nullpo_retv(sd);
  459. if( arena->begin_timer == INVALID_TIMER || !sd->bg_queue.arena || sd->bg_queue.arena != arena ) {
  460. bg->queue_pc_cleanup(sd);
  461. return;
  462. }
  463. if( !response )
  464. bg->queue_pc_cleanup(sd);
  465. else {
  466. struct script_queue *queue = script->queue(arena->queue_id);
  467. int i, count = 0;
  468. sd->bg_queue.ready = 1;
  469. for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) {
  470. struct map_session_data *tsd = map->id2sd(VECTOR_INDEX(queue->entries, i));
  471. if (tsd != NULL && tsd->bg_queue.ready == 1)
  472. count++;
  473. }
  474. /* check if all are ready then cancel timer, and start game */
  475. if (count == VECTOR_LENGTH(queue->entries)) {
  476. timer->delete(arena->begin_timer,bg->begin_timer);
  477. arena->begin_timer = INVALID_TIMER;
  478. bg->begin(arena);
  479. }
  480. }
  481. }
  482. void bg_queue_player_cleanup(struct map_session_data *sd) {
  483. nullpo_retv(sd);
  484. if ( sd->bg_queue.client_has_bg_data ) {
  485. if( sd->bg_queue.arena )
  486. clif->bgqueue_notice_delete(sd,BGQND_CLOSEWINDOW,sd->bg_queue.arena->name);
  487. else
  488. clif->bgqueue_notice_delete(sd,BGQND_FAIL_NOT_QUEUING,bg->arena[0]->name);
  489. }
  490. if( sd->bg_queue.arena )
  491. script->queue_remove(sd->bg_queue.arena->queue_id,sd->status.account_id);
  492. sd->bg_queue.arena = NULL;
  493. sd->bg_queue.ready = 0;
  494. sd->bg_queue.client_has_bg_data = 0;
  495. sd->bg_queue.type = 0;
  496. }
  497. void bg_match_over(struct bg_arena *arena, bool canceled) {
  498. struct script_queue *queue = script->queue(arena->queue_id);
  499. int i;
  500. nullpo_retv(arena);
  501. if( !arena->ongoing )
  502. return;
  503. arena->ongoing = false;
  504. for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) {
  505. struct map_session_data *sd = map->id2sd(VECTOR_INDEX(queue->entries, i));
  506. if (sd == NULL)
  507. continue;
  508. if (sd->bg_queue.arena) {
  509. bg->team_leave(sd, 0);
  510. bg->queue_pc_cleanup(sd);
  511. }
  512. if (canceled)
  513. clif->messagecolor_self(sd->fd, COLOR_RED, "BG Match Canceled: not enough players");
  514. else
  515. pc_setglobalreg(sd, script->add_str(arena->delay_var), (unsigned int)time(NULL));
  516. }
  517. arena->begin_timer = INVALID_TIMER;
  518. arena->fillup_timer = INVALID_TIMER;
  519. /* reset queue */
  520. script->queue_clear(arena->queue_id);
  521. }
  522. void bg_begin(struct bg_arena *arena) {
  523. struct script_queue *queue = script->queue(arena->queue_id);
  524. int i, count = 0;
  525. nullpo_retv(arena);
  526. for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) {
  527. struct map_session_data *sd = map->id2sd(VECTOR_INDEX(queue->entries, i));
  528. if (sd == NULL)
  529. continue;
  530. if (sd->bg_queue.ready == 1)
  531. count++;
  532. else
  533. bg->queue_pc_cleanup(sd);
  534. }
  535. /* TODO/FIXME? I *think* it should check what kind of queue the player used, then check if his party/guild
  536. * (his team) still meet the join criteria (sort of what bg->can_queue does)
  537. */
  538. if( count < arena->min_players ) {
  539. bg->match_over(arena,true);
  540. } else {
  541. arena->ongoing = true;
  542. if( bg->afk_timer_id == INVALID_TIMER && bg->mafksec > 0 )
  543. bg->afk_timer_id = timer->add(timer->gettick()+10000,bg->afk_timer,0,0);
  544. /* TODO: make this a arena-independent var? or just .@? */
  545. mapreg->setreg(script->add_str("$@bg_queue_id"),arena->queue_id);
  546. mapreg->setregstr(script->add_str("$@bg_delay_var$"),bg->gdelay_var);
  547. count = 0;
  548. for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) {
  549. struct map_session_data *sd = map->id2sd(VECTOR_INDEX(queue->entries, i));
  550. if (sd == NULL || sd->bg_queue.ready != 1)
  551. continue;
  552. mapreg->setreg(reference_uid(script->add_str("$@bg_member"), count), sd->status.account_id);
  553. mapreg->setreg(reference_uid(script->add_str("$@bg_member_group"), count),
  554. sd->bg_queue.type == BGQT_GUILD ? sd->status.guild_id :
  555. sd->bg_queue.type == BGQT_PARTY ? sd->status.party_id :
  556. 0
  557. );
  558. mapreg->setreg(reference_uid(script->add_str("$@bg_member_type"), count),
  559. sd->bg_queue.type == BGQT_GUILD ? 1 :
  560. sd->bg_queue.type == BGQT_PARTY ? 2 :
  561. 0
  562. );
  563. count++;
  564. }
  565. mapreg->setreg(script->add_str("$@bg_member_size"),count);
  566. npc->event_do(arena->npc_event);
  567. }
  568. }
  569. int bg_begin_timer(int tid, int64 tick, int id, intptr_t data) {
  570. bg->begin(bg->arena[id]);
  571. bg->arena[id]->begin_timer = INVALID_TIMER;
  572. return 0;
  573. }
  574. int bg_afk_timer(int tid, int64 tick, int id, intptr_t data) {
  575. struct s_mapiterator* iter;
  576. struct map_session_data* sd;
  577. int count = 0;
  578. iter = mapit_getallusers();
  579. for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) {
  580. if( !sd->bg_queue.arena || !sd->bg_id )
  581. continue;
  582. if( DIFF_TICK(sockt->last_tick, sd->idletime) > bg->mafksec )
  583. bg->team_leave(sd,BGTL_AFK);
  584. count++;
  585. }
  586. mapit->free(iter);
  587. if( count )
  588. bg->afk_timer_id = timer->add(timer->gettick()+10000,bg->afk_timer,0,0);
  589. else
  590. bg->afk_timer_id = INVALID_TIMER;
  591. return 0;
  592. }
  593. void bg_queue_pregame(struct bg_arena *arena) {
  594. struct script_queue *queue;
  595. int i;
  596. nullpo_retv(arena);
  597. queue = script->queue(arena->queue_id);
  598. for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) {
  599. struct map_session_data *sd = map->id2sd(VECTOR_INDEX(queue->entries, i));
  600. if (sd != NULL) {
  601. clif->bgqueue_battlebegins(sd,arena->id,SELF);
  602. }
  603. }
  604. arena->begin_timer = timer->add( timer->gettick() + (arena->pregame_duration*1000), bg->begin_timer, arena->id, 0 );
  605. }
  606. int bg_fillup_timer(int tid, int64 tick, int id, intptr_t data) {
  607. bg->queue_pregame(bg->arena[id]);
  608. bg->arena[id]->fillup_timer = INVALID_TIMER;
  609. return 0;
  610. }
  611. void bg_queue_check(struct bg_arena *arena) {
  612. int count;
  613. struct script_queue *queue;
  614. nullpo_retv(arena);
  615. queue = script->queue(arena->queue_id);
  616. count = VECTOR_LENGTH(queue->entries);
  617. if( count == arena->max_players ) {
  618. if( arena->fillup_timer != INVALID_TIMER ) {
  619. timer->delete(arena->fillup_timer,bg->fillup_timer);
  620. arena->fillup_timer = INVALID_TIMER;
  621. }
  622. bg->queue_pregame(arena);
  623. } else if( count >= arena->min_players && arena->fillup_timer == INVALID_TIMER ) {
  624. arena->fillup_timer = timer->add( timer->gettick() + (arena->fillup_duration*1000), bg->fillup_timer, arena->id, 0 );
  625. }
  626. }
  627. void bg_queue_add(struct map_session_data *sd, struct bg_arena *arena, enum bg_queue_types type) {
  628. enum BATTLEGROUNDS_QUEUE_ACK result = bg->can_queue(sd,arena,type);
  629. struct script_queue *queue = NULL;
  630. int i, count = 0;
  631. nullpo_retv(sd);
  632. nullpo_retv(arena);
  633. if( arena->begin_timer != INVALID_TIMER || arena->ongoing ) {
  634. clif->bgqueue_ack(sd,BGQA_FAIL_QUEUING_FINISHED,arena->id);
  635. return;
  636. }
  637. if( result != BGQA_SUCCESS ) {
  638. clif->bgqueue_ack(sd,result,arena->id);
  639. return;
  640. }
  641. switch( type ) { /* guild/party already validated in can_queue */
  642. case BGQT_PARTY: {
  643. struct party_data *p = party->search(sd->status.party_id);
  644. for( i = 0; i < MAX_PARTY; i++ ) {
  645. if( !p->data[i].sd || p->data[i].sd->bg_queue.arena != NULL ) continue;
  646. count++;
  647. }
  648. }
  649. break;
  650. case BGQT_GUILD:
  651. for ( i=0; i<sd->guild->max_member; i++ ) {
  652. if ( !sd->guild->member[i].sd || sd->guild->member[i].sd->bg_queue.arena != NULL )
  653. continue;
  654. count++;
  655. }
  656. break;
  657. case BGQT_INDIVIDUAL:
  658. count = 1;
  659. break;
  660. }
  661. if( !(queue = script->queue(arena->queue_id)) || (VECTOR_LENGTH(queue->entries)+count) > arena->max_players ) {
  662. clif->bgqueue_ack(sd,BGQA_FAIL_PPL_OVERAMOUNT,arena->id);
  663. return;
  664. }
  665. switch( type ) {
  666. case BGQT_INDIVIDUAL:
  667. sd->bg_queue.type = type;
  668. sd->bg_queue.arena = arena;
  669. sd->bg_queue.ready = 0;
  670. script->queue_add(arena->queue_id,sd->status.account_id);
  671. clif->bgqueue_joined(sd, VECTOR_LENGTH(queue->entries));
  672. clif->bgqueue_update_info(sd,arena->id, VECTOR_LENGTH(queue->entries));
  673. break;
  674. case BGQT_PARTY: {
  675. struct party_data *p = party->search(sd->status.party_id);
  676. for( i = 0; i < MAX_PARTY; i++ ) {
  677. if( !p->data[i].sd || p->data[i].sd->bg_queue.arena != NULL ) continue;
  678. p->data[i].sd->bg_queue.type = type;
  679. p->data[i].sd->bg_queue.arena = arena;
  680. p->data[i].sd->bg_queue.ready = 0;
  681. script->queue_add(arena->queue_id,p->data[i].sd->status.account_id);
  682. clif->bgqueue_joined(p->data[i].sd, VECTOR_LENGTH(queue->entries));
  683. clif->bgqueue_update_info(p->data[i].sd,arena->id, VECTOR_LENGTH(queue->entries));
  684. }
  685. }
  686. break;
  687. case BGQT_GUILD:
  688. for ( i=0; i<sd->guild->max_member; i++ ) {
  689. if ( !sd->guild->member[i].sd || sd->guild->member[i].sd->bg_queue.arena != NULL )
  690. continue;
  691. sd->guild->member[i].sd->bg_queue.type = type;
  692. sd->guild->member[i].sd->bg_queue.arena = arena;
  693. sd->guild->member[i].sd->bg_queue.ready = 0;
  694. script->queue_add(arena->queue_id,sd->guild->member[i].sd->status.account_id);
  695. clif->bgqueue_joined(sd->guild->member[i].sd, VECTOR_LENGTH(queue->entries));
  696. clif->bgqueue_update_info(sd->guild->member[i].sd,arena->id, VECTOR_LENGTH(queue->entries));
  697. }
  698. break;
  699. }
  700. clif->bgqueue_ack(sd,BGQA_SUCCESS,arena->id);
  701. bg->queue_check(arena);
  702. }
  703. enum BATTLEGROUNDS_QUEUE_ACK bg_canqueue(struct map_session_data *sd, struct bg_arena *arena, enum bg_queue_types type) {
  704. int tick;
  705. unsigned int tsec;
  706. nullpo_retr(BGQA_FAIL_TYPE_INVALID, sd);
  707. nullpo_retr(BGQA_FAIL_TYPE_INVALID, arena);
  708. if( !(arena->allowed_types & type) )
  709. return BGQA_FAIL_TYPE_INVALID;
  710. if ( sd->status.base_level > arena->max_level || sd->status.base_level < arena->min_level )
  711. return BGQA_FAIL_LEVEL_INCORRECT;
  712. if ( !(sd->class_&JOBL_2) ) /* TODO: maybe make this a per-arena setting, so users may make custom arenas like baby-only,whatever. */
  713. return BGQA_FAIL_CLASS_INVALID;
  714. tsec = (unsigned int)time(NULL);
  715. if ( ( tick = pc_readglobalreg(sd, script->add_str(bg->gdelay_var)) ) && tsec < tick ) {
  716. char response[100];
  717. if( (tick-tsec) > 60 )
  718. sprintf(response, "You are a deserter! Wait %d minute(s) before you can apply again",(tick-tsec)/60);
  719. else
  720. sprintf(response, "You are a deserter! Wait %d seconds before you can apply again",(tick-tsec));
  721. clif->messagecolor_self(sd->fd, COLOR_RED, response);
  722. return BGQA_FAIL_DESERTER;
  723. }
  724. if ( ( tick = pc_readglobalreg(sd, script->add_str(arena->delay_var)) ) && tsec < tick ) {
  725. char response[100];
  726. if( (tick-tsec) > 60 )
  727. sprintf(response, "You can't reapply to this arena so fast. Apply to the different arena or wait %d minute(s)",(tick-tsec)/60);
  728. else
  729. sprintf(response, "You can't reapply to this arena so fast. Apply to the different arena or wait %d seconds",(tick-tsec));
  730. clif->messagecolor_self(sd->fd, COLOR_RED, response);
  731. return BGQA_FAIL_COOLDOWN;
  732. }
  733. if( sd->bg_queue.arena != NULL )
  734. return BGQA_DUPLICATE_REQUEST;
  735. switch(type) {
  736. case BGQT_GUILD:
  737. if( !sd->guild || !sd->state.gmaster_flag )
  738. return BGQA_NOT_PARTY_GUILD_LEADER;
  739. else {
  740. int i, count = 0;
  741. for ( i=0; i<sd->guild->max_member; i++ ) {
  742. if ( !sd->guild->member[i].sd || sd->guild->member[i].sd->bg_queue.arena != NULL )
  743. continue;
  744. count++;
  745. }
  746. if ( count < arena->min_team_players ) {
  747. char response[117];
  748. if( count != sd->guild->connect_member && sd->guild->connect_member >= arena->min_team_players )
  749. sprintf(response, "Can't apply: not enough members in your team/guild that have not entered the queue in individual mode, minimum is %d",arena->min_team_players);
  750. else
  751. sprintf(response, "Can't apply: not enough members in your team/guild, minimum is %d",arena->min_team_players);
  752. clif->messagecolor_self(sd->fd, COLOR_RED, response);
  753. return BGQA_FAIL_TEAM_COUNT;
  754. }
  755. }
  756. break;
  757. case BGQT_PARTY:
  758. if( !sd->status.party_id )
  759. return BGQA_NOT_PARTY_GUILD_LEADER;
  760. else {
  761. struct party_data *p;
  762. if( (p = party->search(sd->status.party_id) ) ) {
  763. int i, count = 0;
  764. bool is_leader = false;
  765. for(i = 0; i < MAX_PARTY; i++) {
  766. if( !p->data[i].sd )
  767. continue;
  768. if( p->party.member[i].leader && sd == p->data[i].sd )
  769. is_leader = true;
  770. if( p->data[i].sd->bg_queue.arena == NULL )
  771. count++;
  772. }
  773. if( !is_leader )
  774. return BGQA_NOT_PARTY_GUILD_LEADER;
  775. if( count < arena->min_team_players ) {
  776. char response[117];
  777. if( count != p->party.count && p->party.count >= arena->min_team_players )
  778. sprintf(response, "Can't apply: not enough members in your team/party that have not entered the queue in individual mode, minimum is %d",arena->min_team_players);
  779. else
  780. sprintf(response, "Can't apply: not enough members in your team/party, minimum is %d",arena->min_team_players);
  781. clif->messagecolor_self(sd->fd, COLOR_RED, response);
  782. return BGQA_FAIL_TEAM_COUNT;
  783. }
  784. } else
  785. return BGQA_NOT_PARTY_GUILD_LEADER;
  786. }
  787. break;
  788. case BGQT_INDIVIDUAL:/* already did */
  789. break;
  790. default:
  791. ShowDebug("bg_canqueue: unknown/unsupported type %d\n",type);
  792. return BGQA_DUPLICATE_REQUEST;
  793. }
  794. return BGQA_SUCCESS;
  795. }
  796. void do_init_battleground(bool minimal) {
  797. if (minimal)
  798. return;
  799. bg->team_db = idb_alloc(DB_OPT_RELEASE_DATA);
  800. timer->add_func_list(bg->send_xy_timer, "bg_send_xy_timer");
  801. timer->add_interval(timer->gettick() + battle_config.bg_update_interval, bg->send_xy_timer, 0, 0, battle_config.bg_update_interval);
  802. bg->config_read();
  803. }
  804. /**
  805. * @see DBApply
  806. */
  807. int bg_team_db_final(DBKey key, DBData *data, va_list ap) {
  808. struct battleground_data* bgd = DB->data2ptr(data);
  809. HPM->data_store_destroy(&bgd->hdata);
  810. return 0;
  811. }
  812. void do_final_battleground(void)
  813. {
  814. bg->team_db->destroy(bg->team_db,bg->team_db_final);
  815. if (bg->arena) {
  816. int i;
  817. for (i = 0; i < bg->arenas; i++) {
  818. if (bg->arena[i])
  819. aFree(bg->arena[i]);
  820. }
  821. aFree(bg->arena);
  822. }
  823. }
  824. void battleground_defaults(void) {
  825. bg = &bg_s;
  826. bg->queue_on = false;
  827. bg->mafksec = 0;
  828. bg->afk_timer_id = INVALID_TIMER;
  829. bg->arena = NULL;
  830. bg->arenas = 0;
  831. /* */
  832. bg->team_db = NULL;
  833. bg->team_counter = 0;
  834. /* */
  835. bg->init = do_init_battleground;
  836. bg->final = do_final_battleground;
  837. /* */
  838. bg->name2arena = bg_name2arena;
  839. bg->queue_add = bg_queue_add;
  840. bg->can_queue = bg_canqueue;
  841. bg->id2pos = bg_id2pos;
  842. bg->queue_pc_cleanup = bg_queue_player_cleanup;
  843. bg->begin = bg_begin;
  844. bg->begin_timer = bg_begin_timer;
  845. bg->queue_pregame = bg_queue_pregame;
  846. bg->fillup_timer = bg_fillup_timer;
  847. bg->queue_ready_ack = bg_queue_ready_ack;
  848. bg->match_over = bg_match_over;
  849. bg->queue_check = bg_queue_check;
  850. bg->team_search = bg_team_search;
  851. bg->getavailablesd = bg_getavailablesd;
  852. bg->team_delete = bg_team_delete;
  853. bg->team_warp = bg_team_warp;
  854. bg->send_dot_remove = bg_send_dot_remove;
  855. bg->team_join = bg_team_join;
  856. bg->team_leave = bg_team_leave;
  857. bg->member_respawn = bg_member_respawn;
  858. bg->create = bg_create;
  859. bg->team_get_id = bg_team_get_id;
  860. bg->send_message = bg_send_message;
  861. bg->send_xy_timer_sub = bg_send_xy_timer_sub;
  862. bg->send_xy_timer = bg_send_xy_timer;
  863. bg->afk_timer = bg_afk_timer;
  864. bg->team_db_final = bg_team_db_final;
  865. /* */
  866. bg->str2teamtype = bg_str2teamtype;
  867. /* */
  868. bg->config_read = bg_config_read;
  869. }