From 690ef2ae18deedd44bf4670eaf751c3888cb5a37 Mon Sep 17 00:00:00 2001 From: zephyrus Date: Sun, 31 Aug 2008 18:37:00 +0000 Subject: * More mercenary updates. - Implemented Mercenary Die, Damage, Run Away when master dies. - Log mercenary damage and gives exp to Master. - Some little bugs. * Fixed Bubble Gums effect works on mvp_sd not sd (according to Doddler). NOTE: Now you can summon a mercenary, make it attack (not skills), follow you even on map change. git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@13158 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/map/clif.c | 77 +++++++++++++++++-------------- src/map/clif.h | 1 + src/map/mercenary.c | 16 ++++++- src/map/mercenary.h | 2 + src/map/mob.c | 127 +++++++++++++++++++++++++++++----------------------- src/map/pc.c | 29 +++++++++--- src/map/status.c | 6 ++- src/map/unit.c | 63 +++++++++++++++----------- 8 files changed, 197 insertions(+), 124 deletions(-) diff --git a/src/map/clif.c b/src/map/clif.c index 674770e4a..c56fc3f26 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -7881,19 +7881,28 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) } //homunculus [blackhole89] - if(merc_is_hom_active(sd->hd)) { + if( merc_is_hom_active(sd->hd) ) + { map_addblock(&sd->hd->bl); clif_spawn(&sd->hd->bl); clif_send_homdata(sd,0,0); clif_hominfo(sd,sd->hd,1); clif_hominfo(sd,sd->hd,0); //for some reason, at least older clients want this sent twice clif_homskillinfoblock(sd); - if (battle_config.hom_setting&0x8) + if( battle_config.hom_setting&0x8 ) status_calc_bl(&sd->hd->bl, SCB_SPEED); //Homunc mimic their master's speed on each map change - if (!(battle_config.hom_setting&0x2)) + if( !(battle_config.hom_setting&0x2) ) skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately } + if( sd->md ) + { + map_addblock(&sd->md->bl); + clif_spawn(&sd->md->bl); + clif_mercenary_info(sd); + clif_mercenary_skillblock(sd); + } + if(sd->state.connect_new) { int lv; sd->state.connect_new = 0; @@ -12364,6 +12373,35 @@ void clif_send_quest_status(struct map_session_data * sd, int quest_id, bool act /*========================================== * Mercenary System *==========================================*/ +void clif_mercenary_updatestatus(struct map_session_data *sd, int type) +{ + struct mercenary_data *md; + int fd; + if( sd == NULL || (md = sd->md) == NULL ) + return; + + fd = sd->fd; + WFIFOHEAD(fd,8); + WFIFOW(fd,0) = 0x02a2; + WFIFOW(fd,2) = type; + switch( type ) + { + case SP_HP: + WFIFOL(fd,4) = md->battle_status.hp; + break; + case SP_MAXHP: + WFIFOL(fd,4) = md->battle_status.max_hp; + break; + case SP_SP: + WFIFOL(fd,4) = md->battle_status.sp; + break; + case SP_MAXSP: + WFIFOL(fd,4) = md->battle_status.max_sp; + break; + } + WFIFOSET(fd,8); +} + void clif_mercenary_info(struct map_session_data *sd) { int fd; @@ -12389,7 +12427,7 @@ void clif_mercenary_info(struct map_session_data *sd) WFIFOW(fd,14) = status->def + (status->vit/2); WFIFOW(fd,16) = status->mdef; WFIFOW(fd,18) = status->flee; - WFIFOW(fd,20) = status->aspd_rate; + WFIFOW(fd,20) = status->amotion; safestrncpy((char*)WFIFOP(fd,22), md->db->name, NAME_LENGTH); WFIFOW(fd,46) = md->db->lv; WFIFOL(fd,48) = status->hp; @@ -12400,7 +12438,7 @@ void clif_mercenary_info(struct map_session_data *sd) WFIFOW(fd,68) = 0; // Loyalty WFIFOL(fd,70) = 0; // Summon Count WFIFOL(fd,74) = md->mercenary.kill_count; - WFIFOW(fd,78) = 0; + WFIFOW(fd,78) = md->battle_status.rhw.range; WFIFOSET(fd,80); } @@ -12435,35 +12473,6 @@ void clif_mercenary_skillblock(struct map_session_data *sd) WFIFOSET(fd,len); } -void clif_mercenary_updatestatus(struct map_session_data *sd, int type) -{ - struct mercenary_data *md; - int fd; - if( sd == NULL || (md = sd->md) == NULL ) - return; - - fd = sd->fd; - WFIFOHEAD(fd,8); - WFIFOW(fd,0) = 0x02a2; - WFIFOW(fd,2) = type; - switch( type ) - { - case SP_HP: - WFIFOL(fd,4) = md->battle_status.hp; - break; - case SP_MAXHP: - WFIFOL(fd,4) = md->battle_status.max_hp; - break; - case SP_SP: - WFIFOL(fd,4) = md->battle_status.sp; - break; - case SP_MAXSP: - WFIFOL(fd,4) = md->battle_status.max_sp; - break; - } - WFIFOSET(fd,8); -} - void clif_parse_mercenary_action(int fd, struct map_session_data* sd) { int option = RFIFOB(fd,2); diff --git a/src/map/clif.h b/src/map/clif.h index 5bd7ca67a..dc9f4ef52 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -443,5 +443,6 @@ void clif_Adopt_reply(struct map_session_data *sd, int type); void clif_mercenary_info(struct map_session_data *sd); void clif_mercenary_skillblock(struct map_session_data *sd); void clif_mercenary_message(int fd, int message); +void clif_mercenary_updatestatus(struct map_session_data *sd, int type); #endif /* _CLIF_H_ */ diff --git a/src/map/mercenary.c b/src/map/mercenary.c index dc49f5e21..740b8d85e 100644 --- a/src/map/mercenary.c +++ b/src/map/mercenary.c @@ -132,10 +132,10 @@ int merc_delete(struct mercenary_data *md, int reply) merc_contract_stop(md); if( !sd ) - return unit_free(&md->bl, 1); + return unit_free(&md->bl, 0); clif_mercenary_message(sd->fd, reply); - return unit_remove_map(&md->bl, 1); + return unit_remove_map(&md->bl, 0); } void merc_contract_stop(struct mercenary_data *md) @@ -213,6 +213,17 @@ int merc_data_received(struct s_mercenary *merc, bool flag) return 1; } +void mercenary_damage(struct mercenary_data *md, struct block_list *src, int hp, int sp) +{ + clif_mercenary_updatestatus(md->master, SP_HP); +} + +int mercenary_dead(struct mercenary_data *md, struct block_list *src) +{ + merc_delete(md, 1); + return 0; +} + int read_mercenarydb(void) { FILE *fp; @@ -292,6 +303,7 @@ int read_mercenarydb(void) status->ele_lv = 1; } + status->aspd_rate = 1000; status->speed = atoi(str[22]); status->adelay = atoi(str[23]); status->amotion = atoi(str[24]); diff --git a/src/map/mercenary.h b/src/map/mercenary.h index bc2046dc7..93c894dda 100644 --- a/src/map/mercenary.h +++ b/src/map/mercenary.h @@ -41,6 +41,8 @@ struct view_data * merc_get_viewdata(int class_); int merc_create(struct map_session_data *sd, int class_, unsigned int lifetime); int merc_data_received(struct s_mercenary *merc, bool flag); int mercenary_save(struct mercenary_data *md); +void mercenary_damage(struct mercenary_data *md, struct block_list *src, int hp, int sp); +int mercenary_dead(struct mercenary_data *md, struct block_list *src); int do_init_mercenary(void); int merc_delete(struct mercenary_data *md, int reply); void merc_contract_stop(struct mercenary_data *md); diff --git a/src/map/mob.c b/src/map/mob.c index 141fc697b..ff5ee6a8e 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -21,6 +21,7 @@ #include "status.h" #include "mob.h" #include "homunculus.h" +#include "mercenary.h" #include "guild.h" #include "itemdb.h" #include "skill.h" @@ -1293,9 +1294,9 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) } } else - if( (abl= map_id2bl(md->attacked_id)) && (!tbl || mob_can_changetarget(md, abl, mode)) ) + if( (abl = map_id2bl(md->attacked_id)) && (!tbl || mob_can_changetarget(md, abl, mode)) ) { - if (md->bl.m != abl->m || abl->prev == NULL + if( md->bl.m != abl->m || abl->prev == NULL || (dist = distance_bl(&md->bl, abl)) >= MAX_MINCHASE || battle_check_target(&md->bl, abl, BCT_ENEMY) <= 0 || (battle_config.mob_ai&0x2 && !status_check_skilluse(&md->bl, abl, 0, 0)) //Retaliate check @@ -1740,63 +1741,78 @@ int mob_respawn(int tid, unsigned int tick, int id, intptr data) void mob_log_damage(struct mob_data *md, struct block_list *src, int damage) { int char_id = 0, flag = 0; - if(damage < 0) return; //Do nothing for absorbed damage. - if(!damage && !(src->type&DEFAULT_ENEMY_TYPE(md))) + if( damage < 0 ) + return; //Do nothing for absorbed damage. + if( !damage && !(src->type&DEFAULT_ENEMY_TYPE(md)) ) return; //Do not log non-damaging effects from non-enemies. - switch (src->type) { - case BL_PC: + switch( src->type ) { - struct map_session_data *sd = (TBL_PC*)src; - char_id = sd->status.char_id; - if (damage) - md->attacked_id = src->id; - break; - } - case BL_HOM: //[orn] - { - struct homun_data *hd = (TBL_HOM*)src; - flag = 1; - if (hd->master) - char_id = hd->master->status.char_id; - if (damage) - md->attacked_id = src->id; - break; - } - case BL_PET: - { - struct pet_data *pd = (TBL_PET*)src; - if (battle_config.pet_attack_exp_to_master && pd->msd) { - char_id = pd->msd->status.char_id; - damage=(damage*battle_config.pet_attack_exp_rate)/100; //Modify logged damage accordingly. + case BL_PC: + { + struct map_session_data *sd = (TBL_PC*)src; + char_id = sd->status.char_id; + if( damage ) + md->attacked_id = src->id; + break; } - //Let mobs retaliate against the pet's master [Skotlex] - if(pd->msd && damage) - md->attacked_id = pd->msd->bl.id; - break; - } - case BL_MOB: - { - struct mob_data* md2 = (TBL_MOB*)src; - if(md2->special_state.ai && md2->master_id) { - struct map_session_data* msd = map_id2sd(md2->master_id); - if (msd) char_id = msd->status.char_id; + case BL_HOM: + { + struct homun_data *hd = (TBL_HOM*)src; + flag = 1; + if( hd->master ) + char_id = hd->master->status.char_id; + if( damage ) + md->attacked_id = src->id; + break; } - if (!damage) + case BL_MER: + { + struct mercenary_data *mer = (TBL_MER*)src; + if( mer->master ) + char_id = mer->master->status.char_id; + if( damage ) + md->attacked_id = src->id; break; - //Let players decide whether to retaliate versus the master or the mob. [Skotlex] - if (md2->master_id && battle_config.retaliate_to_master) - md->attacked_id = md2->master_id; - else + } + case BL_PET: + { + struct pet_data *pd = (TBL_PET*)src; + if( battle_config.pet_attack_exp_to_master && pd->msd ) + { + char_id = pd->msd->status.char_id; + damage = (damage*battle_config.pet_attack_exp_rate)/100; //Modify logged damage accordingly. + } + //Let mobs retaliate against the pet's master [Skotlex] + if( pd->msd && damage ) + md->attacked_id = pd->msd->bl.id; + break; + } + case BL_MOB: + { + struct mob_data* md2 = (TBL_MOB*)src; + if( md2->special_state.ai && md2->master_id ) + { + struct map_session_data* msd = map_id2sd(md2->master_id); + if( msd ) + char_id = msd->status.char_id; + } + if( !damage ) + break; + //Let players decide whether to retaliate versus the master or the mob. [Skotlex] + if( md2->master_id && battle_config.retaliate_to_master ) + md->attacked_id = md2->master_id; + else + md->attacked_id = src->id; + break; + } + default: //For all unhandled types. md->attacked_id = src->id; - break; - } - default: //For all unhandled types. - md->attacked_id = src->id; } - //Log damage... - if(char_id) { + + if( char_id ) + { //Log damage... int i,minpos; unsigned int mindmg; for(i=0,minpos=DAMAGELOG_SIZE-1,mindmg=UINT_MAX;itype == BL_PC) + status = &md->status; + + if( src && src->type == BL_PC ) { sd = (struct map_session_data *)src; mvp_sd = sd; } - status = &md->status; - if( md->guardian_data && md->guardian_data->number >= 0 && md->guardian_data->number < MAX_GUARDIANS ) guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); @@ -2132,14 +2148,15 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) // attempt to drop the item if (rand() % 10000 >= drop_rate) { // Double try by Bubble Gum - if (!(sd && sd->sc.data[SC_ITEMBOOST] && rand() % 10000 < drop_rate)) + if (!(mvp_sd && mvp_sd->sc.data[SC_ITEMBOOST] && rand() % 10000 < drop_rate)) continue; } ditem = mob_setdropitem(md->db->dropitem[i].nameid, 1); //A Rare Drop Global Announce by Lupus - if(drop_rate<=battle_config.rare_drop_announce) { + if( drop_rate <= battle_config.rare_drop_announce ) + { struct item_data *i_data; char message[128]; i_data = itemdb_search(ditem->item_data.nameid); diff --git a/src/map/pc.c b/src/map/pc.c index 0c8af18d3..5e4954eeb 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -873,7 +873,7 @@ int pc_reg_received(struct map_session_data *sd) sd->cashPoints = pc_readaccountreg(sd,"#CASHPOINTS"); sd->kafraPoints = pc_readaccountreg(sd,"#KAFRAPOINTS"); - if ((sd->class_&MAPID_BASEMASK)==MAPID_TAEKWON) + if( (sd->class_&MAPID_BASEMASK) == MAPID_TAEKWON ) { //Better check for class rather than skill to prevent "skill resets" from unsetting this sd->mission_mobid = pc_readglobalreg(sd,"TK_MISSION_ID"); sd->mission_count = pc_readglobalreg(sd,"TK_MISSION_COUNT"); @@ -3571,32 +3571,42 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y //Tag player for rewarping after map-loading is done. [Skotlex] sd->state.rewarp = 1; - sd->mapindex = mapindex; + sd->mapindex = mapindex; sd->bl.m = m; sd->bl.x = sd->ud.to_x = x; sd->bl.y = sd->ud.to_y = y; - if (sd->status.guild_id > 0 && map[m].flag.gvg_castle) + if( sd->status.guild_id > 0 && map[m].flag.gvg_castle ) { // Increased guild castle regen [Valaris] struct guild_castle *gc = guild_mapindex2gc(sd->mapindex); if(gc && gc->guild_id == sd->status.guild_id) sd->regen.state.gc = 1; } - if(sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0) { + if( sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0 ) + { sd->pd->bl.m = m; sd->pd->bl.x = sd->pd->ud.to_x = x; sd->pd->bl.y = sd->pd->ud.to_y = y; sd->pd->ud.dir = sd->ud.dir; } - if(merc_is_hom_active(sd->hd)) { //orn + if( merc_is_hom_active(sd->hd) ) + { sd->hd->bl.m = m; sd->hd->bl.x = sd->hd->ud.to_x = x; sd->hd->bl.y = sd->hd->ud.to_y = y; sd->hd->ud.dir = sd->ud.dir; } + if( sd->md ) + { + sd->md->bl.m = m; + sd->md->bl.x = sd->hd->ud.to_x = x; + sd->md->bl.y = sd->hd->ud.to_y = y; + sd->md->ud.dir = sd->ud.dir; + } + return 0; } @@ -4925,9 +4935,12 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) pet_unlocktarget(sd->pd); } - if(sd->status.hom_id > 0 && battle_config.homunculus_auto_vapor) //orn + if( sd->status.hom_id > 0 && battle_config.homunculus_auto_vapor ) merc_hom_vaporize(sd, 0); + if( sd->md ) + merc_delete(sd->md, 3); // Your mercenary soldier has ran away. + // Leave duel if you die [LuzZza] if(battle_config.duel_autoleave_when_die) { if(sd->duel_group > 0) @@ -4968,6 +4981,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) status_calc_mob(md, 0); status_percent_heal(src,10,0); } + src = battle_get_master(src); // Maybe Player Summon } break; case BL_PET: //Pass on to master... @@ -4976,6 +4990,9 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) case BL_HOM: src = &((TBL_HOM*)src)->master->bl; break; + case BL_MER: + src = &((TBL_MER*)src)->master->bl; + break; } if (src && src->type == BL_PC) diff --git a/src/map/status.c b/src/map/status.c index f11096d7b..5f05296cc 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -698,6 +698,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s case BL_PC: pc_damage((TBL_PC*)target,src,hp,sp); break; case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break; case BL_HOM: merc_damage((TBL_HOM*)target,src,hp,sp); break; + case BL_MER: mercenary_damage((TBL_MER*)target,src,hp,sp); break; } if (status->hp) @@ -717,6 +718,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s case BL_PC: flag = pc_dead((TBL_PC*)target,src); break; case BL_MOB: flag = mob_dead((TBL_MOB*)target, src, flag&4?3:0); break; case BL_HOM: flag = merc_hom_dead((TBL_HOM*)target,src); break; + case BL_MER: flag = mercenary_dead((TBL_MER*)target,src); break; default: //Unhandled case, do nothing to object. flag = 0; break; @@ -2394,9 +2396,9 @@ int status_calc_mercenary(struct mercenary_data *md, int first) } status_calc_misc(&md->bl, status, md->db->lv); - status_cpy(&md->base_status, status); + status_cpy(&md->battle_status, status); status_calc_bl(&md->bl, SCB_ALL); - + return 0; } diff --git a/src/map/unit.c b/src/map/unit.c index 6299175d6..c1fb60031 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -1238,20 +1238,23 @@ int unit_attack(struct block_list *src,int target_id,int continuous) nullpo_retr(0, ud = unit_bl2ud(src)); - target=map_id2bl(target_id); - if(target==NULL || status_isdead(target)) { + target = map_id2bl(target_id); + if( target==NULL || status_isdead(target) ) + { unit_unattackable(src); return 1; } - if( src->type == BL_PC ){ + if( src->type == BL_PC ) + { TBL_PC* sd = (TBL_PC*)src; if( target->type == BL_NPC ) - {// monster npcs [Valaris] + { // monster npcs [Valaris] npc_click(sd,(TBL_NPC*)target); // submitted by leinsirk10 [Celest] return 0; - } else if( pc_is90overweight(sd) ) - {// overwheight - stop attacking and walking + } + else if( pc_is90overweight(sd) ) + { // overwheight - stop attacking and walking unit_stop_attack(src); unit_stop_walking(src,1); return 0; @@ -1427,42 +1430,46 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t struct mob_data *md = NULL; int range; - if((ud=unit_bl2ud(src))==NULL) + if( (ud=unit_bl2ud(src))==NULL ) return 0; - if(ud->attacktimer != tid){ + if( ud->attacktimer != tid ) + { ShowError("unit_attack_timer %d != %d\n",ud->attacktimer,tid); return 0; } + sd = BL_CAST(BL_PC, src); md = BL_CAST(BL_MOB, src); ud->attacktimer = INVALID_TIMER; target=map_id2bl(ud->target); - if(src == NULL || src->prev == NULL || target==NULL || target->prev == NULL) + if( src == NULL || src->prev == NULL || target==NULL || target->prev == NULL ) return 0; - if(status_isdead(src) || status_isdead(target) || !status_check_skilluse(src, target, 0, 0)) + if( status_isdead(src) || status_isdead(target) || !status_check_skilluse(src, target, 0, 0) ) return 0; // can't attack under these conditions - if (src->m != target->m) + if( src->m != target->m ) { - if (src->type == BL_MOB && mob_warpchase((TBL_MOB*)src, target)) + if( src->type == BL_MOB && mob_warpchase((TBL_MOB*)src, target) ) return 1; // Follow up. return 0; } - if(ud->skilltimer != -1 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0)) + if( ud->skilltimer != -1 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0) ) return 0; // can't attack while casting - if(!battle_config.sdelay_attack_enable && DIFF_TICK(ud->canact_tick,tick) > 0 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0)) - { // attacking when under cast delay has restrictions: - if (tid == -1) { //requested attack. + if( !battle_config.sdelay_attack_enable && DIFF_TICK(ud->canact_tick,tick) > 0 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0) ) + { // attacking when under cast delay has restrictions: + if( tid == -1 ) + { //requested attack. if(sd) clif_skill_fail(sd,1,4,0); return 0; } //Otherwise, we are in a combo-attack, delay this until your canact time is over. [Skotlex] - if(ud->state.attack_continue) { - if (DIFF_TICK(ud->canact_tick, ud->attackabletime) > 0) + if( ud->state.attack_continue ) + { + if( DIFF_TICK(ud->canact_tick, ud->attackabletime) > 0 ) ud->attackabletime = ud->canact_tick; ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,src->id,0); } @@ -1472,20 +1479,24 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t sstatus = status_get_status_data(src); range = sstatus->rhw.range; - if(!sd || sd->status.weapon != W_BOW) range++; //Dunno why everyone but bows gets this extra range... - if(unit_is_walking(target)) range++; //Extra range when chasing + if( !sd || sd->status.weapon != W_BOW ) + range++; //Dunno why everyone but bows gets this extra range... + if( unit_is_walking(target) ) + range++; //Extra range when chasing - if(!check_distance_bl(src,target,range) ) { - //Chase if required. + if( !check_distance_bl(src,target,range) ) + { //Chase if required. if(sd) clif_movetoattack(sd,target); else if(ud->state.attack_continue) unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2); return 1; } - if(!battle_check_range(src,target,range)) { + if( !battle_check_range(src,target,range) ) + { //Within range, but no direct line of attack - if(ud->state.attack_continue) { + if( ud->state.attack_continue ) + { if(ud->chaserange > 2) ud->chaserange-=2; unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2); } @@ -1496,7 +1507,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t //Non-players use the sync packet on the walk timer. [Skotlex] if (tid == -1 && sd) clif_fixpos(src); - if(DIFF_TICK(ud->attackabletime,tick) <= 0) + if( DIFF_TICK(ud->attackabletime,tick) <= 0 ) { if (battle_config.attack_direction_change && (src->type&battle_config.attack_direction_change)) { ud->dir = map_calc_dir(src, target->x,target->y ); @@ -2097,6 +2108,8 @@ int unit_free(struct block_list *bl, int clrtype) } if( sd ) sd->md = NULL; + + merc_contract_stop(md); break; } } -- cgit v1.2.3-60-g2f50