diff options
-rw-r--r-- | Changelog-Trunk.txt | 4 | ||||
-rw-r--r-- | src/map/atcommand.c | 2 | ||||
-rw-r--r-- | src/map/clif.c | 67 | ||||
-rw-r--r-- | src/map/clif.h | 3 | ||||
-rw-r--r-- | src/map/script.c | 343 |
5 files changed, 273 insertions, 146 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index 1f7973806..b6f67c86e 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -3,6 +3,10 @@ Date Added AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK. IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. +2007/03/16 + * Corrected the structure and added info on the skill fail packet (0x110). + * Added packet 0x223 as clif_upgrademessage (not used yet). + * More work on Ticket #41. [FlavioJS] 2007/03/15 * Added SC_CHANGEUNDEAD to differentiate it from the other elemental change skills. It now fails on Undead/Dark elemental targets. diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 5ec3c1f1b..72664aa1c 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -3717,7 +3717,7 @@ int atcommand_refine(const int fd, struct map_session_data* sd, const char* comm sd->status.inventory[i].refine = final_refine; current_position = sd->status.inventory[i].equip; pc_unequipitem(sd, i, 3); - clif_refine(fd, sd, 0, i, sd->status.inventory[i].refine); + clif_refine(fd, 0, i, sd->status.inventory[i].refine); clif_delitem(sd, i, 1); clif_additem(sd, i, 1, 0); pc_equipitem(sd, i, current_position); diff --git a/src/map/clif.c b/src/map/clif.c index f5dff2f85..037683391 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -4258,10 +4258,38 @@ int clif_skillcastcancel(struct block_list* bl) return 0; } -/*========================================== - * スキル詠唱失敗 - *------------------------------------------ - */ + +/// only when type==0: +/// if(skill_id==NV_BASIC) +/// btype==0 "skill failed" MsgStringTable[159] +/// btype==1 "no emotions" MsgStringTable[160] +/// btype==2 "no sit" MsgStringTable[161] +/// btype==3 "no chat" MsgStringTable[162] +/// btype==4 "no party" MsgStringTable[163] +/// btype==5 "no shout" MsgStringTable[164] +/// btype==6 "no PKing" MsgStringTable[165] +/// btype==7 "no alligning" MsgStringTable[383] +/// btype>=8: ignored +/// if(skill_id==AL_WARP) "not enough skill level" MsgStringTable[214] +/// if(skill_id==TF_STEAL) "steal failed" MsgStringTable[205] +/// if(skill_id==TF_POISON) "envenom failed" MsgStringTable[207] +/// otherwise "skill failed" MsgStringTable[204] +/// btype irrelevant +/// type==1 "insufficient SP" MsgStringTable[202] +/// type==2 "insufficient HP" MsgStringTable[203] +/// type==3 "insufficient materials" MsgStringTable[808] +/// type==4 "there is a delay after using a skill" MsgStringTable[219] +/// type==5 "insufficient zeny" MsgStringTable[233] +/// type==6 "wrong weapon" MsgStringTable[239] +/// type==7 "red jemstone needed" MsgStringTable[246] +/// type==8 "blue jemstone needed" MsgStringTable[247] +/// type==9 "overweight" MsgStringTable[580] +/// type==10 "skill failed" MsgStringTable[285] +/// type>=11 ignored +/// +/// if(success!=0) doesn't display any of the previous messages +/// Note: when this packet is received an unknown flag is always set to 0, +/// suggesting this is an ACK packet for the UseSkill packets and should be sent on success too [FlavioJS] int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype) { int fd; @@ -4289,9 +4317,8 @@ int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype) WFIFOHEAD(fd,packet_len(0x110)); WFIFOW(fd,0) = 0x110; WFIFOW(fd,2) = skill_id; - WFIFOW(fd,4) = btype; - WFIFOW(fd,6) = 0; - WFIFOB(fd,8) = 0; + WFIFOL(fd,4) = btype; + WFIFOB(fd,8) = 0;// success? WFIFOB(fd,9) = type; WFIFOSET(fd,packet_len(0x110)); @@ -5031,7 +5058,7 @@ int clif_send0199(int map,int type) * 精錬エフェクトを送信する *------------------------------------------ */ -int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val) +void clif_refine(int fd, int fail, int index, int val) { WFIFOHEAD(fd,packet_len(0x188)); WFIFOW(fd,0)=0x188; @@ -5039,8 +5066,19 @@ int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val) WFIFOW(fd,4)=index+2; WFIFOW(fd,6)=val; WFIFOSET(fd,packet_len(0x188)); +} - return 0; +/// result=0: "weapon upgrated: %s" MsgStringTable[911] in rgb(0,255,255) +/// result=1: "weapon upgrated: %s" MsgStringTable[912] in rgb(0,205,205) +/// result=2: "cannot upgrade %s until you level up the upgrade weapon skill" MsgStringTable[913] in rgb(255,200,200) +/// result=3: "you lack the item %s to upgrade the weapon" MsgStringTable[914] in rgb(255,200,200) +void clif_upgrademessage(int fd, int result, int item_id) +{ + WFIFOHEAD(fd,packet_len(0x223)); + WFIFOW(fd,0)=0x223; + WFIFOL(fd,2)=result; + WFIFOW(fd,6)=item_id; + WFIFOSET(fd,packet_len(0x223)); } /*========================================== @@ -7433,10 +7471,10 @@ int clif_wisall(struct map_session_data *sd,int type,int flag) return 0; } -/*========================================== - * サウンドエフェクト - *------------------------------------------ - */ + +/// type=0: play the music once +/// type=1: play the music for the specified amount of time (maybe) +/// type=2: stop the sound? void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,const char *name,int type) { int fd; @@ -7449,7 +7487,7 @@ void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,const ch WFIFOW(fd,0)=0x1d3; memcpy(WFIFOP(fd,2),name,NAME_LENGTH); WFIFOB(fd,26)=type; - WFIFOL(fd,27)=0; + WFIFOL(fd,27)=0;// time WFIFOL(fd,31)=bl->id; WFIFOSET(fd,packet_len(0x1d3)); @@ -11762,6 +11800,7 @@ int clif_parse(int fd) { // printf("clif_parse: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); cmd = RFIFOW(fd,0); + ShowDebug("Received packet 0x%4X\n", cmd); // get packet version before to parse packet_ver = 0; diff --git a/src/map/clif.h b/src/map/clif.h index 7bf9eb682..9dc33db6d 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -307,7 +307,8 @@ int clif_resurrection(struct block_list *bl,int type); int clif_set0199(int fd,int type); int clif_pvpset(struct map_session_data *sd, int pvprank, int pvpnum,int type); int clif_send0199(int map,int type); -int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val); +void clif_refine(int fd, int fail, int index, int val); +void clif_upgrademessage(int fd, int result, int item_id); //petsystem int clif_catch_process(struct map_session_data *sd); diff --git a/src/map/script.c b/src/map/script.c index 6f862400f..1cd5b8feb 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -4177,15 +4177,15 @@ struct script_function buildin_func[] = { BUILDIN_DEF(setmobdata,"iii"), BUILDIN_DEF(mobassist,"i*"), BUILDIN_DEF(mobattach,"i*"), - BUILDIN_DEF(unitwalk,"i*"), + BUILDIN_DEF(unitwalk,"ii?"), BUILDIN_DEF(unitkill,"i"), BUILDIN_DEF(unitwarp,"isii"), - BUILDIN_DEF(unitattack,"i*"), + BUILDIN_DEF(unitattack,"iv?"), BUILDIN_DEF(unitstop,"i"), BUILDIN_DEF(unittalk,"is"), BUILDIN_DEF(unitemote,"ii"), BUILDIN_DEF(unitdeadsit,"ii"), - BUILDIN_DEF(unitskilluseid,"iii*"), // originally by Qamera [Celest] + BUILDIN_DEF(unitskilluseid,"iii?"), // originally by Qamera [Celest] BUILDIN_DEF(unitskillusepos,"iiiii"), // [Celest] // <--- [zBuffer] List of mob control commands BUILDIN_DEF(sleep,"i"), @@ -6469,7 +6469,7 @@ BUILDIN_FUNC(successrefitem) sd->status.inventory[i].refine++; pc_unequipitem(sd,i,2); - clif_refine(sd->fd,sd,0,i,sd->status.inventory[i].refine); + clif_refine(sd->fd,0,i,sd->status.inventory[i].refine); clif_delitem(sd,i,1); //Logs items, got from (N)PC scripts [Lupus] @@ -6520,7 +6520,7 @@ BUILDIN_FUNC(failedrefitem) sd->status.inventory[i].refine = 0; pc_unequipitem(sd,i,3); // 精錬失敗エフェクトのパケット - clif_refine(sd->fd,sd,1,i,sd->status.inventory[i].refine); + clif_refine(sd->fd,1,i,sd->status.inventory[i].refine); pc_delitem(sd,i,1,0); // 他の人にも失敗を通知 @@ -12743,60 +12743,83 @@ BUILDIN_FUNC(mobattach) return 0; } +/// Makes the unit walk to target position or map +/// Returns if it was successfull +/// +/// unitwalk(<unit_id>,<x>,<y>) -> <bool>; +/// unitwalk(<unit_id>,<map_id>) -> <bool>; BUILDIN_FUNC(unitwalk) { - int id,x,y = 0; - struct block_list *bl = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - x = conv_num(st, & (st->stack->stack_data[st->start+3])); - if(st->end > st->start+4) - y = conv_num(st, & (st->stack->stack_data[st->start+4])); + struct block_list* bl; - bl = map_id2bl(id); - if(bl){ - if(y) - push_val(st->stack,C_INT,unit_walktoxy(bl,x,y,0)); // We'll use harder calculations. - else - push_val(st->stack,C_INT,unit_walktobl(bl,map_id2bl(x),65025,1)); - } else { - push_val(st->stack,C_INT,0); + bl = map_id2bl(conv_num(st, script_getdata(st,2))); + if( bl == NULL ) + { + script_pushint(st, 0); + } + else if( script_hasdata(st,4) ) + { + int x = conv_num(st, script_getdata(st,3)); + int y = conv_num(st, script_getdata(st,4)); + script_pushint(st, unit_walktoxy(bl,x,y,0));// We'll use harder calculations. + } + else + { + int map_id = conv_num(st, script_getdata(st,3)); + script_pushint(st, unit_walktobl(bl,map_id2bl(map_id),65025,1)); } return 0; } +/// Kills the unit +/// +/// unitkill <unit_id>; BUILDIN_FUNC(unitkill) { - struct block_list *bl = map_id2bl(conv_num(st, & (st->stack->stack_data[st->start+2]))); - if(bl) + struct block_list* bl = map_id2bl(conv_num(st, script_getdata(st,2))); + if( bl != NULL ) status_kill(bl); return 0; } +/// Warps the unit to the target position in the target map +/// Returns if it was successfull +/// +/// unitwarp(<unit_id>,"<map name>",<x>,<y>) -> <bool>; BUILDIN_FUNC(unitwarp) { - int id,x,y,m = 0; - const char *map; - struct block_list *bl = NULL; + int unit_id; + int map; + short x; + short y; + struct block_list* bl; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - map = conv_str(st, & (st->stack->stack_data[st->start+3])); - x = conv_num(st, & (st->stack->stack_data[st->start+4])); - y = conv_num(st, & (st->stack->stack_data[st->start+5])); + unit_id = conv_num(st, script_getdata(st,2)); + map = map_mapname2mapid(conv_str(st, script_getdata(st,3))); + x = (short)conv_num(st, script_getdata(st,4)); + y = (short)conv_num(st, script_getdata(st,5)); - bl = map_id2bl(id); - m = map_mapname2mapid(map); - if(m && bl){ - push_val(st->stack,C_INT,unit_warp(bl, m, (short)x, (short)y, 0)); - } else { - push_val(st->stack,C_INT,0); + bl = map_id2bl(unit_id); + if( map != 0 && bl != NULL ) + { + script_pushint(st, unit_warp(bl,map,x,y,0)); + } + else + { + script_pushint(st, 0); } return 0; } +/// TODO clean up +/// +/// unitattack(<unit_id>,"<target name>",<action type>) -> <bool>; +/// unitattack(<unit_id>,<target_id>,<action type>) -> <bool>; +/// unitattack(<unit_id>,"<target name>") -> <bool>; +/// unitattack(<unit_id>,<target_id>) -> <bool>; BUILDIN_FUNC(unitattack) { int id = 0, actiontype = 0; @@ -12837,67 +12860,92 @@ BUILDIN_FUNC(unitattack) return 0; } +/// Makes the unit stop attacking and moving +/// +/// unitstop <unit_id>; BUILDIN_FUNC(unitstop) { - int id; - struct block_list *bl = NULL; + int unit_id; + struct block_list* bl; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); + unit_id = conv_num(st, script_getdata(st,2)); - bl = map_id2bl(id); - if(bl){ + bl = map_id2bl(unit_id); + if( bl != NULL ) + { unit_stop_attack(bl); unit_stop_walking(bl,0); - if(bl->type == BL_MOB) - ((TBL_MOB *)bl)->target_id = 0; + if( bl->type == BL_MOB ) + ((TBL_MOB*)bl)->target_id = 0; } return 0; } +/// Makes the unit say the message +/// +/// unittalk <unit_id>,"<message>"; BUILDIN_FUNC(unittalk) { - const char *str; - int id; - char message[255]; - - struct block_list *bl = NULL; + int unit_id; + const char* message; + struct block_list* bl; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - str=conv_str(st,& (st->stack->stack_data[st->start+3])); + unit_id = conv_num(st, script_getdata(st,2)); + message = conv_str(st, script_getdata(st,3)); - bl = map_id2bl(id); - if(bl) { - memcpy(message, status_get_name(bl), NAME_LENGTH); - strcat(message," : "); - strncat(message,str, 228); //Prevent overflow possibility. [Skotlex] - clif_message(bl, message); - if(bl->type == BL_PC) - clif_displaymessage(((TBL_PC*)bl)->fd, message); + bl = map_id2bl(unit_id); + if( bl != NULL ) + { + struct StringBuf* buf = StringBuf_Malloc(); + StringBuf_Printf(buf, "%s : %s", status_get_name(bl), message); + clif_message(bl, StringBuf_Value(buf)); + if( bl->type == BL_PC ) + clif_displaymessage(((TBL_PC*)bl)->fd, StringBuf_Value(buf)); + StringBuf_Free(buf); } return 0; } +/// Makes the unit do an emotion +/// +/// unitemote <unit_id>,<emotion>; +/// +/// @see e_* in const.txt BUILDIN_FUNC(unitemote) { - int id, emo; - struct block_list *bl= NULL; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - emo = conv_num(st, & (st->stack->stack_data[st->start+3])); - if((bl = map_id2bl(id))) - clif_emotion(bl,emo); + int unit_id; + int emotion; + struct block_list* bl; + + unit_id = conv_num(st, script_getdata(st,2)); + emotion = conv_num(st, script_getdata(st,3)); + bl = map_id2bl(unit_id); + if( bl != NULL ) + clif_emotion(bl, emotion); + return 0; } +/// Makes the unit do an action +/// TODO actions +/// +/// unitdeadsit <unit_id>,<action>; BUILDIN_FUNC(unitdeadsit) { - int id, action; - struct block_list *bl = NULL; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - action = conv_num(st, & (st->stack->stack_data[st->start+3])); - if((bl = map_id2bl(id))){ - if(action > -1 && action < 4){ + int unit_id; + int action; + struct block_list* bl; + + unit_id = conv_num(st, script_getdata(st,2)); + action = conv_num(st, script_getdata(st,3)); + + bl = map_id2bl(unit_id); + if( bl != NULL ) + { + if( action > -1 && action < 4 ) + { unsigned char buf[61] = ""; struct view_data *vd = status_get_viewdata(bl); if (vd) vd->dead_sit = action; @@ -12905,116 +12953,149 @@ BUILDIN_FUNC(unitdeadsit) WBUFL(buf, 2) = bl->id; WBUFB(buf,26) = (unsigned char)action; clif_send(buf, 61, bl, AREA); - }else { - ShowError("buildin_unitdeadsit: Invalid action.\n"); - report_src(st); } - }else{ - ShowError("buildin_unitdeadsit: Target is not found.\n"); - report_src(st); } + return 0; } +/// Makes the unit cast the skill on the target or self if no target is specified +/// +/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>}; BUILDIN_FUNC(unitskilluseid) { - int id,skid,sklv; - struct block_list *bl = NULL; + int unit_id; + int skill_id; + int skill_lv; + int target_id; + struct block_list* bl; - id = conv_num(st,& (st->stack->stack_data[st->start+2])); - skid=conv_num(st,& (st->stack->stack_data[st->start+3])); - sklv=conv_num(st,& (st->stack->stack_data[st->start+4])); + unit_id = conv_num(st, script_getdata(st,2)); + skill_id = conv_num(st, script_getdata(st,3)); + skill_lv = conv_num(st, script_getdata(st,4)); + target_id = ( script_hasdata(st,5) ? conv_num(st,script_getdata(st,5)) : unit_id ); - if ((bl = map_id2bl(id))) - unit_skilluse_id(bl,(st->end>st->start+5)?conv_num(st,& (st->stack->stack_data[st->start+5])):bl->id,skid,sklv); + bl = map_id2bl(unit_id); + if( bl != NULL ) + unit_skilluse_id(bl, target_id, skill_id, skill_lv); return 0; } +/// Makes the unit cast the skill on the target position. +/// +/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>; BUILDIN_FUNC(unitskillusepos) { - int skid,sklv,x,y,id; - struct block_list *bl = NULL; + int unit_id; + int skill_id; + int skill_lv; + int skill_x; + int skill_y; + struct block_list* bl; - id = conv_num(st,& (st->stack->stack_data[st->start+2])); - skid=conv_num(st,& (st->stack->stack_data[st->start+3])); - sklv=conv_num(st,& (st->stack->stack_data[st->start+4])); - x=conv_num(st,& (st->stack->stack_data[st->start+5])); - y=conv_num(st,& (st->stack->stack_data[st->start+6])); + unit_id = conv_num(st, script_getdata(st,2)); + skill_id = conv_num(st, script_getdata(st,3)); + skill_lv = conv_num(st, script_getdata(st,4)); + skill_x = conv_num(st, script_getdata(st,5)); + skill_y = conv_num(st, script_getdata(st,6)); - if ((bl=map_id2bl(id))) - unit_skilluse_pos(bl,x,y,skid,sklv); + bl = map_id2bl(unit_id); + if( bl != NULL ) + unit_skilluse_pos(bl, skill_x, skill_y, skill_id, skill_lv); return 0; } // <--- [zBuffer] List of mob control commands -// sleep <mili sec> +/// Pauses the execution of the script, detaching the player +/// +/// sleep <mili seconds>; BUILDIN_FUNC(sleep) { - int tick = conv_num(st,& (st->stack->stack_data[st->start+2])); - struct map_session_data *sd = map_id2sd(st->rid); - if(sd && sd->npc_id == st->oid) { + int ticks; + TBL_PC* sd; + + ticks = conv_num(st, script_getdata(st,2)); + sd = map_id2sd(st->rid); + + // detach the player + if( sd && sd->npc_id == st->oid ) + { sd->npc_id = 0; } st->rid = 0; - if(tick <= 0) { - // 何もしない - } else if( !st->sleep.tick ) { - // 初回実行 + + if( ticks <= 0 ) + {// do nothing + } + else if( st->sleep.tick == 0 ) + {// sleep for the target amount of time st->state = RERUNLINE; - st->sleep.tick = tick; - } else { - // 続行 + st->sleep.tick = ticks; + } + else + {// sleep time is over st->sleep.tick = 0; } return 0; } -// sleep2 <mili sec> +/// Pauses the execution of the script, keeping the player attached +/// Returns if a player is still attached +/// +/// sleep2(<mili secconds>) -> <bool> BUILDIN_FUNC(sleep2) { - int tick = conv_num(st,& (st->stack->stack_data[st->start+2])); - if( tick <= 0 ) { - // 0ms の待機時間を指定された - push_val(st->stack,C_INT,map_id2sd(st->rid) != NULL); - } else if( !st->sleep.tick ) { - // 初回実行時 + int ticks; + + ticks = conv_num(st, script_getdata(st,2)); + + if( ticks <= 0 ) + {// do nothing + script_pushint(st, (map_id2sd(st->rid)!=NULL)); + } + else if( !st->sleep.tick ) + {// sleep for the target amount of time st->state = RERUNLINE; - st->sleep.tick = tick; - } else { - push_val(st->stack,C_INT,map_id2sd(st->rid) != NULL); + st->sleep.tick = ticks; + } + else + {// sleep time is over st->sleep.tick = 0; + script_pushint(st, (map_id2sd(st->rid)!=NULL)); } return 0; } -/*========================================== - * 指定NPCの全てのsleepを再開する - *------------------------------------------ - */ +/// Awakes all the sleep timers of the target npc +/// +/// awake "<npc name>"; BUILDIN_FUNC(awake) { - struct npc_data *nd; + struct npc_data* nd; struct linkdb_node *node = (struct linkdb_node *)sleep_db; - nd = npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - if(nd == NULL) + nd = npc_name2id(conv_str(st, script_getdata(st,2))); + if( nd == NULL ) return 0; - while( node ) { - if( (int)node->key == nd->bl.id) { - struct script_state *tst = node->data; - struct map_session_data *sd = map_id2sd(tst->rid); + while( node ) + { + if( (int)node->key == nd->bl.id ) + {// sleep timer for the npc + struct script_state* tst = (struct script_state*)node->data; + TBL_PC* sd = map_id2sd(tst->rid); - if( tst->sleep.timer == -1 ) { + if( tst->sleep.timer == -1 ) + {// already awake ??? node = node->next; continue; } - if((sd && sd->status.char_id != tst->sleep.charid) || (tst->rid && !sd)) - { //Cancel Execution - tst->state=END; + if( (sd && sd->status.char_id != tst->sleep.charid) || (tst->rid && !sd)) + {// char not online anymore / another char of the same account is online - Cancel execution + tst->state = END; tst->rid = 0; } @@ -13023,7 +13104,9 @@ BUILDIN_FUNC(awake) tst->sleep.timer = -1; tst->sleep.tick = 0; run_script_main(tst); - } else { + } + else + { node = node->next; } } @@ -13071,7 +13154,7 @@ BUILDIN_FUNC(getvariableofnpc) /// Opens a warp portal. /// Has no "portal opening" effect/sound, it opens the portal immediately. /// -/// warpportal(<src x>,<src y>,"<target map>",<target x>,<target y>); +/// warpportal <source x>,<source y>,"<target map>",<target x>,<target y>; /// /// @author blackhole89 BUILDIN_FUNC(warpportal) @@ -13087,7 +13170,7 @@ BUILDIN_FUNC(warpportal) bl = map_id2bl(st->oid); if( bl == NULL ) { - ShowError("script: warpportal: npc is needed"); + ShowError("script:warpportal: npc is needed"); return 1; } |