diff options
Diffstat (limited to 'src/map')
-rw-r--r-- | src/map/Makefile.in | 9 | ||||
-rw-r--r-- | src/map/atcommand.c | 4 | ||||
-rw-r--r-- | src/map/battle.c | 13 | ||||
-rw-r--r-- | src/map/battle.h | 3 | ||||
-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 | 569 | ||||
-rw-r--r-- | src/map/npc.h | 49 | ||||
-rw-r--r-- | src/map/packets_struct.h | 8 | ||||
-rw-r--r-- | src/map/party.h | 23 | ||||
-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 | 26 | ||||
-rw-r--r-- | src/map/status.c | 308 | ||||
-rw-r--r-- | src/map/status.h | 39 | ||||
-rw-r--r-- | src/map/unit.c | 5 | ||||
-rw-r--r-- | src/map/unit.h | 12 |
21 files changed, 1279 insertions, 802 deletions
diff --git a/src/map/Makefile.in b/src/map/Makefile.in index ac1b4d837..ca6f70472 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -45,13 +45,6 @@ else MAP_SERVER_SQL_DEPENDS=needs_mysql endif -HAVE_PCRE=@HAVE_PCRE@ -ifeq ($(HAVE_PCRE),yes) - PCRE_CFLAGS=-DPCRE_SUPPORT @PCRE_CFLAGS@ -else - PCRE_CFLAGS= -endif - @SET_MAKE@ CC = @CC@ @@ -113,7 +106,7 @@ map-server: ../../map-server@EXEEXT@ obj_sql/%.o: %.c $(MAP_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) | obj_sql @echo " CC $<" - @$(CC) @CFLAGS@ @DEFS@ $(COMMON_INCLUDE) $(THIRDPARTY_INCLUDE) $(PCRE_CFLAGS) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< + @$(CC) @CFLAGS@ @DEFS@ $(COMMON_INCLUDE) $(THIRDPARTY_INCLUDE) @PCRE_CFLAGS@ @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< # missing object files $(COMMON_D)/obj_all/common.a: diff --git a/src/map/atcommand.c b/src/map/atcommand.c index ac64bef75..61ac124ca 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 1cbc3baa5..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 ) @@ -7103,6 +7106,7 @@ static const struct battle_data { { "mvp_tomb_enabled", &battle_config.mvp_tomb_enabled, 1, 0, 1 }, { "feature.atcommand_suggestions", &battle_config.atcommand_suggestions_enabled, 0, 0, 1 }, { "min_npc_vendchat_distance", &battle_config.min_npc_vendchat_distance, 3, 0, 100 }, + { "vendchat_near_hiddennpc", &battle_config.vendchat_near_hiddennpc, 0, 0, 1 }, { "atcommand_mobinfo_type", &battle_config.atcommand_mobinfo_type, 0, 0, 1 }, { "homunculus_max_level", &battle_config.hom_max_level, 99, 0, MAX_LEVEL, }, { "homunculus_S_max_level", &battle_config.hom_S_max_level, 150, 0, MAX_LEVEL, }, @@ -7136,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 0ebc0aeb9..68a427e72 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -471,6 +471,7 @@ struct Battle_Config { int atcommand_suggestions_enabled; int min_npc_vendchat_distance; + int vendchat_near_hiddennpc; int atcommand_mobinfo_type; int mob_size_influence; // Enable modifications on earned experience, drop rates and monster status depending on monster size. [mkbu95] @@ -509,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 42d9af27b..f79b15171 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 7044fefcf..21970a42f 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] @@ -137,6 +137,9 @@ int npc_isnear_sub(struct block_list* bl, va_list args) { if( nd->option & (OPTION_HIDE|OPTION_INVISIBLE) ) return 0; + if( battle_config.vendchat_near_hiddennpc && ( nd->class_ == FAKE_NPC || nd->class_ == HIDDEN_WARP_CLASS ) ) + return 0; + return 1; } @@ -2244,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 ) @@ -2404,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) { @@ -2458,37 +2512,12 @@ 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 // Support for using Constants in place of NPC View IDs. int npc_parseview(const char* w4, const char* start, const char* buffer, const char* filepath) { - int val = -1, i = 0; + int val = FAKE_NPC, i = 0; char viewid[1024]; // Max size of name from const.txt, see script->read_constdb. // Extract view ID / constant @@ -2512,7 +2541,7 @@ int npc_parseview(const char* w4, const char* start, const char* buffer, const c } else { // NPC has an ID specified for view id. val = atoi(w4); - if (val != -1) + if (val != FAKE_NPC) // TODO: Add this to the constants table and replace -1 with FAKE_NPC in the scripts, then remove this check. ShowWarning("npc_parseview: Use of numeric NPC view IDs is deprecated and may be removed in a future update. Please use NPC view constants instead. ID '%d' specified in file '%s', line '%d'.\n", val, filepath, strline(buffer, start-buffer)); } @@ -2523,8 +2552,7 @@ int npc_parseview(const char* w4, const char* start, const char* buffer, const c // Checks if given view is an ID or constant. bool npc_viewisid(const char * viewid) { - if(atoi(viewid) != -1) - { + if (atoi(viewid) != FAKE_NPC) { // Loop through view, looking for non-numeric character. while (*viewid) { if (ISDIGIT(*viewid++) == 0) return false; @@ -2534,17 +2562,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; } @@ -2554,8 +2604,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) @@ -2568,26 +2617,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; } @@ -2623,15 +2659,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; @@ -2639,15 +2669,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 } @@ -2666,7 +2689,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; @@ -2765,31 +2788,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 ? FAKE_NPC : 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 } @@ -2889,7 +2899,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; @@ -2946,39 +2956,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 ? FAKE_NPC : 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); @@ -2988,35 +3033,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] @@ -3029,12 +3159,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; @@ -3056,18 +3184,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) { @@ -3090,107 +3216,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 ? FAKE_NPC : 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; @@ -3201,50 +3260,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; @@ -4643,7 +4687,7 @@ int do_init_npc(bool minimal) { npc->fake_nd = (struct npc_data *)aCalloc(1,sizeof(struct npc_data)); npc->fake_nd->bl.m = -1; npc->fake_nd->bl.id = npc->get_new_npc_id(); - npc->fake_nd->class_ = -1; + npc->fake_nd->class_ = FAKE_NPC; npc->fake_nd->speed = 200; strcpy(npc->fake_nd->name,"FAKE_NPC"); memcpy(npc->fake_nd->exname, npc->fake_nd->name, 9); @@ -4732,6 +4776,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; @@ -4742,6 +4788,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 bf3d1494d..d5e4618a1 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 { @@ -109,8 +109,10 @@ struct npc_data { #define START_NPC_NUM 110000000 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, @@ -120,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 { @@ -225,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); @@ -236,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); @@ -286,32 +295,51 @@ HPShared struct npc_interface *npc; /* comes from npc_chat.c */ #ifdef PCRE_SUPPORT #include <pcre/include/pcre.h> -/* Structure containing all info associated with a single pattern block */ +#endif // PCRE_SUPPORT + +/** + * Structure containing all info associated with a single pattern block + */ struct pcrematch_entry { +#ifdef PCRE_SUPPORT struct pcrematch_entry* next; char* pattern; pcre* pcre_; pcre_extra* pcre_extra_; char* label; +#else // not PCRE_SUPPORT + UNAVAILABLE_STRUCT; +#endif // PCRE_SUPPORT }; -/* A set of patterns that can be activated and deactived with a single command */ +/** + * A set of patterns that can be activated and deactived with a single command + */ struct pcrematch_set { +#ifdef PCRE_SUPPORT struct pcrematch_set* prev; struct pcrematch_set* next; struct pcrematch_entry* head; int setid; +#else // not PCRE_SUPPORT + UNAVAILABLE_STRUCT; +#endif // PCRE_SUPPORT }; -/* +/** * Entire data structure hung off a NPC */ struct npc_parse { +#ifdef PCRE_SUPPORT struct pcrematch_set* active; struct pcrematch_set* inactive; +#else // not PCRE_SUPPORT + UNAVAILABLE_STRUCT; +#endif // PCRE_SUPPORT }; struct npc_chat_interface { +#ifdef PCRE_SUPPORT int (*sub) (struct block_list* bl, va_list ap); void (*finalize) (struct npc_data* nd); void (*def_pattern) (struct npc_data* nd, int setid, const char* pattern, const char* label); @@ -321,6 +349,9 @@ struct npc_chat_interface { void (*activate_pcreset) (struct npc_data* nd, int setid); struct pcrematch_set* (*lookup_pcreset) (struct npc_data* nd, int setid); void (*finalize_pcrematch_entry) (struct pcrematch_entry* e); +#else // not PCRE_SUPPORT + UNAVAILABLE_STRUCT; +#endif // PCRE_SUPPORT }; /** @@ -329,6 +360,7 @@ struct npc_chat_interface { * should be moved into core/perhaps its own file once hpm is enhanced for login/char **/ struct pcre_interface { +#ifdef PCRE_SUPPORT pcre *(*compile) (const char *pattern, int options, const char **errptr, int *erroffset, const unsigned char *tableptr); pcre_extra *(*study) (const pcre *code, int options, const char **errptr); int (*exec) (const pcre *code, const pcre_extra *extra, PCRE_SPTR subject, int length, int startoffset, int options, int *ovector, int ovecsize); @@ -337,6 +369,9 @@ struct pcre_interface { void (*free_substring) (const char *stringptr); int (*copy_named_substring) (const pcre *code, const char *subject, int *ovector, int stringcount, const char *stringname, char *buffer, int buffersize); int (*get_substring) (const char *subject, int *ovector, int stringcount, int stringnumber, const char **stringptr); +#else // not PCRE_SUPPORT + UNAVAILABLE_STRUCT; +#endif // PCRE_SUPPORT }; /** @@ -349,6 +384,4 @@ void npc_chat_defaults(void); HPShared struct npc_chat_interface *npc_chat; HPShared struct pcre_interface *libpcre; -#endif // PCRE_SUPPORT - #endif /* MAP_NPC_H */ diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index 58c17ef0a..4a3fb6a4f 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -455,7 +455,7 @@ struct packet_idle_unit2 { unsigned char state; short clevel; #else // ! PACKETVER < 20091103 - char UNUSED; + UNAVAILABLE_STRUCT; #endif // PACKETVER < 20091103 } __attribute__((packed)); @@ -486,7 +486,7 @@ struct packet_spawn_unit2 { unsigned char xSize; unsigned char ySize; #else // ! PACKETVER < 20091103 - char UNUSED; + UNAVAILABLE_STRUCT; #endif // PACKETVER < 20091103 } __attribute__((packed)); @@ -1097,6 +1097,7 @@ struct packet_party_leader_changed { } __attribute__((packed)); struct packet_hotkey { +#ifdef HOTKEY_SAVING short PacketType; #if PACKETVER >= 20141022 char Rotate; @@ -1106,6 +1107,9 @@ struct packet_hotkey { unsigned int ID; // Item/Skill ID short count; // Item Quantity/Skill Level } hotkey[MAX_HOTKEYS]; +#else // not HOTKEY_SAVING + UNAVAILABLE_STRUCT; +#endif // HOTKEY_SAVING } __attribute__((packed)); /** diff --git a/src/map/party.h b/src/map/party.h index df7c03f05..bcc905b94 100644 --- a/src/map/party.h +++ b/src/map/party.h @@ -40,32 +40,27 @@ struct party_data { #define PB_NOTICE_LENGTH (36 + 1) -#ifndef PARTY_RECRUIT struct party_booking_detail { short level; +#ifdef PARTY_RECRUIT + char notice[PB_NOTICE_LENGTH]; +#else // not PARTY_RECRUIT short mapid; short job[PARTY_BOOKING_JOBS]; +#endif // PARTY_RECRUIT }; struct party_booking_ad_info { unsigned int index; - char charname[NAME_LENGTH]; - int expiretime; - struct party_booking_detail p_detail; -}; -#else /* PARTY_RECRUIT */ -struct party_booking_detail { - short level; - char notice[PB_NOTICE_LENGTH]; -}; - -struct party_booking_ad_info { - unsigned int index; +#ifdef PARTY_BOOKING int expiretime; char charname[NAME_LENGTH]; +#else // not PARTY_BOOKING + char charname[NAME_LENGTH]; + int expiretime; +#endif // PARTY_BOOKING struct party_booking_detail p_detail; }; -#endif /* PARTY_RECRUIT */ /*===================================== * Interface : party.h diff --git a/src/map/pc.c b/src/map/pc.c index 906507f87..b0a8ca62f 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 c9464bdab..ef9983371 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..b20e25eb1 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; @@ -14316,15 +14316,17 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 req.sp = cap_value(req.sp * sp_skill_rate_bonus / 100, 0, SHRT_MAX); - if( sc ) { - if( sc->data[SC__LAZINESS] ) + if (sc) { + if (sc->data[SC__LAZINESS]) req.sp += req.sp + sc->data[SC__LAZINESS]->val1 * 10; - if( sc->data[SC_UNLIMITED_HUMMING_VOICE] ) + if (sc->data[SC_UNLIMITED_HUMMING_VOICE]) req.sp += req.sp * sc->data[SC_UNLIMITED_HUMMING_VOICE]->val3 / 100; - if( sc->data[SC_RECOGNIZEDSPELL] ) + if (sc->data[SC_RECOGNIZEDSPELL]) req.sp += req.sp / 4; - if( sc->data[SC_TELEKINESIS_INTENSE] && skill->get_ele(skill_id, skill_lv) == ELE_GHOST) + if (sc->data[SC_TELEKINESIS_INTENSE] && skill->get_ele(skill_id, skill_lv) == ELE_GHOST) req.sp -= req.sp * sc->data[SC_TELEKINESIS_INTENSE]->val2 / 100; + if (sc->data[SC_TARGET_ASPD]) + req.sp -= req.sp * sc->data[SC_TARGET_ASPD]->val1 / 100; } req.zeny = skill->dbs->db[idx].zeny[skill_lv-1]; @@ -14739,6 +14741,9 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 VARCAST_REDUCTION(50); } } + if (sc->data[SC_MYSTICSCROLL]) + VARCAST_REDUCTION(sc->data[SC_MYSTICSCROLL]->val1); + // Fixed cast reduction bonuses if( sc->data[SC__LAZINESS] ) fixcast_r = max(fixcast_r, sc->data[SC__LAZINESS]->val2); @@ -14751,6 +14756,9 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 if (radius_lv) fixcast_r = max(fixcast_r, (status_get_int(bl) + status->get_lv(bl)) / 15 + radius_lv * 5); // [{(Caster?s INT / 15) + (Caster?s Base Level / 15) + (Radius Skill Level x 5)}] % } + if (sc->data[SC_FENRIR_CARD]) + fixcast_r = max(fixcast_r, sc->data[SC_FENRIR_CARD]->val2); + // Fixed cast non percentage bonuses if( sc->data[SC_MANDRAGORA] ) fixed += sc->data[SC_MANDRAGORA]->val1 * 500; diff --git a/src/map/status.c b/src/map/status.c index 1a07f74dc..9062e7227 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -759,22 +759,22 @@ void initChangeTables(void) { // Storing the target job rather than simply SC_SOULLINK simplifies code later on. status->dbs->Skill2SCTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST, - status->dbs->Skill2SCTable[SL_MONK] = (sc_type)MAPID_MONK, - status->dbs->Skill2SCTable[SL_STAR] = (sc_type)MAPID_STAR_GLADIATOR, - status->dbs->Skill2SCTable[SL_SAGE] = (sc_type)MAPID_SAGE, - status->dbs->Skill2SCTable[SL_CRUSADER] = (sc_type)MAPID_CRUSADER, - status->dbs->Skill2SCTable[SL_SUPERNOVICE] = (sc_type)MAPID_SUPER_NOVICE, - status->dbs->Skill2SCTable[SL_KNIGHT] = (sc_type)MAPID_KNIGHT, - status->dbs->Skill2SCTable[SL_WIZARD] = (sc_type)MAPID_WIZARD, - status->dbs->Skill2SCTable[SL_PRIEST] = (sc_type)MAPID_PRIEST, - status->dbs->Skill2SCTable[SL_BARDDANCER] = (sc_type)MAPID_BARDDANCER, - status->dbs->Skill2SCTable[SL_ROGUE] = (sc_type)MAPID_ROGUE, - status->dbs->Skill2SCTable[SL_ASSASIN] = (sc_type)MAPID_ASSASSIN, - status->dbs->Skill2SCTable[SL_BLACKSMITH] = (sc_type)MAPID_BLACKSMITH, - status->dbs->Skill2SCTable[SL_HUNTER] = (sc_type)MAPID_HUNTER, - status->dbs->Skill2SCTable[SL_SOULLINKER] = (sc_type)MAPID_SOUL_LINKER, - - //Status that don't have a skill associated. + status->dbs->Skill2SCTable[SL_MONK] = (sc_type)MAPID_MONK, + status->dbs->Skill2SCTable[SL_STAR] = (sc_type)MAPID_STAR_GLADIATOR, + status->dbs->Skill2SCTable[SL_SAGE] = (sc_type)MAPID_SAGE, + status->dbs->Skill2SCTable[SL_CRUSADER] = (sc_type)MAPID_CRUSADER, + status->dbs->Skill2SCTable[SL_SUPERNOVICE] = (sc_type)MAPID_SUPER_NOVICE, + status->dbs->Skill2SCTable[SL_KNIGHT] = (sc_type)MAPID_KNIGHT, + status->dbs->Skill2SCTable[SL_WIZARD] = (sc_type)MAPID_WIZARD, + status->dbs->Skill2SCTable[SL_PRIEST] = (sc_type)MAPID_PRIEST, + status->dbs->Skill2SCTable[SL_BARDDANCER] = (sc_type)MAPID_BARDDANCER, + status->dbs->Skill2SCTable[SL_ROGUE] = (sc_type)MAPID_ROGUE, + status->dbs->Skill2SCTable[SL_ASSASIN] = (sc_type)MAPID_ASSASSIN, + status->dbs->Skill2SCTable[SL_BLACKSMITH] = (sc_type)MAPID_BLACKSMITH, + status->dbs->Skill2SCTable[SL_HUNTER] = (sc_type)MAPID_HUNTER, + status->dbs->Skill2SCTable[SL_SOULLINKER] = (sc_type)MAPID_SOUL_LINKER, + + // Status that don't have a skill associated. status->dbs->IconChangeTable[SC_WEIGHTOVER50] = SI_WEIGHTOVER50; status->dbs->IconChangeTable[SC_WEIGHTOVER90] = SI_WEIGHTOVER90; status->dbs->IconChangeTable[SC_ATTHASTE_POTION1] = SI_ATTHASTE_POTION1; @@ -802,7 +802,9 @@ void initChangeTables(void) { status->dbs->IconChangeTable[SC_SPL_MATK] = SI_SPL_MATK; status->dbs->IconChangeTable[SC_PLUSATTACKPOWER] = SI_PLUSATTACKPOWER; status->dbs->IconChangeTable[SC_PLUSMAGICPOWER] = SI_PLUSMAGICPOWER; - //Cash Items + status->dbs->IconChangeTable[SC_FOOD_CRITICALSUCCESSVALUE] = SI_FOOD_CRITICALSUCCESSVALUE; + + // Cash Items status->dbs->IconChangeTable[SC_FOOD_STR_CASH] = SI_FOOD_STR_CASH; status->dbs->IconChangeTable[SC_FOOD_AGI_CASH] = SI_FOOD_AGI_CASH; status->dbs->IconChangeTable[SC_FOOD_VIT_CASH] = SI_FOOD_VIT_CASH; @@ -823,18 +825,37 @@ 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; + status->dbs->IconChangeTable[SC_TARGET_ASPD] = SI_TARGET_ASPD; + status->dbs->IconChangeTable[SC_ATKER_ASPD] = SI_ATKER_ASPD; + status->dbs->IconChangeTable[SC_ATKER_MOVESPEED] = SI_ATKER_MOVESPEED; + status->dbs->IconChangeTable[SC_CUP_OF_BOZA] = SI_CUP_OF_BOZA; + + // Eden Crystal Synthesis + status->dbs->IconChangeTable[SC_QUEST_BUFF1] = SI_QUEST_BUFF1; + status->dbs->IconChangeTable[SC_QUEST_BUFF2] = SI_QUEST_BUFF2; + status->dbs->IconChangeTable[SC_QUEST_BUFF3] = SI_QUEST_BUFF3; + + // Geffen Magic Tournament + status->dbs->IconChangeTable[SC_GEFFEN_MAGIC1] = SI_GEFFEN_MAGIC1; + status->dbs->IconChangeTable[SC_GEFFEN_MAGIC2] = SI_GEFFEN_MAGIC2; + status->dbs->IconChangeTable[SC_GEFFEN_MAGIC3] = SI_GEFFEN_MAGIC3; + status->dbs->IconChangeTable[SC_FENRIR_CARD] = SI_FENRIR_CARD; + // Mercenary Bonus Effects status->dbs->IconChangeTable[SC_MER_FLEE] = SI_MER_FLEE; status->dbs->IconChangeTable[SC_MER_ATK] = SI_MER_ATK; status->dbs->IconChangeTable[SC_MER_HP] = SI_MER_HP; status->dbs->IconChangeTable[SC_MER_SP] = SI_MER_SP; status->dbs->IconChangeTable[SC_MER_HIT] = SI_MER_HIT; + // Warlock Spheres status->dbs->IconChangeTable[SC_SUMMON1] = SI_SPHERE_1; status->dbs->IconChangeTable[SC_SUMMON2] = SI_SPHERE_2; status->dbs->IconChangeTable[SC_SUMMON3] = SI_SPHERE_3; status->dbs->IconChangeTable[SC_SUMMON4] = SI_SPHERE_4; status->dbs->IconChangeTable[SC_SUMMON5] = SI_SPHERE_5; + // Warlock Preserved spells status->dbs->IconChangeTable[SC_SPELLBOOK1] = SI_SPELLBOOK1; status->dbs->IconChangeTable[SC_SPELLBOOK2] = SI_SPELLBOOK2; @@ -872,7 +893,7 @@ void initChangeTables(void) { status->dbs->IconChangeTable[SC_BANANA_BOMB] = SI_BANANA_BOMB; status->dbs->IconChangeTable[SC_BANANA_BOMB_SITDOWN_POSTDELAY] = SI_BANANA_BOMB_SITDOWN_POSTDELAY; - //Genetics New Food Items Status Icons + // Genetics New Food Items Status Icons status->dbs->IconChangeTable[SC_SAVAGE_STEAK] = SI_SAVAGE_STEAK; status->dbs->IconChangeTable[SC_COCKTAIL_WARG_BLOOD] = SI_COCKTAIL_WARG_BLOOD; status->dbs->IconChangeTable[SC_MINOR_BBQ] = SI_MINOR_BBQ; @@ -921,7 +942,7 @@ void initChangeTables(void) { status->dbs->IconChangeTable[SC_LJOSALFAR] = SI_LJOSALFAR; status->dbs->IconChangeTable[SC_MERMAID_LONGING] = SI_MERMAID_LONGING; - //Other SC which are not necessarily associated to skills. + // Other SC which are not necessarily associated to skills. status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION1] = SCB_ASPD; status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION2] = SCB_ASPD; status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION3] = SCB_ASPD; @@ -970,6 +991,13 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_ATKER_BLOOD] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_WALKSPEED] |= SCB_SPEED; status->dbs->ChangeFlagTable[SC_ITEMSCRIPT] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_TARGET_BLOOD] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_TARGET_ASPD] |= SCB_MAXSP; + status->dbs->ChangeFlagTable[SC_ATKER_ASPD] |= SCB_MAXHP | SCB_ALL; + status->dbs->ChangeFlagTable[SC_ATKER_MOVESPEED] |= SCB_MAXSP | SCB_ALL; + status->dbs->ChangeFlagTable[SC_FOOD_CRITICALSUCCESSVALUE] |= SCB_CRI; + status->dbs->ChangeFlagTable[SC_CUP_OF_BOZA] |= SCB_VIT | SCB_ALL; + // Cash Items status->dbs->ChangeFlagTable[SC_FOOD_STR_CASH] = SCB_STR; status->dbs->ChangeFlagTable[SC_FOOD_AGI_CASH] = SCB_AGI; @@ -977,12 +1005,14 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_FOOD_DEX_CASH] = SCB_DEX; status->dbs->ChangeFlagTable[SC_FOOD_INT_CASH] = SCB_INT; status->dbs->ChangeFlagTable[SC_FOOD_LUK_CASH] = SCB_LUK; + // Mercenary Bonus Effects status->dbs->ChangeFlagTable[SC_MER_FLEE] |= SCB_FLEE; status->dbs->ChangeFlagTable[SC_MER_ATK] |= SCB_WATK; status->dbs->ChangeFlagTable[SC_MER_HP] |= SCB_MAXHP; status->dbs->ChangeFlagTable[SC_MER_SP] |= SCB_MAXSP; status->dbs->ChangeFlagTable[SC_MER_HIT] |= SCB_HIT; + // Guillotine Cross Poison Effects status->dbs->ChangeFlagTable[SC_PARALYSE] |= SCB_FLEE|SCB_SPEED|SCB_ASPD; status->dbs->ChangeFlagTable[SC_VENOMBLEED] |= SCB_MAXHP; @@ -990,9 +1020,11 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_DEATHHURT] |= SCB_REGEN; status->dbs->ChangeFlagTable[SC_PYREXIA] |= SCB_HIT|SCB_FLEE; status->dbs->ChangeFlagTable[SC_OBLIVIONCURSE] |= SCB_REGEN; + // RG status status->dbs->ChangeFlagTable[SC_SHIELDSPELL_DEF] |= SCB_WATK; status->dbs->ChangeFlagTable[SC_SHIELDSPELL_REF] |= SCB_DEF; + // Meca status status->dbs->ChangeFlagTable[SC_STEALTHFIELD_MASTER] |= SCB_SPEED; @@ -1008,20 +1040,45 @@ 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_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_STOMACHACHE] |= SCB_STR | SCB_AGI | SCB_VIT | SCB_INT | SCB_DEX | SCB_LUK | SCB_SPEED; + + // Geffen Scrolls + status->dbs->ChangeFlagTable[SC_SKELSCROLL] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_DISTRUCTIONSCROLL] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_ROYALSCROLL] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_IMMUNITYSCROLL] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_MYSTICSCROLL] |= SCB_MATK; + status->dbs->ChangeFlagTable[SC_BATTLESCROLL] |= SCB_BATK | SCB_ASPD; + status->dbs->ChangeFlagTable[SC_ARMORSCROLL] |= SCB_DEF | SCB_FLEE; + status->dbs->ChangeFlagTable[SC_FREYJASCROLL] |= SCB_MDEF | SCB_FLEE2; + status->dbs->ChangeFlagTable[SC_SOULSCROLL] |= SCB_MAXHP | SCB_MAXSP; status->dbs->ChangeFlagTable[SC_ALL_RIDING] = SCB_SPEED; status->dbs->ChangeFlagTable[SC_WEDDING] = SCB_SPEED; - status->dbs->ChangeFlagTable[SC_MTF_ASPD] = SCB_ASPD|SCB_HIT; + status->dbs->ChangeFlagTable[SC_MTF_ASPD] = SCB_ASPD | SCB_HIT; status->dbs->ChangeFlagTable[SC_MTF_MATK] = SCB_MATK; status->dbs->ChangeFlagTable[SC_MTF_MLEATKED] |= SCB_ALL; - + + // Eden Crystal Synthesis + status->dbs->ChangeFlagTable[SC_QUEST_BUFF1] |= SCB_BATK | SCB_MATK; + status->dbs->ChangeFlagTable[SC_QUEST_BUFF2] |= SCB_BATK | SCB_MATK; + status->dbs->ChangeFlagTable[SC_QUEST_BUFF3] |= SCB_BATK | SCB_MATK; + + // Geffen Magic Tournament + status->dbs->ChangeFlagTable[SC_GEFFEN_MAGIC1] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_GEFFEN_MAGIC2] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_GEFFEN_MAGIC3] |= SCB_ALL; + status->dbs->ChangeFlagTable[SC_FENRIR_CARD] |= SCB_MATK | SCB_ALL; + + // Costume status->dbs->ChangeFlagTable[SC_MOONSTAR] |= SCB_NONE; status->dbs->ChangeFlagTable[SC_SUPER_STAR] |= SCB_NONE; status->dbs->ChangeFlagTable[SC_STRANGELIGHTS] |= SCB_NONE; @@ -2836,33 +2893,33 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) { sd->sprecov_rate = 0; // Anti-element and anti-race - if((skill_lv=pc->checkskill(sd,CR_TRUST))>0) - sd->subele[ELE_HOLY] += skill_lv*5; - if((skill_lv=pc->checkskill(sd,BS_SKINTEMPER))>0) { + if ((skill_lv = pc->checkskill(sd, CR_TRUST)) > 0) + sd->subele[ELE_HOLY] += skill_lv * 5; + if ((skill_lv = pc->checkskill(sd, BS_SKINTEMPER)) > 0) { sd->subele[ELE_NEUTRAL] += skill_lv; sd->subele[ELE_FIRE] += skill_lv*4; } - if((skill_lv=pc->checkskill(sd,NC_RESEARCHFE))>0) { - sd->subele[ELE_EARTH] += skill_lv*10; - sd->subele[ELE_FIRE] += skill_lv*10; + if ((skill_lv = pc->checkskill(sd, NC_RESEARCHFE)) > 0) { + sd->subele[ELE_EARTH] += skill_lv * 10; + sd->subele[ELE_FIRE] += skill_lv * 10; } - if((skill_lv=pc->checkskill(sd,SA_DRAGONOLOGY))>0 ) { + if ((skill_lv = pc->checkskill(sd, SA_DRAGONOLOGY)) > 0) { #ifdef RENEWAL - skill_lv = skill_lv*2; + skill_lv = skill_lv * 2; #else - skill_lv = skill_lv*4; + skill_lv = skill_lv * 4; #endif - sd->right_weapon.addrace[RC_DRAGON]+=skill_lv; - sd->left_weapon.addrace[RC_DRAGON]+=skill_lv; - sd->magic_addrace[RC_DRAGON]+=skill_lv; + sd->right_weapon.addrace[RC_DRAGON] += skill_lv; + sd->left_weapon.addrace[RC_DRAGON] += skill_lv; + sd->magic_addrace[RC_DRAGON] += skill_lv; #ifdef RENEWAL sd->race_tolerance[RC_DRAGON] += skill_lv; #else - sd->subrace[RC_DRAGON]+=skill_lv; + sd->subrace[RC_DRAGON] += skill_lv; #endif } - if( (skill_lv = pc->checkskill(sd, AB_EUCHARISTICA)) > 0 ) { + if ((skill_lv = pc->checkskill(sd, AB_EUCHARISTICA)) > 0) { sd->right_weapon.addrace[RC_DEMON] += skill_lv; sd->right_weapon.addele[ELE_DARK] += skill_lv; sd->left_weapon.addrace[RC_DEMON] += skill_lv; @@ -2877,12 +2934,12 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) { sd->subele[ELE_DARK] += skill_lv; } - if(sc->count) { - if(sc->data[SC_CONCENTRATION]) { //Update the card-bonus data - sc->data[SC_CONCENTRATION]->val3 = sd->param_bonus[1]; //Agi - sc->data[SC_CONCENTRATION]->val4 = sd->param_bonus[4]; //Dex + if (sc->count) { + if (sc->data[SC_CONCENTRATION]) { // Update the card-bonus data + sc->data[SC_CONCENTRATION]->val3 = sd->param_bonus[1]; // Agi + sc->data[SC_CONCENTRATION]->val4 = sd->param_bonus[4]; // Dex } - if(sc->data[SC_SIEGFRIED]){ + if (sc->data[SC_SIEGFRIED]){ i = sc->data[SC_SIEGFRIED]->val2; sd->subele[ELE_WATER] += i; sd->subele[ELE_EARTH] += i; @@ -2894,7 +2951,7 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) { sd->subele[ELE_GHOST] += i; sd->subele[ELE_UNDEAD] += i; } - if(sc->data[SC_PROVIDENCE]){ + if (sc->data[SC_PROVIDENCE]){ sd->subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2; #ifdef RENEWAL sd->race_tolerance[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2; @@ -2902,49 +2959,92 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) { sd->subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2; #endif } - if(sc->data[SC_ARMORPROPERTY]) { + if (sc->data[SC_ARMORPROPERTY]) { //This status change should grant card-type elemental resist. sd->subele[ELE_WATER] += sc->data[SC_ARMORPROPERTY]->val1; sd->subele[ELE_EARTH] += sc->data[SC_ARMORPROPERTY]->val2; sd->subele[ELE_FIRE] += sc->data[SC_ARMORPROPERTY]->val3; sd->subele[ELE_WIND] += sc->data[SC_ARMORPROPERTY]->val4; } - if(sc->data[SC_ARMOR_RESIST]) { // Undead Scroll + if (sc->data[SC_ARMOR_RESIST]) { // Undead Scroll sd->subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1; sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2; sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3; sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4; } - if( sc->data[SC_FIRE_CLOAK_OPTION] ) { + if (sc->data[SC_FIRE_CLOAK_OPTION]) { i = sc->data[SC_FIRE_CLOAK_OPTION]->val2; sd->subele[ELE_FIRE] += i; sd->subele[ELE_WATER] -= i; } - if( sc->data[SC_WATER_DROP_OPTION] ) { + if (sc->data[SC_WATER_DROP_OPTION]) { i = sc->data[SC_WATER_DROP_OPTION]->val2; sd->subele[ELE_WATER] += i; sd->subele[ELE_WIND] -= i; } - if( sc->data[SC_WIND_CURTAIN_OPTION] ) { + if (sc->data[SC_WIND_CURTAIN_OPTION]) { i = sc->data[SC_WIND_CURTAIN_OPTION]->val2; sd->subele[ELE_WIND] += i; sd->subele[ELE_EARTH] -= i; } - if( sc->data[SC_STONE_SHIELD_OPTION] ) { + if (sc->data[SC_STONE_SHIELD_OPTION]) { i = sc->data[SC_STONE_SHIELD_OPTION]->val2; sd->subele[ELE_EARTH] += i; sd->subele[ELE_FIRE] -= i; } - if( sc->data[SC_MTF_MLEATKED] ) + if (sc->data[SC_MTF_MLEATKED]) sd->subele[ELE_NEUTRAL] += 2; - if( sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3 ) + if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3) sd->magic_addele[ELE_FIRE] += 25; - if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 ) + if (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3) sd->magic_addele[ELE_WATER] += 25; - if( sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 ) + if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3) sd->magic_addele[ELE_WIND] += 25; - if( sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3 ) + if (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3) sd->magic_addele[ELE_EARTH] += 25; + + // Geffen Scrolls + if (sc->data[SC_SKELSCROLL]) { +#ifdef RENEWAL + sd->race_tolerance[RC_DEMIHUMAN] += sc->data[SC_SKELSCROLL]->val1; +#else + sd->subrace[RC_DEMIHUMAN] += sc->data[SC_SKELSCROLL]->val1; +#endif + } + if (sc->data[SC_DISTRUCTIONSCROLL]) { + sd->right_weapon.addrace[RC_ANGEL] += sc->data[SC_DISTRUCTIONSCROLL]->val1; + sd->left_weapon.addrace[RC_ANGEL] += sc->data[SC_DISTRUCTIONSCROLL]->val1; + sd->right_weapon.addele[ELE_HOLY] += sc->data[SC_DISTRUCTIONSCROLL]->val1; + sd->left_weapon.addele[ELE_HOLY] += sc->data[SC_DISTRUCTIONSCROLL]->val1; + sd->right_weapon.addrace[RC_BOSS] += sc->data[SC_DISTRUCTIONSCROLL]->val1; + sd->left_weapon.addrace[RC_BOSS] += sc->data[SC_DISTRUCTIONSCROLL]->val1; + } + if (sc->data[SC_ROYALSCROLL]) { +#ifdef RENEWAL + sd->race_tolerance[RC_BOSS] += sc->data[SC_ROYALSCROLL]->val1; +#else + sd->subrace[RC_BOSS] += sc->data[SC_ROYALSCROLL]->val1; +#endif + } + if (sc->data[SC_IMMUNITYSCROLL]) + sd->subele[ELE_NEUTRAL] += sc->data[SC_IMMUNITYSCROLL]->val1; + + // Geffen Magic Tournament + if (sc->data[SC_GEFFEN_MAGIC1]) { + sd->right_weapon.addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1; + sd->left_weapon.addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1; + } + if (sc->data[SC_GEFFEN_MAGIC2]) + sd->magic_addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC2]->val1; + if (sc->data[SC_GEFFEN_MAGIC3]) { +#ifdef RENEWAL + sd->race_tolerance[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC3]->val1; +#else + sd->subrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC3]->val1; +#endif + } + if (sc->data[SC_CUP_OF_BOZA]) + sd->subele[ELE_FIRE] += sc->data[SC_CUP_OF_BOZA]->val2; } status_cpy(&sd->battle_status, bstatus); @@ -3316,7 +3416,7 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str } else regen->flag&=~sce->val4; //Remove regen as specified by val4 } - if(sc->data[SC_GENTLETOUCH_REVITALIZE]) { + if (sc->data[SC_GENTLETOUCH_REVITALIZE]) { regen->hp += regen->hp * ( 30 * sc->data[SC_GENTLETOUCH_REVITALIZE]->val1 + 50 ) / 100; } if ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1) //if insignia lvl 1 @@ -3324,12 +3424,16 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1) || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1)) regen->rate.hp *= 2; - if( sc->data[SC_VITALITYACTIVATION] ) + if (sc->data[SC_VITALITYACTIVATION]) 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_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_ATKER_ASPD]) + regen->rate.hp += regen->rate.hp * sc->data[SC_ATKER_ASPD]->val2 / 100; + if (sc->data[SC_ATKER_MOVESPEED]) + regen->rate.sp += regen->rate.sp * sc->data[SC_ATKER_MOVESPEED]->val2 / 100; } /// Recalculates parts of an object's battle status according to the specified flags. /// @param flag bitfield of values from enum scb_flag @@ -4287,9 +4391,10 @@ unsigned short status_calc_vit(struct block_list *bl, struct status_change *sc, vit -= sc->data[SC_STOMACHACHE]->val1; if(sc->data[SC_KYOUGAKU]) vit -= sc->data[SC_KYOUGAKU]->val3; - if(sc->data[SC_NOEQUIPARMOR]) - vit -= vit * sc->data[SC_NOEQUIPARMOR]->val2/100; + vit -= vit * sc->data[SC_NOEQUIPARMOR]->val2 / 100; + if (sc->data[SC_CUP_OF_BOZA]) + vit += sc->data[SC_CUP_OF_BOZA]->val1; return (unsigned short)cap_value(vit,0,USHRT_MAX); } @@ -4537,6 +4642,14 @@ unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, batk -= batk * sc->data[SC__ENERVATION]->val2 / 100; if(sc->data[SC_SATURDAY_NIGHT_FEVER]) batk += 100 * sc->data[SC_SATURDAY_NIGHT_FEVER]->val1; + + // Eden Crystal Synthesis + if (sc->data[SC_QUEST_BUFF1]) + batk += sc->data[SC_QUEST_BUFF1]->val1; + if (sc->data[SC_QUEST_BUFF2]) + batk += sc->data[SC_QUEST_BUFF2]->val1; + if (sc->data[SC_QUEST_BUFF3]) + batk += sc->data[SC_QUEST_BUFF3]->val1; return (unsigned short)cap_value(batk,0,USHRT_MAX); } @@ -4703,6 +4816,20 @@ unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, matk += matk * sc->data[SC_MOONLIT_SERENADE]->val2/100; if (sc->data[SC_MTF_MATK]) matk += matk * 25 / 100; + if (sc->data[SC_MYSTICSCROLL]) + matk += matk * sc->data[SC_MYSTICSCROLL]->val1 / 100; + + // Eden Crystal Synthesis + if (sc->data[SC_QUEST_BUFF1]) + matk += sc->data[SC_QUEST_BUFF1]->val1; + if (sc->data[SC_QUEST_BUFF2]) + matk += sc->data[SC_QUEST_BUFF2]->val1; + if (sc->data[SC_QUEST_BUFF3]) + matk += sc->data[SC_QUEST_BUFF3]->val1; + + // Geffen Magic Tournament + if (sc->data[SC_FENRIR_CARD]) + matk += sc->data[SC_FENRIR_CARD]->val1; return (unsigned short)cap_value(matk,0,USHRT_MAX); } @@ -4719,6 +4846,8 @@ signed short status_calc_critical(struct block_list *bl, struct status_change *s if (sc->data[SC_CRITICALPERCENT]) critical += sc->data[SC_CRITICALPERCENT]->val2; + if (sc->data[SC_FOOD_CRITICALSUCCESSVALUE]) + critical += sc->data[SC_FOOD_CRITICALSUCCESSVALUE]->val1; if (sc->data[SC_EXPLOSIONSPIRITS]) critical += sc->data[SC_EXPLOSIONSPIRITS]->val2; if (sc->data[SC_FORTUNE]) @@ -4792,7 +4921,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); } @@ -4881,9 +5012,10 @@ signed short status_calc_flee(struct block_list *bl, struct status_change *sc, i if(status_get_element(bl) == ELE_WATER) //water type flee /= 2; } - if( sc->data[SC_OVERED_BOOST] ) // should be final and unmodifiable by any means flee = sc->data[SC_OVERED_BOOST]->val2; + if (sc->data[SC_ARMORSCROLL]) + flee += sc->data[SC_ARMORSCROLL]->val2; return (short)cap_value(flee,1,SHRT_MAX); } @@ -4904,6 +5036,8 @@ signed short status_calc_flee2(struct block_list *bl, struct status_change *sc, flee2 += sc->data[SC_WHISTLE]->val3*10; if(sc->data[SC__UNLUCKY]) flee2 -= flee2 * sc->data[SC__UNLUCKY]->val2 / 100; + if (sc->data[SC_FREYJASCROLL]) + flee2 += sc->data[SC_FREYJASCROLL]->val2; return (short)cap_value(flee2,10,SHRT_MAX); } @@ -5003,6 +5137,8 @@ defType status_calc_def(struct block_list *bl, struct status_change *sc, int def } if (sc->data[SC_UNLIMIT]) return 1; + if (sc->data[SC_ARMORSCROLL]) + def += sc->data[SC_ARMORSCROLL]->val1; return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX); } @@ -5131,6 +5267,8 @@ defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int md mdef -= mdef *25 / 100; if (sc->data[SC_UNLIMIT]) return 1; + if (sc->data[SC_FREYJASCROLL]) + mdef += sc->data[SC_FREYJASCROLL]->val1; return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX); } @@ -5276,7 +5414,9 @@ unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc val = max( val, sc->data[SC_POWER_OF_GAIA]->val2 ); if( sc->data[SC_MELON_BOMB] ) val = max( val, sc->data[SC_MELON_BOMB]->val1 ); - + if (sc->data[SC_STOMACHACHE]) + val = max(val, 50); + if( sc->data[SC_MARSHOFABYSS] ) // It stacks to other statuses so always put this at the end. val = max( 50, val + 10 * sc->data[SC_MARSHOFABYSS]->val1 ); @@ -5477,6 +5617,10 @@ 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; + if (sc->data[SC_BATTLESCROLL]) + bonus += sc->data[SC_BATTLESCROLL]->val1; } return (bonus + pots); @@ -5638,6 +5782,10 @@ 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; + if (sc->data[SC_BATTLESCROLL]) + aspd_rate += sc->data[SC_BATTLESCROLL]->val1 * 10; return (short)cap_value(aspd_rate,0,SHRT_MAX); } @@ -5683,10 +5831,8 @@ unsigned int status_calc_maxhp(struct block_list *bl, struct status_change *sc, maxhp += 3000; if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) maxhp += 500; - if(sc->data[SC_MER_HP]) maxhp += maxhp * sc->data[SC_MER_HP]->val2/100; - if(sc->data[SC_EPICLESIS]) maxhp += maxhp * 5 * sc->data[SC_EPICLESIS]->val1 / 100; if(sc->data[SC_VENOMBLEED]) @@ -5723,6 +5869,10 @@ unsigned int status_calc_maxhp(struct block_list *bl, struct status_change *sc, maxhp += maxhp * sc->data[SC_GOLDENE_FERSE]->val2 / 100; if(sc->data[SC_FRIGG_SONG]) maxhp += maxhp * sc->data[SC_FRIGG_SONG]->val2 / 100; + if (sc->data[SC_SOULSCROLL]) + maxhp += maxhp * sc->data[SC_SOULSCROLL]->val1 / 100; + if (sc->data[SC_ATKER_ASPD]) + maxhp += maxhp * sc->data[SC_ATKER_ASPD]->val1 / 100; return (unsigned int)cap_value(maxhp,1,UINT_MAX); } @@ -5748,6 +5898,14 @@ 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; + if (sc->data[SC_TARGET_ASPD]) + maxsp += maxsp * sc->data[SC_TARGET_ASPD]->val1 / 100; + if (sc->data[SC_SOULSCROLL]) + maxsp += maxsp * sc->data[SC_SOULSCROLL]->val1 / 100; + if (sc->data[SC_ATKER_MOVESPEED]) + maxsp += maxsp * sc->data[SC_ATKER_MOVESPEED]->val1 / 100; return cap_value(maxsp,1,UINT_MAX); } @@ -6603,16 +6761,16 @@ int status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_typ } //Item resistance (only applies to rate%) - if(sd && SC_COMMON_MIN <= type && type <= SC_COMMON_MAX) + if (sd && SC_COMMON_MIN <= type && type <= SC_COMMON_MAX) { - if( sd->reseff[type-SC_COMMON_MIN] > 0 ) - rate -= rate*sd->reseff[type-SC_COMMON_MIN]/10000; - if( sd->sc.data[SC_TARGET_BLOOD] ) - rate -= rate*sd->sc.data[SC_TARGET_BLOOD]->val1/100; + if (sd->reseff[type-SC_COMMON_MIN] > 0) + rate -= rate * sd->reseff[type-SC_COMMON_MIN] / 10000; + if (sd->sc.data[SC_TARGET_BLOOD]) + rate -= rate * sd->sc.data[SC_TARGET_BLOOD]->val1 / 100; } //Aegis accuracy - if(rate > 0 && rate%10 != 0) rate += (10 - rate%10); + if (rate > 0 && rate%10 != 0) rate += (10 - rate%10); } if (!(rnd()%10000 < rate)) diff --git a/src/map/status.h b/src/map/status.h index 800e3de9f..a9538724d 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -745,6 +745,37 @@ typedef enum sc_type { SC_LJOSALFAR, SC_MERMAID_LONGING, + + SC_ACARAJE, // 590 + SC_TARGET_ASPD, + + // Geffen Scrolls + SC_SKELSCROLL, + SC_DISTRUCTIONSCROLL, + SC_ROYALSCROLL, + SC_IMMUNITYSCROLL, + SC_MYSTICSCROLL, + SC_BATTLESCROLL, + SC_ARMORSCROLL, + SC_FREYJASCROLL, + SC_SOULSCROLL, // 600 + + // Eden Crystal Synthesis + SC_QUEST_BUFF1, + SC_QUEST_BUFF2, + SC_QUEST_BUFF3, + + // Geffen Magic Tournament + SC_GEFFEN_MAGIC1, + SC_GEFFEN_MAGIC2, + SC_GEFFEN_MAGIC3, + SC_FENRIR_CARD, + + SC_ATKER_ASPD, + SC_ATKER_MOVESPEED, + + SC_FOOD_CRITICALSUCCESSVALUE, // 610 + SC_CUP_OF_BOZA, SC_MAX, //Automatically updated max, used in for's to check we are within bounds. } sc_type; @@ -1055,9 +1086,9 @@ enum si_type { SI_L_LIFEPOTION = 294, SI_CRITICALPERCENT = 295, SI_PLUSAVOIDVALUE = 296, - //SI_ATKER_ASPD = 297, - //SI_TARGET_ASPD = 298, - //SI_ATKER_MOVESPEED = 299, + SI_ATKER_ASPD = 297, + SI_TARGET_ASPD = 298, + SI_ATKER_MOVESPEED = 299, SI_ATKER_BLOOD = 300, SI_TARGET_BLOOD = 301, @@ -1510,7 +1541,7 @@ enum si_type { //SI_JITTER_BUFF8 = 748, //SI_JITTER_BUFF9 = 749, //SI_JITTER_BUFF10 = 750, - //SI_CUP_OF_BOZA = 751, + SI_CUP_OF_BOZA = 751, SI_B_TRAP = 752, SI_E_CHAIN = 753, SI_E_QD_SHOT_READY = 754, diff --git a/src/map/unit.c b/src/map/unit.c index 997ab5b7c..7f53966e2 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; diff --git a/src/map/unit.h b/src/map/unit.h index 0d970ca16..0f7a1aec5 100644 --- a/src/map/unit.h +++ b/src/map/unit.h @@ -65,13 +65,11 @@ struct unit_data { struct view_data { #ifdef __64BIT__ - unsigned int class_; -#endif - unsigned short -#ifndef __64BIT__ - class_, -#endif - weapon, + uint32 class_; // FIXME: This shouldn't really depend on the architecture. +#else // not __64BIT__ + uint16 class_; +#endif // __64BIT__ + uint16 weapon, shield, //Or left-hand weapon. robe, head_top, |