version 1.91 | | version 1.92 |
---|
| | |
/* | | /* |
* static char *rcsid_attack_c = | | * static char *rcsid_attack_c = |
* "$Id: attack.c,v 1.91 2003/09/04 06:25:32 temitchell Exp $"; | | * "$Id: attack.c,v 1.92 2003/09/13 05:02:07 mwedel Exp $"; |
*/ | | */ |
/* | | /* |
CrossFire, A Multiplayer game for X-windows | | CrossFire, A Multiplayer game for X-windows |
| | |
| | |
/*#define ATTACK_DEBUG*/ | | /*#define ATTACK_DEBUG*/ |
| | |
| | /* cancels object *op. Cancellation basically means an object loses |
| | * its magical benefits. |
| | */ |
| | void cancellation(object *op) |
| | { |
| | object *tmp; |
| | |
| | if (QUERY_FLAG (op, FLAG_ALIVE) || op->type == CONTAINER || op->type == THROWN_OBJ) { |
| | /* Recur through the inventory */ |
| | for(tmp=op->inv;tmp!=NULL;tmp=tmp->below) |
| | if (!did_make_save_item(tmp, AT_CANCELLATION,op)) |
| | cancellation(tmp); |
| | } |
| | else if(FABS(op->magic)<=(rndm(0, 5))) { |
| | /* Nullify this object. This code could probably be more complete */ |
| | /* in what abilities it should cancel */ |
| | op->magic=0; |
| | CLEAR_FLAG(op, FLAG_DAMNED); |
| | CLEAR_FLAG(op, FLAG_CURSED); |
| | CLEAR_FLAG(op, FLAG_KNOWN_MAGICAL); |
| | CLEAR_FLAG(op, FLAG_KNOWN_CURSED); |
| | if (op->env && op->env->type == PLAYER) { |
| | esrv_send_item (op->env, op); |
| | } |
| | } |
| | } |
| | |
| | |
| | |
/* did_make_save_item just checks to make sure the item actually | | /* did_make_save_item just checks to make sure the item actually |
* made its saving throw based on the tables. It does not take | | * made its saving throw based on the tables. It does not take |
* any further action (like destroying the item). | | * any further action (like destroying the item). |
| | |
if ((tmp = present_arch(at,op->map,op->x,op->y)) == NULL) { | | if ((tmp = present_arch(at,op->map,op->x,op->y)) == NULL) { |
tmp = arch_to_object(at); | | tmp = arch_to_object(at); |
tmp->x=op->x,tmp->y=op->y; | | tmp->x=op->x,tmp->y=op->y; |
| | SET_SLOW_PENALTY(tmp, 0); |
insert_ob_in_map(tmp,op->map,originator,0); | | insert_ob_in_map(tmp,op->map,originator,0); |
} | | } |
if ( ! QUERY_FLAG (op, FLAG_REMOVED)) | | if ( ! QUERY_FLAG (op, FLAG_REMOVED)) |
| | |
found++; | | found++; |
break; | | break; |
} | | } |
} else if (USING_SKILL(hitter, SK_BOXING)) { | | } else if (USING_SKILL(hitter, SK_PUNCHING)) { |
for (i=0; i < MAXATTACKMESS && attack_mess[ATM_PUNCH][i].level != -1; | | for (i=0; i < MAXATTACKMESS && attack_mess[ATM_PUNCH][i].level != -1; |
i++) | | i++) |
if (dam < attack_mess[ATM_PUNCH][i].level) { | | if (dam < attack_mess[ATM_PUNCH][i].level) { |
| | |
new_draw_info(NDI_BLACK, 0, hitter, buf); | | new_draw_info(NDI_BLACK, 0, hitter, buf); |
} else if(get_owner(hitter)!=NULL&&hitter->owner->type==PLAYER) { | | } else if(get_owner(hitter)!=NULL&&hitter->owner->type==PLAYER) { |
/* look for stacked spells and start reducing the message chances */ | | /* look for stacked spells and start reducing the message chances */ |
if (hitter->type == FBALL || hitter->type == FBULLET || | | if (hitter->type == SPELL_EFFECT && |
hitter->type == CONE) { | | (hitter->subtype == SP_EXPLOSION || |
| | hitter->subtype == SP_BULLET || |
| | hitter->subtype == SP_CONE)) { |
i=4; | | i=4; |
map = hitter->map; | | map = hitter->map; |
if (out_of_map(map, hitter->x, hitter->y)) | | if (out_of_map(map, hitter->x, hitter->y)) |
| | |
next = get_map_ob(map, hitter->x, hitter->y); | | next = get_map_ob(map, hitter->x, hitter->y); |
if (next) | | if (next) |
while(next) { | | while(next) { |
if (next->type == FBALL || next->type == FBULLET || | | if (next->type == SPELL_EFFECT && |
next->type == CONE) | | (next->subtype == SP_EXPLOSION || next->subtype==SP_BULLET || |
| | next->subtype == SP_CONE)) |
i*=3; | | i*=3; |
tmp = next; | | tmp = next; |
next = tmp->above; | | next = tmp->above; |
| | |
if(roll==20 || op->stats.ac>=base_wc-roll) { | | if(roll==20 || op->stats.ac>=base_wc-roll) { |
int hitdam = base_dam; | | int hitdam = base_dam; |
if (settings.casting_time == TRUE) { | | if (settings.casting_time == TRUE) { |
if ((hitter->type == PLAYER)&&(hitter->casting > -1)){ | | if ((hitter->type == PLAYER)&&(hitter->casting_time > -1)){ |
hitter->casting = -1; | | hitter->casting_time = -1; |
hitter->spell_state = 1; | | |
new_draw_info(NDI_UNIQUE, 0,hitter,"You attacked and lost " | | new_draw_info(NDI_UNIQUE, 0,hitter,"You attacked and lost " |
"your spell!"); | | "your spell!"); |
} | | } |
if ((op->casting > -1)&&(hitdam > 0)){ | | if ((op->casting_time > -1)&&(hitdam > 0)){ |
op->casting = -1; | | op->casting_time = -1; |
op->spell_state = 1; | | |
if (op->type == PLAYER) { | | if (op->type == PLAYER) { |
new_draw_info(NDI_UNIQUE, 0,op,"You were hit and lost " | | new_draw_info(NDI_UNIQUE, 0,op,"You were hit and lost " |
"your spell!"); | | "your spell!"); |
| | |
* Move the wiz check up here - before, the hitter wouldn't gain exp | | * Move the wiz check up here - before, the hitter wouldn't gain exp |
* exp, but the wiz would still lose exp! If drainee is a wiz, | | * exp, but the wiz would still lose exp! If drainee is a wiz, |
* nothing happens. | | * nothing happens. |
| | * Try to credit the owner. We try to display player -> player drain |
| | * attacks, hence all the != PLAYER checks. |
*/ | | */ |
if (!op_on_battleground(hitter, NULL, NULL) && !QUERY_FLAG(op,FLAG_WAS_WIZ)) { | | if (!op_on_battleground(hitter, NULL, NULL) && !QUERY_FLAG(op,FLAG_WAS_WIZ)) { |
object *owner = get_owner(hitter), *old_skill; | | object *owner = get_owner(hitter); |
| | |
/* Try to find the owner of the thing doing the drain - thus, if | | |
* you summon vampires, you get some exp. Most of this code | | |
* below is taken directly from kill_object | | |
*/ | | |
if (owner && owner != hitter) { | | if (owner && owner != hitter) { |
old_skill = owner->chosen_skill; | | |
owner->chosen_skill = hitter->chosen_skill; | | |
owner->exp_obj=hitter->exp_obj; | | |
| | |
/* Not sure if this will happen or not - I'm think it could with | | |
* summoned pets - they may use a skill and thus the chosen_skill | | |
* gets updated to something they have. | | |
*/ | | |
if (owner->chosen_skill && owner->chosen_skill->env != owner) { | | |
LOG(llevDebug,"kill_object: chosen skill doesn't belong to owner? (%s, %s)\n", | | |
owner->chosen_skill->name, owner->name); | | |
owner->chosen_skill = NULL; | | |
} | | |
if (owner->exp_obj && owner->exp_obj->env != owner) { | | |
LOG(llevDebug,"kill_object: exp_obj doesn't belong to owner? (%s, %s)\n", | | |
owner->exp_obj->name, owner->name); | | |
owner->exp_obj = NULL; | | |
} | | |
if (op->type != PLAYER || owner->type != PLAYER) | | if (op->type != PLAYER || owner->type != PLAYER) |
add_exp(owner,op->stats.exp/(rate*2)); | | change_exp(owner, op->stats.exp/(rate*2), |
owner->chosen_skill = old_skill; | | hitter->chosen_skill? hitter->chosen_skill->skill:NULL, SK_EXP_TOTAL); |
} else { | | } else if (op->type != PLAYER || hitter->type != PLAYER) { |
if (op->type != PLAYER || hitter->type != PLAYER) | | change_exp(hitter, op->stats.exp/(rate*2), |
add_exp(hitter,op->stats.exp/(rate*2)); | | hitter->chosen_skill->skill, 0); |
} | | } |
add_exp(op,-op->stats.exp/rate); | | change_exp(op,-op->stats.exp/rate, NULL, 0); |
} | | } |
dam = 1; /* Drain is an effect. Still return 1 - otherwise, if you have pure | | dam = 1; /* Drain is an effect. Still return 1 - otherwise, if you have pure |
* drain attack, you won't know that you are actually sucking out EXP, | | * drain attack, you won't know that you are actually sucking out EXP, |
| | |
*/ | | */ |
int kill_object(object *op,int dam, object *hitter, int type) | | int kill_object(object *op,int dam, object *hitter, int type) |
{ | | { |
char buf[MAX_BUF]; | | char buf[MAX_BUF], *skill; |
int maxdam=0; | | int maxdam=0; |
int battleg=0; /* true if op standing on battleground */ | | int battleg=0; /* true if op standing on battleground */ |
int killed_script_rtn = 0; | | int killed_script_rtn = 0; |
object *owner=NULL, *old_skill; | | object *owner=NULL; |
int evtid; | | int evtid; |
CFParm CFP; | | CFParm CFP; |
| | object *skop=NULL; |
event *evt; | | event *evt; |
| | |
if (op->stats.hp>=0) | | if (op->stats.hp>=0) |
| | |
killed_script_rtn = *(int *)(CFR->Value[0]); | | killed_script_rtn = *(int *)(CFR->Value[0]); |
if (killed_script_rtn) | | if (killed_script_rtn) |
return 0; | | return 0; |
}; | | } |
} | | } |
/* GROS: Handle for the global kill event */ | | /* GROS: Handle for the global kill event */ |
evtid = EVENT_GKILL; | | evtid = EVENT_GKILL; |
| | |
} | | } |
if(QUERY_FLAG (op, FLAG_FRIENDLY) && op->type != PLAYER) { | | if(QUERY_FLAG (op, FLAG_FRIENDLY) && op->type != PLAYER) { |
remove_friendly_object(op); | | remove_friendly_object(op); |
if (get_owner (op) != NULL && op->owner->type == PLAYER) | | if (get_owner (op) != NULL && op->owner->type == PLAYER && |
op->owner->contr->golem=NULL; | | op->owner->contr->ranges[range_golem] == op) { |
| | op->owner->contr->ranges[range_golem]=NULL; |
| | op->owner->contr->golem_count=0; |
| | } |
else | | else |
LOG (llevError, "BUG: hit_player(): Encountered golem without owner.\n"); | | LOG (llevError, "BUG: hit_player(): Encountered golem without owner.\n"); |
| | |
| | |
*/ | | */ |
if(op->type == PLAYER && hitter != op && !battleg) | | if(op->type == PLAYER && hitter != op && !battleg) |
change_luck(owner, -1); | | change_luck(owner, -1); |
| | |
| | /* This code below deals with finding the appropriate skill |
| | * to credit exp to. This is a bit problematic - we should |
| | * probably never really have to look at current_weapon->skill |
| | */ |
| | if (hitter->skill && hitter->type!=PLAYER) skill = hitter->skill; |
| | else if (owner->chosen_skill) { |
| | skill = owner->chosen_skill->skill; |
| | skop = owner->chosen_skill; |
| | } |
| | else if (QUERY_FLAG(owner, FLAG_READY_WEAPON)) skill=owner->current_weapon->skill; |
| | else |
| | LOG(llevError,"kill_object - unable to find skill that killed monster\n"); |
| | |
| | /* We have the skill we want to credit to - now find the object this goes |
| | * to. |
| | */ |
| | if (!skop && skill) { |
| | int i; |
| | |
| | for (i=0; i<NUM_SKILLS; i++) |
| | if (owner->contr->last_skill_ob[i] && |
| | !strcmp(owner->contr->last_skill_ob[i]->skill, skill)) { |
| | skop = owner->contr->last_skill_ob[i]; |
| | break; |
| | } |
| | } |
} /* Was it a player that hit somethign */ | | } /* Was it a player that hit somethign */ |
| | |
| | |
| | |
if(owner != hitter ) { | | if(owner != hitter ) { |
(void) sprintf(buf,"%s killed %s with %s%s.",owner->name, | | (void) sprintf(buf,"%s killed %s with %s%s.",owner->name, |
query_name(op),query_name(hitter), battleg? " (duel)":""); | | query_name(op),query_name(hitter), battleg? " (duel)":""); |
owner->exp_obj=hitter->exp_obj; | | |
} | | } |
else | | else { |
(void) sprintf(buf,"%s killed %s%s.",hitter->name,op->name, | | (void) sprintf(buf,"%s killed %s%s.",hitter->name,op->name, |
battleg? " (duel)":""); | | battleg? " (duel)":""); |
| | } |
| | /* These may have been set in the player code section above */ |
| | if (!skop) skop = hitter->chosen_skill; |
| | if (!skill && skop) skill=skop->skill; |
| | |
new_draw_info(NDI_ALL, op->type==PLAYER?1:10, NULL, buf); | | new_draw_info(NDI_ALL, op->type==PLAYER?1:10, NULL, buf); |
| | |
| | |
/* If you didn't kill yourself, and your not the wizard */ | | /* If you didn't kill yourself, and your not the wizard */ |
if(hitter!=op&&!QUERY_FLAG(op, FLAG_WAS_WIZ)) { | | if(hitter!=op&&!QUERY_FLAG(op, FLAG_WAS_WIZ)) { |
int exp=op->stats.exp; | | int exp; |
| | |
if(!settings.simple_exp && hitter->level>op->level) | | |
exp=(exp*(op->level+1))/MAX(hitter->level+1, 1); | | |
| | |
if (owner != hitter) { | | |
old_skill = owner->chosen_skill; | | |
owner->chosen_skill = hitter->chosen_skill; | | |
owner->exp_obj=hitter->exp_obj; | | |
| | |
/* Not sure if this will happen or not - I'm think it could with | | |
* summoned pets - they may use a skill and thus the chosen_skill | | |
* gets updated to something they have. | | |
*/ | | |
if (owner->chosen_skill && owner->chosen_skill->env != owner) { | | |
LOG(llevDebug,"kill_object: chosen skill doesn't belong to owner? (%s, %s)\n", | | |
owner->chosen_skill->name, owner->name); | | |
owner->chosen_skill = NULL; | | |
} | | |
if (owner->exp_obj && owner->exp_obj->env != owner) { | | |
LOG(llevDebug,"kill_object: exp_obj doesn't belong to owner? (%s, %s)\n", | | |
owner->exp_obj->name, owner->name); | | |
owner->exp_obj = NULL; | | |
} | | |
} | | |
| | |
exp = calc_skill_exp(owner,op); | | exp = calc_skill_exp(owner,op, skop); |
| | |
/* Really don't give much experience for killing other players */ | | /* Really don't give much experience for killing other players */ |
if (op->type==PLAYER) { | | if (op->type==PLAYER) { |
| | |
if (battleg) exp = 0; | | if (battleg) exp = 0; |
| | |
if(owner->type!=PLAYER || owner->contr->party_number<=0) { | | if(owner->type!=PLAYER || owner->contr->party_number<=0) { |
add_exp(owner,exp); | | change_exp(owner,exp, skill, 0); |
} | | } |
else { | | else { |
int shares=0,count=0; | | int shares=0,count=0; |
| | |
} | | } |
} | | } |
if(count==1 || shares>exp) | | if(count==1 || shares>exp) |
add_exp(owner,exp); | | change_exp(owner,exp, skill, SK_EXP_TOTAL); |
else { | | else { |
int share=exp/shares,given=0,nexp; | | int share=exp/shares,given=0,nexp; |
for(pl=first_player;pl!=NULL;pl=pl->next) { | | for(pl=first_player;pl!=NULL;pl=pl->next) { |
if(pl->ob->contr->party_number==no && on_same_map(pl->ob, owner)) { | | if(pl->ob->contr->party_number==no && on_same_map(pl->ob, owner)) { |
nexp=(pl->ob->level+4)*share; | | nexp=(pl->ob->level+4)*share; |
add_exp(pl->ob,nexp); | | change_exp(pl->ob,nexp, skop->skill, SK_EXP_TOTAL); |
given+=nexp; | | given+=nexp; |
} | | } |
} | | } |
exp-=given; | | exp-=given; |
add_exp(owner,exp); /* give any remainder to the player */ | | /* give any remainder to the player */ |
| | change_exp(owner,exp, skill, SK_EXP_ADD_SKILL); |
} | | } |
} /* else part of a party */ | | } /* else part of a party */ |
| | |
/* set this back after we have added the experience */ | | |
if (owner != hitter) owner->chosen_skill = old_skill; | | |
| | |
} /* end if person didn't kill himself */ | | } /* end if person didn't kill himself */ |
| | |
if(op->type!=PLAYER) { | | if(op->type!=PLAYER) { |
| | |
if(hitter->type == PLAYER && hitter->contr->peaceful == 1) | | if(hitter->type == PLAYER && hitter->contr->peaceful == 1) |
return 1; | | return 1; |
| | |
if(owner = get_owner(hitter)){ | | if((owner = get_owner(hitter))!=NULL) { |
if(owner->type == PLAYER && owner->contr->peaceful == 1) | | if(owner->type == PLAYER && owner->contr->peaceful == 1) |
friendlyfire = 2; | | friendlyfire = 2; |
} | | } |
| | |
if(hitter->type == CONE || hitter->type == FBALL || hitter->type == FIREWALL | | if (hitter->type == SPELL || hitter->type == POISONING || |
|| hitter->type == SWARM_SPELL || hitter->type == POISONCLOUD | | hitter->type == DISEASE || hitter->type == RUNE) |
|| hitter->type == POISONING || hitter->type == DISEASE || hitter->type == RUNE) | | |
| | |
friendlyfire = 0; | | friendlyfire = 0; |
} | | } |
| | |
return friendlyfire; | | return friendlyfire; |
} | | } |
| | |
| | |
object *tmp; | | object *tmp; |
int maxduration; | | int maxduration; |
| | |
tmp = present_in_ob(CONFUSION,op); | | tmp = present_in_ob_by_name(FORCE,"confusion", op); |
if(!tmp) { | | if(!tmp) { |
tmp = get_archetype("confusion"); | | tmp = get_archetype("force"); |
tmp = insert_ob_in_ob(tmp,op); | | tmp = insert_ob_in_ob(tmp,op); |
} | | } |
| | |
/* Duration added per hit and max. duration of confusion both depend | | /* Duration added per hit and max. duration of confusion both depend |
on the player's resistance */ | | * on the player's resistance |
tmp->stats.food += MAX(1, 5*(100-op->resist[ATNR_CONFUSION])/100); | | */ |
| | tmp->speed = 0.05; |
| | tmp->subtype = FORCE_CONFUSION; |
| | tmp->duration = 8 + MAX(1, 5*(100-op->resist[ATNR_CONFUSION])/100); |
| | if (tmp->name) free_string(tmp->name); |
| | tmp->name = add_string("confusion"); |
maxduration = MAX(2, 30*(100-op->resist[ATNR_CONFUSION])/100); | | maxduration = MAX(2, 30*(100-op->resist[ATNR_CONFUSION])/100); |
if( tmp->stats.food > maxduration) | | if( tmp->duration > maxduration) |
tmp->stats.food = maxduration; | | tmp->duration = maxduration; |
| | |
if(op->type == PLAYER && !QUERY_FLAG(op,FLAG_CONFUSED)) | | if(op->type == PLAYER && !QUERY_FLAG(op,FLAG_CONFUSED)) |
new_draw_info(NDI_UNIQUE, 0,op,"You suddenly feel very confused!"); | | new_draw_info(NDI_UNIQUE, 0,op,"You suddenly feel very confused!"); |
| | |
} | | } |
| | |
| | |
/* Attempts to kill 'op'. hitter is the attack object, dam i | | /* Attempts to kill 'op'. hitter is the attack object, dam is |
* the computed damaged. | | * the computed damaged. |
*/ | | */ |
void deathstrike_player(object *op, object *hitter, int *dam) | | void deathstrike_player(object *op, object *hitter, int *dam) |
{ /* The intention of a death attack is to kill outright things | | { |
| | /* The intention of a death attack is to kill outright things |
** that are a lot weaker than the attacker, have a chance of killing | | ** that are a lot weaker than the attacker, have a chance of killing |
** things somewhat weaker than the caster, and no chance of | | ** things somewhat weaker than the caster, and no chance of |
** killing something equal or stronger than the attacker. | | ** killing something equal or stronger than the attacker. |
| | |
op->arch->name, op->name); | | op->arch->name, op->name); |
def_lev = 1; | | def_lev = 1; |
} | | } |
atk_lev = SK_level (hitter) / 2; | | atk_lev = (hitter->chosen_skill?hitter->chosen_skill->level:hitter->level) / 2; |
/* LOG(llevDebug,"Deathstrike - attack level %d, defender level %d\n", | | /* LOG(llevDebug,"Deathstrike - attack level %d, defender level %d\n", |
atk_lev, def_lev); */ | | atk_lev, def_lev); */ |
| | |
| | |
/* determine if the object is an 'aimed' missile */ | | /* determine if the object is an 'aimed' missile */ |
int is_aimed_missile ( object *op) { | | int is_aimed_missile ( object *op) { |
| | |
if(op&&QUERY_FLAG(op,FLAG_FLYING)&& | | /* I broke what used to be one big if into a few nested |
(op->type==ARROW||op->type==THROWN_OBJ | | * ones so that figuring out the logic is at least possible. |
||op->type==FBULLET||op->type==FBALL)) | | */ |
| | if (op && QUERY_FLAG(op,FLAG_FLYING)) { |
| | if (op->type==ARROW || op->type==THROWN_OBJ) |
return 1; | | return 1; |
| | else if (op->type==SPELL_EFFECT && (op->subtype == SP_BULLET || |
| | op->subtype == SP_EXPLOSION)) |
| | return 1; |
| | } |
return 0; | | return 0; |
} | | } |
| | |