version 1.59 | | version 1.60 |
---|
| | |
/* | | /* |
* static char *rcsid_attack_c = | | * static char *rcsid_attack_c = |
* "$Id: attack.c,v 1.59 2001/11/28 04:44:42 michtoen Exp $"; | | * "$Id: attack.c,v 1.60 2001/12/14 10:31:34 darth_bob Exp $"; |
*/ | | */ |
/* | | /* |
CrossFire, A Multiplayer game for X-windows | | CrossFire, A Multiplayer game for X-windows |
| | |
} | | } |
} | | } |
| | |
/* This returns the amount of damage hitter does to op with the appropriate | | /* This returns the amount of damage hitter does to op with the |
* attacktype. Only 1 attacktype should be set at a time. This doesn't | | * appropriate attacktype. Only 1 attacktype should be set at a time. |
* damage the player, but returns how much it should take. However, it will | | * This doesn't damage the player, but returns how much it should |
* do other effects (paralyzation, slow, etc.) | | * take. However, it will do other effects (paralyzation, slow, etc.) |
* Note - changed for PR code - we now pass the attack number and not | | * Note - changed for PR code - we now pass the attack number and not |
* the attacktype. Makes it easier for the PR code. | | * the attacktype. Makes it easier for the PR code. */ |
*/ | | |
| | |
int hit_player_attacktype(object *op, object *hitter, int dam, | | int hit_player_attacktype(object *op, object *hitter, int dam, |
uint32 attacknum, int magic) { | | uint32 attacknum, int magic) { |
| | |
int does_slay=0; | | int doesnt_slay = 1; |
int level_diff; /* for special attacktypes (paralyze, etc) */ | | |
uint32 attacktype=1<<attacknum; | | |
| | |
/* Catch anyone that may be trying to send us a bitmask instead of the number */ | | /* Catch anyone that may be trying to send us a bitmask instead of the number */ |
if (attacknum>=NROFATTACKS) { | | if ((attacknum >= NROFATTACKS) || (attacknum < 0)) { |
LOG(llevError, "hit_player_attacktype: Invalid attacknumber passed: %x\n", attacknum); | | LOG(llevError, "hit_player_attacktype: Invalid attacknumber passed: %x\n", attacknum); |
return 0; | | return 0; |
} | | } |
| | |
if (!attacktype) { | | |
LOG(llevError,"hit_player_attacktype called without an attacktype\n"); | | |
return 0; | | |
} | | |
if (dam < 0) { | | if (dam < 0) { |
LOG(llevError,"hit_player_attacktype called with negative damage: %d\n", dam); | | LOG(llevError,"hit_player_attacktype called with negative damage: %d\n", dam); |
return 0; | | return 0; |
} | | } |
| | |
/* AT_INTERNAL is supposed to do exactly dam. Put a case here so | | /* AT_INTERNAL is supposed to do exactly dam. Put a case here so |
* people can't mess with that or it otherwise get confused. | | * people can't mess with that or it otherwise get confused. */ |
*/ | | if (attacknum == ATNR_INTERNAL) return dam; |
if (attacktype & AT_INTERNAL) return dam; | | |
| | |
if (hitter->slaying) { | | if (hitter->slaying) { |
if (((op->race !=NULL && strstr(hitter->slaying,op->race)) || | | if( |
(op->arch && op->arch->name!=NULL && strstr(op->arch->name, hitter->slaying)))) { | | ((op->race != NULL) && |
does_slay=1; | | strstr(hitter->slaying, op->race) |
| | ) || |
| | (op->arch && |
| | (op->arch->name != NULL) && |
| | strstr(op->arch->name, hitter->slaying)) |
| | ) { |
| | doesnt_slay = 0; |
dam*=3; | | dam*=3; |
} | | } |
} | | } |
| | |
/* Adjust the damage for resistance. Note that neg. values increase damage. */ | | /* Adjust the damage for resistance. Note that neg. values increase damage. */ |
if (op->resist[attacknum] || 1) { | | if(1) { // if (op->resist[attacknum] || 1) { // This is a nop! |
/* basically: dam = dam*(100-op->resist[attacknum])/100; | | /* basically: dam = dam*(100-op->resist[attacknum])/100; |
* in case 0>dam>1, we try to "simulate" a float value-effect */ | | * in case 0>dam>1, we try to "simulate" a float value-effect */ |
dam = dam*(100-op->resist[attacknum]); | | dam *= (100-op->resist[attacknum]); |
if (dam >= 100) | | if (dam >= 100) |
dam /= 100; | | dam /= 100; |
else | | else |
dam = (dam > (random_roll(0, 99, op, PREFER_LOW))) ? 1 : 0; | | dam = (dam > (random_roll(0, 99, op, PREFER_LOW))) ? 1 : 0; |
| | // why not simulate for all values? |
| | // dam = (dam / 100) + (((dam % 100) > (random_roll(0, 99, op, PREFER_LOW))) ? 1 : 0) |
} | | } |
| | |
/* Special hack. By default, if immune to something, you shouldn't need | | /* Special hack. By default, if immune to something, you |
* to worry. However, acid is an exception, since it can still damage | | * shouldn't need to worry. However, acid is an exception, since |
* your items. Only include attacktypes if special processing is needed | | * it can still damage your items. Only include attacktypes if |
*/ | | * special processing is needed */ |
if ((op->resist[attacknum]>=100) && !does_slay && !(attacktype & AT_ACID)) | | |
return 0; | | if ((op->resist[attacknum] >= 100) && |
| | doesnt_slay && |
| | (attacknum != ATNR_ACID)) |
| | return 0; // why do we carefully propagate dam everywhere else? |
| | |
/* Keep this in order - makes things easier to find */ | | /* Keep this in order - makes things easier to find */ |
| | |
if (attacktype & AT_PHYSICAL) { | | switch(attacknum) { |
| | case ATNR_PHYSICAL: |
/* here also check for diseases */ | | /* here also check for diseases */ |
check_physically_infect(op,hitter); | | check_physically_infect(op,hitter); |
| | break; |
| | |
/* Don't need to do anything for magic, fire, electricity, cold */ | | /* Don't need to do anything for: |
} else if (attacktype & | | magic, |
(AT_CONFUSION|AT_POISON|AT_SLOW|AT_PARALYZE|AT_FEAR|AT_CANCELLATION| | | fire, |
AT_DEPLETE|AT_BLIND)) { | | electricity, |
/* Give bonus/penalty for saving throw. */ | | cold */ |
int save_adj=op->resist[attacknum]/10; | | |
| | case ATNR_CONFUSION: |
| | case ATNR_POISON: |
| | case ATNR_SLOW: |
| | case ATNR_PARALYZE: |
| | case ATNR_FEAR: |
| | case ATNR_CANCELLATION: |
| | case AT_DEPLETE: |
| | case AT_BLIND: { |
/* chance for inflicting a special attack depends on the | | /* chance for inflicting a special attack depends on the |
difference between attacker's and defender's level */ | | difference between attacker's and defender's level */ |
level_diff = MIN(110, MAX(0, op->level - hitter->level)); | | int level_diff = MIN(110, MAX(0, op->level - hitter->level)); |
| | |
/* First, only creatures/players with speed can be affected. | | /* First, only creatures/players with speed can be affected. |
* Second, just getting hit doesn't mean it always affects you. | | * Second, just getting hit doesn't mean it always affects |
* Third, you still get a saving through against the effect. | | * you. Third, you still get a saving through against the |
*/ | | * effect. */ |
if (op->speed && (QUERY_FLAG(op, FLAG_MONSTER) || op->type==PLAYER) && | | if (op->speed && |
!(rndm(0, (attacktype&AT_SLOW?6:3)-1)) && | | (QUERY_FLAG(op, FLAG_MONSTER) || op->type==PLAYER) && |
((random_roll(1, 20, op, PREFER_LOW)+save_adj) < savethrow[level_diff])) { | | !(rndm(0, (attacknum == ATNR_SLOW?6:3)-1)) && |
| | ((random_roll(1, 20, op, PREFER_LOW) + |
| | op->resist[attacknum]/10) < savethrow[level_diff])) { |
| | |
| | switch(attacknum) { |
/* Player has been hit by something */ | | /* Player has been hit by something */ |
if (attacktype & AT_CONFUSION) confuse_player(op,hitter,dam); | | case AT_CONFUSION: |
else if (attacktype & AT_POISON) poison_player(op,hitter,dam); | | confuse_player(op,hitter,dam); |
else if (attacktype & AT_SLOW) slow_player(op,hitter,dam); | | break; |
else if (attacktype & AT_PARALYZE) paralyze_player(op,hitter,dam); | | case AT_POISON: |
else if (attacktype & AT_FEAR) SET_FLAG(op, FLAG_SCARED); | | poison_player(op,hitter,dam); |
else if (attacktype & AT_CANCELLATION) cancellation(op); | | break; |
else if (attacktype & AT_DEPLETE) drain_stat(op); | | case AT_SLOW: |
else if (attacktype & AT_BLIND && !QUERY_FLAG(op,FLAG_UNDEAD) && | | slow_player(op,hitter,dam); |
!QUERY_FLAG(op,FLAG_GENERATOR)) blind_player(op,hitter,dam); | | break; |
| | case AT_PARALYZE: |
| | paralyze_player(op,hitter,dam); |
| | break; |
| | case AT_FEAR: |
| | SET_FLAG(op, FLAG_SCARED); |
| | break; |
| | case AT_CANCELLATION: |
| | cancellation(op); |
| | break; |
| | case AT_DEPLETE: |
| | drain_stat(op); |
| | break; |
| | case AT_BLIND: |
| | if(!QUERY_FLAG(op,FLAG_UNDEAD) && |
| | !QUERY_FLAG(op,FLAG_GENERATOR)) |
| | blind_player(op,hitter,dam); |
| | break; |
} | | } |
dam=0; /* These are all effects and don't do real damage */ | | |
} | | } |
else if (attacktype & AT_ACID) { | | dam = 0; /* These are all effects and don't do real damage */ |
object *tmp; | | } break; |
| | case ATNR_ACID: |
| | { |
int flag=0; | | int flag=0; |
| | |
/* Items only get corroded if you're not on a battleground and if | | /* Items only get corroded if you're not on a battleground and |
* your acid resistance is below 50%. */ | | * if your acid resistance is below 50%. */ |
if (!op_on_battleground(op, NULL, NULL) && (op->resist[ATNR_ACID]<50)) { | | if (!op_on_battleground(op, NULL, NULL) && |
| | (op->resist[ATNR_ACID] < 50)) |
| | { |
| | object *tmp; |
for(tmp=op->inv;tmp!=NULL;tmp=tmp->below) { | | for(tmp=op->inv;tmp!=NULL;tmp=tmp->below) { |
| | |
if(!QUERY_FLAG(tmp,FLAG_APPLIED)||tmp->resist[ATNR_ACID]>=10) | | if(!QUERY_FLAG(tmp, FLAG_APPLIED) || |
continue; /* >= 10% acid res. on itmes will protect these */ | | (tmp->resist[ATNR_ACID] >= 10)) |
| | /* >= 10% acid res. on itmes will protect these */ |
| | continue; |
| | |
if(!(tmp->material&M_IRON)) | | if(!(tmp->material&M_IRON)) |
continue; | | continue; |
if(tmp->magic< -4) /* Let's stop at -5 */ | | if(tmp->magic< -4) /* Let's stop at -5 */ |
continue; | | continue; |
| | |
if(tmp->type==RING|| /* removed boots and gloves from exlusion list in PR */ | | if(tmp->type==RING || |
tmp->type==GIRDLE||tmp->type==AMULET||tmp->type==WAND|| | | /* removed boots and gloves from exclusion list in |
tmp->type==ROD||tmp->type==HORN) | | PR */ |
| | tmp->type==GIRDLE || |
| | tmp->type==AMULET || |
| | tmp->type==WAND || |
| | tmp->type==ROD || |
| | tmp->type==HORN) |
continue; /* To avoid some strange effects */ | | continue; /* To avoid some strange effects */ |
| | |
/* High damage acid has better chance of corroding objects */ | | /* High damage acid has better chance of corroding |
if(rndm(0, dam+4)>random_roll(0, 39, op, PREFER_HIGH)+2*tmp->magic) { | | objects */ |
| | if(rndm(0, dam+4) > |
| | random_roll(0, 39, op, PREFER_HIGH)+2*tmp->magic) { |
if(op->type==PLAYER) { | | if(op->type==PLAYER) { |
/* Make this more visible */ | | /* Make this more visible */ |
new_draw_info_format(NDI_UNIQUE|NDI_RED,0, op, | | new_draw_info_format(NDI_UNIQUE|NDI_RED,0, op, |
| | |
esrv_send_item(op, tmp); | | esrv_send_item(op, tmp); |
} | | } |
} | | } |
if(flag) fix_player(op); /* Something was corroded */ | | if(flag) |
| | fix_player(op); /* Something was corroded */ |
} | | } |
} | | } |
else if (attacktype & AT_DRAIN) { | | break; |
| | case ATNR_DRAIN: |
| | { |
/* rate is the proportion of exp drained. High rate means | | /* rate is the proportion of exp drained. High rate means |
* not much is drained, low rate means a lot is drained. | | * not much is drained, low rate means a lot is drained. |
*/ | | */ |
int rate = 50; | | int rate; |
| | |
if (op->resist[ATNR_DRAIN]>0) rate *= (100+op->resist[ATNR_DRAIN])/100; | | |
else if (op->resist[ATNR_DRAIN]<0) rate /= (100 - op->resist[ATNR_DRAIN])/100; | | |
| | |
/* full protection has no effect. Nothing else in this function needs | | if(op->resist[ATNR_DRAIN] > 0) |
* to get done, so just return. | | rate = 50 + op->resist[ATNR_DRAIN] / 2; |
*/ | | else if(op->resist[ATNR_DRAIN] < 0) |
if (!rate) return 0; | | rate = 5000 / (100 - op->resist[ATNR_DRAIN]); |
| | |
| | /* full protection has no effect. Nothing else in this |
| | * function needs to get done, so just return. */ |
| | if(!rate) |
| | return 0; |
| | |
if(op->stats.exp<=rate) { | | if(op->stats.exp<=rate) { |
if(op->type==GOLEM) | | if(op->type==GOLEM) |
dam=999; /* It's force is "sucked" away. 8) */ | | dam = 999; /* Its force is "sucked" away. 8) */ |
else /* If we can't drain, lets try to do physical damage */ | | else |
| | /* If we can't drain, lets try to do physical damage */ |
dam=hit_player_attacktype(op, hitter, dam, ATNR_PHYSICAL, magic); | | dam=hit_player_attacktype(op, hitter, dam, ATNR_PHYSICAL, magic); |
} else { | | } else { |
if(hitter->stats.hp<hitter->stats.maxhp && | | if(hitter->stats.hp<hitter->stats.maxhp && |
| | |
} | | } |
dam=0; /* Drain is an effect */ | | dam=0; /* Drain is an effect */ |
} | | } |
} else if (attacktype & AT_TURN_UNDEAD) { | | } break; |
| | case ATNR_TURN_UNDEAD: |
| | { |
if (QUERY_FLAG(op,FLAG_UNDEAD)) { | | if (QUERY_FLAG(op,FLAG_UNDEAD)) { |
object *owner=get_owner(hitter)==NULL?hitter:get_owner(hitter); | | object *owner=get_owner(hitter)==NULL?hitter:get_owner(hitter); |
object *god = find_god (determine_god (owner)); | | object *god = find_god (determine_god (owner)); |
int div = 1; | | int div = 1; |
| | |
/* if undead are not an enemy of your god, you turn them at half | | /* if undead are not an enemy of your god, you turn them |
* strength */ | | * at half strength */ |
if ( ! god || ! god->slaying | | if ( ! god || ! god->slaying |
|| strstr (god->slaying, undead_name) == NULL) | | || strstr (god->slaying, undead_name) == NULL) |
div = 2; | | div = 2; |
/* Give a bonus if you resist turn unded */ | | /* Give a bonus if you resist turn undead */ |
if (op->level < | | if (op->level * div < |
(turn_bonus[owner->stats.Wis]+owner->level + (op->resist[ATNR_TURN_UNDEAD]/100)) / div) | | (turn_bonus[owner->stats.Wis]+owner->level + |
| | (op->resist[ATNR_TURN_UNDEAD]/100))) |
SET_FLAG(op, FLAG_SCARED); | | SET_FLAG(op, FLAG_SCARED); |
} | | } |
else dam=0; /*don't damage non undead - should we damage undead? */ | | else |
| | dam = 0; /* don't damage non undead - should we damage |
} else if (attacktype & AT_DEATH) { | | undead? */ |
| | } break; |
| | case ATNR_DEATH: |
| | { |
deathstrike_player(op, hitter, &dam); | | deathstrike_player(op, hitter, &dam); |
| | } break; |
} else if (attacktype & AT_CHAOS) { | | case ATNR_CHAOS: |
LOG(llevError,"%s was hit by %s with non-specific chaos.\n", | | { |
query_name(op),query_name(hitter)); | | LOG(llevError, |
| | "%s was hit by %s with non-specific chaos.\n", |
| | query_name(op), |
| | query_name(hitter)); |
dam=0; | | dam=0; |
| | } break; |
} else if (attacktype & AT_COUNTERSPELL) { | | case ATNR_COUNTERSPELL: |
LOG(llevError,"%s was hit by %s with counterspell attack.\n", | | { |
query_name(op),query_name(hitter)); | | LOG(llevError, |
dam=0; /* This should never happen. Counterspell is handled seperately | | "%s was hit by %s with counterspell attack.\n", |
* and filtered out. If this does happen, Counterspell has no | | query_name(op), |
* effect on anything but spells, so it does no damage. */ | | query_name(hitter)); |
| | dam = 0; |
} else if (attacktype & AT_HOLYWORD) { | | /* This should never happen. Counterspell is handled |
| | * seperately and filtered out. If this does happen, |
| | * Counterspell has no effect on anything but spells, so it |
| | * does no damage. */ |
| | } break; |
| | case ATNR_HOLYWORD: |
| | { |
/* Holyword only affects a limited range of creatures */ | | /* Holyword only affects a limited range of creatures */ |
/* Affects enemies of your god (*3 for slaying applied above) * | | /* Affects enemies of your god (*3 for slaying applied above) * |
* Affects undead even if not enemies, unless they are friends * | | * Affects undead even if not enemies, unless they are friends * |
| | |
/* As with turn undead above, give a bonus on the saving throw */ | | /* As with turn undead above, give a bonus on the saving throw */ |
if((op->level+(op->resist[ATNR_HOLYWORD]/100))<owner->level+turn_bonus[owner->stats.Wis]) | | if((op->level+(op->resist[ATNR_HOLYWORD]/100))<owner->level+turn_bonus[owner->stats.Wis]) |
SET_FLAG(op, FLAG_SCARED); | | SET_FLAG(op, FLAG_SCARED); |
| | } break; |
} | | } |
return dam; | | return dam; |
} | | } |