/** * Sends a message to a channel. * * @param chan The destination channel. * @param sd The source character. * @param msg The message to send. * * If no source character is specified, it'll send an anonymous message. */ void channel_send(struct channel_data *chan, struct map_session_data *sd, const char *msg) { char message[150]; nullpo_retv(chan); nullpo_retv(msg); if (sd && chan->msg_delay != 0 && DIFF_TICK(sd->hchsysch_tick + chan->msg_delay*1000, timer->gettick()) > 0 && !pc_has_permission(sd, PC_PERM_HCHSYS_ADMIN)) { clif->messagecolor_self(sd->fd, COLOR_RED, msg_sd(sd,1455)); return; } else if (sd) { safesnprintf(message, 150, "[ #%s ] %s : %s", chan->name, sd->status.name, msg); clif->channel_msg(chan,sd,message); if (chan->type == HCS_TYPE_IRC) ircbot->relay(sd->status.name,msg); if (chan->msg_delay != 0) sd->hchsysch_tick = timer->gettick(); } else { safesnprintf(message, 150, "[ #%s ] %s", chan->name, msg); clif->channel_msg2(chan, message); if (chan->type == HCS_TYPE_IRC) ircbot->relay(NULL, msg); } }
/*========================================== * Pet Attack Skill [Skotlex] *------------------------------------------*/ int pet_attackskill(struct pet_data *pd, int target_id) { struct block_list *bl; int inf; if (!battle_config.pet_status_support || !pd->a_skill || (battle_config.pet_equip_required && !pd->pet.equip)) return 0; if (DIFF_TICK(pd->ud.canact_tick, gettick()) > 0) return 0; if (rand()%100 < (pd->a_skill->rate +pd->pet.intimate*pd->a_skill->bonusrate/1000)) { //Skotlex: Use pet's skill bl=map_id2bl(target_id); if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL || status_isdead(bl) || !check_distance_bl(&pd->bl, bl, pd->db->range3)) return 0; inf = skill_get_inf(pd->a_skill->id); if (inf & INF_GROUND_SKILL) unit_skilluse_pos(&pd->bl, bl->x, bl->y, pd->a_skill->id, pd->a_skill->lv); else //Offensive self skill? Could be stuff like GX. unit_skilluse_id(&pd->bl,(inf&INF_SELF_SKILL?pd->bl.id:bl->id), pd->a_skill->id, pd->a_skill->lv); return 1; //Skill invoked. } return 0; }
/*====================================== * CORE : Timer Heap *-------------------------------------- */ static void push_timer_heap(int index) { int i,h; if(timer_heap==NULL || timer_heap[0]+1>=timer_heap_max){ int first = timer_heap==NULL; timer_heap_max += 256; timer_heap = realloc(timer_heap,sizeof(int)*timer_heap_max); if(timer_heap==NULL){ printf("out of memory : push_timer_heap\n"); exit(1); } if(first) timer_heap[0]=0; } timer_heap[0]++; for(h=timer_heap[0]-1,i=(h-1)/2; h>0 && DIFF_TICK(timer_data[index].tick , timer_data[timer_heap[i+1]].tick)<0; i=(h-1)/2) { timer_heap[h+1]=timer_heap[i+1],h=i; } timer_heap[h+1]=index; }
int chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex] #ifdef ENABLE_SC_SAVING int i, count=0; unsigned int tick; struct status_change_data data; struct status_change *sc = &sd->sc; const struct TimerData *timer; chrif_check(-1); tick = gettick(); WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data)); WFIFOW(char_fd,0) = 0x2b1c; WFIFOL(char_fd,4) = sd->status.account_id; WFIFOL(char_fd,8) = sd->status.char_id; for (i = 0; i < SC_MAX; i++) { if (!sc->data[i]) continue; if (sc->data[i]->timer != INVALID_TIMER) { timer = get_timer(sc->data[i]->timer); if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0) continue; data.tick = DIFF_TICK(timer->tick,tick); //Duration that is left before ending. } else data.tick = -1; //Infinite duration data.type = i; data.val1 = sc->data[i]->val1; data.val2 = sc->data[i]->val2; data.val3 = sc->data[i]->val3; data.val4 = sc->data[i]->val4; memcpy(WFIFOP(char_fd,14 +count*sizeof(struct status_change_data)), &data, sizeof(struct status_change_data)); count++; } if (count == 0) return 0; //Nothing to save. WFIFOW(char_fd,12) = count; WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size WFIFOSET(char_fd,WFIFOW(char_fd,2)); #endif return 0; }
/** * Get current Mercenary lifetime * @param md The Mercenary * @return The Lifetime **/ int mercenary_get_lifetime(struct mercenary_data *md) { const struct TimerData * td; if( md == NULL || md->contract_timer == INVALID_TIMER ) return 0; td = get_timer(md->contract_timer); return (td != NULL) ? DIFF_TICK(td->tick, gettick()) : 0; }
int elemental_get_lifetime(struct elemental_data *ed) { const struct TimerData * td; if( ed == NULL || ed->summon_timer == INVALID_TIMER ) return 0; td = get_timer(ed->summon_timer); return (td != NULL) ? DIFF_TICK(td->tick, gettick()) : 0; }
int bg_send_xy_timer_sub(DBKey key, void *data, va_list ap) { struct battleground_data *bg = (struct battleground_data *)data; struct map_session_data *sd; char output[128]; int i, m; nullpo_ret(bg); m = map_mapindex2mapid(bg->mapindex); bg->reveal_flag = !bg->reveal_flag; // Switch for( i = 0; i < MAX_BG_MEMBERS; i++ ) { if( (sd = bg->members[i].sd) == NULL ) continue; if( battle_config.bg_idle_autokick && DIFF_TICK(last_tick, sd->idletime) >= battle_config.bg_idle_autokick && bg->g ) { sprintf(output, "- AFK [%s] Kicked -", sd->status.name); clif_broadcast2(&sd->bl, output, (int)strlen(output)+1, bg->color, 0x190, 20, 0, 0, BG); bg_team_leave(sd,3); clif_displaymessage(sd->fd, "You have been kicked from Battleground because of your AFK status."); pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); continue; } if( sd->bl.x != bg->members[i].x || sd->bl.y != bg->members[i].y ) { // xy update bg->members[i].x = sd->bl.x; bg->members[i].y = sd->bl.y; clif_bg_xy(sd); } if( bg->reveal_pos && bg->reveal_flag && sd->bl.m == m ) // Reveal each 4 seconds map_foreachinmap(bg_reveal_pos,m,BL_PC,sd,1,bg->color); if( battle_config.bg_idle_announce && !sd->state.bg_afk && DIFF_TICK(last_tick, sd->idletime) >= battle_config.bg_idle_announce && bg->g ) { // Idle announces sd->state.bg_afk = 1; sprintf(output, "%s : %s seens to be away. AFK Warning - Can be kicked out with @reportafk", bg->g->name, sd->status.name); clif_bg_message(bg, bg->bg_id, bg->g->name, output, strlen(output) + 1); } } return 0; }
// Existence check of WISP data int check_ttl_wisdata_sub(void *key, void *data, va_list ap) { unsigned long tick; struct WisData *wd = (struct WisData *)data; tick = va_arg(ap, unsigned long); if (DIFF_TICK(tick, wd->tick) > WISDATA_TTL && wis_delnum < WISDELLIST_MAX) wis_dellist[wis_delnum++] = wd->id; return 0; }
/** * Existence check of WISP data * @see DBApply */ int check_ttl_wisdata_sub(DBKey key, DBData *data, va_list ap) { int64 tick; struct WisData *wd = DB->data2ptr(data); tick = va_arg(ap, int64); if (DIFF_TICK(tick, wd->tick) > WISDATA_TTL && wis_delnum < WISDELLIST_MAX) wis_dellist[wis_delnum++] = wd->id; return 0; }
int auth_db_cleanup_sub(DBKey key,void *data,va_list ap) { struct auth_node *node=(struct auth_node*)data; if(DIFF_TICK(gettick(),node->node_created)>30000) { ShowNotice("Character (aid: %d) not authed within 30 seconds of character select!\n", node->account_id); if (node->char_dat) aFree(node->char_dat); db_remove(auth_db, key); return 1; } return 0; }
// Wisリストの生存チェック int check_ttl_wislist() { unsigned long tick=gettick(); struct WisList* p=wis_list, **prev=&wis_list; for( ; p; prev=&p->next,p=p->next ){ if( DIFF_TICK(tick,p->tick)>WISLIST_TTL ){ *prev=p->next; free(p); p=*prev; } } return 0; }
void bg_block_skill_status(struct battleground_data *bg, int skillnum) { const struct TimerData * td; char output[128]; int seconds, idx; idx = battle_config.guild_skills_separed_delay ? skillnum - GD_SKILLBASE : 0; if( bg == NULL || bg->g == NULL || idx < 0 || idx >= MAX_GUILDSKILL || bg->skill_block_timer[idx] == INVALID_TIMER ) return; if( (td = get_timer(bg->skill_block_timer[idx])) == NULL ) return; seconds = DIFF_TICK(td->tick,gettick())/1000; sprintf(output, "%s : Cannot use team skill %s. %d seconds remaining...", bg->g->name, skill_get_desc(skillnum), seconds); clif_bg_message(bg, bg->bg_id, bg->g->name, output, strlen(output) + 1); }
/** * Attempt to send mail * @param sd Sender * @param dest_name Destination name * @param title Mail title * @param body_msg Mail message * @param body_len Message's length */ void mail_send(struct map_session_data *sd, const char *dest_name, const char *title, const char *body_msg, int body_len) { struct mail_message msg; nullpo_retv(sd); if( sd->state.trading ) return; if( DIFF_TICK(sd->cansendmail_tick, gettick()) > 0 ) { clif_displaymessage(sd->fd,msg_txt(sd,675)); //"Cannot send mails too fast!!." clif_Mail_send(sd->fd, true); // fail return; } if( body_len > MAIL_BODY_LENGTH ) body_len = MAIL_BODY_LENGTH; if( !mail_setattachment(sd, &msg) ) { // Invalid Append condition clif_Mail_send(sd->fd, true); // fail mail_removeitem(sd,0); mail_removezeny(sd,0); return; } msg.id = 0; // id will be assigned by charserver msg.send_id = sd->status.char_id; msg.dest_id = 0; // will attempt to resolve name safestrncpy(msg.send_name, sd->status.name, NAME_LENGTH); safestrncpy(msg.dest_name, (char*)dest_name, NAME_LENGTH); safestrncpy(msg.title, (char*)title, MAIL_TITLE_LENGTH); if (msg.title[0] == '\0') { return; // Message has no length and somehow client verification was skipped. } if (body_len) safestrncpy(msg.body, (char*)body_msg, body_len + 1); else memset(msg.body, 0x00, MAIL_BODY_LENGTH); msg.timestamp = time(NULL); if( !intif_Mail_send(sd->status.account_id, &msg) ) mail_deliveryfail(sd, &msg); sd->cansendmail_tick = gettick() + battle_config.mail_delay; // Flood Protection }
/** * Format message from player to send to the channel * - Also truncate extra characters if message is too long * @param channel: Channel data * @param sd: Player data * @param msg: Message to send * @return * 0: Success * -1: Invalid player, channel, or message * -2: Delay message from last message */ int channel_send(struct Channel *channel, struct map_session_data *sd, const char *msg) { int idx = 0; if(!channel || !sd || !msg || (idx = channel_pc_haschan(sd, channel)) < 0) return -1; if(!pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) && channel->msg_delay != 0 && DIFF_TICK(sd->channel_tick[idx] + channel->msg_delay, gettick()) > 0) { clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,1455),false,SELF); //You're talking too fast! return -2; } else { char output[CHAT_SIZE_MAX]; unsigned long color = channel->color; if((channel->opt&CHAN_OPT_COLOR_OVERRIDE) && sd->fontcolor && sd->fontcolor < channel_config.colors_count && channel_config.colors[sd->fontcolor]) color = channel_config.colors[sd->fontcolor]; safesnprintf(output, CHAT_SIZE_MAX, "%s %s : %s", channel->alias, sd->status.name, msg); clif_channel_msg(channel,output,color); sd->channel_tick[idx] = gettick(); } return 0; }
/** * This can still happen (client times out while waiting for char to confirm auth data) * @see DBApply */ int auth_db_cleanup_sub(DBKey key, DBData *data, va_list ap) { struct auth_node *node = DB->data2ptr(data); const char* states[] = { "Login", "Logout", "Map change" }; if(DIFF_TICK(gettick(),node->node_created)>60000) { switch (node->state) { case ST_LOGOUT: //Re-save attempt (->sd should never be null here). node->node_created = gettick(); //Refresh tick (avoid char-server load if connection is really bad) chrif_save(node->sd, 1); break; default: //Clear data. any connected players should have timed out by now. ShowInfo("auth_db: Node (state %s) timed out for %d:%d\n", states[node->state], node->account_id, node->char_id); chrif_char_offline_nsd(node->account_id, node->char_id); chrif_auth_delete(node->account_id, node->char_id, node->state); break; } return 1; } return 0; }
/*=============================================================== * Action that elemental perform after changing mode. * Activates one of the skills of the new mode. *-------------------------------------------------------------*/ int elemental_change_mode_ack(struct elemental_data *ed, int mode) { struct block_list *bl = &ed->master->bl; short skillnum, skilllv; int i; nullpo_ret(ed); if( !bl ) return 0; // Select a skill. ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&mode)); if( i == MAX_ELESKILLTREE ) return 0; skillnum = ed->db->skill[i].id; skilllv = ed->db->skill[i].lv; if( elemental_skillnotok(skillnum, ed) ) return 0; if( ed->ud.skilltimer != -1 ) return 0; else if( DIFF_TICK(gettick(), ed->ud.canact_tick) < 0 ) return 0; ed->target_id = bl->id; // Set new target ed->last_thinktime = gettick(); if( skill_get_inf(skillnum) & INF_GROUND_SKILL ) unit_skilluse_pos(&ed->bl, bl->x, bl->y, skillnum, skilllv); else unit_skilluse_id(&ed->bl,bl->id,skillnum,skilllv); ed->target_id = 0; // Reset target after casting the skill to avoid continious attack. return 1; }
//This can still happen (client times out while waiting for char to confirm auth data) int auth_db_cleanup_sub(DBKey key,void *data,va_list ap) { struct auth_node *node=(struct auth_node*)data; const char* states[] = { "Iniciar Sessao", "Encerrar Sessao", "Mudanca de Mapa" }; if(DIFF_TICK(gettick(),node->node_created)>60000) { switch (node->state) { case ST_LOGOUT: //Re-save attempt (->sd should never be null here). node->node_created = gettick(); //Refresh tick (avoid char-server load if connection is really bad) chrif_save(node->sd, 1); break; default: //Clear data. any connected players should have timed out by now. ShowInfo("auth_db: No (estado %s) tempo esgotado para %d:%d\n", states[node->state], node->account_id, node->char_id); chrif_char_offline_nsd(node->account_id, node->char_id); chrif_auth_delete(node->account_id, node->char_id, node->state); break; } return 1; } return 0; }
int bg_team_join(int bg_id, struct map_session_data *sd) { // Player joins team int i; struct battleground_data *bg = bg_team_search(bg_id); struct map_session_data *pl_sd; if( bg == NULL || sd == NULL || sd->bg_id ) return 0; ARR_FIND(0, MAX_BG_MEMBERS, i, bg->members[i].sd == NULL); if( i == MAX_BG_MEMBERS ) return 0; // No free slots pc_update_last_action(sd,0); // Start count from here... sd->bg_id = bg_id; sd->bg_kills = 0; sd->state.bg_afk = 0; bg->members[i].sd = sd; bg->members[i].x = sd->bl.x; bg->members[i].y = sd->bl.y; bg->count++; if( bg->creation_tick == 0 ) bg->creation_tick = last_tick; // Creation Tick = First member joined. if( bg->leader_char_id == 0 ) { // First Join = Team Leader bg->leader_char_id = sd->status.char_id; sd->bmaster_flag = bg; } if( battle_config.bg_ranked_mode && sd->status.bgstats.rank_games < battle_config.bg_ranked_max_games && DIFF_TICK(last_tick,bg->creation_tick) < 60 ) { char output[128]; bg->members[i].ranked = true; sd->status.bgstats.rank_games++; sprintf(output,"-- RANKED BATTLEGROUND MATCH %d OF %d --", sd->status.bgstats.rank_games, battle_config.bg_ranked_max_games); clif_displaymessage(sd->fd,output); } guild_send_dot_remove(sd); if( battle_config.bg_eAmod_mode ) { clif_bg_belonginfo(sd); clif_charnameupdate(sd); } for( i = 0; i < MAX_BG_MEMBERS; i++ ) { if( (pl_sd = bg->members[i].sd) == NULL ) continue; if( battle_config.bg_eAmod_mode ) { // Simulate Guild Information clif_guild_basicinfo(pl_sd); clif_bg_emblem(pl_sd, bg->g); clif_bg_memberlist(pl_sd); } if( pl_sd != sd ) clif_bg_hp_single(sd->fd,pl_sd); } if( battle_config.bg_eAmod_mode ) clif_guild_emblem_area(&sd->bl); clif_bg_hp(sd); clif_bg_xy(sd); return 1; }
/*========================================== * *------------------------------------------*/ int chrif_parse(int fd) { int packet_len, cmd; // only process data from the char-server if ( fd != char_fd ) { ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd); do_close(fd); return 0; } if ( session[fd]->flag.eof ) { do_close(fd); char_fd = -1; chrif_on_disconnect(); return 0; } else if ( session[fd]->flag.ping ) {/* we've reached stall time */ if( DIFF_TICK(last_tick, session[fd]->rdata_tick) > (stall_time * 2) ) {/* we can't wait any longer */ set_eof(fd); return 0; } else if( session[fd]->flag.ping != 2 ) { /* we haven't sent ping out yet */ chrif_keepalive(fd); session[fd]->flag.ping = 2; } } while ( RFIFOREST(fd) >= 2 ) { cmd = RFIFOW(fd,0); if (cmd < 0x2af8 || cmd >= 0x2af8 + ARRAYLENGTH(packet_len_table) || packet_len_table[cmd-0x2af8] == 0) { int r = intif_parse(fd); // Passed on to the intif if (r == 1) continue; // Treated in intif if (r == 2) return 0; // Didn't have enough data (len==-1) ShowWarning("chrif_parse: session #%d, intif_parse failed (unrecognized command 0x%.4x).\n", fd, cmd); set_eof(fd); return 0; } if ( ( packet_len = packet_len_table[cmd-0x2af8] ) == -1) { // dynamic-length packet, second WORD holds the length if (RFIFOREST(fd) < 4) return 0; packet_len = RFIFOW(fd,2); } if ((int)RFIFOREST(fd) < packet_len) return 0; //ShowDebug("Received packet 0x%4x (%d bytes) from char-server (connection %d)\n", RFIFOW(fd,0), packet_len, fd); switch(cmd) { case 0x2af9: chrif_connectack(fd); break; case 0x2afb: chrif_sendmapack(fd); break; case 0x2afd: chrif_authok(fd); break; case 0x2b00: map_setusers(RFIFOL(fd,2)); chrif_keepalive(fd); break; case 0x2b03: clif->charselectok(RFIFOL(fd,2), RFIFOB(fd,6)); break; case 0x2b04: chrif_recvmap(fd); break; case 0x2b06: chrif_changemapserverack(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOW(fd,18), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); break; case 0x2b09: map_addnickdb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break; case 0x2b0a: socket_datasync(fd, false); break; case 0x2b0d: chrif_changedsex(fd); break; case 0x2b0f: chrif_char_ask_name_answer(RFIFOL(fd,2), (char*)RFIFOP(fd,6), RFIFOW(fd,30), RFIFOW(fd,32)); break; case 0x2b12: chrif_divorceack(RFIFOL(fd,2), RFIFOL(fd,6)); break; case 0x2b14: chrif_accountban(fd); break; case 0x2b1b: chrif_recvfamelist(fd); break; case 0x2b1d: chrif_load_scdata(fd); break; case 0x2b1e: chrif_update_ip(fd); break; case 0x2b1f: chrif_disconnectplayer(fd); break; case 0x2b20: chrif_removemap(fd); break; case 0x2b21: chrif_save_ack(fd); break; case 0x2b22: chrif_updatefamelist_ack(fd); break; case 0x2b24: chrif_keepalive_ack(fd); break; case 0x2b25: chrif_deadopt(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; case 0x2b27: chrif_authfail(fd); break; default: ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd); set_eof(fd); return 0; } if ( fd == char_fd ) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex] RFIFOSKIP(fd, packet_len); } return 0; }
static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_data *sd, unsigned int tick) { struct block_list *target = NULL; int master_dist, view_range, mode; nullpo_ret(ed); nullpo_ret(sd); if( ed->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL ) return 0; if( DIFF_TICK(tick,ed->last_thinktime) < MIN_ELETHINKTIME ) return 0; ed->last_thinktime = tick; if( ed->ud.skilltimer != -1 ) return 0; if( ed->ud.walktimer != -1 && ed->ud.walkpath.path_pos <= 2 ) return 0; //No thinking when you just started to walk. if(ed->ud.walkpath.path_pos < ed->ud.walkpath.path_len && ed->ud.target == sd->bl.id) return 0; //No thinking until be near the master. if( ed->sc.count && ed->sc.data[SC_BLIND] ) view_range = 3; else view_range = ed->db->range2; mode = status_get_mode(&ed->bl); master_dist = distance_bl(&sd->bl, &ed->bl); if( master_dist > AREA_SIZE ) { // Master out of vision range. elemental_unlocktarget(ed); unit_warp(&ed->bl,sd->bl.m,sd->bl.x,sd->bl.y,3); return 0; } else if( master_dist > MAX_ELEDISTANCE ) { // Master too far, chase. short x = sd->bl.x, y = sd->bl.y; if( ed->target_id ) elemental_unlocktarget(ed); if( ed->ud.walktimer != -1 && ed->ud.target == sd->bl.id ) return 0; //Already walking to him if( DIFF_TICK(tick, ed->ud.canmove_tick) < 0 ) return 0; //Can't move yet. if( map_search_freecell(&ed->bl, sd->bl.m, &x, &y, MIN_ELEDISTANCE, MIN_ELEDISTANCE, 1) && unit_walktoxy(&ed->bl, x, y, 0) ) return 0; } if( mode == EL_MODE_AGGRESSIVE ) { target = map_id2bl(ed->ud.target); if( !target ) map_foreachinrange(elemental_ai_sub_timer_activesearch, &ed->bl, ed->db->range2, BL_CHAR, ed, &target, status_get_mode(&ed->bl)); if( !target ) { //No targets available. elemental_unlocktarget(ed); return 1; } if( battle_check_range(&ed->bl,target,ed->db->range2) && rand()%100 < 2 ) // 2% chance to cast attack skill. { if( elemental_action(ed,target,tick) ) return 1; } //Attempt to attack. //At this point we know the target is attackable, we just gotta check if the range matches. if( ed->ud.target == target->id && ed->ud.attacktimer != -1 ) //Already locked. return 1; if( battle_check_range(&ed->bl, target, ed->base_status.rhw.range) ) { //Target within range, engage unit_attack(&ed->bl,target->id,1); return 1; } //Follow up if possible. if( !unit_walktobl(&ed->bl, target, ed->base_status.rhw.range, 2) ) elemental_unlocktarget(ed); } return 0; }
int elemental_action(struct elemental_data *ed, struct block_list *bl, unsigned int tick) { short skillnum, skilllv; int i; nullpo_ret(ed); nullpo_ret(bl); if( !ed->master ) return 0; if( ed->target_id ) elemental_unlocktarget(ed); // Remove previous target. ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&EL_SKILLMODE_AGGRESSIVE)); if( i == MAX_ELESKILLTREE ) return 0; skillnum = ed->db->skill[i].id; skilllv = ed->db->skill[i].lv; if( elemental_skillnotok(skillnum, ed) ) return 0; if( ed->ud.skilltimer != -1 ) return 0; else if( DIFF_TICK(tick, ed->ud.canact_tick) < 0 ) return 0; ed->target_id = ed->ud.skilltarget = bl->id; // Set new target ed->last_thinktime = tick; // Not in skill range. if( !battle_check_range(&ed->bl,bl,skill_get_range(skillnum,skilllv)) ) { // Try to walk to the target. if( !unit_walktobl(&ed->bl, bl, skill_get_range(skillnum,skilllv), 2) ) elemental_unlocktarget(ed); else { // Walking, waiting to be in range. Client don't handle it, then we must handle it here. int walk_dist = distance_bl(&ed->bl,bl) - skill_get_range(skillnum,skilllv); ed->ud.skillid = skillnum; ed->ud.skilllv = skilllv; if( skill_get_inf(skillnum) & INF_GROUND_SKILL ) ed->ud.skilltimer = add_timer( tick+status_get_speed(&ed->bl)*walk_dist, skill_castend_pos, ed->bl.id, 0 ); else ed->ud.skilltimer = add_timer( tick+status_get_speed(&ed->bl)*walk_dist, skill_castend_id, ed->bl.id, 0 ); } return 1; } //Otherwise, just cast the skill. if( skill_get_inf(skillnum) & INF_GROUND_SKILL ) unit_skilluse_pos(&ed->bl, bl->x, bl->y, skillnum, skilllv); else unit_skilluse_id(&ed->bl, bl->id, skillnum, skilllv); // Reset target. ed->target_id = 0; return 1; }
int chlogif_parse(int fd) { struct char_session_data* sd = NULL; // only process data from the login-server if( fd != login_fd ) { ShowDebug("parse_fromlogin: Disconnecting invalid session #%d (is not the login-server)\n", fd); do_close(fd); return 0; } if( session[fd]->flag.eof ) { do_close(fd); login_fd = -1; chlogif_on_disconnect(); return 0; } else if ( session[fd]->flag.ping ) {/* we've reached stall time */ if( DIFF_TICK(last_tick, session[fd]->rdata_tick) > (stall_time * 2) ) {/* we can't wait any longer */ set_eof(fd); return 0; } else if( session[fd]->flag.ping != 2 ) { /* we haven't sent ping out yet */ WFIFOHEAD(fd,2);// sends a ping packet to login server (will receive pong 0x2718) WFIFOW(fd,0) = 0x2719; WFIFOSET(fd,2); session[fd]->flag.ping = 2; } } sd = (struct char_session_data*)session[fd]->session_data; while(RFIFOREST(fd) >= 2) { int next=1; uint16 command = RFIFOW(fd,0); switch( command ) { case 0x2741: next = chlogif_parse_BankingAck(fd); break; case 0x2743: next = chlogif_parse_vipack(fd); break; // acknowledgement of connect-to-loginserver request case 0x2711: next = chlogif_parse_ackconnect(fd,sd); break; // acknowledgement of account authentication request case 0x2713: next = chlogif_parse_ackaccreq(fd, sd); break; // account data case 0x2717: next = chlogif_parse_reqaccdata(fd, sd); break; // login-server alive packet case 0x2718: next = chlogif_parse_keepalive(fd, sd); break; // changesex reply case 0x2723: next = chlogif_parse_ackchangesex(fd, sd); break; // reply to an account_reg2 registry request case 0x2729: next = chlogif_parse_ackacc2req(fd, sd); break; // State change of account/ban notification (from login-server) case 0x2731: next = chlogif_parse_accbannotification(fd, sd); break; // Login server request to kick a character out. [Skotlex] case 0x2734: next = chlogif_parse_askkick(fd,sd); break; // ip address update signal from login server case 0x2735: next = chlogif_parse_updip(fd,sd); break; // @accinfo result case 0x2721: next = chlogif_parse_AccInfoAck(fd); break; default: ShowError("Unknown packet 0x%04x received from login-server, disconnecting.\n", command); set_eof(fd); return 0; } if(next==0) return 0; //do not parse next data } RFIFOFLUSH(fd); return 0; }
int elemental_action(struct elemental_data *ed, struct block_list *bl, unsigned int tick) { struct skill_condition req; uint16 skill_id, skill_lv; int i; nullpo_ret(ed); nullpo_ret(bl); if( !ed->master ) return 0; if( ed->target_id ) elemental_unlocktarget(ed); // Remove previous target. ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&EL_SKILLMODE_AGGRESSIVE)); if( i == MAX_ELESKILLTREE ) return 0; skill_id = ed->db->skill[i].id; skill_lv = ed->db->skill[i].lv; if( elemental_skillnotok(skill_id, ed) ) return 0; if( ed->ud.skilltimer != INVALID_TIMER ) return 0; else if( DIFF_TICK(tick, ed->ud.canact_tick) < 0 ) return 0; ed->target_id = ed->ud.skilltarget = bl->id; // Set new target ed->last_thinktime = tick; // Not in skill range. if( !battle_check_range(&ed->bl,bl,skill_get_range(skill_id,skill_lv)) ) { // Try to walk to the target. if( !unit_walktobl(&ed->bl, bl, skill_get_range(skill_id,skill_lv), 2) ) elemental_unlocktarget(ed); else { // Walking, waiting to be in range. Client don't handle it, then we must handle it here. int walk_dist = distance_bl(&ed->bl,bl) - skill_get_range(skill_id,skill_lv); ed->ud.skill_id = skill_id; ed->ud.skill_lv = skill_lv; if( skill_get_inf(skill_id) & INF_GROUND_SKILL ) ed->ud.skilltimer = add_timer( tick+status_get_speed(&ed->bl)*walk_dist, skill_castend_pos, ed->bl.id, 0 ); else ed->ud.skilltimer = add_timer( tick+status_get_speed(&ed->bl)*walk_dist, skill_castend_id, ed->bl.id, 0 ); } return 1; } req = elemental_skill_get_requirements(skill_id, skill_lv); if(req.hp || req.sp){ struct map_session_data *sd = BL_CAST(BL_PC, battle_get_master(&ed->bl)); if( sd ){ if( sd->skill_id_old != SO_EL_ACTION && //regardless of remaining HP/SP it can be cast (status_get_hp(&ed->bl) < req.hp || status_get_sp(&ed->bl) < req.sp) ) return 1; else status_zap(&ed->bl, req.hp, req.sp); } } //Otherwise, just cast the skill. if( skill_get_inf(skill_id) & INF_GROUND_SKILL ) unit_skilluse_pos(&ed->bl, bl->x, bl->y, skill_id, skill_lv); else unit_skilluse_id(&ed->bl, bl->id, skill_id, skill_lv); // Reset target. ed->target_id = 0; return 1; }
/** * Attempt to send mail * @param sd Sender * @param dest_name Destination name * @param title Mail title * @param body_msg Mail message * @param body_len Message's length */ void mail_send(struct map_session_data *sd, const char *dest_name, const char *title, const char *body_msg, int body_len) { struct mail_message msg; nullpo_retv(sd); if( sd->state.trading ) return; if( DIFF_TICK(sd->cansendmail_tick, gettick()) > 0 ) { clif_displaymessage(sd->fd,msg_txt(sd,675)); //"Cannot send mails too fast!!." clif_Mail_send(sd, WRITE_MAIL_FAILED); // fail return; } if( battle_config.mail_daily_count ){ mail_refresh_remaining_amount(sd); // After calling mail_refresh_remaining_amount the status should always be there if( sd->sc.data[SC_DAILYSENDMAILCNT] == NULL || sd->sc.data[SC_DAILYSENDMAILCNT]->val2 >= battle_config.mail_daily_count ){ clif_Mail_send(sd, WRITE_MAIL_FAILED_CNT); return; }else{ sc_start2( &sd->bl, &sd->bl, SC_DAILYSENDMAILCNT, 100, date_get_dayofyear(), sd->sc.data[SC_DAILYSENDMAILCNT]->val2 + 1, -1 ); } } if( body_len > MAIL_BODY_LENGTH ) body_len = MAIL_BODY_LENGTH; if( !mail_setattachment(sd, &msg) ) { // Invalid Append condition int i; clif_Mail_send(sd, WRITE_MAIL_FAILED); // fail for( i = 0; i < MAIL_MAX_ITEM; i++ ){ mail_removeitem(sd,0,sd->mail.item[i].index + 2, sd->mail.item[i].amount); } mail_removezeny(sd,false); return; } msg.id = 0; // id will be assigned by charserver msg.send_id = sd->status.char_id; msg.dest_id = 0; // will attempt to resolve name safestrncpy(msg.send_name, sd->status.name, NAME_LENGTH); safestrncpy(msg.dest_name, (char*)dest_name, NAME_LENGTH); safestrncpy(msg.title, (char*)title, MAIL_TITLE_LENGTH); msg.type = MAIL_INBOX_NORMAL; if (msg.title[0] == '\0') { return; // Message has no length and somehow client verification was skipped. } if (body_len) safestrncpy(msg.body, (char*)body_msg, body_len + 1); else memset(msg.body, 0x00, MAIL_BODY_LENGTH); msg.timestamp = time(NULL); if( !intif_Mail_send(sd->status.account_id, &msg) ) mail_deliveryfail(sd, &msg); sd->cansendmail_tick = gettick() + battle_config.mail_delay; // Flood Protection }
static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_data *sd, unsigned int tick) { struct block_list *target = NULL; int master_dist, view_range, mode; nullpo_ret(ed); nullpo_ret(sd); if( ed->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL ) return 0; // Check if caster can sustain the summoned elemental if( DIFF_TICK(tick,ed->last_spdrain_time) >= 10000 ){// Drain SP every 10 seconds int sp = 5; switch(ed->vd->class_){ case ELEMENTALID_AGNI_M: case ELEMENTALID_AQUA_M: case ELEMENTALID_VENTUS_M: case ELEMENTALID_TERA_M: sp = 8; break; case ELEMENTALID_AGNI_L: case ELEMENTALID_AQUA_L: case ELEMENTALID_VENTUS_L: case ELEMENTALID_TERA_L: sp = 11; break; } if( status_get_sp(&sd->bl) < sp ){ // Can't sustain delete it. elemental_delete(sd->ed,0); return 0; } status_zap(&sd->bl,0,sp); ed->last_spdrain_time = tick; } if( DIFF_TICK(tick,ed->last_thinktime) < MIN_ELETHINKTIME ) return 0; ed->last_thinktime = tick; if( ed->ud.skilltimer != INVALID_TIMER ) return 0; if( ed->ud.walktimer != INVALID_TIMER && ed->ud.walkpath.path_pos <= 2 ) return 0; //No thinking when you just started to walk. if(ed->ud.walkpath.path_pos < ed->ud.walkpath.path_len && ed->ud.target == sd->bl.id) return 0; //No thinking until be near the master. if( ed->sc.count && ed->sc.data[SC_BLIND] ) view_range = 3; else view_range = ed->db->range2; mode = status_get_mode(&ed->bl); master_dist = distance_bl(&sd->bl, &ed->bl); if( master_dist > AREA_SIZE ) { // Master out of vision range. elemental_unlocktarget(ed); unit_warp(&ed->bl,sd->bl.m,sd->bl.x,sd->bl.y,CLR_TELEPORT); clif_elemental_updatestatus(sd,SP_HP); clif_elemental_updatestatus(sd,SP_SP); return 0; } else if( master_dist > MAX_ELEDISTANCE ) { // Master too far, chase. short x = sd->bl.x, y = sd->bl.y; if( ed->target_id ) elemental_unlocktarget(ed); if( ed->ud.walktimer != INVALID_TIMER && ed->ud.target == sd->bl.id ) return 0; //Already walking to him if( DIFF_TICK(tick, ed->ud.canmove_tick) < 0 ) return 0; //Can't move yet. if( map_search_freecell(&ed->bl, sd->bl.m, &x, &y, MIN_ELEDISTANCE, MIN_ELEDISTANCE, 1) && unit_walktoxy(&ed->bl, x, y, 0) ) return 0; } if( mode == EL_MODE_AGGRESSIVE ) { target = map_id2bl(ed->ud.target); if( !target ) map_foreachinrange(elemental_ai_sub_timer_activesearch, &ed->bl, view_range, BL_CHAR, ed, &target, status_get_mode(&ed->bl)); if( !target ) { //No targets available. elemental_unlocktarget(ed); return 1; } if( battle_check_range(&ed->bl,target,view_range) && rnd()%100 < 2 ) { // 2% chance to cast attack skill. if( elemental_action(ed,target,tick) ) return 1; } //Attempt to attack. //At this point we know the target is attackable, we just gotta check if the range matches. if( ed->ud.target == target->id && ed->ud.attacktimer != INVALID_TIMER ) //Already locked. return 1; if( battle_check_range(&ed->bl, target, ed->base_status.rhw.range) ) {//Target within range, engage unit_attack(&ed->bl,target->id,1); return 1; } //Follow up if possible. if( !unit_walktobl(&ed->bl, target, ed->base_status.rhw.range, 2) ) elemental_unlocktarget(ed); } return 0; }
int do_timer(unsigned int tick) { int i,nextmin=1000; #if 0 static int disp_tick=0; if(DIFF_TICK(disp_tick,tick)<-5000 || DIFF_TICK(disp_tick,tick)>5000){ printf("timer %d(%d+%d)\n",timer_data_num,timer_heap[0],free_timer_list_pos); disp_tick=tick; } #endif while((i=top_timer_heap())>=0){ if(DIFF_TICK(timer_data[i].tick , tick)>0){ nextmin=DIFF_TICK(timer_data[i].tick , tick); break; } pop_timer_heap(); timer_data[i].type |= TIMER_REMOVE_HEAP; if(timer_data[i].func){ if(DIFF_TICK(timer_data[i].tick , tick) < -1000){ // 1秒以上の大幅な遅延が発生しているので、 // timer処理タイミングを現在値とする事で // 呼び出し時タイミング(引数のtick)相対で処理してる // timer関数の次回処理タイミングを遅らせる timer_data[i].func(i,tick,timer_data[i].id,timer_data[i].data); } else { timer_data[i].func(i,timer_data[i].tick,timer_data[i].id,timer_data[i].data); } } if(timer_data[i].type&TIMER_REMOVE_HEAP){ switch(timer_data[i].type & ~TIMER_REMOVE_HEAP){ case TIMER_ONCE_AUTODEL: timer_data[i].type=0; if(free_timer_list_pos>=free_timer_list_max){ free_timer_list_max+=256; free_timer_list=realloc(free_timer_list,free_timer_list_max*sizeof(free_timer_list[0])); if(free_timer_list==NULL){ printf("out of memory : do_timer\n"); exit(1); } } free_timer_list[free_timer_list_pos++]=i; break; case TIMER_INTERVAL: if(DIFF_TICK(timer_data[i].tick , tick) < -1000){ timer_data[i].tick = tick+timer_data[i].interval; } else { timer_data[i].tick += timer_data[i].interval; } timer_data[i].type &= ~TIMER_REMOVE_HEAP; push_timer_heap(i); break; } } } if(nextmin<10) nextmin=10; return nextmin; }