version 1.41 | | version 1.42 |
---|
| | |
/* | | /* |
* static char *rcsid_skills_c = | | * static char *rcsid_skills_c = |
* "$Id: skills.c,v 1.41 2003/09/04 06:25:32 temitchell Exp $"; | | * "$Id: skills.c,v 1.42 2003/09/13 05:02:12 mwedel Exp $"; |
*/ | | */ |
/* | | /* |
CrossFire, A Multiplayer game for X-windows | | CrossFire, A Multiplayer game for X-windows |
| | |
Copyright (C) 2001 Mark Wedel & Crossfire Development Team | | Copyright (C) 2003 Mark Wedel & Crossfire Development Team |
Copyright (C) 1992 Frank Tore Johansen | | Copyright (C) 1992 Frank Tore Johansen |
| | |
This program is free software; you can redistribute it and/or modify | | This program is free software; you can redistribute it and/or modify |
| | |
The authors can be reached via e-mail to crossfire-devel@real-time.com | | The authors can be reached via e-mail to crossfire-devel@real-time.com |
*/ | | */ |
| | |
/* Initial coding: 6 Sep 1994, Nick Williams (njw@cs.city.ac.uk) */ | | |
| | |
/* Generalized code + added hiding and lockpicking skills, */ | | |
/* March 3, 1995, brian thomas (thomas@astro.psu.edu) */ | | |
| | |
/* Added more skills, fixed bug in stealing code */ | | |
/* April 21, 1995, brian thomas (thomas@astro.psu.edu) */ | | |
| | |
/* Added more skills, fixed bugs, see skills.h */ | | |
/* May/June, 1995, brian thomas (thomas@astro.psu.edu) */ | | |
| | |
/* July 95 Code re-vamped. Now we add the experience objects, all | | |
* player activities which gain experience will bbe through the use | | |
* of skillls. Thus, I added hand_weapons, missile_weapons, and | | |
* remove_traps skills -b.t. | | |
*/ | | |
| | |
/* Aug 95 - Added more skills (disarm traps, spellcasting, praying). | | |
* Also, hand_weapons is now "melee_weapons". b.t. | | |
*/ | | |
| | |
/* Oct 95 - changed the praying skill to accomodate MULTIPLE_GODS | | |
* hack - b.t. | | |
*/ | | |
| | |
/* Dec 95 - modified the literacy and inscription (writing) skills. b.t. | | |
*/ | | |
| | |
/* Mar 96 - modified the stealing skill. Objects with type FLESH or | | |
* w/o a type cannot be stolen by players. b.t. | | |
*/ | | |
| | |
/* Sept 96 - changed parsing of params through use_skill command, also | | |
* added in throw skill -b.t. */ | | |
| | |
/* Oct 96 - altered hiding and stealing code for playbalance. -b.t. */ | | |
| | |
/* Sept 97 - yet another alteration to the stealing code. Lets allow | | |
* multiple stealing, after having alerted the victim. But only subsequent | | |
* steals made while we are unseen (in dark, invisible, hidden) will have | | |
* any chance of success. Also, on each subsequent attempt, we raise the | | |
* wisdom of the npc a bit, which makes it ultimately possible for the | | |
* npc to detect the theif, regardless of the situation. -b.t. */ | | |
| | |
#include <global.h> | | #include <global.h> |
#include <object.h> | | #include <object.h> |
#ifndef __CEXTRACT__ | | #ifndef __CEXTRACT__ |
| | |
#include <spells.h> | | #include <spells.h> |
#include <book.h> | | #include <book.h> |
| | |
| | /* adj_stealchance() - increased values indicate better attempts */ |
| | static int adj_stealchance (object *op, object *victim, int roll) { |
| | object *equip; |
| | |
| | if(!op||!victim||!roll) return -1; |
| | |
| | if(op->type==PLAYER && op->body_used[BODY_ARMS] <=0) { |
| | new_draw_info(NDI_UNIQUE, 0,op,"But you have no free hands to steal with!"); |
| | roll=-1; |
| | } |
| | |
| | /* ADJUSTMENTS */ |
| | |
| | /* Its harder to steal from hostile beings! */ |
| | if(!QUERY_FLAG(victim, FLAG_UNAGGRESSIVE)) roll = roll/2; |
| | |
| | /* Easier to steal from sleeping beings, or if the thief is |
| | * unseen */ |
| | if(QUERY_FLAG(victim, FLAG_SLEEP)) |
| | roll = roll*3; |
| | else if(op->invisible) |
| | roll = roll*2; |
| | |
| | /* check stealing 'encumberance'. Having this equipment applied makes |
| | * it quite a bit harder to steal. |
| | */ |
| | for(equip=op->inv;equip;equip=equip->below) { |
| | if(equip->type==WEAPON&&QUERY_FLAG(equip,FLAG_APPLIED)) { |
| | roll -= equip->weight/10000; |
| | } |
| | if(equip->type==BOW&&QUERY_FLAG(equip,FLAG_APPLIED)) |
| | roll -= equip->weight/5000; |
| | if(equip->type==SHIELD&&QUERY_FLAG(equip,FLAG_APPLIED)) { |
| | roll -= equip->weight/2000; |
| | } |
| | if(equip->type==ARMOUR&&QUERY_FLAG(equip,FLAG_APPLIED)) |
| | roll -= equip->weight/5000; |
| | if(equip->type==GLOVES&&QUERY_FLAG(equip,FLAG_APPLIED)) |
| | roll -= equip->weight/100; |
| | } |
| | if(roll<0) roll=0; |
| | return roll; |
| | } |
| | |
/* | | /* |
* When stealing: dependent on the intelligence/wisdom of whom you're | | * When stealing: dependent on the intelligence/wisdom of whom you're |
* stealing from (op in attempt_steal), offset by your dexterity and | | * stealing from (op in attempt_steal), offset by your dexterity and |
| | |
* or not. | | * or not. |
*/ | | */ |
| | |
int attempt_steal(object* op, object* who) | | static int attempt_steal(object* op, object* who, object *skill) |
{ | | { |
object *success=NULL, *tmp=NULL, *next; | | object *success=NULL, *tmp=NULL, *next; |
int roll=0, chance=0, stats_value = get_weighted_skill_stats(who)*3; | | int roll=0, chance=0, stats_value; |
int victim_lvl=op->level*3, thief_lvl = SK_level(who)*10; | | |
rv_vector rv; | | rv_vector rv; |
| | |
| | stats_value = ((who->stats.Dex + who->stats.Int) * 3) / 2; |
| | |
/* if the victim is aware of a thief in the area (FLAG_NO_STEAL set on them) | | /* if the victim is aware of a thief in the area (FLAG_NO_STEAL set on them) |
* they will try to prevent stealing if they can. Only unseen theives will | | * they will try to prevent stealing if they can. Only unseen theives will |
* have much chance of success. | | * have much chance of success. |
| | |
| | |
if (QUERY_FLAG(tmp,FLAG_WAS_WIZ) || QUERY_FLAG(tmp, FLAG_APPLIED) | | if (QUERY_FLAG(tmp,FLAG_WAS_WIZ) || QUERY_FLAG(tmp, FLAG_APPLIED) |
|| !(tmp->type) | | || !(tmp->type) |
|| tmp->type == EXPERIENCE || tmp->type == ABILITY | | || tmp->type == EXPERIENCE || tmp->type == SPELL |
|| QUERY_FLAG(tmp,FLAG_STARTEQUIP) | | || QUERY_FLAG(tmp,FLAG_STARTEQUIP) |
|| QUERY_FLAG(tmp,FLAG_NO_STEAL) | | || QUERY_FLAG(tmp,FLAG_NO_STEAL) |
|| tmp->invisible ) continue; | | || tmp->invisible ) continue; |
| | |
/* Okay, try stealing this item. Dependent on dexterity of thief, | | /* Okay, try stealing this item. Dependent on dexterity of thief, |
* skill level, see the adj_stealroll fctn for more detail. */ | | * skill level, see the adj_stealroll fctn for more detail. |
| | */ |
| | |
roll=die_roll(2, 100, who, PREFER_LOW)/2; /* weighted 1-100 */ | | roll=die_roll(2, 100, who, PREFER_LOW)/2; /* weighted 1-100 */ |
| | |
if((chance=adj_stealchance(who,op,(stats_value+thief_lvl-victim_lvl)))==-1) | | if((chance=adj_stealchance(who,op,(stats_value+skill->level * 10 - op->level * 3)))==-1) |
return 0; | | return 0; |
else if (roll < chance ) { | | else if (roll < chance ) { |
if (op->type == PLAYER) | | if (op->type == PLAYER) |
| | |
* attempt to steal something heavy off them, they're bound to notice | | * attempt to steal something heavy off them, they're bound to notice |
*/ | | */ |
| | |
if((roll>=SK_level(who))||!chance | | if((roll>=skill->level) || !chance |
||(tmp&&tmp->weight>(250*(random_roll(0, stats_value+thief_lvl-1, who, PREFER_LOW))))) { | | ||(tmp && tmp->weight>(250*(random_roll(0, stats_value+skill->level * 10-1, who, PREFER_LOW))))) { |
| | |
/* victim figures out where the thief is! */ | | /* victim figures out where the thief is! */ |
if(who->hide) make_visible(who); | | if(who->hide) make_visible(who); |
| | |
if(op->type != PLAYER) { | | if(op->type != PLAYER) { |
| | |
/* The unaggressives look after themselves 8) */ | | /* The unaggressives look after themselves 8) */ |
if(who->type==PLAYER) { | | if(who->type==PLAYER) { |
npc_call_help(op); | | npc_call_help(op); |
| | |
} | | } |
CLEAR_FLAG(op, FLAG_UNAGGRESSIVE); | | CLEAR_FLAG(op, FLAG_UNAGGRESSIVE); |
/* all remaining npc items are guarded now. Set flag NO_STEAL | | /* all remaining npc items are guarded now. Set flag NO_STEAL |
* on the victim. */ | | * on the victim. |
| | */ |
SET_FLAG(op,FLAG_NO_STEAL); | | SET_FLAG(op,FLAG_NO_STEAL); |
} else { /* stealing from another player */ | | } else { /* stealing from another player */ |
char buf[MAX_BUF]; | | char buf[MAX_BUF]; |
| | |
return success? 1:0; | | return success? 1:0; |
} | | } |
| | |
/* adj_stealchance() - increased values indicate better attempts */ | | |
| | |
int adj_stealchance (object *op, object *victim, int roll) { | | |
object *equip; | | |
int used_hands=0; | | |
| | |
if(!op||!victim||!roll) return -1; | | |
| | |
/* ADJUSTMENTS */ | | |
| | |
/* Its harder to steal from hostile beings! */ | | |
if(!QUERY_FLAG(victim, FLAG_UNAGGRESSIVE)) roll = roll/2; | | |
| | |
/* Easier to steal from sleeping beings, or if the thief is | | |
* unseen */ | | |
if(QUERY_FLAG(victim, FLAG_SLEEP)) | | |
roll = roll*3; | | |
else if(op->invisible) | | |
roll = roll*2; | | |
| | |
/* check stealing 'encumberance'. Having this equipment applied makes | | |
* it quite a bit harder to steal. */ | | |
for(equip=op->inv;equip;equip=equip->below) { | | |
if(equip->type==WEAPON&&QUERY_FLAG(equip,FLAG_APPLIED)) { | | |
roll -= equip->weight/10000; | | |
used_hands++; | | |
} | | |
if(equip->type==BOW&&QUERY_FLAG(equip,FLAG_APPLIED)) | | |
roll -= equip->weight/5000; | | |
if(equip->type==SHIELD&&QUERY_FLAG(equip,FLAG_APPLIED)) { | | |
roll -= equip->weight/2000; | | |
used_hands++; | | |
} | | |
if(equip->type==ARMOUR&&QUERY_FLAG(equip,FLAG_APPLIED)) | | |
roll -= equip->weight/5000; | | |
if(equip->type==GLOVES&&QUERY_FLAG(equip,FLAG_APPLIED)) | | |
roll -= equip->weight/100; | | |
} | | |
| | |
if(roll<0) roll=0; | | |
if(op->type==PLAYER && used_hands>=2) { | | |
new_draw_info(NDI_UNIQUE, 0,op,"But you have no free hands to steal with!"); | | |
roll=-1; | | |
} | | |
| | |
return roll; | | |
} | | |
| | |
int steal(object* op, int dir) | | int steal(object* op, int dir, object *skill) |
{ | | { |
object *tmp, *next; | | object *tmp, *next; |
sint16 x, y; | | sint16 x, y; |
| | |
*/ | | */ |
if (tmp->head) tmp=tmp->head; | | if (tmp->head) tmp=tmp->head; |
if(tmp->type!=PLAYER&&!QUERY_FLAG(tmp, FLAG_MONSTER)) continue; | | if(tmp->type!=PLAYER&&!QUERY_FLAG(tmp, FLAG_MONSTER)) continue; |
if (attempt_steal(tmp, op)) { | | if (attempt_steal(tmp, op, skill)) { |
if(tmp->type==PLAYER) /* no xp for stealing from another player */ | | if(tmp->type==PLAYER) /* no xp for stealing from another player */ |
return 0; | | return 0; |
else return (calc_skill_exp(op,tmp)); | | else return (calc_skill_exp(op,tmp, skill)); |
} | | } |
} | | } |
return 0; | | return 0; |
} | | } |
| | |
| | static int attempt_pick_lock (object *door, object *pl, object *skill) |
| | { |
| | int difficulty= pl->map->difficulty ? pl->map->difficulty : 0; |
| | int success = 0, number; /* did we get anything? */ |
| | |
| | |
| | /* Try to pick the lock on this item (doors only for now). |
| | * Dependent on dexterity/skill SK_level of the player and |
| | * the map level difficulty. |
| | */ |
| | number = (die_roll(2, 40, pl, PREFER_LOW)-2)/2; |
| | if (number < (pl->stats.Dex + skill->level - difficulty)) { |
| | remove_door(door); |
| | success = 1; |
| | } else if (door->inv && (door->inv->type==RUNE || door->inv->type==TRAP)) { /* set off any traps? */ |
| | spring_trap(door->inv,pl); |
| | } |
| | return success; |
| | } |
| | |
| | |
/* Implementation by bt. (thomas@astro.psu.edu) | | /* Implementation by bt. (thomas@astro.psu.edu) |
* monster implementation 7-7-95 by bt. | | * monster implementation 7-7-95 by bt. |
*/ | | */ |
| | |
int pick_lock(object *pl, int dir) | | int pick_lock(object *pl, int dir, object *skill) |
{ | | { |
char buf[MAX_BUF]; | | |
object *tmp; | | object *tmp; |
int x = pl->x + freearr_x[dir]; | | int x = pl->x + freearr_x[dir]; |
int y = pl->y + freearr_y[dir]; | | int y = pl->y + freearr_y[dir]; |
int success = 0; | | |
| | |
if(!dir) dir=pl->facing; | | if(!dir) dir=pl->facing; |
| | |
/* For all the stacked objects at this point find a door*/ | | /* For all the stacked objects at this point find a door*/ |
| | |
sprintf(buf, "There is no lock there."); | | |
if (out_of_map(pl->map,x,y)) { | | if (out_of_map(pl->map,x,y)) { |
new_draw_info(NDI_UNIQUE, 0,pl,buf); | | new_draw_info(NDI_UNIQUE, 0,pl,"There is no lock there."); |
return 0; | | return 0; |
} | | } |
| | |
for(tmp=get_map_ob(pl->map,x,y); tmp; tmp=tmp->above) { | | for(tmp=get_map_ob(pl->map,x,y); tmp; tmp=tmp->above) |
if(!tmp) continue; | | if (tmp->type == DOOR || tmp->type == LOCKED_DOOR) break; |
switch(tmp->type) { | | |
case DOOR: | | if (!tmp) { |
if (!QUERY_FLAG(tmp, FLAG_NO_PASS)) { | | new_draw_info(NDI_UNIQUE, 0,pl,"There is no lock there."); |
strcpy(buf,"The door has no lock!"); | | return 0; |
} else { | | |
if (attempt_pick_lock(tmp, pl)) { | | |
success = 1; | | |
sprintf(buf, "you pick the lock."); | | |
} else { | | |
sprintf(buf, "you fail to pick the lock."); | | |
} | | |
} | | |
break; | | |
case LOCKED_DOOR: | | |
sprintf(buf, "you can't pick that lock!"); | | |
break; | | |
default: | | |
break; | | |
} | | |
} | | } |
new_draw_info(NDI_UNIQUE, 0,pl,buf); | | if (tmp->type == LOCKED_DOOR) { |
if(success) | | new_draw_info(NDI_UNIQUE, 0,pl, "You can't pick that lock!"); |
return calc_skill_exp(pl,NULL); | | |
else | | |
return 0; | | return 0; |
} | | } |
| | |
int attempt_pick_lock ( object *door, object *pl) | | if (!QUERY_FLAG(tmp, FLAG_NO_PASS)) { |
{ | | new_draw_info(NDI_UNIQUE, 0,pl,"The door has no lock!"); |
int bonus = SK_level(pl); | | return 0; |
int difficulty= pl->map->difficulty ? pl->map->difficulty : 0; | | } |
int dex = get_skill_stat1(pl) ? get_skill_stat1(pl) : 10; | | |
int success = 0, number; /* did we get anything? */ | | |
| | |
/* If has can_pass set, then its not locked! */ | | |
if(!QUERY_FLAG(door,FLAG_NO_PASS)) return 0; | | |
| | |
/* Try to pick the lock on this item (doors only for now). | | |
* Dependent on dexterity/skill SK_level of the player and | | |
* the map level difficulty. | | |
*/ | | |
| | |
number = (die_roll(2, 40, pl, PREFER_LOW)-2)/2; | | if (attempt_pick_lock(tmp, pl, skill)) { |
if (number < ((dex + bonus) - difficulty)) { | | new_draw_info(NDI_UNIQUE, 0,pl,"You pick the lock."); |
remove_door(door); | | return calc_skill_exp(pl,NULL, skill); |
success = 1; | | } else { |
} else if (door->inv && (door->inv->type==RUNE || door->inv->type==TRAP)) { /* set off any traps? */ | | new_draw_info(NDI_UNIQUE, 0,pl, "You fail to pick the lock."); |
spring_trap(door->inv,pl); | | return 0; |
} | | } |
return success; | | |
} | | } |
| | |
| | |
/* HIDE CODE. The user becomes undetectable (not just 'invisible') for | | /* HIDE CODE. The user becomes undetectable (not just 'invisible') for |
* a short while (success and duration dependant on player SK_level, | | * a short while (success and duration dependant on player SK_level, |
* dexterity, charisma, and map difficulty). | | * dexterity, charisma, and map difficulty). |
| | |
* July 7, 1995 - made hiding possible for monsters. -b.t. | | * July 7, 1995 - made hiding possible for monsters. -b.t. |
*/ | | */ |
| | |
| | static int attempt_hide(object *op, object *skill) { |
| | int number,difficulty=op->map->difficulty; |
| | int terrain = hideability(op); |
| | |
| | if(terrain<-10) /* not enough cover here */ |
| | return 0; |
| | |
| | /* Hiding success and duration dependant on skill level, |
| | * op->stats.Dex, map difficulty and terrain. |
| | */ |
| | |
| | number = (die_roll(2, 25, op, PREFER_LOW)-2)/2; |
| | if(!stand_near_hostile(op) && (number < (op->stats.Dex + skill->level + terrain - difficulty))) { |
| | op->invisible += 100; /* set the level of 'hiddeness' */ |
| | if(op->type==PLAYER) |
| | op->contr->tmp_invis=1; |
| | op->hide=1; |
| | return 1; |
| | } |
| | return 0; |
| | } |
| | |
/* patched this to take terrain into consideration */ | | /* patched this to take terrain into consideration */ |
| | |
int hide(object *op) { | | int hide(object *op, object *skill) { |
char buf[MAX_BUF]; | | |
int level= SK_level(op); | | |
| | |
/* the preliminaries -- Can we really hide now? */ | | /* the preliminaries -- Can we really hide now? */ |
/* this keeps monsters from using invisibilty spells and hiding */ | | /* this keeps monsters from using invisibilty spells and hiding */ |
| | |
if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) { | | if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) { |
sprintf(buf,"You don't need to hide while invisible!"); | | new_draw_info(NDI_UNIQUE, 0,op,"You don't need to hide while invisible!"); |
new_draw_info(NDI_UNIQUE, 0,op,buf); | | |
return 0; | | return 0; |
} else if (!op->hide && op->invisible>0 && op->type == PLAYER) { | | } else if (!op->hide && op->invisible>0 && op->type == PLAYER) { |
sprintf(buf,"Your attempt to hide breaks the invisibility spell!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Your attempt to hide breaks the invisibility spell!"); |
new_draw_info(NDI_UNIQUE, 0,op,buf); | | |
make_visible(op); | | make_visible(op); |
return 0; | | |
} | | } |
| | |
if(op->invisible>(50*level)) { | | if(op->invisible>(50*skill->level)) { |
new_draw_info(NDI_UNIQUE,0,op,"You are as hidden as you can get."); | | new_draw_info(NDI_UNIQUE,0,op,"You are as hidden as you can get."); |
return 0; | | return 0; |
} | | } |
| | |
if(attempt_hide(op)) { | | if(attempt_hide(op, skill)) { |
new_draw_info(NDI_UNIQUE, 0,op,"You hide in the shadows."); | | new_draw_info(NDI_UNIQUE, 0,op,"You hide in the shadows."); |
update_object(op,UP_OBJ_FACE); | | update_object(op,UP_OBJ_FACE); |
return calc_skill_exp(op, NULL); | | return calc_skill_exp(op, NULL, skill); |
} | | } |
new_draw_info(NDI_UNIQUE,0,op,"You fail to conceal yourself."); | | new_draw_info(NDI_UNIQUE,0,op,"You fail to conceal yourself."); |
return 0; | | return 0; |
} | | } |
| | |
int attempt_hide(object *op) { | | |
int level = SK_level(op); | | |
int success=0,number,difficulty=op->map->difficulty; | | |
int dexterity = get_skill_stat1(op); | | |
int terrain = hideability(op); | | |
| | |
level = level>5?level/5:1; | | |
| | |
/* first things... no hiding next to a hostile monster */ | | |
dexterity = dexterity ? dexterity : 15; | | |
| | |
if(terrain<-10) /* not enough cover here */ | | |
return 0; | | |
| | |
/* Hiding success and duration dependant on SK_level, | | |
* dexterity, map difficulty and terrain. | | |
*/ | | |
| | |
number = (die_roll(2, 25, op, PREFER_LOW)-2)/2; | | |
if(!stand_near_hostile(op) && number | | |
&& (number < (dexterity + level + terrain - difficulty ))) | | |
{ | | |
success = 1; | | |
op->invisible += 100; /* set the level of 'hiddeness' */ | | |
if(op->type==PLAYER) | | |
op->contr->tmp_invis=1; | | |
op->hide=1; | | |
} | | |
return success; | | |
} | | |
| | |
/* stop_jump() - End of jump. Clear flags, restore the map, and | | /* stop_jump() - End of jump. Clear flags, restore the map, and |
* freeze the jumper a while to simulate the exhaustion | | * freeze the jumper a while to simulate the exhaustion |
* of jumping. | | * of jumping. |
*/ | | */ |
| | static void stop_jump(object *pl, int dist, int spaces) { |
static int stop_jump(object *pl, int dist, int spaces) { | | |
/* int load=dist/(pl->speed*spaces); */ | | |
| | |
CLEAR_FLAG(pl,FLAG_FLYING); | | CLEAR_FLAG(pl,FLAG_FLYING); |
insert_ob_in_map(pl,pl->map,pl,0); | | insert_ob_in_map(pl,pl->map,pl,0); |
return 0; | | |
} | | } |
| | |
| | |
static int attempt_jump (object *pl, int dir, int spaces) { | | static int attempt_jump (object *pl, int dir, int spaces, object *skill) { |
object *tmp; | | object *tmp; |
int i,exp=0,dx=freearr_x[dir],dy=freearr_y[dir], mflags; | | int i,exp=0,dx=freearr_x[dir],dy=freearr_y[dir], mflags; |
sint16 x, y; | | sint16 x, y; |
| | |
| | |
mflags = get_map_flags(m, &m, x, y, &x, &y); | | mflags = get_map_flags(m, &m, x, y, &x, &y); |
| | |
| | |
if (mflags & P_OUT_OF_MAP) { | | if (mflags & P_OUT_OF_MAP) { |
(void) stop_jump(pl,i,spaces); | | (void) stop_jump(pl,i,spaces); |
return 0; | | return 0; |
} | | } |
if (mflags & P_WALL) { | | if (mflags & P_WALL) { |
new_draw_info(NDI_UNIQUE, 0,pl,"Your jump is blocked."); | | new_draw_info(NDI_UNIQUE, 0,pl,"Your jump is blocked."); |
(void) stop_jump(pl,i,spaces); | | stop_jump(pl,i,spaces); |
return 0; | | return 0; |
} | | } |
| | |
| | |
(pl->type==PLAYER && pl->contr->party_number==-1) || | | (pl->type==PLAYER && pl->contr->party_number==-1) || |
(pl->type==PLAYER && tmp->type==PLAYER && | | (pl->type==PLAYER && tmp->type==PLAYER && |
pl->contr->party_number!=tmp->contr->party_number)) | | pl->contr->party_number!=tmp->contr->party_number)) |
exp = skill_attack(tmp,pl,pl->facing,"kicked"); /* pl makes an attack */ | | exp = skill_attack(tmp,pl,pl->facing,"kicked", skill); /* pl makes an attack */ |
(void) stop_jump(pl,i,spaces); | | stop_jump(pl,i,spaces); |
return exp; /* note that calc_skill_exp() is already called by skill_attack() */ | | return exp; /* note that calc_skill_exp() is already called by skill_attack() */ |
} | | } |
/* If the space has fly on set (no matter what the space is), | | /* If the space has fly on set (no matter what the space is), |
| | |
pl->map = m; | | pl->map = m; |
if (pl->contr) | | if (pl->contr) |
esrv_map_scroll(&pl->contr->socket, dx, dy); | | esrv_map_scroll(&pl->contr->socket, dx, dy); |
(void) stop_jump(pl,i,spaces); | | stop_jump(pl,i,spaces); |
return calc_skill_exp(pl,NULL); | | return calc_skill_exp(pl,NULL, skill); |
} | | } |
} | | } |
pl->x = x; | | pl->x = x; |
| | |
if (pl->contr) | | if (pl->contr) |
esrv_map_scroll(&pl->contr->socket, dx, dy); | | esrv_map_scroll(&pl->contr->socket, dx, dy); |
} | | } |
(void) stop_jump(pl,i,spaces); | | stop_jump(pl,i,spaces); |
return calc_skill_exp(pl,NULL); | | return calc_skill_exp(pl,NULL, skill); |
} | | } |
| | |
/* jump() - this is both a new type of movement for player/monsters and | | /* jump() - this is both a new type of movement for player/monsters and |
* an attack as well. -b.t. | | * an attack as well. |
| | * Perhaps we should allow more spaces based on level, eg, level 50 |
| | * jumper can jump several spaces? |
*/ | | */ |
| | |
int jump(object *pl, int dir) | | int jump(object *pl, int dir, object *skill) |
{ | | { |
int spaces=0,stats; | | int spaces=0,stats; |
int str = get_skill_stat1(pl); | | int str = pl->stats.Str; |
int dex = get_skill_stat2(pl); | | int dex = pl->stats.Dex; |
| | |
dex = dex ? dex : 15; | | dex = dex ? dex : 15; |
str = str ? str : 10; | | str = str ? str : 10; |
| | |
stats=str*str*str*dex; | | stats=str*str*str*dex * skill->level; |
| | |
if(pl->carrying!=0) /* don't want div by zero !! */ | | if(pl->carrying!=0) /* don't want div by zero !! */ |
spaces=(int) (stats/pl->carrying); | | spaces=(int) (stats/pl->carrying); |
| | |
new_draw_info(NDI_UNIQUE, 0,pl,"You are carrying too much weight to jump."); | | new_draw_info(NDI_UNIQUE, 0,pl,"You are carrying too much weight to jump."); |
return 0; | | return 0; |
} | | } |
return attempt_jump(pl,dir,spaces); | | return attempt_jump(pl,dir,spaces, skill); |
} | | } |
| | |
| | |
| | |
* - b.t. (thomas@astro.psu.edu) | | * - b.t. (thomas@astro.psu.edu) |
*/ | | */ |
| | |
int skill_ident(object *pl) { | | static int do_skill_detect_curse(object *pl, object *skill) { |
char buf[MAX_BUF]; | | |
int success=0; | | |
| | |
if(!pl->chosen_skill) /* should'nt happen... */ | | |
return 0; | | |
| | |
if(pl->type != PLAYER) return 0; /* only players will skill-identify */ | | |
| | |
sprintf(buf, "You look at the objects nearby..."); | | |
new_draw_info(NDI_UNIQUE, 0,pl,buf); | | |
| | |
switch (pl->chosen_skill->stats.sp) { | | |
case SK_SMITH: | | |
success += do_skill_ident(pl,WEAPON) + do_skill_ident(pl,ARMOUR) | | |
+ do_skill_ident(pl,BRACERS) + do_skill_ident(pl,CLOAK) | | |
+ do_skill_ident(pl,BOOTS) + do_skill_ident(pl,SHIELD) | | |
+ do_skill_ident(pl,GIRDLE) + do_skill_ident(pl,HELMET) | | |
+ do_skill_ident(pl,GLOVES); | | |
break; | | |
case SK_BOWYER: | | |
success += do_skill_ident(pl,BOW) + do_skill_ident(pl,ARROW); | | |
break; | | |
case SK_ALCHEMY: | | |
success += do_skill_ident(pl,POTION) + do_skill_ident(pl,POISON) | | |
+ do_skill_ident(pl,AMULET) + do_skill_ident(pl,CONTAINER) | | |
+ do_skill_ident(pl,DRINK) + do_skill_ident(pl,INORGANIC); | | |
break; | | |
case SK_WOODSMAN: | | |
success += do_skill_ident(pl,FOOD) + do_skill_ident(pl,DRINK) | | |
+ do_skill_ident(pl,FLESH); | | |
break; | | |
case SK_JEWELER: | | |
success += do_skill_ident(pl,GEM) + do_skill_ident(pl,RING); | | |
break; | | |
case SK_LITERACY: | | |
success += do_skill_ident(pl,SPELLBOOK) | | |
+ do_skill_ident(pl,SCROLL) + do_skill_ident(pl,BOOK); | | |
break; | | |
case SK_THAUMATURGY: | | |
success += do_skill_ident(pl,WAND) + do_skill_ident(pl,ROD) | | |
+ do_skill_ident(pl,HORN); | | |
break; | | |
case SK_DET_CURSE: | | |
success = do_skill_detect_curse(pl); | | |
if(success) | | |
new_draw_info(NDI_UNIQUE, 0,pl,"...and discover cursed items!"); | | |
break; | | |
case SK_DET_MAGIC: | | |
success = do_skill_detect_magic(pl); | | |
if(success) | | |
new_draw_info(NDI_UNIQUE, 0,pl, | | |
"...and discover items imbued with mystic forces!"); | | |
break; | | |
default: | | |
LOG(llevError,"Error: bad call to skill_ident()"); | | |
return 0; | | |
break; | | |
} | | |
if(!success) { | | |
sprintf(buf,"...and learn nothing more."); | | |
new_draw_info(NDI_UNIQUE, 0,pl,buf); | | |
} | | |
| | |
return success; | | |
} | | |
| | |
int do_skill_detect_curse(object *pl) { | | |
object *tmp; | | object *tmp; |
int success=0; | | int success=0; |
| | |
/* check the player inventory - stop after 1st success or | | |
* run out of unidented items | | |
*/ | | |
for(tmp=pl->inv;tmp;tmp=tmp->below) | | for(tmp=pl->inv;tmp;tmp=tmp->below) |
if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_KNOWN_CURSED) | | if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_KNOWN_CURSED) |
&& (QUERY_FLAG(tmp,FLAG_CURSED) || QUERY_FLAG(tmp,FLAG_DAMNED)) ) { | | && (QUERY_FLAG(tmp,FLAG_CURSED) || QUERY_FLAG(tmp,FLAG_DAMNED)) && |
| | tmp->item_power < skill->level) { |
SET_FLAG(tmp,FLAG_KNOWN_CURSED); | | SET_FLAG(tmp,FLAG_KNOWN_CURSED); |
esrv_update_item(UPD_FLAGS, pl, tmp); | | esrv_update_item(UPD_FLAGS, pl, tmp); |
success+=calc_skill_exp(pl,tmp); | | success+= calc_skill_exp(pl,tmp, skill); |
} | | } |
return success; | | return success; |
} | | } |
| | |
int do_skill_detect_magic(object *pl) { | | static int do_skill_detect_magic(object *pl, object *skill) { |
object *tmp; | | object *tmp; |
int success=0; | | int success=0; |
| | |
/* check the player inventory - stop after 1st success or | | |
* run out of unidented items | | |
*/ | | |
for(tmp=pl->inv;tmp;tmp=tmp->below) | | for(tmp=pl->inv;tmp;tmp=tmp->below) |
if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL) | | if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL) |
&& (is_magical(tmp)) ) { | | && (is_magical(tmp)) && tmp->item_power < skill->level) { |
SET_FLAG(tmp,FLAG_KNOWN_MAGICAL); | | SET_FLAG(tmp,FLAG_KNOWN_MAGICAL); |
esrv_update_item(UPD_FLAGS, pl, tmp); | | esrv_update_item(UPD_FLAGS, pl, tmp); |
success+=calc_skill_exp(pl,tmp); | | success+=calc_skill_exp(pl,tmp, skill); |
} | | } |
return success; | | return success; |
} | | } |
| | |
/* Helper function for do_skill_ident, so that we can loop | | /* Helper function for do_skill_ident, so that we can loop |
over inventory AND objects on the ground conveniently. */ | | * over inventory AND objects on the ground conveniently. |
int do_skill_ident2(object *tmp,object *pl, int obj_class) | | */ |
| | int do_skill_ident2(object *tmp,object *pl, int obj_class, object *skill) |
{ | | { |
int success=0,chance; | | int success=0,chance, ip; |
int skill_value = SK_level(pl) + get_weighted_skill_stats(pl); | | int skill_value = skill->level * pl->stats.Int?pl->stats.Int:10; |
| | |
if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_NO_SKILL_IDENT) | | if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_NO_SKILL_IDENT) |
&& need_identify(tmp) | | && need_identify(tmp) && !tmp->invisible && tmp->type==obj_class) { |
&& !tmp->invisible && tmp->type==obj_class) { | | ip = tmp->magic; |
| | if (tmp->item_power > ip) ip=tmp->item_power; |
| | |
chance = die_roll(3, 10, pl, PREFER_LOW)-3 + | | chance = die_roll(3, 10, pl, PREFER_LOW)-3 + |
rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1); | | rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1); |
if(skill_value >= chance) { | | if(skill_value >= chance) { |
| | |
if (tmp->map) | | if (tmp->map) |
esrv_send_item(pl, tmp); | | esrv_send_item(pl, tmp); |
} | | } |
success += calc_skill_exp(pl,tmp); | | success += calc_skill_exp(pl,tmp, skill); |
} else | | } else |
SET_FLAG(tmp, FLAG_NO_SKILL_IDENT); | | SET_FLAG(tmp, FLAG_NO_SKILL_IDENT); |
| | } |
| | return success; |
| | } |
| | |
| | /* do_skill_ident() - workhorse for skill_ident() -b.t. |
| | */ |
| | static int do_skill_ident(object *pl, int obj_class, object *skill) { |
| | object *tmp; |
| | int success=0; |
| | |
| | for(tmp=pl->inv;tmp;tmp=tmp->below) |
| | success+=do_skill_ident2(tmp,pl,obj_class, skill); |
| | /* check the ground */ |
| | |
| | for(tmp=get_map_ob(pl->map,pl->x,pl->y);tmp;tmp=tmp->above) |
| | success+=do_skill_ident2(tmp,pl,obj_class, skill); |
| | |
| | return success; |
| | } |
| | |
| | int skill_ident(object *pl, object *skill) { |
| | int success=0; |
| | |
| | if(pl->type != PLAYER) return 0; /* only players will skill-identify */ |
| | |
| | new_draw_info(NDI_UNIQUE, 0,pl,"You look at the objects nearby..."); |
| | |
| | switch (skill->subtype) { |
| | case SK_SMITHERY: |
| | success += do_skill_ident(pl,WEAPON, skill) + do_skill_ident(pl,ARMOUR, skill) |
| | + do_skill_ident(pl,BRACERS,skill) + do_skill_ident(pl,CLOAK,skill) |
| | + do_skill_ident(pl,BOOTS,skill) + do_skill_ident(pl,SHIELD,skill) |
| | + do_skill_ident(pl,GIRDLE,skill) + do_skill_ident(pl,HELMET,skill) |
| | + do_skill_ident(pl,GLOVES,skill); |
| | break; |
| | |
| | case SK_BOWYER: |
| | success += do_skill_ident(pl,BOW,skill) + do_skill_ident(pl,ARROW,skill); |
| | break; |
| | |
| | case SK_ALCHEMY: |
| | success += do_skill_ident(pl,POTION,skill) + do_skill_ident(pl,POISON,skill) |
| | + do_skill_ident(pl,AMULET,skill) + do_skill_ident(pl,CONTAINER,skill) |
| | + do_skill_ident(pl,DRINK,skill) + do_skill_ident(pl,INORGANIC,skill); |
| | break; |
| | |
| | case SK_WOODSMAN: |
| | success += do_skill_ident(pl,FOOD,skill) + do_skill_ident(pl,DRINK,skill) |
| | + do_skill_ident(pl,FLESH,skill); |
| | break; |
| | |
| | case SK_JEWELER: |
| | success += do_skill_ident(pl,GEM,skill) + do_skill_ident(pl,RING,skill); |
| | break; |
| | |
| | case SK_LITERACY: |
| | success += do_skill_ident(pl,SPELLBOOK,skill) |
| | + do_skill_ident(pl,SCROLL,skill) + do_skill_ident(pl,BOOK,skill); |
| | break; |
| | |
| | case SK_THAUMATURGY: |
| | success += do_skill_ident(pl,WAND,skill) + do_skill_ident(pl,ROD,skill) |
| | + do_skill_ident(pl,HORN,skill); |
| | break; |
| | |
| | case SK_DET_CURSE: |
| | success = do_skill_detect_curse(pl,skill); |
| | if(success) |
| | new_draw_info(NDI_UNIQUE, 0,pl,"...and discover cursed items!"); |
| | break; |
| | |
| | case SK_DET_MAGIC: |
| | success = do_skill_detect_magic(pl,skill); |
| | if(success) |
| | new_draw_info(NDI_UNIQUE, 0,pl, |
| | "...and discover items imbued with mystic forces!"); |
| | break; |
| | |
| | default: |
| | LOG(llevError,"Error: bad call to skill_ident()"); |
| | return 0; |
| | break; |
| | } |
| | if(!success) { |
| | new_draw_info(NDI_UNIQUE, 0,pl,"...and learn nothing more."); |
} | | } |
return success; | | return success; |
} | | } |
/* do_skill_ident() - workhorse for skill_ident() -b.t. */ | | |
/* Sept 95. I put in a probability for identification of artifacts. | | |
* highly magical artifacts will be more difficult to ident -b.t. | | |
*/ | | |
int do_skill_ident(object *pl, int obj_class) { | | |
object *tmp; | | |
int success=0; | | |
/* check the player inventory */ | | |
for(tmp=pl->inv;tmp;tmp=tmp->below) | | |
success+=do_skill_ident2(tmp,pl,obj_class); | | |
/* check the ground */ | | |
for(tmp=get_map_ob(pl->map,pl->x,pl->y);tmp;tmp=tmp->above) | | |
success+=do_skill_ident2(tmp,pl,obj_class); | | |
| | |
return success; | | |
} | | |
| | |
/* players using this skill can 'charm' a monster -- | | /* players using this skill can 'charm' a monster -- |
* into working for them. It can only be used on | | * into working for them. It can only be used on |
| | |
* -b.t. (thomas@astro.psu.edu) | | * -b.t. (thomas@astro.psu.edu) |
*/ | | */ |
| | |
int use_oratory(object *pl, int dir) { | | int use_oratory(object *pl, int dir, object *skill) { |
int x=pl->x+freearr_x[dir],y=pl->y+freearr_y[dir],chance; | | sint16 x=pl->x+freearr_x[dir],y=pl->y+freearr_y[dir]; |
int stat1 = get_skill_stat1(pl); | | int mflags,chance; |
object *tmp; | | object *tmp; |
| | mapstruct *m; |
| | |
if(pl->type!=PLAYER) return 0; /* only players use this skill */ | | if(pl->type!=PLAYER) return 0; /* only players use this skill */ |
if(out_of_map(pl->map,x,y)) return 0; | | m = pl->map; |
| | mflags =get_map_flags(m, &m, x,y, &x, &y); |
| | if (mflags & P_OUT_OF_MAP) return 0; |
| | |
| | /* Save some processing - we have the flag already anyways |
| | */ |
| | if (!(mflags & P_IS_ALIVE)) { |
| | new_draw_info(NDI_UNIQUE, 0, pl, "There is nothing to orate to."); |
| | return 0; |
| | } |
| | |
for(tmp=get_map_ob(pl->map,x,y);tmp;tmp=tmp->above) { | | for(tmp=get_map_ob(m,x,y);tmp;tmp=tmp->above) { |
if(!tmp) return 0; | | /* can't persuade players - return because there is nothing else |
if(!QUERY_FLAG(tmp,FLAG_MONSTER)) continue; | | |
/* can't persude players - return because there is nothing else | | |
* on that space to charm. Same for multi space monsters and | | * on that space to charm. Same for multi space monsters and |
* special monsters - we don't allow them to be charmed, and there | | * special monsters - we don't allow them to be charmed, and there |
* is no reason to do further processing since they should be the | | * is no reason to do further processing since they should be the |
| | |
if(tmp->more || tmp->head) return 0; | | if(tmp->more || tmp->head) return 0; |
if(tmp->msg) return 0; | | if(tmp->msg) return 0; |
| | |
| | if(QUERY_FLAG(tmp,FLAG_MONSTER)) break; |
| | } |
| | |
| | if (!tmp) { |
| | new_draw_info(NDI_UNIQUE, 0, pl, "There is nothing to orate to."); |
| | return 0; |
| | } |
| | |
new_draw_info_format(NDI_UNIQUE, | | new_draw_info_format(NDI_UNIQUE, |
0,pl, "You orate to the %s.",query_name(tmp)); | | 0,pl, "You orate to the %s.",query_name(tmp)); |
| | |
"Too bad the %s isn't listening!\n",query_name(tmp)); | | "Too bad the %s isn't listening!\n",query_name(tmp)); |
return 0; | | return 0; |
} | | } |
| | |
/* it's already allied! */ | | /* it's already allied! */ |
if(QUERY_FLAG(tmp,FLAG_FRIENDLY)&&(tmp->move_type==PETMOVE)){ | | if(QUERY_FLAG(tmp,FLAG_FRIENDLY)&&(tmp->move_type==PETMOVE)){ |
if(get_owner(tmp)==pl) { | | if(get_owner(tmp)==pl) { |
new_draw_info(NDI_UNIQUE, 0,pl, | | new_draw_info(NDI_UNIQUE, 0,pl, |
"Your follower loves your speach.\n"); | | "Your follower loves your speach.\n"); |
return 0; | | return 0; |
} else if(SK_level(pl)>tmp->level) { /* you steal the follower! */ | | } else if (skill->level > tmp->level) { |
| | /* you steal the follower. Perhaps we should really look at the |
| | * level of the owner above? |
| | */ |
set_owner(tmp,pl); | | set_owner(tmp,pl); |
new_draw_info_format(NDI_UNIQUE, 0,pl, | | new_draw_info_format(NDI_UNIQUE, 0,pl, |
"You convince the %s to follow you instead!\n", | | "You convince the %s to follow you instead!\n", |
| | |
* be used by a couple players to gets lots of exp. | | * be used by a couple players to gets lots of exp. |
*/ | | */ |
return 0; | | return 0; |
| | } else { |
| | /* In this case, you can't steal it from the other player */ |
| | return 0; |
} | | } |
} /* Creature was already a pet of someone */ | | } /* Creature was already a pet of someone */ |
| | |
chance=SK_level(pl)*2+(stat1-2*tmp->stats.Int)/2; | | chance=skill->level*2+(pl->stats.Cha-2*tmp->stats.Int)/2; |
| | |
/* Ok, got a 'sucker' lets try to make them a follower */ | | /* Ok, got a 'sucker' lets try to make them a follower */ |
if(chance>0 && tmp->level<(random_roll(0, chance-1, pl, PREFER_HIGH)-1)) { | | if(chance>0 && tmp->level<(random_roll(0, chance-1, pl, PREFER_HIGH)-1)) { |
| | |
query_name(tmp)); | | query_name(tmp)); |
| | |
set_owner(tmp,pl); | | set_owner(tmp,pl); |
SET_FLAG(tmp,FLAG_MONSTER); | | |
tmp->stats.exp = 0; | | tmp->stats.exp = 0; |
add_friendly_object(tmp); | | add_friendly_object(tmp); |
SET_FLAG(tmp,FLAG_FRIENDLY); | | SET_FLAG(tmp,FLAG_FRIENDLY); |
tmp->move_type = PETMOVE; | | tmp->move_type = PETMOVE; |
return calc_skill_exp(pl,tmp); | | return calc_skill_exp(pl,tmp, skill); |
} | | } |
/* Charm failed. Creature may be angry now */ | | /* Charm failed. Creature may be angry now */ |
else if((SK_level(pl)+((stat1-10)/2)) < | | else if((skill->level+((pl->stats.Cha-10)/2)) < random_roll(1, 2*tmp->level, pl, PREFER_LOW)) { |
random_roll(1, 2*tmp->level, pl, PREFER_LOW)) { | | |
new_draw_info_format(NDI_UNIQUE, 0,pl, | | new_draw_info_format(NDI_UNIQUE, 0,pl, |
"Your speach angers the %s!\n",query_name(tmp)); | | "Your speach angers the %s!\n",query_name(tmp)); |
if(QUERY_FLAG(tmp,FLAG_FRIENDLY)) { | | if(QUERY_FLAG(tmp,FLAG_FRIENDLY)) { |
| | |
} | | } |
CLEAR_FLAG(tmp,FLAG_UNAGGRESSIVE); | | CLEAR_FLAG(tmp,FLAG_UNAGGRESSIVE); |
} | | } |
} /* For loop cyclign through the objects on this space */ | | |
return 0; /* Fall through - if we get here, we didn't charm anything */ | | return 0; /* Fall through - if we get here, we didn't charm anything */ |
} | | } |
| | |
| | |
* of anything better! -b.t. | | * of anything better! -b.t. |
*/ | | */ |
| | |
int singing(object *pl, int dir) { | | int singing(object *pl, int dir, object *skill) { |
int i,exp = 0,stat1=get_skill_stat1(pl),chance; | | int i,exp = 0,chance, mflags; |
object *tmp; | | object *tmp; |
| | mapstruct *m; |
| | sint16 x, y; |
| | |
if(pl->type!=PLAYER) return 0; /* only players use this skill */ | | if(pl->type!=PLAYER) return 0; /* only players use this skill */ |
| | |
new_draw_info_format(NDI_UNIQUE,0,pl, "You sing"); | | new_draw_info_format(NDI_UNIQUE,0,pl, "You sing"); |
for(i=dir;i<(dir+MIN(SK_level(pl),SIZEOFFREE));i++) { | | for(i=dir;i<(dir+MIN(skill->level,SIZEOFFREE));i++) { |
if (out_of_map(pl->map,pl->x+freearr_x[i],pl->y+freearr_y[i])) | | x = pl->x+freearr_x[i]; |
continue; | | y = pl->y+freearr_y[i]; |
for(tmp=get_map_ob(pl->map,pl->x+freearr_x[i],pl->y+freearr_y[i]); | | m = pl->map; |
tmp;tmp=tmp->above) { | | |
if(!tmp) return 0; | | mflags =get_map_flags(m, &m, x,y, &x, &y); |
if(!QUERY_FLAG(tmp,FLAG_MONSTER)) continue; | | if (mflags & P_OUT_OF_MAP) continue; |
| | if (!(mflags & P_IS_ALIVE)) continue; |
| | |
| | for(tmp=get_map_ob(m, x, y); tmp;tmp=tmp->above) |
| | if(QUERY_FLAG(tmp,FLAG_MONSTER)) break; |
/* can't affect players */ | | /* can't affect players */ |
if(tmp->type==PLAYER) break; | | if(tmp->type==PLAYER) break; |
| | |
/* Only the head listens to music - not other parts. head | | /* Whole bunch of checks to see if this is a type of monster that would |
* is only set if an object has extra parts. This is also | | * listen to singing. |
* necessary since the other parts may not have appropriate | | |
* skills/flags set. | | |
*/ | | */ |
if (tmp->head) break; | | if (tmp && QUERY_FLAG(tmp, FLAG_MONSTER) && |
| | !QUERY_FLAG(tmp, FLAG_NO_STEAL) && /* Been charmed or abused before */ |
/* the following monsters can't be calmed */ | | !QUERY_FLAG(tmp, FLAG_SPLITTING) && /* no ears */ |
| | !QUERY_FLAG(tmp, FLAG_HITBACK) && /* was here before */ |
if(QUERY_FLAG(tmp,FLAG_SPLITTING) /* have no ears! */ | | (tmp->level <= skill->level) && |
|| QUERY_FLAG(tmp,FLAG_HITBACK)) break; | | (!tmp->head) && |
| | !QUERY_FLAG(tmp, FLAG_UNDEAD) && |
/* if(tmp->stats.Int>0) break; *//* is too smart */ | | !QUERY_FLAG(tmp,FLAG_UNAGGRESSIVE) && /* already calm */ |
if(tmp->level>SK_level(pl)) break; /* too powerfull */ | | !QUERY_FLAG(tmp,FLAG_FRIENDLY)) { /* already calm */ |
if(QUERY_FLAG(tmp,FLAG_UNDEAD)) break; /* undead dont listen! */ | | |
| | |
if(QUERY_FLAG(tmp,FLAG_UNAGGRESSIVE) /* already calm */ | | |
||QUERY_FLAG(tmp,FLAG_FRIENDLY)) | | |
break; | | |
| | |
/* stealing isn't really related (although, maybe it should | | /* stealing isn't really related (although, maybe it should |
* be). This is mainly to prevent singing to the same monster | | * be). This is mainly to prevent singing to the same monster |
* over and over again and getting exp for it. | | * over and over again and getting exp for it. |
*/ | | */ |
chance=SK_level(pl)*2+(stat1-5-tmp->stats.Int)/2; | | chance=skill->level*2+(pl->stats.Cha-5-tmp->stats.Int)/2; |
if(chance && tmp->level*2<random_roll(0, chance-1, pl, PREFER_HIGH)) { | | if(chance && tmp->level*2<random_roll(0, chance-1, pl, PREFER_HIGH)) { |
SET_FLAG(tmp,FLAG_UNAGGRESSIVE); | | SET_FLAG(tmp,FLAG_UNAGGRESSIVE); |
new_draw_info_format(NDI_UNIQUE, 0,pl, | | new_draw_info_format(NDI_UNIQUE, 0,pl, |
"You calm down the %s\n",query_name(tmp)); | | "You calm down the %s\n",query_name(tmp)); |
tmp->stats.Int = 1; /* this prevents re-pacification */ | | |
/* Give exp only if they are not aware */ | | /* Give exp only if they are not aware */ |
if(!QUERY_FLAG(tmp,FLAG_NO_STEAL)) | | if(!QUERY_FLAG(tmp,FLAG_NO_STEAL)) |
exp += calc_skill_exp(pl,tmp); | | exp += calc_skill_exp(pl,tmp, skill); |
SET_FLAG(tmp,FLAG_NO_STEAL); | | SET_FLAG(tmp,FLAG_NO_STEAL); |
} else { | | } else { |
new_draw_info_format(NDI_UNIQUE, 0,pl, | | new_draw_info_format(NDI_UNIQUE, 0,pl, |
"Too bad the %s isn't listening!\n",query_name(tmp)); | | "Too bad the %s isn't listening!\n",query_name(tmp)); |
| | SET_FLAG(tmp,FLAG_NO_STEAL); |
} | | } |
} | | } |
} | | } |
return exp; | | return exp; |
} | | } |
| | |
/* The FIND_TRAPS skill. This routine is taken mostly from the | | /* The find_traps skill (aka, search). Checks for traps |
* command_search loop. It seemed easier to have a separate command, | | * on the spaces or in certain objects |
* rather than overhaul the existing code - this makes sure things | | |
* still work for those people who don't want to have skill code | | |
* implemented. | | |
*/ | | */ |
| | |
int find_traps (object *pl) { | | int find_traps (object *pl, object *skill) { |
object *tmp,*tmp2; | | object *tmp,*tmp2; |
int i,expsum=0; | | int i,expsum=0, mflags; |
/*First we search all around us for runes and traps*/ | | sint16 x,y; |
| | mapstruct *m; |
| | |
| | /* First we search all around us for runes and traps, which are |
| | * all type RUNE |
| | */ |
| | |
for(i=0;i<9;i++) { | | for(i=0;i<9;i++) { |
| | x = pl->x+freearr_x[i]; |
| | y = pl->y+freearr_y[i]; |
| | m = pl->map; |
| | |
| | mflags =get_map_flags(m, &m, x,y, &x, &y); |
| | if (mflags & P_OUT_OF_MAP) continue; |
| | |
/* Check everything in the square for trapness */ | | /* Check everything in the square for trapness */ |
if(out_of_map(pl->map,pl->x + freearr_x[i],pl->y + freearr_y[i])) continue; | | for(tmp = get_map_ob(m, x, y); tmp!=NULL;tmp=tmp->above) { |
for(tmp = get_map_ob(pl->map, pl->x + freearr_x[i], pl->y +freearr_y[i]); | | |
tmp!=NULL;tmp=tmp->above) { | | |
| | |
/* And now we'd better do an inventory traversal of each | | /* And now we'd better do an inventory traversal of each |
of these objects' inventory */ | | * of these objects' inventory |
| | * We can narrow this down a bit - no reason to search through |
| | * the players inventory or monsters for that matter. |
| | */ |
| | if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) { |
for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below) | | for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below) |
if(tmp2->type==RUNE || tmp2->type==TRAP) | | if(tmp2->type==RUNE || tmp2->type==TRAP) |
if(trap_see(pl,tmp2)) { | | if(trap_see(pl,tmp2)) { |
trap_show(tmp2,tmp); | | trap_show(tmp2,tmp); |
if(tmp2->stats.Cha>1) { | | if(tmp2->stats.Cha>1) { |
if (!tmp2->owner || tmp2->owner->type!=PLAYER) | | if (!tmp2->owner || tmp2->owner->type!=PLAYER) |
expsum += calc_skill_exp(pl,tmp2); | | expsum += calc_skill_exp(pl,tmp2, skill); |
/* do the following so calc_skill_exp will know | | |
* how much xp to award for disarming | | |
*/ | | |
tmp2->stats.exp = tmp2->stats.Cha * tmp2->level; | | |
tmp2->stats.Cha = 1; /* unhide the trap */ | | tmp2->stats.Cha = 1; /* unhide the trap */ |
} | | } |
} | | } |
if(tmp->type==RUNE || tmp->type==TRAP) | | } |
if(trap_see(pl,tmp)) { | | if((tmp->type==RUNE || tmp->type == TRAP) && trap_see(pl,tmp)) { |
trap_show(tmp,tmp); | | trap_show(tmp,tmp); |
if(tmp->stats.Cha>1) { | | if(tmp->stats.Cha>1) { |
if (!tmp->owner || tmp->owner->type!=PLAYER) | | if (!tmp->owner || tmp->owner->type!=PLAYER) |
expsum += calc_skill_exp(pl,tmp); | | expsum += calc_skill_exp(pl,tmp, skill); |
/* do the following so calc_skill_exp will know | | |
* how much xp to award for disarming | | |
*/ | | |
tmp->stats.exp = tmp->stats.Cha * tmp->level; | | |
tmp->stats.Cha = 1; /* unhide the trap */ | | tmp->stats.Cha = 1; /* unhide the trap */ |
} | | } |
} | | } |
} | | } |
} | | } |
| | new_draw_info(NDI_BLACK, 0, pl, "You search the area."); |
return expsum; | | return expsum; |
} | | } |
| | |
| | /* remove_trap() - This skill will disarm any previously discovered trap |
| | * the algorithm is based (almost totally) on the old command_disarm() - b.t. |
| | */ |
| | |
| | int remove_trap (object *op, int dir, object *skill) { |
| | object *tmp,*tmp2; |
| | int i,success=0,mflags; |
| | mapstruct *m; |
| | sint16 x,y; |
| | |
| | for(i=0;i<9;i++) { |
| | x = op->x + freearr_x[i]; |
| | y = op->y + freearr_y[i]; |
| | m = op->map; |
| | |
| | mflags =get_map_flags(m, &m, x,y, &x, &y); |
| | if (mflags & P_OUT_OF_MAP) continue; |
| | |
| | /* Check everything in the square for trapness */ |
| | for(tmp = get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above) { |
| | /* And now we'd better do an inventory traversal of each |
| | * of these objects inventory. Like above, only |
| | * do this for interesting objects. |
| | */ |
| | |
| | if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) { |
| | for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below) |
| | if((tmp2->type==RUNE || tmp2->type == TRAP) && tmp2->stats.Cha<=1) { |
| | trap_show(tmp2,tmp); |
| | if(trap_disarm(op,tmp2,1, skill) && (!tmp2->owner || tmp2->owner->type!=PLAYER)) { |
| | tmp->stats.exp = tmp->stats.Cha * tmp->level; |
| | success += calc_skill_exp(op,tmp2, skill); |
| | } |
| | } |
| | } |
| | if((tmp->type==RUNE || tmp->type==TRAP) && tmp->stats.Cha<=1) { |
| | trap_show(tmp,tmp); |
| | if (trap_disarm(op,tmp,1,skill) && (!tmp->owner || tmp->owner->type!=PLAYER)) { |
| | tmp->stats.exp = tmp->stats.Cha * tmp->level; |
| | success += calc_skill_exp(op,tmp,skill); |
| | } |
| | } |
| | } |
| | } |
| | return success; |
| | } |
| | |
| | |
/* pray() - when this skill is called from do_skill(), it allows | | /* pray() - when this skill is called from do_skill(), it allows |
* the player to regain lost grace points at a faster rate. -b.t. | | * the player to regain lost grace points at a faster rate. -b.t. |
* This always returns 0 - return value is used by calling function | | * This always returns 0 - return value is used by calling function |
| | |
* give infinite exp by returning true in any cases. | | * give infinite exp by returning true in any cases. |
*/ | | */ |
| | |
int pray (object *pl) { | | int pray (object *pl, object *skill) { |
char buf[MAX_BUF]; | | char buf[MAX_BUF]; |
object *tmp; | | object *tmp; |
| | |
if(pl->type!=PLAYER) return 0; | | if(pl->type!=PLAYER) return 0; |
| | |
strcpy(buf,"You pray."); | | strcpy(buf,"You pray."); |
| | |
/* Check all objects - we could stop at floor objects, | | /* Check all objects - we could stop at floor objects, |
* but if someone buries an altar, I don't see a problem with | | * but if someone buries an altar, I don't see a problem with |
* going through all the objects, and it shouldn't be much slower | | * going through all the objects, and it shouldn't be much slower |
| | |
/* Only if the altar actually belongs to someone do you get special benefits */ | | /* Only if the altar actually belongs to someone do you get special benefits */ |
if(tmp && tmp->type==HOLY_ALTAR && tmp->other_arch) { | | if(tmp && tmp->type==HOLY_ALTAR && tmp->other_arch) { |
sprintf(buf,"You pray over the %s.",tmp->name); | | sprintf(buf,"You pray over the %s.",tmp->name); |
pray_at_altar(pl,tmp); | | pray_at_altar(pl,tmp, skill); |
break; /* Only pray at one altar */ | | break; /* Only pray at one altar */ |
} | | } |
} | | } |
| | |
if(pl->stats.grace < pl->stats.maxgrace) { | | if(pl->stats.grace < pl->stats.maxgrace) { |
pl->stats.grace++; | | pl->stats.grace++; |
pl->last_grace = -1; | | pl->last_grace = -1; |
} else return 0; | | } |
| | |
/* Is this really right? This will basically increase food | | |
* consumption, hp & sp regeneration, and everything else that | | |
* do_some_living does. | | |
*/ | | |
do_some_living(pl); | | |
return 0; | | return 0; |
} | | } |
| | |
| | |
* the level of the user. - b.t. thomas@astro.psu.edu | | * the level of the user. - b.t. thomas@astro.psu.edu |
*/ | | */ |
| | |
/* July 95 I commented out 'factor' - this should now be handled by | | void meditate (object *pl, object *skill) { |
* get_skill_time() -b.t. */ | | |
| | |
/* Sept 95. Now meditation is level dependant (score). User may | | |
* meditate w/ more armour on as they get higher level | | |
* Probably a better way to do this is based on overall encumberance | | |
* -b.t. | | |
*/ | | |
| | |
void meditate (object *pl) { | | |
object *tmp; | | object *tmp; |
int lvl = pl->level; | | |
/* int factor = 10/(1+(pl->level/10)+(pl->stats.Int/15)+(pl->stats.Wis/15)); */ | | |
| | |
if(pl->type!=PLAYER) return; /* players only */ | | if(pl->type!=PLAYER) return; /* players only */ |
| | |
/* check if pl has removed encumbering armour and weapons */ | | /* check if pl has removed encumbering armour and weapons */ |
| | if(QUERY_FLAG(pl,FLAG_READY_WEAPON) && (skill->level<6)) { |
if(QUERY_FLAG(pl,FLAG_READY_WEAPON) && (lvl<6)) { | | |
new_draw_info(NDI_UNIQUE,0,pl, | | new_draw_info(NDI_UNIQUE,0,pl, |
"You can't concentrate while wielding a weapon!\n"); | | "You can't concentrate while wielding a weapon!\n"); |
return; | | return; |
} else { | | } else { |
for(tmp=pl->inv;tmp;tmp=tmp->below) | | for(tmp=pl->inv;tmp;tmp=tmp->below) |
if(( (tmp->type==ARMOUR && lvl<12) | | if (( (tmp->type==ARMOUR && skill->level<12) |
|| (tmp->type==HELMET && lvl<10) | | || (tmp->type==HELMET && skill->level<10) |
|| (tmp->type==SHIELD && lvl<6) | | || (tmp->type==SHIELD && skill->level<6) |
|| (tmp->type==BOOTS && lvl<4) | | || (tmp->type==BOOTS && skill->level<4) |
|| (tmp->type==GLOVES && lvl<2) ) | | || (tmp->type==GLOVES && skill->level<2) ) |
&& QUERY_FLAG(tmp,FLAG_APPLIED)) { | | && QUERY_FLAG(tmp,FLAG_APPLIED)) { |
new_draw_info(NDI_UNIQUE,0,pl, | | new_draw_info(NDI_UNIQUE,0,pl, |
"You can't concentrate while wearing so much armour!\n"); | | "You can't concentrate while wearing so much armour!\n"); |
| | |
*/ | | */ |
| | |
new_draw_info(NDI_UNIQUE,0,pl, "You meditate."); | | new_draw_info(NDI_UNIQUE,0,pl, "You meditate."); |
/* pl->speed_left -= (int) FABS(factor); */ | | |
| | |
if(pl->stats.sp < pl->stats.maxsp) { | | if(pl->stats.sp < pl->stats.maxsp) { |
pl->stats.sp++; | | pl->stats.sp++; |
| | |
} else if (pl->stats.hp < pl->stats.maxhp) { | | } else if (pl->stats.hp < pl->stats.maxhp) { |
pl->stats.hp++; | | pl->stats.hp++; |
pl->last_heal = -1; | | pl->last_heal = -1; |
} else return; | | |
| | |
do_some_living(pl); | | |
} | | |
| | |
/* write_on_item() - wrapper for write_note and write_scroll */ | | |
| | |
int write_on_item (object *pl,char *params) { | | |
object *item; | | |
char *string=params; | | |
int msgtype; | | |
| | |
if(pl->type!=PLAYER) return 0; | | |
if (!params) { | | |
params=""; | | |
string=params; | | |
} | | |
| | |
/* Need to be able to read before we can write! */ | | |
| | |
if(!find_skill(pl,SK_LITERACY)) { | | |
new_draw_info(NDI_UNIQUE,0,pl, | | |
"You must learn to read before you can write!"); | | |
return 0; | | |
} | | |
| | |
/* if skill name occurs at begining of the string | | |
* we have to reset pointer to miss it and trailing space(s) | | |
*/ | | |
/* | | |
* GROS: Bugfix here. if you type | | |
* use_skill inscription bla | | |
* params will contain "bla" only, so looking for the skill name | | |
* shouldn't be done anymore. | | |
*/ | | |
/* if(lookup_skill_by_name(params)>=0){ | | |
for(i=strcspn(string," ");i>0;i--) string++; | | |
for(i=strspn(string," ");i>0;i--) string++; | | |
} | | |
*/ | | |
/* if there is a message then it goes in a book and no message means | | |
* write active spell into the scroll | | |
*/ | | |
msgtype = (string[0]!='\0') ? BOOK : SCROLL; | | |
| | |
/* find an item of correct type to write on */ | | |
if ( !(item = find_marked_object(pl))){ | | |
new_draw_info(NDI_UNIQUE,0,pl,"You don't have anything marked."); | | |
return 0; | | |
} | | |
| | |
if(item) { | | |
if(QUERY_FLAG(item,FLAG_UNPAID)) { | | |
new_draw_info(NDI_UNIQUE,0,pl, | | |
"You had better pay for that before you write on it."); | | |
return 0; | | |
} | | |
switch(msgtype) { | | |
case SCROLL: | | |
return write_scroll(pl,item); | | |
break; | | |
case BOOK: { | | |
return write_note(pl,item,string); | | |
break; | | |
} | | |
default: | | |
break; | | |
} | | |
} | | } |
new_draw_info_format(NDI_UNIQUE,0,pl,"You have no %s to write on", | | |
msgtype==BOOK ? "book" : "scroll"); | | |
return 0; | | |
} | | } |
| | |
/* write_note() - this routine allows players to inscribe messages in | | /* write_note() - this routine allows players to inscribe messages in |
* ordinary 'books' (anything that is type BOOK). b.t. | | * ordinary 'books' (anything that is type BOOK). b.t. |
*/ | | */ |
| | |
int write_note(object *pl, object *item, char *msg) { | | static int write_note(object *pl, object *item, char *msg, object *skill) { |
char buf[BOOK_BUF]; | | char buf[BOOK_BUF]; |
object *newBook = NULL; | | object *newBook = NULL; |
event *evt; | | event *evt; |
| | |
if(!msg) { | | if(!msg) { |
new_draw_info(NDI_UNIQUE,0,pl,"No message to write!"); | | new_draw_info(NDI_UNIQUE,0,pl,"No message to write!"); |
new_draw_info_format(NDI_UNIQUE,0,pl,"Usage: use_skill %s <message>", | | new_draw_info_format(NDI_UNIQUE,0,pl,"Usage: use_skill %s <message>", |
skills[SK_INSCRIPTION].name); | | skill->skill); |
return 0; | | return 0; |
} | | } |
if (strcasestr_local(msg, "endmsg")) { | | if (strcasestr_local(msg, "endmsg")) { |
| | |
return 0; | | return 0; |
} | | } |
/* GROS: Handle for plugin book writing (trigger) event */ | | /* GROS: Handle for plugin book writing (trigger) event */ |
if ((evt = find_event(item, EVENT_TRIGGER)) != NULL) | | if ((evt = find_event(item, EVENT_TRIGGER)) != NULL) { |
{ | | |
CFParm CFP; | | CFParm CFP; |
int k, l, m; | | int k, l, m; |
k = EVENT_TRIGGER; | | k = EVENT_TRIGGER; |
| | |
CFP.Value[8] = &l; | | CFP.Value[8] = &l; |
CFP.Value[9] = evt->hook; | | CFP.Value[9] = evt->hook; |
CFP.Value[10]= evt->options; | | CFP.Value[10]= evt->options; |
if (findPlugin(evt->plugin)>=0) | | if (findPlugin(evt->plugin)>=0) { |
{ | | |
((PlugList[findPlugin(evt->plugin)].eventfunc) (&CFP)); | | ((PlugList[findPlugin(evt->plugin)].eventfunc) (&CFP)); |
return strlen(msg); | | return strlen(msg); |
} | | } |
| | |
| | |
buf[0] = 0; | | buf[0] = 0; |
if(!book_overflow(item->msg,msg,BOOK_BUF)) { /* add msg string to book */ | | if(!book_overflow(item->msg,msg,BOOK_BUF)) { /* add msg string to book */ |
if(item->msg) { | | if(item->msg) |
strcpy(buf,item->msg); | | strcpy(buf,item->msg); |
if (item->nrof < 2) | | |
free_string(item->msg); | | |
} | | |
strcat(buf,msg); | | strcat(buf,msg); |
strcat(buf,"\n"); /* new msg needs a LF */ | | strcat(buf,"\n"); /* new msg needs a LF */ |
if(item->nrof > 1) { | | if(item->nrof > 1) { |
| | |
decrease_ob(item); | | decrease_ob(item); |
esrv_send_item(pl, item); | | esrv_send_item(pl, item); |
newBook->nrof = 1; | | newBook->nrof = 1; |
| | if (newBook->msg) free_string(newBook->msg); |
newBook->msg = add_string(buf); | | newBook->msg = add_string(buf); |
newBook = insert_ob_in_ob(newBook, pl); | | newBook = insert_ob_in_ob(newBook, pl); |
esrv_send_item(pl, newBook); | | esrv_send_item(pl, newBook); |
} else { | | } else { |
| | if (item->msg) free_string(item->msg); |
item->msg=add_string(buf); | | item->msg=add_string(buf); |
esrv_send_item(pl, item); | | /* This shouldn't be necessary - the object hasn't changed in any visible way */ |
| | /* esrv_send_item(pl, item);*/ |
} | | } |
new_draw_info_format(NDI_UNIQUE,0,pl, "You write in the %s.",query_short_name(item)); | | new_draw_info_format(NDI_UNIQUE,0,pl, "You write in the %s.",query_short_name(item)); |
return strlen(msg); | | return strlen(msg); |
} else | | } else |
new_draw_info_format(NDI_UNIQUE,0,pl, "Your message won't fit in the %s!",query_short_name(item)); | | new_draw_info_format(NDI_UNIQUE,0,pl, "Your message won't fit in the %s!",query_short_name(item)); |
| | |
return 0; | | return 0; |
} | | } |
| | |
| | |
* that is attempted. -b.t. thomas@astro.psu.edu | | * that is attempted. -b.t. thomas@astro.psu.edu |
*/ | | */ |
| | |
int write_scroll (object *pl, object *scroll) { | | static int write_scroll (object *pl, object *scroll, object *skill) { |
int success=0,confused=0,chosen_spell=-1,stat1=get_skill_stat1(pl); | | int success=0,confused=0; |
object *newScroll; | | object *newscroll, *chosen_spell, *tmp; |
| | |
/* this is a sanity check */ | | /* this is a sanity check */ |
if (scroll->type!=SCROLL) { | | if (scroll->type!=SCROLL) { |
| | |
} | | } |
| | |
/* Check if we are ready to attempt inscription */ | | /* Check if we are ready to attempt inscription */ |
chosen_spell=pl->contr->chosen_spell; | | chosen_spell=pl->contr->ranges[range_magic]; |
if(chosen_spell<0) { | | if(!chosen_spell) { |
new_draw_info(NDI_UNIQUE,0,pl, | | new_draw_info(NDI_UNIQUE,0,pl, |
"You need a spell readied in order to inscribe!"); | | "You need a spell readied in order to inscribe!"); |
return 0; | | return 0; |
} | | } |
if(spells[chosen_spell].scroll_chance==0) { /* Tried to write non-scroll spell */ | | if(chosen_spell->stats.grace > pl->stats.grace) { |
new_draw_info_format(NDI_UNIQUE,0,pl,"The spell %s cannot be inscribed.", | | |
spells[chosen_spell].name); | | |
return 0; | | |
} | | |
if(spells[chosen_spell].cleric && spells[chosen_spell].sp>pl->stats.grace) { | | |
new_draw_info_format(NDI_UNIQUE,0,pl, | | new_draw_info_format(NDI_UNIQUE,0,pl, |
"You don't have enough grace to write a scroll of %s." | | "You don't have enough grace to write a scroll of %s.", |
,spells[chosen_spell].name); | | chosen_spell->name); |
return 0; | | return 0; |
} | | } |
else if(spells[chosen_spell].sp>pl->stats.sp) { | | if(chosen_spell->stats.sp > pl->stats.sp) { |
new_draw_info_format(NDI_UNIQUE,0,pl, | | new_draw_info_format(NDI_UNIQUE,0,pl, |
"You don't have enough mana to write a scroll of %s." | | "You don't have enough mana to write a scroll of %s.", |
,spells[chosen_spell].name); | | chosen_spell->name); |
return 0; | | return 0; |
} | | } |
| | |
| | |
* accidently read it while trying to write the new one. give player | | * accidently read it while trying to write the new one. give player |
* a 50% chance to overwrite spell at their own level | | * a 50% chance to overwrite spell at their own level |
*/ | | */ |
if(scroll->stats.sp && | | if((scroll->stats.sp || scroll->inv) && |
random_roll(0, scroll->level*2, pl, PREFER_LOW)>SK_level(pl)) { | | random_roll(0, scroll->level*2, pl, PREFER_LOW)>skill->level) { |
new_draw_info_format(NDI_UNIQUE,0,pl, | | new_draw_info_format(NDI_UNIQUE,0,pl, |
"Oops! You accidently read it while trying to write on it."); | | "Oops! You accidently read it while trying to write on it."); |
manual_apply(pl,scroll,0); | | manual_apply(pl,scroll,0); |
change_skill(pl,SK_INSCRIPTION); | | |
return 0; | | return 0; |
} | | } |
| | |
/* ok, we are ready to try inscription */ | | /* ok, we are ready to try inscription */ |
| | |
if(QUERY_FLAG(pl,FLAG_CONFUSED)) confused = 1; | | if(QUERY_FLAG(pl,FLAG_CONFUSED)) confused = 1; |
| | |
/* Lost mana/grace no matter what */ | | /* Lost mana/grace no matter what */ |
if (spells[chosen_spell].cleric) | | pl->stats.grace-=chosen_spell->stats.grace; |
pl->stats.grace-=spells[chosen_spell].sp; | | pl->stats.sp-=chosen_spell->stats.sp; |
else | | |
pl->stats.sp-=spells[chosen_spell].sp; | | |
| | |
if (random_roll(0, spells[chosen_spell].level*4-1, pl, PREFER_LOW) < | | if (random_roll(0, chosen_spell->level*4-1, pl, PREFER_LOW) < skill->level) { |
SK_level(pl)) { | | if (scroll->nrof > 1) { |
newScroll = get_object(); | | newscroll = get_object(); |
copy_object(scroll, newScroll); | | copy_object(scroll, newscroll); |
decrease_ob(scroll); | | decrease_ob(scroll); |
newScroll->nrof = 1; | | newscroll->nrof = 1; |
| | |
if(!confused) { | | |
newScroll->level= (SK_level(pl)>spells[chosen_spell].level ? | | |
SK_level(pl) : spells[chosen_spell].level); | | |
} else { | | } else { |
/* a confused scribe gets a random spell */ | | newscroll = scroll; |
do | | |
chosen_spell=rndm(0, NROFREALSPELLS-1); | | |
while (spells[chosen_spell].scroll_chance==0); | | |
| | |
newScroll->level=SK_level(pl)>spells[chosen_spell].level ? | | |
spells[chosen_spell].level : | | |
(random_roll(1, SK_level(pl), pl, PREFER_HIGH)); | | |
} | | } |
| | |
if(newScroll->stats.sp==chosen_spell) | | if(!confused) { |
new_draw_info(NDI_UNIQUE,0,pl, "You overwrite the scroll."); | | newscroll->level= MAX(skill->level, chosen_spell->level); |
else { | | |
new_draw_info(NDI_UNIQUE,0,pl, | | new_draw_info(NDI_UNIQUE,0,pl, |
"You succeed in writing a new scroll."); | | "You succeed in writing a new scroll."); |
newScroll->stats.sp=chosen_spell; | | } else { |
| | chosen_spell = find_random_spell_in_ob(pl, NULL); |
| | if (!chosen_spell) return 0; |
| | |
| | newscroll->level= MAX(skill->level, chosen_spell->level); |
| | new_draw_info(NDI_UNIQUE,0,pl, |
| | "In your confused state, you write down some odd spell."); |
| | } |
| | |
| | if (newscroll->inv) { |
| | remove_ob(newscroll->inv); |
| | free_object(newscroll->inv); |
} | | } |
| | tmp = get_object(); |
| | copy_object(chosen_spell, tmp); |
| | insert_ob_in_ob(newscroll, tmp); |
| | |
/* wait until finished manipulating the scroll before inserting it */ | | /* wait until finished manipulating the scroll before inserting it */ |
newScroll=insert_ob_in_ob(newScroll,pl); | | if (newscroll != scroll) |
esrv_send_item(pl, newScroll); | | newscroll=insert_ob_in_ob(newscroll,pl); |
success = calc_skill_exp(pl,newScroll); | | esrv_send_item(pl, newscroll); |
| | success = calc_skill_exp(pl,newscroll, skill); |
if(!confused) success *= 2; | | if(!confused) success *= 2; |
return success; | | return success; |
| | |
} else { /* Inscription has failed */ | | } else { /* Inscription has failed */ |
| | |
if(spells[chosen_spell].level>SK_level(pl) || confused){ /*backfire!*/ | | if(chosen_spell->level>skill->level || confused) { /*backfire!*/ |
new_draw_info(NDI_UNIQUE,0,pl, | | new_draw_info(NDI_UNIQUE,0,pl, |
"Ouch! Your attempt to write a new scroll strains your mind!"); | | "Ouch! Your attempt to write a new scroll strains your mind!"); |
if(random_roll(0, 1, pl, PREFER_LOW)==1) | | if(random_roll(0, 1, pl, PREFER_LOW)==1) |
drain_specific_stat(pl,4); | | drain_specific_stat(pl,4); |
else { | | else { |
confuse_player(pl,pl,99); | | confuse_player(pl,pl,99); |
/* return (-3*calc_skill_exp(pl,newScroll));*/ | | return (-30*chosen_spell->level); |
return (-30*spells[chosen_spell].level); | | |
} | | } |
} else if(random_roll(0, stat1-1, pl, PREFER_HIGH) < 15) { | | } else if(random_roll(0, pl->stats.Int-1, pl, PREFER_HIGH) < 15) { |
new_draw_info(NDI_UNIQUE,0,pl, | | new_draw_info(NDI_UNIQUE,0,pl, |
"Your attempt to write a new scroll rattles your mind!"); | | "Your attempt to write a new scroll rattles your mind!"); |
confuse_player(pl,pl,99); | | confuse_player(pl,pl,99); |
} else | | } else |
new_draw_info(NDI_UNIQUE,0,pl,"You fail to write a new scroll."); | | new_draw_info(NDI_UNIQUE,0,pl,"You fail to write a new scroll."); |
} | | } |
/* return (-1*calc_skill_exp(pl,newScroll));*/ | | return 0; |
return (-10*spells[chosen_spell].level); | | |
} | | } |
| | |
/* remove_trap() - This skill will disarm any previously discovered trap | | /* write_on_item() - wrapper for write_note and write_scroll */ |
* the algorithm is based (almost totally) on the old command_disarm() - b.t. | | int write_on_item (object *pl,char *params, object *skill) { |
*/ | | object *item; |
| | char *string=params; |
| | int msgtype; |
| | archetype *skat; |
| | |
int remove_trap (object *op, int dir) { | | if(pl->type!=PLAYER) return 0; |
object *tmp,*tmp2; | | |
int i,x,y,success=0; | | |
| | |
for(i=0;i<9;i++) { | | if (!params) { |
x = op->x + freearr_x[i]; | | params=""; |
y = op->y + freearr_y[i]; | | string=params; |
if(out_of_map(op->map,x,y)) | | } |
continue; | | skat = get_archetype_by_type_subtype(SKILL, SK_LITERACY); |
| | |
/* Check everything in the square for trapness */ | | /* Need to be able to read before we can write! */ |
for(tmp = get_map_ob(op->map,x,y);tmp!=NULL;tmp=tmp->above) { | | if(!find_skill_by_name(pl,skat->clone.skill)) { |
| | new_draw_info(NDI_UNIQUE,0,pl, |
| | "You must learn to read before you can write!"); |
| | return 0; |
| | } |
| | |
/* And now we'd better do an inventory traversal of each | | /* if there is a message then it goes in a book and no message means |
* of these objects' inventory */ | | * write active spell into the scroll |
| | */ |
| | msgtype = (string[0]!='\0') ? BOOK : SCROLL; |
| | |
for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below) | | /* find an item of correct type to write on */ |
if((tmp2->type==RUNE || tmp2->type==TRAP)&&tmp2->stats.Cha<=1) { | | if ( !(item = find_marked_object(pl))){ |
trap_show(tmp2,tmp); | | new_draw_info(NDI_UNIQUE,0,pl,"You don't have any marked item to write on."); |
if(trap_disarm(op,tmp2,1) && (!tmp2->owner || tmp2->owner->type!=PLAYER)) | | return 0; |
success += calc_skill_exp(op,tmp2); | | |
} | | } |
| | |
if((tmp->type==RUNE || tmp->type==TRAP)&&tmp->stats.Cha<=1) { | | if(QUERY_FLAG(item,FLAG_UNPAID)) { |
trap_show(tmp,tmp); | | new_draw_info(NDI_UNIQUE,0,pl, |
if (trap_disarm(op,tmp,1) && (!tmp->owner || tmp->owner->type!=PLAYER)) | | "You had better pay for that before you write on it."); |
success += calc_skill_exp(op,tmp); | | return 0; |
} | | |
} | | } |
| | if (msgtype != item->type) { |
| | new_draw_info_format(NDI_UNIQUE,0,pl,"You have no %s to write on", |
| | msgtype==BOOK ? "book" : "scroll"); |
| | return 0; |
} | | } |
| | |
return success; | | if (msgtype == SCROLL) { |
| | return write_scroll(pl,item, skill); |
| | } else if (msgtype == BOOK) { |
| | return write_note(pl,item,string, skill); |
| | } |
| | return 0; |
} | | } |
| | |
int skill_throw (object *op, object *part, int dir, char *params) { | | |
| | |
object *throw_ob; | | |
| | |
if(op->type==PLAYER) | | |
throw_ob = find_throw_ob(op,params); | | |
else | | |
throw_ob = find_mon_throw_ob(op); | | |
| | |
return do_throw(op,part, throw_ob,dir); | | |
} | | |
| | |
/* find_throw_ob() - if we request an object, then | | /* find_throw_ob() - if we request an object, then |
* we search for it in the inventory of the owner (you've | | * we search for it in the inventory of the owner (you've |
| | |
* 'throwable' (ie not applied cursed obj, worn, etc). | | * 'throwable' (ie not applied cursed obj, worn, etc). |
*/ | | */ |
| | |
object *find_throw_ob( object *op, char *request ) { | | static object *find_throw_ob( object *op, char *request ) { |
object *tmp; | | object *tmp; |
| | |
if(!op) { /* safety */ | | if(!op) { /* safety */ |
| | |
for(tmp=op->inv;tmp;tmp=tmp->below) { | | for(tmp=op->inv;tmp;tmp=tmp->below) { |
/* can't toss invisible or inv-locked items */ | | /* can't toss invisible or inv-locked items */ |
if(tmp->invisible||QUERY_FLAG(tmp,FLAG_INV_LOCKED)) continue; | | if(tmp->invisible||QUERY_FLAG(tmp,FLAG_INV_LOCKED)) continue; |
if(!request||!strcmp(query_name(tmp),request) | | if(!request || !strcmp(query_name(tmp),request) || !strcmp(tmp->name,request)) |
||!strcmp(tmp->name,request)) break; | | break; |
} | | } |
| | |
/* this should prevent us from throwing away | | /* this should prevent us from throwing away |
* cursed items, worn armour, etc. Only weapons | | * cursed items, worn armour, etc. Only weapons |
* can be thrown from 'hand'. */ | | * can be thrown from 'hand'. |
if(tmp) { | | */ |
| | if (!tmp) return NULL; |
| | |
if (QUERY_FLAG(tmp,FLAG_APPLIED)) { | | if (QUERY_FLAG(tmp,FLAG_APPLIED)) { |
if(tmp->type!=WEAPON) { | | if(tmp->type!=WEAPON) { |
new_draw_info_format(NDI_UNIQUE, 0,op, | | new_draw_info_format(NDI_UNIQUE, 0,op, |
| | |
} | | } |
} | | } |
} else if (QUERY_FLAG(tmp, FLAG_UNPAID)) { | | } else if (QUERY_FLAG(tmp, FLAG_UNPAID)) { |
new_draw_info_format(NDI_UNIQUE, 0, op, "You should pay for the %s" | | new_draw_info_format(NDI_UNIQUE, 0, op, "You should pay for the %s first.", query_name(tmp)); |
" first.", query_name(tmp)); | | |
tmp = NULL; | | tmp = NULL; |
} | | } |
} | | |
| | |
if (tmp && QUERY_FLAG (tmp, FLAG_INV_LOCKED)) { | | if (tmp && QUERY_FLAG (tmp, FLAG_INV_LOCKED)) { |
LOG (llevError, "BUG: find_throw_ob(): object is locked\n"); | | LOG (llevError, "BUG: find_throw_ob(): object is locked\n"); |
tmp=NULL; | | tmp=NULL; |
} | | } |
| | |
return tmp; | | return tmp; |
} | | } |
| | |
| | |
* This combination becomes the 'thrown object'. -b.t. | | * This combination becomes the 'thrown object'. -b.t. |
*/ | | */ |
| | |
object *make_throw_ob (object *orig) { | | static object *make_throw_ob (object *orig) { |
object *toss_item=NULL; | | object *toss_item; |
| | |
| | if(!orig) return NULL; |
| | |
if(orig) { | | |
toss_item=get_object(); | | toss_item=get_object(); |
if (QUERY_FLAG (orig, FLAG_APPLIED)) { | | if (QUERY_FLAG (orig, FLAG_APPLIED)) { |
LOG (llevError, "BUG: make_throw_ob(): ob is applied\n"); | | LOG (llevError, "BUG: make_throw_ob(): ob is applied\n"); |
| | |
toss_item->type = THROWN_OBJ; | | toss_item->type = THROWN_OBJ; |
CLEAR_FLAG(toss_item,FLAG_CHANGING); | | CLEAR_FLAG(toss_item,FLAG_CHANGING); |
toss_item->stats.dam = 0; /* default damage */ | | toss_item->stats.dam = 0; /* default damage */ |
#ifdef DEBUG_THROW | | |
LOG(llevDebug," inserting %s(%d) in toss_item(%d)\n", | | |
orig->name,orig->count,toss_item->count); | | |
#endif | | |
insert_ob_in_ob(orig,toss_item); | | insert_ob_in_ob(orig,toss_item); |
} | | |
| | |
return toss_item; | | return toss_item; |
} | | } |
| | |
| | |
/* do_throw() - op throws any object toss_item. This code | | /* do_throw() - op throws any object toss_item. This code |
* was borrowed from fire_bow (see above). | | * was borrowed from fire_bow. |
* Returns 1 if skill was successfully used, 0 if not | | * Returns 1 if skill was successfully used, 0 if not |
*/ | | */ |
| | |
int do_throw(object *op, object *part, object *toss_item, int dir) { | | static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill) { |
object *throw_ob=toss_item, *left=NULL; | | object *throw_ob=toss_item, *left=NULL; |
tag_t left_tag; | | tag_t left_tag; |
int eff_str = 0,maxc,str=op->stats.Str,dam=0; | | int eff_str = 0,maxc,str=op->stats.Str,dam=0; |
| | |
return 0; | | return 0; |
} | | } |
| | |
| | |
/* Because throwing effectiveness must be reduced by the | | /* Because throwing effectiveness must be reduced by the |
* encumbrance of the thrower and weight of the object. THus, | | * encumbrance of the thrower and weight of the object. THus, |
* we use the concept of 'effective strength' as defined below. | | * we use the concept of 'effective strength' as defined below. |
| | |
*/ | | */ |
| | |
if((throw_ob = get_split_ob(throw_ob, 1))==NULL) { | | if((throw_ob = get_split_ob(throw_ob, 1))==NULL) { |
#ifdef DEBUG_THROW | | |
LOG(llevDebug," get_splt_ob faild to split throw ob %s\n",left->name); | | |
#endif | | |
throw_ob = left; | | throw_ob = left; |
remove_ob(left); | | remove_ob(left); |
if (op->type==PLAYER) | | if (op->type==PLAYER) |
| | |
} | | } |
| | |
/* special case: throwing powdery substances like dust, dirt */ | | /* special case: throwing powdery substances like dust, dirt */ |
if(QUERY_FLAG(throw_ob,FLAG_DUST)) { | | if(throw_ob->type == POTION && throw_ob->subtype == POT_DUST) { |
cast_dust(op,throw_ob,dir); | | cast_dust(op,throw_ob,dir); |
return 1; | | return 1; |
} | | } |
| | |
* If unsuccessfull at making the "thrown_obj", we just reinsert | | * If unsuccessfull at making the "thrown_obj", we just reinsert |
* the original object back into inventory and exit | | * the original object back into inventory and exit |
*/ | | */ |
if((toss_item = make_throw_ob(throw_ob))) | | if((toss_item = make_throw_ob(throw_ob))) { |
throw_ob = toss_item; | | throw_ob = toss_item; |
| | if (throw_ob->skill) free_string(throw_ob->skill); |
| | throw_ob->skill = add_string(skill->skill); |
| | } |
else { | | else { |
insert_ob_in_ob(throw_ob,op); | | insert_ob_in_ob(throw_ob,op); |
return 0; | | return 0; |
| | |
| | |
/* replace 25 with a call to clone.arch wc? messes up w/ NPC */ | | /* replace 25 with a call to clone.arch wc? messes up w/ NPC */ |
throw_ob->stats.wc = 25 - dex_bonus[op->stats.Dex?dex_bonus[op->stats.Dex]:0] | | throw_ob->stats.wc = 25 - dex_bonus[op->stats.Dex?dex_bonus[op->stats.Dex]:0] |
- thaco_bonus[eff_str] - SK_level(op); | | - thaco_bonus[eff_str] - skill->level; |
| | |
| | |
/* the properties of objects which are meant to be thrown (ie dart, | | /* the properties of objects which are meant to be thrown (ie dart, |
| | |
if(throw_ob->stats.wc>30) throw_ob->stats.wc=30; | | if(throw_ob->stats.wc>30) throw_ob->stats.wc=30; |
| | |
/* how long to pause the thrower. Higher values mean less pause */ | | /* how long to pause the thrower. Higher values mean less pause */ |
pause_f = ((2*eff_str)/3)+20+SK_level(op); | | pause_f = ((2*eff_str)/3)+20+skill->level; |
| | |
/* Put a lower limit on this */ | | /* Put a lower limit on this */ |
if (pause_f < 10) pause_f=10; | | if (pause_f < 10) pause_f=10; |
| | |
move_arrow(throw_ob); | | move_arrow(throw_ob); |
return 1; | | return 1; |
} | | } |
| | |
| | int skill_throw (object *op, object *part, int dir, char *params, object *skill) { |
| | object *throw_ob; |
| | |
| | if(op->type==PLAYER) |
| | throw_ob = find_throw_ob(op,params); |
| | else |
| | throw_ob = find_mon_throw_ob(op); |
| | |
| | return do_throw(op,part, throw_ob,dir, skill); |
| | } |
| | |