diff options
Diffstat (limited to 'src/map')
-rw-r--r-- | src/map/atcommand.c | 4 | ||||
-rw-r--r-- | src/map/battle.c | 12 | ||||
-rw-r--r-- | src/map/battle.h | 2 | ||||
-rw-r--r-- | src/map/battleground.c | 163 | ||||
-rw-r--r-- | src/map/clif.c | 8 | ||||
-rw-r--r-- | src/map/map.c | 14 | ||||
-rw-r--r-- | src/map/mob.c | 9 | ||||
-rw-r--r-- | src/map/npc.c | 557 | ||||
-rw-r--r-- | src/map/npc.h | 14 | ||||
-rw-r--r-- | src/map/pc.c | 148 | ||||
-rw-r--r-- | src/map/pc.h | 3 | ||||
-rw-r--r-- | src/map/script.c | 613 | ||||
-rw-r--r-- | src/map/script.h | 55 | ||||
-rw-r--r-- | src/map/skill.c | 8 | ||||
-rw-r--r-- | src/map/status.c | 18 | ||||
-rw-r--r-- | src/map/status.h | 2 | ||||
-rw-r--r-- | src/map/unit.c | 5 |
17 files changed, 954 insertions, 681 deletions
diff --git a/src/map/atcommand.c b/src/map/atcommand.c index ff88f2c63..de71819de 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -77,7 +77,7 @@ struct atcmd_binding_data* get_atcommandbind_byname(const char* name) { } const char* atcommand_msgsd(struct map_session_data *sd, int msg_number) { - Assert_retr("??", msg_number >= 0 && msg_number < MAX_MSG); + Assert_retr("??", msg_number >= 0 && msg_number < MAX_MSG && atcommand->msg_table[0][msg_number] != NULL); if (!sd || sd->lang_id >= atcommand->max_message_table || !atcommand->msg_table[sd->lang_id][msg_number]) return atcommand->msg_table[0][msg_number]; return atcommand->msg_table[sd->lang_id][msg_number]; @@ -85,7 +85,7 @@ const char* atcommand_msgsd(struct map_session_data *sd, int msg_number) { const char* atcommand_msgfd(int fd, int msg_number) { struct map_session_data *sd = sockt->session_is_valid(fd) ? sockt->session[fd]->session_data : NULL; - Assert_retr("??", msg_number >= 0 && msg_number < MAX_MSG); + Assert_retr("??", msg_number >= 0 && msg_number < MAX_MSG && atcommand->msg_table[0][msg_number] != NULL); if (!sd || sd->lang_id >= atcommand->max_message_table || !atcommand->msg_table[sd->lang_id][msg_number]) return atcommand->msg_table[0][msg_number]; return atcommand->msg_table[sd->lang_id][msg_number]; diff --git a/src/map/battle.c b/src/map/battle.c index ba640fedf..d1cdd19c4 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -229,10 +229,13 @@ int battle_delay_damage_sub(int tid, int64 tick, int id, intptr_t data) { src = map->id2bl(dat->src_id); //Check to see if you haven't teleported. [Skotlex] - if( src - && (target->type != BL_PC || ((TBL_PC*)target)->invincible_timer == INVALID_TIMER) - && (dat->skill_id == MO_EXTREMITYFIST || (target->m == src->m && check_distance_bl(src, target, dat->distance)) ) - ) { + if (src && ( + battle_config.fix_warp_hit_delay_abuse ? + (dat->skill_id == MO_EXTREMITYFIST || target->m != src->m || check_distance_bl(src, target, dat->distance)) + : + ((target->type != BL_PC || ((TBL_PC*)target)->invincible_timer == INVALID_TIMER) + && (dat->skill_id == MO_EXTREMITYFIST || (target->m == src->m && check_distance_bl(src, target, dat->distance)))) + )) { map->freeblock_lock(); status_fix_damage(src, target, dat->damage, dat->delay); if( dat->attack_type && !status->isdead(target) && dat->additional_effects ) @@ -7137,6 +7140,7 @@ static const struct battle_data { { "boss_icewall_walk_block", &battle_config.boss_icewall_walk_block, 0, 0, 255, }, { "feature.roulette", &battle_config.feature_roulette, 1, 0, 1, }, { "show_monster_hp_bar", &battle_config.show_monster_hp_bar, 1, 0, 1, }, + { "fix_warp_hit_delay_abuse", &battle_config.fix_warp_hit_delay_abuse, 0, 0, 1, }, }; #ifndef STATS_OPT_OUT /** diff --git a/src/map/battle.h b/src/map/battle.h index 55ddf3b9b..68a427e72 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -510,6 +510,8 @@ struct Battle_Config { int feature_roulette; int show_monster_hp_bar; // [Frost] + + int fix_warp_hit_delay_abuse; }; /* criteria for battle_config.idletime_critera */ diff --git a/src/map/battleground.c b/src/map/battleground.c index cc5384d21..208b4b99f 100644 --- a/src/map/battleground.c +++ b/src/map/battleground.c @@ -471,24 +471,34 @@ struct bg_arena *bg_name2arena (char *name) { } return NULL; } -int bg_id2pos ( int queue_id, int account_id ) { - struct hQueue *queue = script->queue(queue_id); - if( queue ) { - int i, pos = 1; - for(i = 0; i < queue->size; i++ ) { - if( queue->item[i] > 0 ) { - if( queue->item[i] == account_id ) { - return pos; - } - pos++; - } + +/** + * Returns a character's position in a battleground's queue. + * + * @param queue_id The ID of the battleground's queue. + * @param account_id The character's account ID. + * + * @return the position (starting at 1). + * @retval 0 if the queue doesn't exist or the given account ID isn't present in it. + */ +int bg_id2pos(int queue_id, int account_id) +{ + struct script_queue *queue = script->queue(queue_id); + if (queue) { + int i; + ARR_FIND(0, VECTOR_LENGTH(queue->entries), i, VECTOR_INDEX(queue->entries, i) == account_id); + if (i != VECTOR_LENGTH(queue->entries)) { + return i+1; } } return 0; } -void bg_queue_ready_ack (struct bg_arena *arena, struct map_session_data *sd, bool response) { + +void bg_queue_ready_ack(struct bg_arena *arena, struct map_session_data *sd, bool response) +{ nullpo_retv(arena); nullpo_retv(sd); + if( arena->begin_timer == INVALID_TIMER || !sd->bg_queue.arena || sd->bg_queue.arena != arena ) { bg->queue_pc_cleanup(sd); return; @@ -496,18 +506,18 @@ void bg_queue_ready_ack (struct bg_arena *arena, struct map_session_data *sd, bo if( !response ) bg->queue_pc_cleanup(sd); else { - struct hQueue *queue = &script->hq[arena->queue_id]; + struct script_queue *queue = script->queue(arena->queue_id); int i, count = 0; sd->bg_queue.ready = 1; - for( i = 0; i < queue->size; i++ ) { - if (queue->item[i] > 0 && (sd = map->id2sd(queue->item[i])) != NULL) { - if( sd->bg_queue.ready == 1 ) - count++; - } + for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) { + struct map_session_data *tsd = map->id2sd(VECTOR_INDEX(queue->entries, i)); + + if (tsd != NULL && tsd->bg_queue.ready == 1) + count++; } /* check if all are ready then cancel timer, and start game */ - if( count == queue->items ) { + if (count == VECTOR_LENGTH(queue->entries)) { timer->delete(arena->begin_timer,bg->begin_timer); arena->begin_timer = INVALID_TIMER; bg->begin(arena); @@ -531,7 +541,7 @@ void bg_queue_player_cleanup(struct map_session_data *sd) { sd->bg_queue.type = 0; } void bg_match_over(struct bg_arena *arena, bool canceled) { - struct hQueue *queue = &script->hq[arena->queue_id]; + struct script_queue *queue = script->queue(arena->queue_id); int i; nullpo_retv(arena); @@ -539,19 +549,20 @@ void bg_match_over(struct bg_arena *arena, bool canceled) { return; arena->ongoing = false; - for( i = 0; i < queue->size; i++ ) { - struct map_session_data * sd = NULL; + for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) { + struct map_session_data *sd = map->id2sd(VECTOR_INDEX(queue->entries, i)); - if (queue->item[i] > 0 && (sd = map->id2sd(queue->item[i])) != NULL) { - if( sd->bg_queue.arena ) { - bg->team_leave(sd, 0); - bg->queue_pc_cleanup(sd); - } - if (canceled) - clif->messagecolor_self(sd->fd, COLOR_RED, "BG Match Canceled: not enough players"); - else - pc_setglobalreg(sd, script->add_str(arena->delay_var), (unsigned int)time(NULL)); + if (sd == NULL) + continue; + + if (sd->bg_queue.arena) { + bg->team_leave(sd, 0); + bg->queue_pc_cleanup(sd); } + if (canceled) + clif->messagecolor_self(sd->fd, COLOR_RED, "BG Match Canceled: not enough players"); + else + pc_setglobalreg(sd, script->add_str(arena->delay_var), (unsigned int)time(NULL)); } arena->begin_timer = INVALID_TIMER; @@ -560,19 +571,20 @@ void bg_match_over(struct bg_arena *arena, bool canceled) { script->queue_clear(arena->queue_id); } void bg_begin(struct bg_arena *arena) { - struct hQueue *queue = &script->hq[arena->queue_id]; + struct script_queue *queue = script->queue(arena->queue_id); int i, count = 0; nullpo_retv(arena); - for( i = 0; i < queue->size; i++ ) { - struct map_session_data * sd = NULL; + for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) { + struct map_session_data *sd = map->id2sd(VECTOR_INDEX(queue->entries, i)); - if (queue->item[i] > 0 && (sd = map->id2sd(queue->item[i])) != NULL) { - if( sd->bg_queue.ready == 1 ) - count++; - else - bg->queue_pc_cleanup(sd); - } + if (sd == NULL) + continue; + + if (sd->bg_queue.ready == 1) + count++; + else + bg->queue_pc_cleanup(sd); } /* TODO/FIXME? I *think* it should check what kind of queue the player used, then check if his party/guild * (his team) still meet the join criteria (sort of what bg->can_queue does) @@ -591,25 +603,24 @@ void bg_begin(struct bg_arena *arena) { mapreg->setregstr(script->add_str("$@bg_delay_var$"),bg->gdelay_var); count = 0; - for( i = 0; i < queue->size; i++ ) { - struct map_session_data * sd = NULL; - - if (queue->item[i] > 0 && (sd = map->id2sd(queue->item[i])) != NULL) { - if( sd->bg_queue.ready == 1 ) { - mapreg->setreg(reference_uid(script->add_str("$@bg_member"), count), sd->status.account_id); - mapreg->setreg(reference_uid(script->add_str("$@bg_member_group"), count), - sd->bg_queue.type == BGQT_GUILD ? sd->status.guild_id : - sd->bg_queue.type == BGQT_PARTY ? sd->status.party_id : - 0 - ); - mapreg->setreg(reference_uid(script->add_str("$@bg_member_type"), count), - sd->bg_queue.type == BGQT_GUILD ? 1 : - sd->bg_queue.type == BGQT_PARTY ? 2 : - 0 - ); - count++; - } - } + for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) { + struct map_session_data *sd = map->id2sd(VECTOR_INDEX(queue->entries, i)); + + if (sd == NULL || sd->bg_queue.ready != 1) + continue; + + mapreg->setreg(reference_uid(script->add_str("$@bg_member"), count), sd->status.account_id); + mapreg->setreg(reference_uid(script->add_str("$@bg_member_group"), count), + sd->bg_queue.type == BGQT_GUILD ? sd->status.guild_id : + sd->bg_queue.type == BGQT_PARTY ? sd->status.party_id : + 0 + ); + mapreg->setreg(reference_uid(script->add_str("$@bg_member_type"), count), + sd->bg_queue.type == BGQT_GUILD ? 1 : + sd->bg_queue.type == BGQT_PARTY ? 2 : + 0 + ); + count++; } mapreg->setreg(script->add_str("$@bg_member_size"),count); @@ -645,15 +656,15 @@ int bg_afk_timer(int tid, int64 tick, int id, intptr_t data) { } void bg_queue_pregame(struct bg_arena *arena) { - struct hQueue *queue; + struct script_queue *queue; int i; - nullpo_retv(arena); - queue = &script->hq[arena->queue_id]; - for( i = 0; i < queue->size; i++ ) { - struct map_session_data * sd = NULL; - if (queue->item[i] > 0 && (sd = map->id2sd(queue->item[i])) != NULL) { + queue = script->queue(arena->queue_id); + for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) { + struct map_session_data *sd = map->id2sd(VECTOR_INDEX(queue->entries, i)); + + if (sd != NULL) { clif->bgqueue_battlebegins(sd,arena->id,SELF); } } @@ -667,9 +678,11 @@ int bg_fillup_timer(int tid, int64 tick, int id, intptr_t data) { void bg_queue_check(struct bg_arena *arena) { int count; - + struct script_queue *queue; nullpo_retv(arena); - count = script->hq[arena->queue_id].items; + + queue = script->queue(arena->queue_id); + count = VECTOR_LENGTH(queue->entries); if( count == arena->max_players ) { if( arena->fillup_timer != INVALID_TIMER ) { timer->delete(arena->fillup_timer,bg->fillup_timer); @@ -682,7 +695,7 @@ void bg_queue_check(struct bg_arena *arena) { } void bg_queue_add(struct map_session_data *sd, struct bg_arena *arena, enum bg_queue_types type) { enum BATTLEGROUNDS_QUEUE_ACK result = bg->can_queue(sd,arena,type); - struct hQueue *queue; + struct script_queue *queue = NULL; int i, count = 0; nullpo_retv(sd); @@ -718,7 +731,7 @@ void bg_queue_add(struct map_session_data *sd, struct bg_arena *arena, enum bg_q break; } - if( !(queue = script->queue(arena->queue_id)) || (queue->items+count) > arena->max_players ) { + if( !(queue = script->queue(arena->queue_id)) || (VECTOR_LENGTH(queue->entries)+count) > arena->max_players ) { clif->bgqueue_ack(sd,BGQA_FAIL_PPL_OVERAMOUNT,arena->id); return; } @@ -729,8 +742,8 @@ void bg_queue_add(struct map_session_data *sd, struct bg_arena *arena, enum bg_q sd->bg_queue.arena = arena; sd->bg_queue.ready = 0; script->queue_add(arena->queue_id,sd->status.account_id); - clif->bgqueue_joined(sd,script->hq[arena->queue_id].items); - clif->bgqueue_update_info(sd,arena->id,script->hq[arena->queue_id].items); + clif->bgqueue_joined(sd, VECTOR_LENGTH(queue->entries)); + clif->bgqueue_update_info(sd,arena->id, VECTOR_LENGTH(queue->entries)); break; case BGQT_PARTY: { struct party_data *p = party->search(sd->status.party_id); @@ -740,8 +753,8 @@ void bg_queue_add(struct map_session_data *sd, struct bg_arena *arena, enum bg_q p->data[i].sd->bg_queue.arena = arena; p->data[i].sd->bg_queue.ready = 0; script->queue_add(arena->queue_id,p->data[i].sd->status.account_id); - clif->bgqueue_joined(p->data[i].sd,script->hq[arena->queue_id].items); - clif->bgqueue_update_info(p->data[i].sd,arena->id,script->hq[arena->queue_id].items); + clif->bgqueue_joined(p->data[i].sd, VECTOR_LENGTH(queue->entries)); + clif->bgqueue_update_info(p->data[i].sd,arena->id, VECTOR_LENGTH(queue->entries)); } } break; @@ -753,8 +766,8 @@ void bg_queue_add(struct map_session_data *sd, struct bg_arena *arena, enum bg_q sd->guild->member[i].sd->bg_queue.arena = arena; sd->guild->member[i].sd->bg_queue.ready = 0; script->queue_add(arena->queue_id,sd->guild->member[i].sd->status.account_id); - clif->bgqueue_joined(sd->guild->member[i].sd,script->hq[arena->queue_id].items); - clif->bgqueue_update_info(sd->guild->member[i].sd,arena->id,script->hq[arena->queue_id].items); + clif->bgqueue_joined(sd->guild->member[i].sd, VECTOR_LENGTH(queue->entries)); + clif->bgqueue_update_info(sd->guild->member[i].sd,arena->id, VECTOR_LENGTH(queue->entries)); } break; } diff --git a/src/map/clif.c b/src/map/clif.c index 80703fa12..621f0b44a 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -610,12 +610,12 @@ bool clif_send(const void* buf, int len, struct block_list* bl, enum send_target case BG_QUEUE: if( sd && sd->bg_queue.arena ) { - struct hQueue *queue = &script->hq[sd->bg_queue.arena->queue_id]; + struct script_queue *queue = script->queue(sd->bg_queue.arena->queue_id); - for( i = 0; i < queue->size; i++ ) { - struct map_session_data *qsd = NULL; + for (i = 0; i < VECTOR_LENGTH(queue->entries); i++) { + struct map_session_data *qsd = map->id2sd(VECTOR_INDEX(queue->entries, i)); - if (queue->item[i] > 0 && (qsd = map->id2sd(queue->item[i])) != NULL) { + if (qsd != NULL) { WFIFOHEAD(qsd->fd,len); memcpy(WFIFOP(qsd->fd,0), buf, len); WFIFOSET(qsd->fd,len); diff --git a/src/map/map.c b/src/map/map.c index cd2ba17c2..fff1593a4 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -1802,16 +1802,16 @@ int map_quit(struct map_session_data *sd) { skill->cooldown_save(sd); pc->itemcd_do(sd,false); - for( i = 0; i < sd->queues_count; i++ ) { - struct hQueue *queue; - if( (queue = script->queue(sd->queues[i])) && queue->onLogOut[0] != '\0' ) { - npc->event(sd, queue->onLogOut, 0); + for (i = 0; i < VECTOR_LENGTH(sd->script_queues); i++) { + struct script_queue *queue = script->queue(VECTOR_INDEX(sd->script_queues, i)); + if (queue && queue->event_logout[0] != '\0') { + npc->event(sd, queue->event_logout, 0); } } /* two times, the npc event above may assign a new one or delete others */ - for( i = 0; i < sd->queues_count; i++ ) { - if( sd->queues[i] != -1 ) - script->queue_remove(sd->queues[i],sd->status.account_id); + while (VECTOR_LENGTH(sd->script_queues)) { + int qid = VECTOR_LAST(sd->script_queues); + script->queue_remove(qid, sd->status.account_id); } npc->script_event(sd, NPCE_LOGOUT); diff --git a/src/map/mob.c b/src/map/mob.c index 2b519462d..6cbbd3a2a 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -142,17 +142,11 @@ void mvptomb_create(struct mob_data *md, char *killer, time_t time) if ( md->tomb_nid ) mob->mvptomb_destroy(md); - nd = npc->create_npc(md->bl.m, md->bl.x, md->bl.y); + nd = npc->create_npc(TOMB, md->bl.m, md->bl.x, md->bl.y, md->ud.dir, MOB_TOMB); md->tomb_nid = nd->bl.id; - nd->dir = md->ud.dir; - nd->bl.type = BL_NPC; safestrncpy(nd->name, msg_txt(856), sizeof(nd->name)); // "Tomb" - nd->class_ = 565; - nd->speed = 200; - nd->subtype = TOMB; - nd->u.tomb.md = md; nd->u.tomb.kill_time = time; @@ -165,7 +159,6 @@ void mvptomb_create(struct mob_data *md, char *killer, time_t time) map->addblock(&nd->bl); status->set_viewdata(&nd->bl, nd->class_); clif->spawn(&nd->bl); - } void mvptomb_destroy(struct mob_data *md) { diff --git a/src/map/npc.c b/src/map/npc.c index 16fc07fe8..6ecc22282 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -53,8 +53,8 @@ static int npc_mob=0; static int npc_delay_mob=0; static int npc_cache_mob=0; -static char *npc_last_path; -static char *npc_last_ref; +static const char *npc_last_path; +static const char *npc_last_ref; struct npc_path_data *npc_last_npd; //For holding the view data of npc classes. [Skotlex] @@ -2247,16 +2247,9 @@ int npc_unload(struct npc_data* nd, bool single) npc_chat->finalize(nd); // deallocate npc PCRE data structures #endif - if( single && nd->path ) { - struct npc_path_data* npd = NULL; - if( nd->path && nd->path != npc_last_ref ) { - npd = strdb_get(npc->path_db, nd->path); - } - - if( npd && --npd->references == 0 ) { - strdb_remove(npc->path_db, nd->path);/* remove from db */ - aFree(nd->path);/* remove now that no other instances exist */ - } + if (single && nd->path != NULL) { + npc->releasepathreference(nd->path); + nd->path = NULL; } if( single && nd->bl.m != -1 ) @@ -2407,6 +2400,64 @@ void npc_delsrcfile(const char* name) } } +/** + * Retains a reference to filepath, for use in nd->path + * + * @param filepath The file path to retain. + * @return A retained reference to filepath. + */ +const char *npc_retainpathreference(const char *filepath) +{ + struct npc_path_data * npd = NULL; + nullpo_ret(filepath); + + if (npc_last_path == filepath) { + if (npc_last_npd != NULL) + npc_last_npd->references++; + return npc_last_ref; + } + + if ((npd = strdb_get(npc->path_db,filepath)) == NULL) { + CREATE(npd, struct npc_path_data, 1); + strdb_put(npc->path_db, filepath, npd); + + CREATE(npd->path, char, strlen(filepath)+1); + safestrncpy(npd->path, filepath, strlen(filepath)+1); + + npd->references = 0; + } + + npd->references++; + + npc_last_npd = npd; + npc_last_ref = npd->path; + npc_last_path = filepath; + + return npd->path; +} + +/** + * Releases a previously retained filepath. + * + * @param filepath The file path to release. + */ +void npc_releasepathreference(const char *filepath) +{ + struct npc_path_data* npd = NULL; + + nullpo_retv(filepath); + + if (filepath != npc_last_ref) { + npd = strdb_get(npc->path_db, filepath); + } + + if (npd != NULL && --npd->references == 0) { + char *npcpath = npd->path; + strdb_remove(npc->path_db, filepath);/* remove from db */ + aFree(npcpath); + } +} + /// Parses and sets the name and exname of a npc. /// Assumes that m, x and y are already set in nd. void npc_parsename(struct npc_data* nd, const char* name, const char* start, const char* buffer, const char* filepath) { @@ -2461,31 +2512,6 @@ void npc_parsename(struct npc_data* nd, const char* name, const char* start, con ShowDebug("other npc in '%s' :\n display name '%s'\n unique name '%s'\n map=%s, x=%d, y=%d\n",dnd->path, dnd->name, dnd->exname, other_mapname, dnd->bl.x, dnd->bl.y); safestrncpy(nd->exname, newname, sizeof(nd->exname)); } - - if( npc_last_path != filepath ) { - struct npc_path_data * npd = NULL; - - if( !(npd = strdb_get(npc->path_db,filepath) ) ) { - CREATE(npd, struct npc_path_data, 1); - strdb_put(npc->path_db, filepath, npd); - - CREATE(npd->path, char, strlen(filepath)+1); - safestrncpy(npd->path, filepath, strlen(filepath)+1); - - npd->references = 0; - } - - nd->path = npd->path; - npd->references++; - - npc_last_npd = npd; - npc_last_ref = npd->path; - npc_last_path = (char*) filepath; - } else { - nd->path = npc_last_ref; - if( npc_last_npd ) - npc_last_npd->references++; - } } // Parse View @@ -2537,17 +2563,39 @@ bool npc_viewisid(const char * viewid) return true; } -struct npc_data* npc_create_npc(int m, int x, int y) +/** + * Creates a new NPC. + * + * @remark + * When creating a npc with subtype TOMB, no ID is assigned. The caller + * must assign the dead mob ID after the NPC is created. + * + * @param subtype The NPC subtype. + * @param m The map id. + * @param x The x coordinate on map. + * @param y The y coordinate on map. + * @param dir The facing direction. + * @param class_ The NPC view class. + * @return A pointer to the created NPC data (ownership passed to the caller). + */ +struct npc_data *npc_create_npc(enum npc_subtype subtype, int m, int x, int y, uint8 dir, int16 class_) { struct npc_data *nd; CREATE(nd, struct npc_data, 1); - nd->bl.id = npc->get_new_npc_id(); + nd->subtype = subtype; + nd->bl.type = BL_NPC; + if (subtype != TOMB) { + nd->bl.id = npc->get_new_npc_id(); + } nd->bl.prev = nd->bl.next = NULL; nd->bl.m = m; nd->bl.x = x; nd->bl.y = y; + nd->dir = dir; nd->area_size = AREA_SIZE + 1; + nd->class_ = class_; + nd->speed = 200; return nd; } @@ -2557,8 +2605,7 @@ struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short int i, flag = 0; struct npc_data *nd; - nd = npc->create_npc(from_mapid, from_x, from_y); - map->addnpc(from_mapid, nd); + nd = npc->create_npc(WARP, from_mapid, from_x, from_y, 0, battle_config.warp_point_debug ? WARP_DEBUG_CLASS : WARP_CLASS); safestrncpy(nd->exname, name, ARRAYLENGTH(nd->exname)); if (npc->name2id(nd->exname) != NULL) @@ -2571,26 +2618,13 @@ struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short snprintf(nd->exname, ARRAYLENGTH(nd->exname), "warp%d_%d_%d_%d", i, from_mapid, from_x, from_y); safestrncpy(nd->name, nd->exname, ARRAYLENGTH(nd->name)); - if( battle_config.warp_point_debug ) - nd->class_ = WARP_DEBUG_CLASS; - else - nd->class_ = WARP_CLASS; - nd->speed = 200; - nd->u.warp.mapindex = to_mapindex; nd->u.warp.x = to_x; nd->u.warp.y = to_y; nd->u.warp.xs = xs; nd->u.warp.ys = xs; - nd->bl.type = BL_NPC; - nd->subtype = WARP; - npc->setcells(nd); - map->addblock(&nd->bl); - status->set_viewdata(&nd->bl, nd->class_); - nd->ud = &npc->base_ud; - if( map->list[nd->bl.m].users ) - clif->spawn(&nd->bl); - strdb_put(npc->name_db, nd->exname, nd); + + npc->add_to_location(nd); return nd; } @@ -2626,15 +2660,9 @@ const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const char* s return strchr(start,'\n');;//try next } - nd = npc->create_npc(m, x, y); - map->addnpc(m, nd); + nd = npc->create_npc(WARP, m, x, y, 0, battle_config.warp_point_debug ? WARP_DEBUG_CLASS : WARP_CLASS); npc->parsename(nd, w3, start, buffer, filepath); - - if (!battle_config.warp_point_debug) - nd->class_ = WARP_CLASS; - else - nd->class_ = WARP_DEBUG_CLASS; - nd->speed = 200; + nd->path = npc->retainpathreference(filepath); nd->u.warp.mapindex = i; nd->u.warp.x = to_x; @@ -2642,15 +2670,8 @@ const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const char* s nd->u.warp.xs = xs; nd->u.warp.ys = ys; npc_warp++; - nd->bl.type = BL_NPC; - nd->subtype = WARP; - npc->setcells(nd); - map->addblock(&nd->bl); - status->set_viewdata(&nd->bl, nd->class_); - nd->ud = &npc->base_ud; - if( map->list[nd->bl.m].users ) - clif->spawn(&nd->bl); - strdb_put(npc->name_db, nd->exname, nd); + + npc->add_to_location(nd); return strchr(start,'\n');// continue } @@ -2669,7 +2690,7 @@ const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const char* s size_t items_count = 40; // Starting items size char *p; - int x, y, dir, m, i; + int x, y, dir, m, i, class_; struct npc_data *nd; enum npc_subtype type; @@ -2768,31 +2789,18 @@ const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const char* s return strchr(start,'\n');// continue } - nd = npc->create_npc(m, x, y); + class_ = m == -1 ? -1 : npc->parseview(w4, start, buffer, filepath); + nd = npc->create_npc(type, m, x, y, dir, class_); CREATE(nd->u.shop.shop_item, struct npc_item_list, i); memcpy(nd->u.shop.shop_item, items, sizeof(items[0])*i); aFree(items); nd->u.shop.count = i; npc->parsename(nd, w3, start, buffer, filepath); - nd->class_ = m == -1 ? -1 : npc->parseview(w4, start, buffer, filepath); - nd->speed = 200; + nd->path = npc->retainpathreference(filepath); ++npc_shop; - nd->bl.type = BL_NPC; - nd->subtype = type; - if( m >= 0 ) {// normal shop npc - map->addnpc(m,nd); - map->addblock(&nd->bl); - status->set_viewdata(&nd->bl, nd->class_); - nd->ud = &npc->base_ud; - nd->dir = dir; - if( map->list[nd->bl.m].users ) - clif->spawn(&nd->bl); - } else {// 'floating' shop - map->addiddb(&nd->bl); - } - strdb_put(npc->name_db, nd->exname, nd); + npc->add_to_location(nd); return strchr(start,'\n');// continue } @@ -2892,7 +2900,7 @@ const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, const char* { int x, y, dir = 0, m, xs = 0, ys = 0; // [Valaris] thanks to fov struct script_code *scriptroot; - int i; + int i, class_; const char* end; const char* script_start; @@ -2949,39 +2957,74 @@ const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, const char* npc->convertlabel_db(label_list,filepath); } - nd = npc->create_npc(m, x, y); - if( sscanf(w4, "%*[^,],%d,%d", &xs, &ys) == 2 ) - {// OnTouch area defined + class_ = m == -1 ? -1 : npc->parseview(w4, start, buffer, filepath); + nd = npc->create_npc(SCRIPT, m, x, y, dir, class_); + if (sscanf(w4, "%*[^,],%d,%d", &xs, &ys) == 2) { + // OnTouch area defined nd->u.scr.xs = xs; nd->u.scr.ys = ys; - } - else - {// no OnTouch area + } else { + // no OnTouch area nd->u.scr.xs = -1; nd->u.scr.ys = -1; } npc->parsename(nd, w3, start, buffer, filepath); - nd->class_ = m == -1 ? -1 : npc->parseview(w4, start, buffer, filepath); - nd->speed = 200; + nd->path = npc->retainpathreference(filepath); nd->u.scr.script = scriptroot; nd->u.scr.label_list = label_list; nd->u.scr.label_list_num = label_list_num; if( options&NPO_TRADER ) nd->u.scr.trader = true; nd->u.scr.shop = NULL; - ++npc_script; - nd->bl.type = BL_NPC; - nd->subtype = SCRIPT; + npc->add_to_location(nd); - if( m >= 0 ) { - map->addnpc(m, nd); - nd->ud = &npc->base_ud; - nd->dir = dir; + //----------------------------------------- + // Loop through labels to export them as necessary + for (i = 0; i < nd->u.scr.label_list_num; i++) { + if (npc->event_export(nd, i)) { + ShowWarning("npc_parse_script: duplicate event %s::%s in file '%s'.\n", + nd->exname, nd->u.scr.label_list[i].name, filepath); + if (retval) *retval = EXIT_FAILURE; + } + npc->timerevent_export(nd, i); + } + + nd->u.scr.timerid = INVALID_TIMER; + + if( options&NPO_ONINIT ) { + char evname[EVENT_NAME_LENGTH]; + struct event_data *ev; + + snprintf(evname, ARRAYLENGTH(evname), "%s::OnInit", nd->exname); + + if( ( ev = (struct event_data*)strdb_get(npc->ev_db, evname) ) ) { + + //Execute OnInit + script->run_npc(nd->u.scr.script,ev->pos,0,nd->bl.id); + + } + } + + return end; +} + +/** + * Registers the NPC and adds it to its location (on map or floating). + * + * @param nd The NPC to register. + */ +void npc_add_to_location(struct npc_data *nd) +{ + nullpo_retv(nd); + + if (nd->bl.m >= 0) { + map->addnpc(nd->bl.m, nd); npc->setcells(nd); map->addblock(&nd->bl); - if( nd->class_ >= 0 ) { + nd->ud = &npc->base_ud; + if (nd->class_ >= 0) { status->set_viewdata(&nd->bl, nd->class_); if( map->list[nd->bl.m].users ) clif->spawn(&nd->bl); @@ -2991,35 +3034,120 @@ const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, const char* map->addiddb(&nd->bl); } strdb_put(npc->name_db, nd->exname, nd); +} + +/** + * Duplicates a script (@see npc_duplicate_sub) + */ +bool npc_duplicate_script_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options) +{ + int i; + bool retval = true; + + ++npc_script; + nd->u.scr.xs = xs; + nd->u.scr.ys = ys; + nd->u.scr.script = snd->u.scr.script; + nd->u.scr.label_list = snd->u.scr.label_list; + nd->u.scr.label_list_num = snd->u.scr.label_list_num; + nd->u.scr.shop = snd->u.scr.shop; + nd->u.scr.trader = snd->u.scr.trader; + + //add the npc to its location + npc->add_to_location(nd); - //----------------------------------------- // Loop through labels to export them as necessary for (i = 0; i < nd->u.scr.label_list_num; i++) { if (npc->event_export(nd, i)) { - ShowWarning("npc_parse_script: duplicate event %s::%s in file '%s'.\n", - nd->exname, nd->u.scr.label_list[i].name, filepath); - if (retval) *retval = EXIT_FAILURE; + ShowWarning("npc_parse_duplicate: duplicate event %s::%s in file '%s'.\n", + nd->exname, nd->u.scr.label_list[i].name, nd->path); + retval = false; } npc->timerevent_export(nd, i); } nd->u.scr.timerid = INVALID_TIMER; - if( options&NPO_ONINIT ) { + if (options&NPO_ONINIT) { + // From npc_parse_script char evname[EVENT_NAME_LENGTH]; struct event_data *ev; snprintf(evname, ARRAYLENGTH(evname), "%s::OnInit", nd->exname); - if( ( ev = (struct event_data*)strdb_get(npc->ev_db, evname) ) ) { - + if ((ev = (struct event_data*)strdb_get(npc->ev_db, evname)) != NULL) { //Execute OnInit script->run_npc(nd->u.scr.script,ev->pos,0,nd->bl.id); - } } + return retval; +} - return end; +/** + * Duplicates a shop or cash shop (@see npc_duplicate_sub) + */ +bool npc_duplicate_shop_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options) +{ + ++npc_shop; + nd->u.shop.shop_item = snd->u.shop.shop_item; + nd->u.shop.count = snd->u.shop.count; + + //add the npc to its location + npc->add_to_location(nd); + + return true; +} + +/** + * Duplicates a warp (@see npc_duplicate_sub) + */ +bool npc_duplicate_warp_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options) +{ + ++npc_warp; + nd->u.warp.xs = xs; + nd->u.warp.ys = ys; + nd->u.warp.mapindex = snd->u.warp.mapindex; + nd->u.warp.x = snd->u.warp.x; + nd->u.warp.y = snd->u.warp.y; + + //Add the npc to its location + npc->add_to_location(nd); + + return true; +} + +/** + * Duplicates a warp, shop, cashshop or script. + * + * @param nd An already initialized NPC data. Expects bl->m, bl->x, bl->y, + * name, exname, path, dir, class_, speed, subtype to be already + * filled. + * @param snd The source NPC to duplicate. + * @param class_ The npc view class. + * @param dir The facing direction. + * @param xs The x-span, if any. + * @param ys The y-span, if any. + * @param options The NPC options. + * @retval false if there were any issues while creating and validating the NPC. + */ +bool npc_duplicate_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options) +{ + nd->src_id = snd->bl.id; + switch (nd->subtype) { + case SCRIPT: + return npc->duplicate_script_sub(nd, snd, xs, ys, options); + + case SHOP: + case CASHSHOP: + return npc->duplicate_shop_sub(nd, snd, xs, ys, options); + + case WARP: + return npc->duplicate_warp_sub(nd, snd, xs, ys, options); + + case TOMB: + return false; + } + return false; } /// Duplicate a warp, shop, cashshop or script. [Orcao] @@ -3032,12 +3160,10 @@ const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, const char* const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath, int options, int *retval) { int x, y, dir, m, xs = -1, ys = -1; char srcname[128]; - int i; - const char* end; + const char *end; size_t length; - int src_id; - int type; + int class_; struct npc_data* nd; struct npc_data* dnd; @@ -3059,18 +3185,16 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch if (retval) *retval = EXIT_FAILURE; return end;// next line, try to continue } - src_id = dnd->bl.id; - type = dnd->subtype; // get placement - if ((type==SHOP || type==CASHSHOP || type==SCRIPT) && strcmp(w1, "-") == 0) { + if ((dnd->subtype==SHOP || dnd->subtype==CASHSHOP || dnd->subtype==SCRIPT) && strcmp(w1, "-") == 0) { // floating shop/chashshop/script x = y = dir = 0; m = -1; } else { char mapname[32]; int fields = sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir); - if (type == WARP && fields == 3) { + if (dnd->subtype == WARP && fields == 3) { // <map name>,<x>,<y> dir = 0; } else if (fields != 4) { @@ -3093,107 +3217,40 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch return end;//try next } - if( type == WARP && sscanf(w4, "%d,%d", &xs, &ys) == 2 );// <spanx>,<spany> - else if( type == SCRIPT && sscanf(w4, "%*[^,],%d,%d", &xs, &ys) == 2);// <sprite id>,<triggerX>,<triggerY> - else if( type == WARP ) { + if (dnd->subtype == WARP && sscanf(w4, "%d,%d", &xs, &ys) == 2) { // <spanx>,<spany> + ; + } else if (dnd->subtype == SCRIPT && sscanf(w4, "%*[^,],%d,%d", &xs, &ys) == 2) { // <sprite id>,<triggerX>,<triggerY> + ; + } else if (dnd->subtype == WARP) { ShowError("npc_parse_duplicate: Invalid span format for duplicate warp in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); if (retval) *retval = EXIT_FAILURE; return end;// next line, try to continue } - nd = npc->create_npc(m, x, y); + class_ = m == -1 ? -1 : npc->parseview(w4, start, buffer, filepath); + nd = npc->create_npc(dnd->subtype, m, x, y, dir, class_); npc->parsename(nd, w3, start, buffer, filepath); - nd->class_ = m == -1 ? -1 : npc->parseview(w4, start, buffer, filepath); - nd->speed = 200; - nd->src_id = src_id; - nd->bl.type = BL_NPC; - nd->subtype = (enum npc_subtype)type; - switch( type ) { - case SCRIPT: - ++npc_script; - nd->u.scr.xs = xs; - nd->u.scr.ys = ys; - nd->u.scr.script = dnd->u.scr.script; - nd->u.scr.label_list = dnd->u.scr.label_list; - nd->u.scr.label_list_num = dnd->u.scr.label_list_num; - nd->u.scr.shop = dnd->u.scr.shop; - nd->u.scr.trader = dnd->u.scr.trader; - break; - - case SHOP: - case CASHSHOP: - ++npc_shop; - nd->u.shop.shop_item = dnd->u.shop.shop_item; - nd->u.shop.count = dnd->u.shop.count; - break; - - case WARP: - ++npc_warp; - if( !battle_config.warp_point_debug ) - nd->class_ = WARP_CLASS; - else - nd->class_ = WARP_DEBUG_CLASS; - nd->u.warp.xs = xs; - nd->u.warp.ys = ys; - nd->u.warp.mapindex = dnd->u.warp.mapindex; - nd->u.warp.x = dnd->u.warp.x; - nd->u.warp.y = dnd->u.warp.y; - break; - } - - //Add the npc to its location - if( m >= 0 ) { - map->addnpc(m, nd); - nd->ud = &npc->base_ud; - nd->dir = dir; - npc->setcells(nd); - map->addblock(&nd->bl); - if( nd->class_ >= 0 ) { - status->set_viewdata(&nd->bl, nd->class_); - if( map->list[nd->bl.m].users ) - clif->spawn(&nd->bl); - } - } else { - // we skip map->addnpc, but still add it to the list of ID's - map->addiddb(&nd->bl); - } - strdb_put(npc->name_db, nd->exname, nd); - - if( type != SCRIPT ) - return end; - - //----------------------------------------- - // Loop through labels to export them as necessary - for (i = 0; i < nd->u.scr.label_list_num; i++) { - if (npc->event_export(nd, i)) { - ShowWarning("npc_parse_duplicate: duplicate event %s::%s in file '%s'.\n", - nd->exname, nd->u.scr.label_list[i].name, filepath); - if (retval) *retval = EXIT_FAILURE; - } - npc->timerevent_export(nd, i); + nd->path = npc->retainpathreference(filepath); + if (!npc->duplicate_sub(nd, dnd, xs, ys, options)) { + if (retval) *retval = EXIT_FAILURE; } - nd->u.scr.timerid = INVALID_TIMER; - - if (options&NPO_ONINIT) { - // From npc_parse_script - char evname[EVENT_NAME_LENGTH]; - struct event_data *ev; - - snprintf(evname, ARRAYLENGTH(evname), "%s::OnInit", nd->exname); - - if( ( ev = (struct event_data*)strdb_get(npc->ev_db, evname) ) ) { - - //Execute OnInit - script->run_npc(nd->u.scr.script,ev->pos,0,nd->bl.id); - - } - } return end; } -int npc_duplicate4instance(struct npc_data *snd, int16 m) { +/** + * Duplicates an NPC for instancing purposes. + * + * @param snd The NPC to duplicate. + * @param m The instanced map ID. + * @return success state. + * @retval 0 in case of successful creation. + */ +int npc_duplicate4instance(struct npc_data *snd, int16 m) +{ char newname[NAME_LENGTH]; + int dm = -1, im = -1, xs = -1, ys = -1; + struct npc_data *nd = NULL; if( m == -1 || map->list[m].instance_id == -1 ) return 1; @@ -3204,50 +3261,35 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) { return 1; } - if( snd->subtype == WARP ) { // Adjust destination, if instanced - struct npc_data *wnd = NULL; // New NPC - int dm = map->mapindex2mapid(snd->u.warp.mapindex), im; - if( dm < 0 ) return 1; - - if( ( im = instance->mapid2imapid(dm, map->list[m].instance_id) ) == -1 ) { + switch (snd->subtype) { + case SCRIPT: + xs = snd->u.scr.xs; + ys = snd->u.scr.ys; + break; + case WARP: + xs = snd->u.warp.xs; + ys = snd->u.warp.ys; + // Adjust destination, if instanced + if ((dm = map->mapindex2mapid(snd->u.warp.mapindex)) < 0) { + return 1; + } + if ((im = instance->mapid2imapid(dm, map->list[m].instance_id)) == -1) { ShowError("npc_duplicate4instance: warp (%s) leading to instanced map (%s), but instance map is not attached to current instance.\n", map->list[dm].name, snd->exname); return 1; } + break; + default: // Other types have no xs/ys + break; + } - wnd = npc->create_npc(m, snd->bl.x, snd->bl.y); - map->addnpc(m, wnd); - safestrncpy(wnd->name, "", ARRAYLENGTH(wnd->name)); - safestrncpy(wnd->exname, newname, ARRAYLENGTH(wnd->exname)); - wnd->class_ = WARP_CLASS; - wnd->speed = 200; - wnd->u.warp.mapindex = map_id2index(im); - wnd->u.warp.x = snd->u.warp.x; - wnd->u.warp.y = snd->u.warp.y; - wnd->u.warp.xs = snd->u.warp.xs; - wnd->u.warp.ys = snd->u.warp.ys; - wnd->bl.type = BL_NPC; - wnd->subtype = WARP; - npc->setcells(wnd); - map->addblock(&wnd->bl); - status->set_viewdata(&wnd->bl, wnd->class_); - wnd->ud = &npc->base_ud; - if( map->list[wnd->bl.m].users ) - clif->spawn(&wnd->bl); - strdb_put(npc->name_db, wnd->exname, wnd); - } else { - static char w1[50], w2[50], w3[50], w4[50]; - const char* stat_buf = "- call from instancing subsystem -\n"; - - snprintf(w1, sizeof(w1), "%s,%d,%d,%d", map->list[m].name, snd->bl.x, snd->bl.y, snd->dir); - snprintf(w2, sizeof(w2), "duplicate(%s)", snd->exname); - snprintf(w3, sizeof(w3), "%s::%s", snd->name, newname); - - if( snd->u.scr.xs >= 0 && snd->u.scr.ys >= 0 ) - snprintf(w4, sizeof(w4), "%d,%d,%d", snd->class_, snd->u.scr.xs, snd->u.scr.ys); // Touch Area - else - snprintf(w4, sizeof(w4), "%d", snd->class_); - - npc->parse_duplicate(w1, w2, w3, w4, stat_buf, stat_buf, "INSTANCING", NPO_NONE, NULL); + nd = npc->create_npc(snd->subtype, m, snd->bl.x, snd->bl.y, snd->dir, snd->class_); + safestrncpy(nd->name, snd->name, sizeof(nd->name)); + safestrncpy(nd->exname, newname, sizeof(nd->exname)); + nd->path = npc->retainpathreference("INSTANCING"); + npc->duplicate_sub(nd, snd, xs, ys, NPO_NONE); + if (nd->subtype == WARP) { + // Adjust destination, if instanced + nd->u.warp.mapindex = map_id2index(im); } return 0; @@ -4735,6 +4777,8 @@ void npc_defaults(void) { npc->clearsrcfile = npc_clearsrcfile; npc->addsrcfile = npc_addsrcfile; npc->delsrcfile = npc_delsrcfile; + npc->retainpathreference = npc_retainpathreference; + npc->releasepathreference = npc_releasepathreference; npc->parsename = npc_parsename; npc->parseview = npc_parseview; npc->viewisid = npc_viewisid; @@ -4745,6 +4789,11 @@ void npc_defaults(void) { npc->convertlabel_db = npc_convertlabel_db; npc->skip_script = npc_skip_script; npc->parse_script = npc_parse_script; + npc->add_to_location = npc_add_to_location; + npc->duplicate_script_sub = npc_duplicate_script_sub; + npc->duplicate_shop_sub = npc_duplicate_shop_sub; + npc->duplicate_warp_sub = npc_duplicate_warp_sub; + npc->duplicate_sub = npc_duplicate_sub; npc->parse_duplicate = npc_parse_duplicate; npc->duplicate4instance = npc_duplicate4instance; npc->setcells = npc_setcells; diff --git a/src/map/npc.h b/src/map/npc.h index e12f942ae..e7fc16a21 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -70,7 +70,7 @@ struct npc_data { unsigned short stat_point; struct npc_parse *chatdb; - char* path;/* path dir */ + const char *path; ///< Source path reference enum npc_subtype subtype; int src_id; union { @@ -112,6 +112,7 @@ enum actor_classes { FAKE_NPC = -1, WARP_CLASS = 45, HIDDEN_WARP_CLASS = 139, + MOB_TOMB = 565, WARP_DEBUG_CLASS = 722, FLAG_CLASS = 722, INVISIBLE_CLASS = 32767, @@ -121,7 +122,7 @@ enum actor_classes { #define MAX_NPC_CLASS 1000 // New NPC range #define MAX_NPC_CLASS2_START 10001 -#define MAX_NPC_CLASS2_END 10110 +#define MAX_NPC_CLASS2_END 10174 //Script NPC events. enum npce_event { @@ -226,10 +227,12 @@ struct npc_interface { void (*clearsrcfile) (void); void (*addsrcfile) (const char *name); void (*delsrcfile) (const char *name); + const char *(*retainpathreference) (const char *filepath); + void (*releasepathreference) (const char *filepath); void (*parsename) (struct npc_data *nd, const char *name, const char *start, const char *buffer, const char *filepath); int (*parseview) (const char *w4, const char *start, const char *buffer, const char *filepath); bool (*viewisid) (const char *viewid); - struct npc_data* (*create_npc) (int m, int x, int y); + struct npc_data *(*create_npc) (enum npc_subtype subtype, int m, int x, int y, uint8 dir, int16 class_); struct npc_data* (*add_warp) (char *name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y); const char* (*parse_warp) (char *w1, char *w2, char *w3, char *w4, const char *start, const char *buffer, const char *filepath, int *retval); const char* (*parse_shop) (char *w1, char *w2, char *w3, char *w4, const char *start, const char *buffer, const char *filepath, int *retval); @@ -237,6 +240,11 @@ struct npc_interface { void (*convertlabel_db) (struct npc_label_list *label_list, const char *filepath); const char* (*skip_script) (const char *start, const char *buffer, const char *filepath, int *retval); const char* (*parse_script) (char *w1, char *w2, char *w3, char *w4, const char *start, const char *buffer, const char *filepath, int options, int *retval); + void (*add_to_location) (struct npc_data *nd); + bool (*duplicate_script_sub) (struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options); + bool (*duplicate_shop_sub) (struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options); + bool (*duplicate_warp_sub) (struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options); + bool (*duplicate_sub) (struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options); const char* (*parse_duplicate) (char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath, int options, int *retval); int (*duplicate4instance) (struct npc_data *snd, int16 m); void (*setcells) (struct npc_data *nd); diff --git a/src/map/pc.c b/src/map/pc.c index 4d4f41521..43adf331b 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1126,8 +1126,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->bg_queue.client_has_bg_data = 0; sd->bg_queue.type = 0; - sd->queues = NULL; - sd->queues_count = 0; + VECTOR_INIT(sd->script_queues); sd->state.dialog = 0; @@ -2186,6 +2185,7 @@ int pc_bonus_subele(struct map_session_data* sd, unsigned char ele, short rate, int pc_bonus(struct map_session_data *sd,int type,int val) { struct status_data *bst; int bonus; + int i; nullpo_ret(sd); bst = &sd->base_status; @@ -2436,20 +2436,48 @@ int pc_bonus(struct map_session_data *sd,int type,int val) { sd->matk_rate += val; break; case SP_IGNORE_DEF_ELE: - if(val >= ELE_MAX) { + if( (val >= ELE_MAX && val != ELE_ALL) || (val < ELE_NEUTRAL) ) { ShowError("pc_bonus: SP_IGNORE_DEF_ELE: Invalid element %d\n", val); break; } - if(!sd->state.lr_flag) - sd->right_weapon.ignore_def_ele |= 1<<val; - else if(sd->state.lr_flag == 1) - sd->left_weapon.ignore_def_ele |= 1<<val; + if ( val == ELE_ALL ) { + for ( i = ELE_NEUTRAL; i < ELE_MAX; i++ ) { + if(!sd->state.lr_flag) + sd->right_weapon.ignore_def_ele |= 1<<i; + else if(sd->state.lr_flag == 1) + sd->left_weapon.ignore_def_ele |= 1<<i; + } + } else { + if(!sd->state.lr_flag) + sd->right_weapon.ignore_def_ele |= 1<<val; + else if(sd->state.lr_flag == 1) + sd->left_weapon.ignore_def_ele |= 1<<val; + } break; case SP_IGNORE_DEF_RACE: - if(!sd->state.lr_flag) - sd->right_weapon.ignore_def_race |= 1<<val; - else if(sd->state.lr_flag == 1) - sd->left_weapon.ignore_def_race |= 1<<val; + if (val == RC_MAX || (val > RC_NONDEMIPLAYER && val != RC_ALL) || val < RC_FORMLESS ) { + ShowWarning("pc_bonus: SP_IGNORE_DEF_RACE: Invalid Race(%d)\n",val); + break; + } + if ( val >= RC_MAX ) { + for ( i = RC_FORMLESS; i < RC_BOSS; i++ ) { + if ( (val == RC_NONPLAYER && i == RC_PLAYER) || + (val == RC_NONDEMIHUMAN && i == RC_DEMIHUMAN) || + (val == RC_DEMIPLAYER && (i != RC_PLAYER && i != RC_DEMIHUMAN)) || + (val == RC_NONDEMIPLAYER && (i == RC_PLAYER || i == RC_DEMIHUMAN)) + ) + continue; + if(!sd->state.lr_flag) + sd->right_weapon.ignore_def_race |= 1<<i; + else if(sd->state.lr_flag == 1) + sd->left_weapon.ignore_def_race |= 1<<i; + } + } else { + if(!sd->state.lr_flag) + sd->right_weapon.ignore_def_race |= 1<<val; + else if(sd->state.lr_flag == 1) + sd->left_weapon.ignore_def_race |= 1<<val; + } break; case SP_ATK_RATE: if(sd->state.lr_flag != 2) @@ -2470,16 +2498,40 @@ int pc_bonus(struct map_session_data *sd,int type,int val) { } break; case SP_IGNORE_MDEF_ELE: - if(val >= ELE_MAX) { + if( (val >= ELE_MAX && val != ELE_ALL) || (val < ELE_NEUTRAL) ) { ShowError("pc_bonus: SP_IGNORE_MDEF_ELE: Invalid element %d\n", val); break; } - if(sd->state.lr_flag != 2) - sd->bonus.ignore_mdef_ele |= 1<<val; + if (sd->state.lr_flag != 2) { + if ( val == ELE_ALL ) { + for ( i = ELE_NEUTRAL; i < ELE_MAX; i++ ) { + sd->bonus.ignore_mdef_ele |= 1<<i; + } + } else { + sd->bonus.ignore_mdef_ele |= 1<<val; + } + } break; case SP_IGNORE_MDEF_RACE: - if(sd->state.lr_flag != 2) - sd->bonus.ignore_mdef_race |= 1<<val; + if (val == RC_MAX || (val > RC_NONDEMIPLAYER && val != RC_ALL) || val < RC_FORMLESS ) { + ShowWarning("pc_bonus: SP_IGNORE_MDEF_RACE: Invalid Race(%d)\n",val); + break; + } + if(sd->state.lr_flag != 2) { + if ( val >= RC_MAX ) { + for ( i = RC_FORMLESS; i < RC_BOSS; i++ ) { + if ( (val == RC_NONPLAYER && i == RC_PLAYER) || + (val == RC_NONDEMIHUMAN && i == RC_DEMIHUMAN) || + (val == RC_DEMIPLAYER && (i != RC_PLAYER && i != RC_DEMIHUMAN)) || + (val == RC_NONDEMIPLAYER && (i == RC_PLAYER || i == RC_DEMIHUMAN)) + ) + continue; + sd->bonus.ignore_mdef_race |= 1<<i; + } + } else { + sd->bonus.ignore_mdef_race |= 1<<val; + } + } break; case SP_PERFECT_HIT_RATE: if(sd->state.lr_flag != 2 && sd->bonus.perfect_hit < val) @@ -2494,24 +2546,48 @@ int pc_bonus(struct map_session_data *sd,int type,int val) { sd->critical_rate+=val; break; case SP_DEF_RATIO_ATK_ELE: - if(val >= ELE_MAX) { + if( (val >= ELE_MAX && val != ELE_ALL) || (val < ELE_NEUTRAL) ) { ShowError("pc_bonus: SP_DEF_RATIO_ATK_ELE: Invalid element %d\n", val); break; } - if(!sd->state.lr_flag) - sd->right_weapon.def_ratio_atk_ele |= 1<<val; - else if(sd->state.lr_flag == 1) - sd->left_weapon.def_ratio_atk_ele |= 1<<val; + if ( val == ELE_ALL ) { + for ( i = ELE_NEUTRAL; i < ELE_MAX; i++ ) { + if(!sd->state.lr_flag) + sd->right_weapon.def_ratio_atk_ele |= 1<<i; + else if(sd->state.lr_flag == 1) + sd->left_weapon.def_ratio_atk_ele |= 1<<i; + } + } else { + if(!sd->state.lr_flag) + sd->right_weapon.def_ratio_atk_ele |= 1<<val; + else if(sd->state.lr_flag == 1) + sd->left_weapon.def_ratio_atk_ele |= 1<<val; + } break; case SP_DEF_RATIO_ATK_RACE: - if(val >= RC_MAX) { - ShowError("pc_bonus: SP_DEF_RATIO_ATK_RACE: Invalid race %d\n", val); + if (val == RC_MAX || (val > RC_NONDEMIPLAYER && val != RC_ALL) || val < RC_FORMLESS ) { + ShowWarning("pc_bonus: SP_DEF_RATIO_ATK_RACE: Invalid Race(%d)\n",val); break; } - if(!sd->state.lr_flag) - sd->right_weapon.def_ratio_atk_race |= 1<<val; - else if(sd->state.lr_flag == 1) - sd->left_weapon.def_ratio_atk_race |= 1<<val; + if ( val >= RC_MAX ) { + for ( i = RC_FORMLESS; i < RC_BOSS; i++ ) { + if ( (val == RC_NONPLAYER && i == RC_PLAYER) || + (val == RC_NONDEMIHUMAN && i == RC_DEMIHUMAN) || + (val == RC_DEMIPLAYER && (i != RC_PLAYER && i != RC_DEMIHUMAN)) || + (val == RC_NONDEMIPLAYER && (i == RC_PLAYER || i == RC_DEMIHUMAN)) + ) + continue; + if(!sd->state.lr_flag) + sd->right_weapon.def_ratio_atk_race |= 1<<i; + else if(sd->state.lr_flag == 1) + sd->left_weapon.def_ratio_atk_race |= 1<<i; + } + } else { + if(!sd->state.lr_flag) + sd->right_weapon.def_ratio_atk_race |= 1<<val; + else if(sd->state.lr_flag == 1) + sd->left_weapon.def_ratio_atk_race |= 1<<val; + } break; case SP_HIT_RATE: if(sd->state.lr_flag != 2) @@ -5449,11 +5525,11 @@ int pc_setpos(struct map_session_data* sd, unsigned short map_index, int x, int int i; sd->state.pmap = sd->bl.m; - for( i = 0; i < sd->queues_count; i++ ) { - struct hQueue *queue; - if( (queue = script->queue(sd->queues[i])) && queue->onMapChange[0] != '\0' ) { - pc->setregstr(sd, script->add_str("QMapChangeTo"), map->list[m].name); - npc->event(sd, queue->onMapChange, 0); + for (i = 0; i < VECTOR_LENGTH(sd->script_queues); i++) { + struct script_queue *queue = script->queue(VECTOR_INDEX(sd->script_queues, i)); + if (queue && queue->event_mapchange[0] != '\0') { + pc->setregstr(sd, script->add_str("@Queue_Destination_Map$"), map->list[m].name); + npc->event(sd, queue->event_mapchange, 0); } } @@ -7557,10 +7633,10 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { npc->event(sd, bgd->die_event, 0); } - for( i = 0; i < sd->queues_count; i++ ) { - struct hQueue *queue; - if( (queue = script->queue(sd->queues[i])) && queue->onDeath[0] != '\0' ) - npc->event(sd, queue->onDeath, 0); + for (i = 0; i < VECTOR_LENGTH(sd->script_queues); i++ ) { + struct script_queue *queue = script->queue(VECTOR_INDEX(sd->script_queues, i)); + if (queue && queue->event_death[0] != '\0') + npc->event(sd, queue->event_death, 0); } npc->script_event(sd,NPCE_DIE); diff --git a/src/map/pc.h b/src/map/pc.h index 2c8b24acf..62571f12a 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -531,8 +531,7 @@ END_ZEROED_BLOCK; enum bg_queue_types type; } bg_queue; - int *queues; - unsigned int queues_count; + VECTOR_DECL(int) script_queues; /* Made Possible Thanks to Yommy~! */ unsigned int cryptKey; ///< Packet obfuscation key to be used for the next received packet diff --git a/src/map/script.c b/src/map/script.c index 54d8d338d..cfc7ed052 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -4627,22 +4627,16 @@ void do_final_script(void) { aFree(script->buildin); - if( script->hqs ) { - for( i = 0; i < script->hqs; i++ ) { - if( script->hq[i].item != NULL ) - aFree(script->hq[i].item); - } + for (i = 0; i < VECTOR_LENGTH(script->hq); i++) { + VECTOR_CLEAR(VECTOR_INDEX(script->hq, i).entries); } - if (script->hqis && script->hqi) { - for( i = 0; i < script->hqis; i++ ) { - if( script->hqi[i].item != NULL ) - aFree(script->hqi[i].item); - } + VECTOR_CLEAR(script->hq); + + for (i = 0; i < VECTOR_LENGTH(script->hqi); i++) { + VECTOR_CLEAR(VECTOR_INDEX(script->hqi, i).entries); } - if( script->hq != NULL ) - aFree(script->hq); - if( script->hqi != NULL ) - aFree(script->hqi); + VECTOR_CLEAR(script->hqi); + if( script->word_buf != NULL ) aFree(script->word_buf); @@ -5020,6 +5014,9 @@ void do_init_script(bool minimal) { ers_chunk_size(script->st_ers, 10); ers_chunk_size(script->stack_ers, 10); + VECTOR_INIT(script->hq); + VECTOR_INIT(script->hqi); + script->parse_builtin(); script->read_constdb(); script->hardcoded_constants(); @@ -18789,329 +18786,434 @@ BUILDIN(montransform) { return true; } -struct hQueue *script_hqueue_get(int idx) { - if( idx < 0 || idx >= script->hqs || script->hq[idx].size == -1 ) +/** + * Returns the queue with he given index, if it exists. + * + * @param idx The queue index. + * + * @return The queue, or NULL if it doesn't exist. + */ +struct script_queue *script_hqueue_get(int idx) +{ + if (idx < 0 || idx >= VECTOR_LENGTH(script->hq) || !VECTOR_INDEX(script->hq, idx).valid) return NULL; - return &script->hq[idx]; + return &VECTOR_INDEX(script->hq, idx); } -int script_hqueue_create(void) { - int idx = script->hqs; + +/** + * Creates a new queue. + * + * @return The index of the created queue. + */ +int script_hqueue_create(void) +{ + struct script_queue *queue = NULL; int i; - for(i = 0; i < script->hqs; i++) { - if( script->hq[i].size == -1 ) { - break; - } + ARR_FIND(0, VECTOR_LENGTH(script->hq), i, !VECTOR_INDEX(script->hq, i).valid); + + if (i == VECTOR_LENGTH(script->hq)) { + VECTOR_ENSURE(script->hq, 1, 1); + VECTOR_PUSHZEROED(script->hq); } + queue = &VECTOR_INDEX(script->hq, i); - if( i == script->hqs ) { - RECREATE(script->hq, struct hQueue, ++script->hqs); - script->hq[ idx ].item = NULL; - } else - idx = i; - - script->hq[ idx ].id = idx; - script->hq[ idx ].size = 0; - script->hq[ idx ].items = 0; - script->hq[ idx ].onDeath[0] = '\0'; - script->hq[ idx ].onLogOut[0] = '\0'; - script->hq[ idx ].onMapChange[0] = '\0'; - return idx; -} -/* set .@id,queue(); */ -/* creates queue, returns created queue id */ -BUILDIN(queue) { + memset(&VECTOR_INDEX(script->hq, i), 0, sizeof(VECTOR_INDEX(script->hq, i))); + + queue->id = i; + queue->valid = true; + return i; +} + +/** + * Script command queue: Creates a queue and returns its id. + * + * @code{.herc} + * .@queue_id = queue(); + * @endcode + */ +BUILDIN(queue) +{ script_pushint(st,script->queue_create()); return true; } -/* set .@length,queuesize(.@queue_id); */ -/* returns queue length */ -BUILDIN(queuesize) { + +/** + * Script command queuesize: Returns the length of the given queue. + * + * Returns 0 on error. + * + * \code{.herc} + * .@size = queuesize(<queue id>); + * \endcode + */ +BUILDIN(queuesize) +{ int idx = script_getnum(st, 2); - if( idx < 0 || idx >= script->hqs || script->hq[idx].size == -1 ) { + if (idx < 0 || idx >= VECTOR_LENGTH(script->hq) || !VECTOR_INDEX(script->hq, idx).valid) { ShowWarning("buildin_queuesize: unknown queue id %d\n",idx); script_pushint(st, 0); - } else { - script_pushint(st, script->hq[ idx ].items); + return true; } + script_pushint(st, VECTOR_LENGTH(VECTOR_INDEX(script->hq, idx).entries)); return true; } + +/** + * Adds an entry to the given queue. + * + * @param idx The queue index. + * @param var The entry to add. + * @retval false if the queue is invalid or the entry is already in the queue. + * @retval true in case of success. + */ bool script_hqueue_add(int idx, int var) { - if( idx < 0 || idx >= script->hqs || script->hq[idx].size == -1 ) { - ShowWarning("script_hqueue_add: unknown queue id %d\n",idx); - return true; - } else { - int i; - for (i = 0; i < script->hq[idx].size; i++) { - if (script->hq[idx].item[i] == var) { - return true; - } - } - - if (i == script->hq[idx].size) { - struct map_session_data *sd; + int i; + struct map_session_data *sd = NULL; + struct script_queue *queue = NULL; - for (i = 0; i < script->hq[idx].size; i++) { - if( script->hq[idx].item[i] == 0 ) { - break; - } - } + if (idx < 0 || idx >= VECTOR_LENGTH(script->hq) || !VECTOR_INDEX(script->hq, idx).valid) { + ShowWarning("script_hqueue_add: unknown queue id %d\n",idx); + return false; + } - if (i == script->hq[idx].size) - RECREATE(script->hq[idx].item, int, ++script->hq[idx].size); + queue = &VECTOR_INDEX(script->hq, idx); - script->hq[idx].item[i] = var; - script->hq[idx].items++; - if (var >= START_ACCOUNT_NUM && (sd = map->id2sd(var)) != NULL) { - for (i = 0; i < sd->queues_count; i++) { - if (sd->queues[i] == -1) { - break; - } - } + ARR_FIND(0, VECTOR_LENGTH(queue->entries), i, VECTOR_INDEX(queue->entries, i) == var); + if (i != VECTOR_LENGTH(queue->entries)) { + return false; // Entry already exists + } - if (i == sd->queues_count) - RECREATE(sd->queues, int, ++sd->queues_count); + VECTOR_ENSURE(queue->entries, 1, 1); + VECTOR_PUSH(queue->entries, var); - sd->queues[i] = idx; - } - - } + if (var >= START_ACCOUNT_NUM && (sd = map->id2sd(var)) != NULL) { + VECTOR_ENSURE(sd->script_queues, 1, 1); + VECTOR_PUSH(sd->script_queues, idx); } - return false; + return true; } -/* queueadd(.@queue_id,.@var_id); */ -/* adds a new entry to the queue, returns 1 if already in queue, 0 otherwise */ -BUILDIN(queueadd) { + +/** + * Script command queueadd: Adds a new entry to the given queue. + * + * Returns 0 (false) if already in queue or in case of error, 1 (true) + * otherwise. + * + * @code{.herc} + * .@size = queuesize(.@queue_id); + * @endcode + */ +BUILDIN(queueadd) +{ int idx = script_getnum(st, 2); int var = script_getnum(st, 3); - script_pushint(st,script->queue_add(idx,var)?1:0); + if (script->queue_add(idx, var)) + script_pushint(st, 1); + else + script_pushint(st, 0); return true; } -bool script_hqueue_remove(int idx, int var) { - if( idx < 0 || idx >= script->hqs || script->hq[idx].size == -1 ) { - ShowWarning("script_hqueue_remove: unknown queue id %d (used with var %d)\n",idx,var); - return true; - } else { - int i; - for(i = 0; i < script->hq[idx].size; i++) { - if( script->hq[idx].item[i] == var ) { - break; - } - } +/** + * Removes an entry from the given queue. + * + * @param idx The queue index. + * @param var The entry to remove. + * @retval true if the entry was removed. + * @retval false if the entry wasn't in queue. + */ +bool script_hqueue_remove(int idx, int var) +{ + int i; + struct map_session_data *sd = NULL; + struct script_queue *queue = NULL; - if( i != script->hq[idx].size ) { - struct map_session_data *sd; + if (idx < 0 || idx >= VECTOR_LENGTH(script->hq) || !VECTOR_INDEX(script->hq, idx).valid) { + ShowWarning("script_hqueue_remove: unknown queue id %d (used with var %d)\n",idx,var); + return false; + } - script->hq[idx].item[i] = -1; - script->hq[idx].items--; + queue = &VECTOR_INDEX(script->hq, idx); - if (var >= START_ACCOUNT_NUM && (sd = map->id2sd(var)) != NULL) { - for(i = 0; i < sd->queues_count; i++) { - if( sd->queues[i] == idx ) { - break; - } - } + ARR_FIND(0, VECTOR_LENGTH(queue->entries), i, VECTOR_INDEX(queue->entries, i) == var); + if (i == VECTOR_LENGTH(queue->entries)) + return false; - if( i != sd->queues_count ) - sd->queues[i] = -1; - } + VECTOR_ERASE(queue->entries, i); - } + if (var >= START_ACCOUNT_NUM && (sd = map->id2sd(var)) != NULL) { + ARR_FIND(0, VECTOR_LENGTH(sd->script_queues), i, VECTOR_INDEX(sd->script_queues, i) == queue->id); + + if (i != VECTOR_LENGTH(sd->script_queues)) + VECTOR_ERASE(sd->script_queues, i); } - return false; + return true; } -/* queueremove(.@queue_id,.@var_id); */ -/* removes a entry from the queue, returns 1 if not in queue, 0 otherwise */ -BUILDIN(queueremove) { + +/** + * Script command queueremove: Removes an entry from a queue. + * + * Returns 1 (true) on success, 0 (false) if the item wasn't in queue. + * + * @code{.herc} + * queueremove(.@queue_id, .@value); + * @endcode + */ +BUILDIN(queueremove) +{ int idx = script_getnum(st, 2); int var = script_getnum(st, 3); - script_pushint(st, script->queue_remove(idx,var)?1:0); + if (script->queue_remove(idx,var)) + script_pushint(st, 1); + else + script_pushint(st, 0); return true; } -/* queueopt(.@queue_id,optionType,<optional val>); */ -/* modifies the queue's options, when val is not provided the option is removed */ -/* when OnMapChange event is triggered, it sets a temp char var @QMapChangeTo$ with the destination map name */ -/* returns 1 when fails, 0 on success */ -BUILDIN(queueopt) { +/** + * Script command queueopt: Modifies the options of a queue. + * + * When the option value isn't provided, the option is removed. + * + * Returns 1 (true) on success, 0 (false) on failure. + * + * The optionType is one of: + * - QUEUEOPT_DEATH + * - QUEUEOPT_LOGOUT + * - QUEUEOPT_MAPCHANGE + * + * When the QUEUEOPT_MAPCHANGE event is triggered, it sets a temporary + * character variable \c @Queue_Destination_Map$ with the destination map name. + * + * @code{.herc} + * queueopt(.@queue_id, optionType, <optional val>); + * @endcode + */ +BUILDIN(queueopt) +{ int idx = script_getnum(st, 2); int var = script_getnum(st, 3); + struct script_queue *queue = NULL; - if( idx < 0 || idx >= script->hqs || script->hq[idx].size == -1 ) { + if (idx < 0 || idx >= VECTOR_LENGTH(script->hq) || !VECTOR_INDEX(script->hq, idx).valid) { ShowWarning("buildin_queueopt: unknown queue id %d\n",idx); - script_pushint(st, 1); - } else if( var <= HQO_NONE || var >= HQO_MAX ) { - ShowWarning("buildin_queueopt: unknown optionType %d\n",var); - script_pushint(st, 1); - } else { - switch( (enum hQueueOpt)var ) { - case HQO_OnDeath: - if( script_hasdata(st, 4) ) - safestrncpy(script->hq[idx].onDeath, script_getstr(st, 4), EVENT_NAME_LENGTH); - else - script->hq[idx].onDeath[0] = '\0'; - break; - case HQO_onLogOut: - if( script_hasdata(st, 4) ) - safestrncpy(script->hq[idx].onLogOut, script_getstr(st, 4), EVENT_NAME_LENGTH); - else - script->hq[idx].onLogOut[0] = '\0'; - break; - case HQO_OnMapChange: - if( script_hasdata(st, 4) ) - safestrncpy(script->hq[idx].onMapChange, script_getstr(st, 4), EVENT_NAME_LENGTH); - else - script->hq[idx].onMapChange[0] = '\0'; - break; - default: - ShowWarning("buildin_queueopt: unsupported optionType %d\n",var); - script_pushint(st, 1); - break; - } + script_pushint(st, 0); + return true; } + queue = &VECTOR_INDEX(script->hq, idx); + + switch (var) { + case SQO_ONDEATH: + if (script_hasdata(st, 4)) + safestrncpy(queue->event_death, script_getstr(st, 4), EVENT_NAME_LENGTH); + else + queue->event_death[0] = '\0'; + break; + case SQO_ONLOGOUT: + if (script_hasdata(st, 4)) + safestrncpy(queue->event_logout, script_getstr(st, 4), EVENT_NAME_LENGTH); + else + queue->event_logout[0] = '\0'; + break; + case SQO_ONMAPCHANGE: + if (script_hasdata(st, 4)) + safestrncpy(queue->event_mapchange, script_getstr(st, 4), EVENT_NAME_LENGTH); + else + queue->event_mapchange[0] = '\0'; + break; + default: + ShowWarning("buildin_queueopt: unsupported optionType %d\n",var); + script_pushint(st, 0); + return true; + } + script_pushint(st, 1); return true; } + +/** + * Deletes a queue. + * + * @param idx The queue index. + * + * @retval true if the queue was correctly deleted. + * @retval false if the queue didn't exist. + */ bool script_hqueue_del(int idx) { - if( idx < 0 || idx >= script->hqs || script->hq[idx].size == -1 ) { - ShowWarning("script_queue_del: unknown queue id %d\n",idx); - return true; - } else { - int i; - for (i = 0; i < script->hq[idx].size; i++) { - struct map_session_data *sd; - if (script->hq[idx].item[i] >= START_ACCOUNT_NUM && (sd = map->id2sd(script->hq[idx].item[i])) != NULL) { - int j; - for(j = 0; j < sd->queues_count; j++) { - if( sd->queues[j] == script->hq[idx].item[i] ) { - break; - } - } + if (!script->queue_clear(idx)) + return false; - if( j != sd->queues_count ) - sd->queues[j] = -1; - } - script->hq[idx].item[i] = 0; - } + VECTOR_INDEX(script->hq, idx).valid = false; - script->hq[idx].size = -1; - script->hq[idx].items = 0; - } - return false; + return true; } -/* queuedel(.@queue_id); */ -/* deletes queue of id .@queue_id, returns 1 if id not found, 0 otherwise */ -BUILDIN(queuedel) { + +/** + * Script command queuedel: Deletes a queue. + * + * Returns 1 (true) on success, 0 (false) if the queue doesn't exist. + * + * @code{.herc} + * queuedel(.@queue_id); + * @endcode + */ +BUILDIN(queuedel) +{ int idx = script_getnum(st, 2); - script_pushint(st,script->queue_del(idx)?1:0); + if (script->queue_del(idx)) + script_pushint(st, 1); + else + script_pushint(st, 0); return true; } -void script_hqueue_clear(int idx) { - if( idx < 0 || idx >= script->hqs || script->hq[idx].size == -1 ) { + +/** + * Clears a queue. + * + * @param idx The queue index. + * + * @retval true if the queue was correctly cleared. + * @retval false if the queue didn't exist. + */ +bool script_hqueue_clear(int idx) +{ + struct script_queue *queue = NULL; + + if (idx < 0 || idx >= VECTOR_LENGTH(script->hq) || !VECTOR_INDEX(script->hq, idx).valid) { ShowWarning("script_hqueue_clear: unknown queue id %d\n",idx); - return; - } else { - struct map_session_data *sd; - int i, j; + return false; + } - for(i = 0; i < script->hq[idx].size; i++) { - if( script->hq[idx].item[i] > 0 ) { + queue = &VECTOR_INDEX(script->hq, idx); - if (script->hq[idx].item[i] >= START_ACCOUNT_NUM && (sd = map->id2sd(script->hq[idx].item[i])) != NULL) { - for(j = 0; j < sd->queues_count; j++) { - if( sd->queues[j] == idx ) { - break; - } - } + while (VECTOR_LENGTH(queue->entries) > 0) { + int entry = VECTOR_POP(queue->entries); + struct map_session_data *sd = NULL; - if( j != sd->queues_count ) - sd->queues[j] = -1; - } - script->hq[idx].item[i] = 0; - } + if (entry >= START_ACCOUNT_NUM && (sd = map->id2sd(entry)) != NULL) { + int i; + ARR_FIND(0, VECTOR_LENGTH(sd->script_queues), i, VECTOR_INDEX(sd->script_queues, i) == queue->id); + + if (i != VECTOR_LENGTH(sd->script_queues)) + VECTOR_ERASE(sd->script_queues, i); } - script->hq[idx].items = 0; } - return; + VECTOR_CLEAR(queue->entries); + + return true; } -/* set .@id, queueiterator(.@queue_id); */ -/* creates a new queue iterator, returns its id */ -BUILDIN(queueiterator) { + +/** + * Script command queueiterator: Creates a new queue iterator. + * + * Returns the iterator's id or -1 in case of failure. + * + * @code{.herc} + * .@id = queueiterator(.@queue_id); + * @endcode + */ +BUILDIN(queueiterator) +{ int qid = script_getnum(st, 2); - struct hQueue *queue = NULL; - int idx = script->hqis; + struct script_queue *queue = NULL; + struct script_queue_iterator *iter = NULL; int i; - if( qid < 0 || qid >= script->hqs || script->hq[qid].size == -1 || !(queue = script->queue(qid)) ) { + if (qid < 0 || qid >= VECTOR_LENGTH(script->hq) || !VECTOR_INDEX(script->hq, qid).valid || !(queue = script->queue(qid))) { ShowWarning("queueiterator: invalid queue id %d\n",qid); + script_pushint(st, -1); return true; } - /* what if queue->size is 0? (iterating a empty queue?) */ - if( queue->size <= 0 ) { - ShowWarning("queueiterator: attempting to iterate on on empty queue id %d!\n",qid); - return true; - } + ARR_FIND(0, VECTOR_LENGTH(script->hqi), i, !VECTOR_INDEX(script->hqi, i).valid); - for(i = 0; i < script->hqis; i++) { - if( script->hqi[i].items == -1 ) { - break; - } + if (i == VECTOR_LENGTH(script->hqi)) { + VECTOR_ENSURE(script->hqi, 1, 1); + VECTOR_PUSHZEROED(script->hqi); } - if( i == script->hqis ) { - RECREATE(script->hqi, struct hQueueIterator, ++script->hqis); - script->hqi[ idx ].item = NULL; - } else - idx = i; - - RECREATE(script->hqi[ idx ].item, int, queue->size); + iter = &VECTOR_INDEX(script->hqi, i); - memcpy(script->hqi[idx].item, queue->item, sizeof(int)*queue->size); + VECTOR_ENSURE(iter->entries, VECTOR_LENGTH(queue->entries), 1); + VECTOR_PUSHARRAY(iter->entries, VECTOR_DATA(queue->entries), VECTOR_LENGTH(queue->entries)); - script->hqi[ idx ].items = queue->size; - script->hqi[ idx ].pos = 0; + iter->pos = 0; + iter->valid = true; - script_pushint(st,idx); + script_pushint(st, i); return true; } -/* Queue Iterator Get Next */ -/* returns next/first member in the iterator, 0 if none */ -BUILDIN(qiget) { + +/** + * Script command qiget: returns the next/first member in the iterator. + * + * Returns 0 if there's no next item. + * + * @code{.herc} + * for (.@i = qiget(.@iter); qicheck(.@iter); .@i = qiget(.@iter)) { + * // ... + * } + * @endcode + */ +BUILDIN(qiget) +{ int idx = script_getnum(st, 2); + struct script_queue_iterator *it = NULL; - if( idx < 0 || idx >= script->hqis ) { + if (idx < 0 || idx >= VECTOR_LENGTH(script->hqi) || !VECTOR_INDEX(script->hqi, idx).valid) { ShowWarning("buildin_qiget: unknown queue iterator id %d\n",idx); script_pushint(st, 0); - } else if (script->hqi[idx].pos >= script->hqi[idx].items) { - script_pushint(st, 0); - } else { - struct hQueueIterator *it = &script->hqi[idx]; - script_pushint(st, it->item[it->pos++]); + return true; } + it = &VECTOR_INDEX(script->hqi, idx); + + if (it->pos >= VECTOR_LENGTH(it->entries)) { + if (it->pos == VECTOR_LENGTH(it->entries)) + ++it->pos; // Move beyond the last position to invalidate qicheck + script_pushint(st, 0); + return true; + } + script_pushint(st, VECTOR_INDEX(it->entries, it->pos++)); return true; } -/* Queue Iterator Check */ -/* returns 1:0 if there is a next member in the iterator */ -BUILDIN(qicheck) { + +/** + * Script command qicheck: Checks if the current member in the given iterator (from the last call to qiget) exists. + * + * Returns 1 if it exists, 0 otherwise. + * + * @code{.herc} + * for (.@i = qiget(.@iter); qicheck(.@iter); .@i = qiget(.@iter)) { + * // ... + * } + * @endcode + */ +BUILDIN(qicheck) +{ int idx = script_getnum(st, 2); + struct script_queue_iterator *it = NULL; - if( idx < 0 || idx >= script->hqis ) { + if (idx < 0 || idx >= VECTOR_LENGTH(script->hqi) || !VECTOR_INDEX(script->hqi, idx).valid) { ShowWarning("buildin_qicheck: unknown queue iterator id %d\n",idx); script_pushint(st, 0); - } else if (script->hqi[idx].pos >= script->hqi[idx].items) { + return true; + } + + it = &VECTOR_INDEX(script->hqi, idx); + + if (it->pos <= 0 || it->pos > VECTOR_LENGTH(it->entries)) { script_pushint(st, 0); } else { script_pushint(st, 1); @@ -19119,20 +19221,37 @@ BUILDIN(qicheck) { return true; } -/* Queue Iterator Check */ -BUILDIN(qiclear) { + +/** + * Script command qiclear: Destroys a queue iterator. + * + * Returns true (1) on success, false (0) on failure. + * + * @code{.herc} + * qiclear(.@iter); + * @endcode + */ +BUILDIN(qiclear) +{ int idx = script_getnum(st, 2); + struct script_queue_iterator *it = NULL; - if( idx < 0 || idx >= script->hqis ) { + if (idx < 0 || idx >= VECTOR_LENGTH(script->hqi) || !VECTOR_INDEX(script->hqi, idx).valid) { ShowWarning("buildin_qiclear: unknown queue iterator id %d\n",idx); - script_pushint(st, 1); - } else { - script->hqi[idx].items = -1; script_pushint(st, 0); + return true; } + it = &VECTOR_INDEX(script->hqi, idx); + + VECTOR_CLEAR(it->entries); + it->pos = 0; + it->valid = false; + + script_pushint(st, 1); return true; } + /** * packageitem({<optional container_item_id>}) * when no item id is provided it tries to assume it comes from the current item id being processed (if any) @@ -20366,7 +20485,7 @@ void script_parse_builtin(void) { BUILDIN_DEF(showevent, "i?"), /** - * hQueue [Ind/Hercules] + * script_queue [Ind/Hercules] **/ BUILDIN_DEF(queue,""), BUILDIN_DEF(queuesize,"i"), @@ -20573,10 +20692,6 @@ void script_defaults(void) { script->stack_ers = NULL; script->array_ers = NULL; - script->hq = NULL; - script->hqi = NULL; - script->hqs = script->hqis = 0; - script->buildin = NULL; script->buildin_count = 0; diff --git a/src/map/script.h b/src/map/script.h index ff660dec8..b153cf81a 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -223,12 +223,13 @@ typedef enum c_op { #endif // PCRE_SUPPORT } c_op; -enum hQueueOpt { - HQO_NONE, - HQO_onLogOut, - HQO_OnDeath, - HQO_OnMapChange, - HQO_MAX, +/// Script queue options +enum ScriptQueueOptions { + SQO_NONE, ///< No options set + SQO_ONLOGOUT, ///< Execute event on logout + SQO_ONDEATH, ///< Execute event on death + SQO_ONMAPCHANGE, ///< Execute event on map change + SQO_MAX, }; enum e_script_state { RUN,STOP,END,RERUNLINE,GOTO,RETFUNC,CLOSE }; @@ -391,22 +392,27 @@ struct script_stack { struct reg_db scope; ///< scope variables }; -/* [Ind/Hercules] */ -struct hQueue { - int id; - int *item; - int items;/* how many actual items are in the array */ - int size;/* size of the *item array, not the current amount of items in it since it can have empty slots */ - /* events */ - char onLogOut[EVENT_NAME_LENGTH]; - char onDeath[EVENT_NAME_LENGTH]; - char onMapChange[EVENT_NAME_LENGTH]; +/** + * Data structure to represent a script queue. + * @author Ind/Hercules + */ +struct script_queue { + int id; ///< Queue identifier + VECTOR_DECL(int) entries; ///< Items in the queue. + bool valid; ///< Whether the queue is valid. + /// Events + char event_logout[EVENT_NAME_LENGTH]; ///< Logout event + char event_death[EVENT_NAME_LENGTH]; ///< Death event + char event_mapchange[EVENT_NAME_LENGTH]; ///< Map change event }; -struct hQueueIterator { - int *item; - int items; - int pos; +/** + * Iterator for a struct script_queue. + */ +struct script_queue_iterator { + VECTOR_DECL(int) entries; ///< Entries in the queue (iterator's cached copy) + bool valid; ///< Whether the queue is valid (initialized - not necessarily having entries available) + int pos; ///< Iterator's cursor }; struct script_state { @@ -515,9 +521,8 @@ struct script_interface { struct eri *st_ers; struct eri *stack_ers; /* */ - struct hQueue *hq; - struct hQueueIterator *hqi; - int hqs, hqis; + VECTOR_DECL(struct script_queue) hq; + VECTOR_DECL(struct script_queue_iterator) hqi; /* */ char **buildin; unsigned int buildin_count; @@ -667,12 +672,12 @@ struct script_interface { void (*setd_sub) (struct script_state *st, struct map_session_data *sd, const char *varname, int elem, void *value, struct reg_db *ref); void (*attach_state) (struct script_state* st); /* */ - struct hQueue *(*queue) (int idx); + struct script_queue *(*queue) (int idx); bool (*queue_add) (int idx, int var); bool (*queue_del) (int idx); bool (*queue_remove) (int idx, int var); int (*queue_create) (void); - void (*queue_clear) (int idx); + bool (*queue_clear) (int idx); /* */ const char * (*parse_curly_close) (const char *p); const char * (*parse_syntax_close) (const char *p); diff --git a/src/map/skill.c b/src/map/skill.c index cdf1c031f..8984bf0fa 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -11878,8 +11878,8 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 //Take into account these hit more times than the timer interval can handle. do skill->attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0); - while(--src->val2 && x == bl->x && y == bl->y - && ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status->isdead(bl)); + while (src->alive != 0 && --src->val2 != 0 && x == bl->x && y == bl->y + && ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status->isdead(bl)); if (src->val2<=0) skill->delunit(src); @@ -11961,8 +11961,8 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 sg->limit = DIFF_TICK32(tick,sg->tick); break; } - } while( x == bl->x && y == bl->y && sg->alive_count - && ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status->isdead(bl) ); + } while (src->alive != 0 && x == bl->x && y == bl->y && sg->alive_count != 0 + && ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status->isdead(bl)); map->freeblock_unlock(); } break; diff --git a/src/map/status.c b/src/map/status.c index 1a07f74dc..4cd73d3ac 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -823,6 +823,7 @@ void initChangeTables(void) { status->dbs->IconChangeTable[SC_L_LIFEPOTION] = SI_L_LIFEPOTION; status->dbs->IconChangeTable[SC_ATKER_BLOOD] = SI_ATKER_BLOOD; status->dbs->IconChangeTable[SC_TARGET_BLOOD] = SI_TARGET_BLOOD; + status->dbs->IconChangeTable[SC_ACARAJE] = SI_ACARAJE; // Mercenary Bonus Effects status->dbs->IconChangeTable[SC_MER_FLEE] = SI_MER_FLEE; status->dbs->IconChangeTable[SC_MER_ATK] = SI_MER_ATK; @@ -1008,12 +1009,13 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_MUSTLE_M] |= SCB_MAXHP; status->dbs->ChangeFlagTable[SC_LIFE_FORCE_F] |= SCB_MAXSP; status->dbs->ChangeFlagTable[SC_EXTRACT_WHITE_POTION_Z] |= SCB_REGEN; - status->dbs->ChangeFlagTable[SC_VITATA_500] |= SCB_REGEN; + status->dbs->ChangeFlagTable[SC_VITATA_500] |= SCB_REGEN | SCB_MAXSP; status->dbs->ChangeFlagTable[SC_EXTRACT_SALAMINE_JUICE] |= SCB_ASPD; status->dbs->ChangeFlagTable[SC_REBOUND] |= SCB_SPEED|SCB_REGEN; status->dbs->ChangeFlagTable[SC_DEFSET] |= SCB_DEF|SCB_DEF2; status->dbs->ChangeFlagTable[SC_MDEFSET] |= SCB_MDEF|SCB_MDEF2; status->dbs->ChangeFlagTable[SC_MYSTERIOUS_POWDER] |= SCB_MAXHP; + status->dbs->ChangeFlagTable[SC_ACARAJE] |= SCB_HIT | SCB_ASPD; status->dbs->ChangeFlagTable[SC_ALL_RIDING] = SCB_SPEED; status->dbs->ChangeFlagTable[SC_WEDDING] = SCB_SPEED; @@ -3328,8 +3330,8 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str regen->flag &=~RGN_SP; if(sc->data[SC_EXTRACT_WHITE_POTION_Z]) regen->rate.hp += regen->rate.hp * sc->data[SC_EXTRACT_WHITE_POTION_Z]->val1/100; - if(sc->data[SC_VITATA_500]) - regen->rate.sp += regen->rate.sp * sc->data[SC_VITATA_500]->val1/100; + if (sc->data[SC_VITATA_500]) + regen->rate.sp += regen->rate.sp * sc->data[SC_VITATA_500]->val1 / 100; } /// Recalculates parts of an object's battle status according to the specified flags. /// @param flag bitfield of values from enum scb_flag @@ -4792,7 +4794,9 @@ signed short status_calc_hit(struct block_list *bl, struct status_change *sc, in hit /= 2; if(sc->data[SC_ILLUSIONDOPING]) hit -= hit * (5 + sc->data[SC_ILLUSIONDOPING]->val1) / 100; //custom - + if (sc->data[SC_ACARAJE]) + hit += sc->data[SC_ACARAJE]->val1; + return (short)cap_value(hit,1,SHRT_MAX); } @@ -5477,6 +5481,8 @@ short status_calc_aspd(struct block_list *bl, struct status_change *sc, short fl bonus += sc->data[SC_GS_GATLINGFEVER]->val1; if (sc->data[SC_STAR_COMFORT]) bonus += 3 * sc->data[SC_STAR_COMFORT]->val1; + if (sc->data[SC_ACARAJE]) + bonus += sc->data[SC_ACARAJE]->val2; } return (bonus + pots); @@ -5638,6 +5644,8 @@ short status_calc_aspd_rate(struct block_list *bl, struct status_change *sc, int aspd_rate += sc->data[SC_PAIN_KILLER]->val2 * 10; if( sc->data[SC_GOLDENE_FERSE]) aspd_rate -= sc->data[SC_GOLDENE_FERSE]->val3 * 10; + if (sc->data[SC_ACARAJE]) + aspd_rate += sc->data[SC_ACARAJE]->val2 * 10; return (short)cap_value(aspd_rate,0,SHRT_MAX); } @@ -5748,6 +5756,8 @@ unsigned int status_calc_maxsp(struct block_list *bl, struct status_change *sc, maxsp += maxsp * sc->data[SC_LIFE_FORCE_F]->val1/100; if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3) maxsp += 50; + if (sc->data[SC_VITATA_500]) + maxsp += maxsp * sc->data[SC_VITATA_500]->val2 / 100; return cap_value(maxsp,1,UINT_MAX); } diff --git a/src/map/status.h b/src/map/status.h index 800e3de9f..7635248d0 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -745,6 +745,8 @@ typedef enum sc_type { SC_LJOSALFAR, SC_MERMAID_LONGING, + + SC_ACARAJE, // 590 SC_MAX, //Automatically updated max, used in for's to check we are within bounds. } sc_type; diff --git a/src/map/unit.c b/src/map/unit.c index 7c253c5c2..3046db9e1 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2645,10 +2645,7 @@ int unit_free(struct block_list *bl, clr_type clrtype) { aFree(sd->instance); sd->instance = NULL; } - if( sd->queues != NULL ) { - aFree(sd->queues); - sd->queues = NULL; - } + VECTOR_CLEAR(sd->script_queues); if( sd->quest_log != NULL ) { aFree(sd->quest_log); sd->quest_log = NULL; |