- [+] climbing

- [+] climb "into"walls
        - [+] stopclimbing(int onpurpose)
            - [+] can we move forwards?
                - [+] if not:
                    - [+] if onpurpose, fail
                    - [+] otherwise fall onto whoever is there??? or
                          they block you from falling??
            - [+] move forwards
            - [+] kill f_climbing flag
        - [+] startclimbing()
            - [+] move onto the wall
            - [+] set your facing to face away from the wall.
            - [+] add F_CLIMBING
            - [+] must pass a CLIMB check first or you fail to climb!
            - [+] can't climb if burdened
        - [+] once climbing:
            - [+] can't cast spells
            - [+] cannot change facing
            - [+] always strafe if you go sideways
            - [+] change los functions to let you see out!
            - [+] different move verb ("climb" instead of move/fly)
            - [+] different canmove() code
                - [+] you can only move sideways (along the wall) or
                      forwards (off the wall)
                    - [+] ie. cellindir(lf, lf->facing) == newcell
                          (forwards)
                    - [+] OR
                    - [+] ie. cellindir(lf, lf->facing + 2 (wrapped))
                          == newcell (right)
                    - [+] OR
                    - [+] ie. cellindir(lf, lf->facing - 2 (wrapped))
                          == newcell (left)
                - [+] AND you can only move sidways if:
                    - [+] the new cell is a wall
                    - [+] the cell in (lf->facing) of the new cell is
                          NOT a wall
            - [+] if you climb off a wall, tell the player that they
                  stopped climbing
            - [+] moving while climbing:
                - [+] must pass a sc_climb skill check to move
                - [+] costs 1 stamina to move
                    - [+] warn before climbing if your stam == 1
            - [+] attacking
                - [+] large penalty to hit the climber (higher for
                      smaller monstesr)
                - [+] if monsters miss you and you are climbing, chance
                      they'll give up.
                - [+] attacking FROM a wall gets a big penalty unless
                      you are very skilled at climbing
            - [+] startlfturn
                - [+] lose stam each turn.
                - [+] if stam drops to 0, you fall off or stop climbing
                - [+] you fall if burdened
                - [+] don't regen stam while climing
        - [+] ai:
            - [+] monsters won't start targeting lfs who are climbing
            - [+] monsters don't pay stamina to move while climb
            - [+] make monsters be able to climb when fleeing
    - [+] let you climb down holes instead of falling
This commit is contained in:
Rob Pearce 2011-11-02 22:34:15 +00:00
parent 1222ee9550
commit 06cf19e7c9
12 changed files with 498 additions and 148 deletions

39
ai.c
View File

@ -286,7 +286,7 @@ object_t *aigetrangedattack(lifeform_t *lf, enum RANGEATTACK *ra, int *range) {
// animal and lower intelligence won't use ranged
// attacks.
if (db) dblog(".oO { no ranged attack due to low iq. }");
*ra = RA_NONE;
if (ra) *ra = RA_NONE;
return NULL;
}
@ -297,8 +297,8 @@ object_t *aigetrangedattack(lifeform_t *lf, enum RANGEATTACK *ra, int *range) {
getobname(o, gunname, o->amt);
if (db) dblog(".oO { will fire my gun (%s) at target. }",gunname);
}
*ra = RA_GUN;
*range = getfirearmrange(o);
if (ra) *ra = RA_GUN;
if (range) *range = getfirearmrange(o);
return o;
}
@ -313,8 +313,8 @@ object_t *aigetrangedattack(lifeform_t *lf, enum RANGEATTACK *ra, int *range) {
if (db) dblog(".oO { will zap %s instead of moving }", o->type->name);
*ra = RA_WAND;
*range = getvisrange(lf, B_TRUE); // ie unlimited
if (ra) *ra = RA_WAND;
if (range) *range = getvisrange(lf, B_TRUE); // ie unlimited
return o;
}
}
@ -324,15 +324,15 @@ object_t *aigetrangedattack(lifeform_t *lf, enum RANGEATTACK *ra, int *range) {
o = getbestthrowmissile(lf);
if (o) {
if (db) dblog(".oO { will throw %s at my target }", o->type->name);
*ra = RA_THROW;
*range = getmaxthrowrange(lf, o);
if (ra) *ra = RA_THROW;
if (range) *range = getmaxthrowrange(lf, o);
return o;
}
}
if (db) dblog(".oO { found no ranged attack. }");
*ra = RA_NONE;
*range = 0;
if (ra) *ra = RA_NONE;
if (range) *range = 0;
return NULL;
}
@ -1442,15 +1442,27 @@ void aiturn(lifeform_t *lf) {
if (lf->los[n] != lf->cell) { // not ourself
who = lf->los[n]->lf;
if (who && !isdead(who) && !isunconscious(who) && cansee(lf, who)) {
int chance = 100;
int chance = 100; // chance that we ('lf') will attack 'who'
int reachpenalty;
// targets sleeping in a tent will probably be ignored
if (isresting(lf)) {
if (isresting(who)) {
object_t *restob;
restob = getrestob(who);
if (restob && (restob->type->id == OT_TENT)) {
chance = 5;
}
}
// will usually ignore targets who we can't reach
if (!canreach(lf, who, &reachpenalty)) {
if (!aigetrangedattack(lf, NULL, NULL)) { // no ranged attack?
// 1 size to small = 53% chance to attack
// 1 size to small = 6% chance to attack
chance = 100 - (reachpenalty*47);
if (db) dblog(".oO { target %d (%s) is %d sizes out of reach. %d%% chance to ignore }",who->id, who->race->name,
reachpenalty, reachpenalty*47);
}
}
if (pctchance(chance)) {
if (lfhasflagval(lf, F_HATESRACE, who->race->id, NA, NA, NULL) ||
lfhasflagval(lf, F_HATESRACE, who->race->baseid, NA, NA, NULL) ) {
@ -1778,6 +1790,11 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG
if ((ot->id == OT_S_BLINDNESS) && isblind(victim)) {
specificcheckok = B_FALSE;
}
if (ot->id == OT_A_CLIMB) {
if (!canclimb(lf, NULL)) {
specificcheckok = B_FALSE;
}
}
if (ot->id == OT_A_DISARM) {
if (!getweapon(victim)) {
specificcheckok = B_FALSE;

View File

@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ai.h"
#include "attack.h"
#include "defs.h"
#include "flag.h"
@ -1146,6 +1147,13 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag)
}
}
// chance that ai will give up if we can't reach the victim
if (!isplayer(lf) && !canreach(lf, victim, NULL)) {
if (pctchance(90)) {
loseaitargets(lf);
}
}
}
}
@ -1956,7 +1964,7 @@ int rolltohit(lifeform_t *lf, lifeform_t *victim, object_t *wep, int *critical)
} else if (critical && *critical) {
gothit = B_TRUE;
} else {
enum LFSIZE sizetoreach = SZ_ANY;
int reachpenalty = 0;
// actually roll...
acc = getlfaccuracy(lf, wep);
@ -1977,22 +1985,9 @@ int rolltohit(lifeform_t *lf, lifeform_t *victim, object_t *wep, int *critical)
acc += 30;
}
// harder to hit flying/levitating enemies, unless you are
// large (ie. tall) enough to reach them.
// if you are smaller than "sizetoreach", you get a penalty
switch (isairborne(victim)) {
case F_FLYING:
sizetoreach = SZ_HUGE;
break;
case F_LEVITATING:
sizetoreach = SZ_HUMAN;
break;
default: sizetoreach = SZ_ANY; break;
if (!canreach(lf, victim, &reachpenalty)) {
acc -= (15*reachpenalty);
}
if (sizetoreach != SZ_ANY) {
acc -= (15*(sizetoreach - getlfsize(lf)));
}
// modify for defender's evasion
if (isprone(victim) || !cansee(victim, lf)) {
@ -2012,6 +2007,20 @@ int rolltohit(lifeform_t *lf, lifeform_t *victim, object_t *wep, int *critical)
// modify for lore level
if (lorelev != PR_INEPT) lorelev += (lorelev*10);
// modify for attacking while climbing
if (isclimbing(lf)) {
switch (getskill(lf, SK_CLIMBING)) {
case PR_INEPT: acc -= 100; break;
case PR_NOVICE: acc -= 100; break;
case PR_BEGINNER: acc -= 75; break;
case PR_ADEPT: acc -= 50; break;
case PR_SKILLED: acc -= 25; break;
case PR_EXPERT: acc -= 10; break;
default:
case PR_MASTER: break;
}
}
limit(&acc, 0, 100);
//if (aidb) dblog(".oO { my modified chance to hit is %d %% }", acc);

19
data.c
View File

@ -164,6 +164,7 @@ void initjobs(void) {
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 gold coins");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "3 potions of healing");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "2 bananas");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "rope");
// initial skills
addflag(lastjob->flags, F_STARTSKILL, SK_ATHLETICS, PR_NOVICE, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_CARTOGRAPHY, PR_SKILLED, NA, NULL);
@ -1897,7 +1898,7 @@ void initobjects(void) {
addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL);
///////////////////
// death / ncromancy
// death / necromancy
///////////////////
// l1
addot(OT_S_STENCH, "stench", "Nauseates the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY);
@ -3082,9 +3083,10 @@ void initobjects(void) {
addflag(lastot->flags, F_NEEDSGRAB, B_TRUE, NA, NA, NULL);
addot(OT_A_CHECKSTAIRS, "check stairs", "Attempt to determine what lies on the other end of a staircase.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addot(OT_A_CLIMB, "climb wall", "Climb up a wall to escape your enemies.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY);
addflag(lastot->flags, F_STAMCOST, 2, NA, NA, NULL);
addot(OT_A_CLIMB, "climb", "Climb up or down whatever is in front of you (walls or pits).", MT_NOTHING, 0, OC_ABILITY, SZ_TINY);
addflag(lastot->flags, F_STAMCOST, 1, NA, NA, NULL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addflag(lastot->flags, F_AICASTTOFLEE, ST_ANYWHERE, NA, NA, NULL);
addot(OT_A_COOK, "cook", "Combine food and water into a healthy meals.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addot(OT_A_DARKWALK, "darkwalk", "Step between the shadows.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY);
@ -8197,6 +8199,7 @@ void initrace(void) {
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, NULL); // don't announce spellcasting
addflag(lastrace->flags, F_STARTSKILL, SK_CLIMBING, PR_MASTER, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_S_WEB, 3, 3, "pw:1;range:4;");
addflag(lastrace->flags, F_CASTCHANCE, 60, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
@ -8217,6 +8220,7 @@ void initrace(void) {
addflag(lastrace->flags, F_STARTATT, A_AGI, AT_GTAVERAGE, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_STARTSKILL, SK_CLIMBING, PR_MASTER, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "5d4+1");
addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 4, NA, NULL);
@ -8247,6 +8251,7 @@ void initrace(void) {
addflag(lastrace->flags, F_STARTATT, A_AGI, AT_GTAVERAGE, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_STARTSKILL, SK_CLIMBING, PR_MASTER, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "4d4+1");
addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 4, NA, NULL);
@ -8904,7 +8909,13 @@ void initskills(void) {
addskilldesc(SK_CHANNELING, PR_EXPERT, "^gThe power level of wands and scrolls is increased by 8.^n", B_FALSE);
addskilldesc(SK_CHANNELING, PR_MASTER, "^gThe power level of wands and scrolls is increased by 10.^n", B_FALSE);
addskill(SK_CLIMBING, "Climbing", "Helps you to climb walls, mountains or other terrain.", 50);
addskilldesc(SK_CLIMBING, PR_INEPT, "- Increases you chances of successfully climbing by 10% per level.", B_FALSE);
addskilldesc(SK_CLIMBING, PR_INEPT, "Increases your chances of successfully climbing by 10% per level.", B_FALSE);
addskilldesc(SK_CLIMBING, PR_NOVICE, "You gain the 'climb walls' ability. Cannot attack while climbing.", B_FALSE);
addskilldesc(SK_CLIMBING, PR_BEGINNER, "-75% accuracy penalty while climbing.", B_FALSE);
addskilldesc(SK_CLIMBING, PR_ADEPT, "-50% accuracy penalty while climbing.", B_FALSE);
addskilldesc(SK_CLIMBING, PR_SKILLED, "-25% accuracy penalty while climbing.", B_FALSE);
addskilldesc(SK_CLIMBING, PR_EXPERT, "-10% accuracy penalty while climbing.", B_FALSE);
addskilldesc(SK_CLIMBING, PR_MASTER, "No accuracy penalty or stamina cost to remain climbing.", B_FALSE);
addskill(SK_COOKING, "Cooking", "Your ability to combine foods into nutritious meals.", 50);
addskilldesc(SK_COOKING, PR_NOVICE, "^gYou now recognise water and rotting food.^n", B_TRUE);
addskilldesc(SK_COOKING, PR_BEGINNER, "^gYou can now recognise all kinds of bad food.^n", B_TRUE);

3
defs.h
View File

@ -2735,6 +2735,8 @@ enum ERROR {
E_WALLINWAY = 1,
E_LFINWAY,
E_NOSPACE,
E_BADCLIMBDIR,
E_STOPCLIMBING,
E_SELNOTHING,
E_ALREADYUSING,
E_WEARINGSOMETHINGELSE,
@ -2797,6 +2799,7 @@ enum ERROR {
E_PRONE,
E_PENTAGRAM,
E_SWIMMING,
E_CLIMBING,
E_DANGEROUS,
E_INJURED,
};

1
flag.c
View File

@ -420,6 +420,7 @@ int flagcausesstatredraw(lifeform_t *lf, enum FLAG fid) {
case F_ASLEEP:
case F_ATTRMOD:
case F_BLIND:
case F_CLIMBING:
case F_CONFUSED:
case F_DRUNK:
case F_EATING:

29
io.c
View File

@ -735,7 +735,8 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src
if (lfhasflag(c->lf, F_CLIMBING)) {
if (strlen(extrainfo)) strcat(extrainfo, ", ");
strcat(extrainfo, "climbing");
strcat(extrainfo, "climbing ");
strcat(extrainfo, c->type->name);
}
f = lfhasflag(c->lf, F_ATTACHEDTO);
@ -5488,16 +5489,17 @@ char *makedesc_skill(enum SKILL skid, char *retbuf, enum SKILLLEVEL levhilite) {
for (i = 0; i < sk->nskilldesc; i++) {
if (sk->skilldesclev[i] == slev) {
if (slev == PR_INEPT) {
snprintf(buf, BUFLEN, "%s\n",sk->skilldesctext[i]);
// extra blank line after this one
snprintf(buf, BUFLEN, "%s\n\n",sk->skilldesctext[i]);
} else {
int hilitethis = B_FALSE;
char atxlevel[BUFLEN];
if ((levhilite != PR_INEPT) && (slev == levhilite)) {
hilitethis = B_TRUE;
}
snprintf(buf, BUFLEN, "%sAt %s level%s: %s\n",
hilitethis ? "^w" : "",
getskilllevelname(sk->skilldesclev[i]),
hilitethis ? "^n" : "",
snprintf(atxlevel, BUFLEN, "At %s level", getskilllevelname(sk->skilldesclev[i]));
snprintf(buf, BUFLEN, "%s%-18s%s: %s\n", // right-align "at xx level"
hilitethis ? "^w" : "", atxlevel, hilitethis ? "^n" : "",
sk->skilldesctext[i]);
}
strncat(retbuf, buf, HUGEBUFLEN);
@ -6055,7 +6057,7 @@ void doenter(lifeform_t *lf) {
if (isplayer(lf)) {
enterob = hasob(lf->cell->obpile, OT_PORTAL);
if (enterob) {
usestairs(lf, enterob, B_TRUE);
usestairs(lf, enterob, B_TRUE, B_FALSE);
return;
}
@ -6487,7 +6489,7 @@ void dostairs(int dir) {
object_t *o;
o = hasobwithflagval(player->cell->obpile, F_CLIMBABLE, dir, NA, NA, NULL);
if (o) {
usestairs(player, o, B_TRUE);
usestairs(player, o, B_TRUE, B_FALSE);
} else {
msg("There are no stairs going %s here!", getdirname(dir));
}
@ -8262,6 +8264,11 @@ void drawstatus(void) {
wprintw(statwin, " Swim");
unsetcol(statwin, C_BOLDBLUE);
}
if (isclimbing(player)) {
setcol(statwin, C_BOLDBLUE);
wprintw(statwin, " Climb");
unsetcol(statwin, C_BOLDBLUE);
}
if (isairborne(player)) {
if (lfhasflag(player, F_FLYING)) {
@ -9478,6 +9485,12 @@ void showlfstats(lifeform_t *lf, int showall) {
if (f && (f->known)) {
wrapprint(mainwin, &y, &x, "%s %s flying. ", you(lf), is(lf));
}
if (isswimming(lf)) {
wrapprint(mainwin, &y, &x, "%s %s swimming. ", you(lf), is(lf));
}
if (isclimbing(lf)) {
wrapprint(mainwin, &y, &x, "%s %s currently climbing. ", you(lf), is(lf));
}
f = lfhasknownflag(lf, F_PRONE);
if (f && (f->known)) {
wrapprint(mainwin, &y, &x, "%s %s lying on the ground. ", you(lf), is(lf));

241
lf.c
View File

@ -515,6 +515,10 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) {
reason = E_SWIMMING;
return B_FALSE;
}
if (isclimbing(lf)) {
reason = E_CLIMBING;
return B_FALSE;
}
reason = E_OK;
@ -586,6 +590,38 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) {
return castable;
}
int canclimb(lifeform_t *lf, enum ERROR *reason) {
cell_t *cc;
cc = getcellindir(lf->cell, lf->facing);
if (isclimbing(lf)) {
if (reason) *reason = E_CLIMBING;
return B_FALSE;
} else if (!cc) {
if (reason) *reason = E_BADCLIMBDIR;
return B_FALSE;
} else if (!cc->type->solid) {
if (hasobwithflagval(cc->obpile, F_PIT, D_DOWN, NA, NA, NULL)) {
// ok
} else {
if (reason) *reason = E_BADCLIMBDIR;
return B_FALSE;
}
} else if (cc->lf) {
if (reason) *reason = E_LFINWAY;
return B_FALSE;
} else if (isswimming(lf)) {
if (reason) *reason = E_SWIMMING;
return B_FALSE;
} else if (isimmobile(lf)) {
if (reason) *reason = E_CANTMOVE;
return B_FALSE;
} else if (isburdened(lf) || lfhasflag(lf, F_GRAVBOOSTED)) {
if (reason) *reason = E_TOOHEAVY;
return B_FALSE;
}
return B_TRUE;
}
int candrink(lifeform_t *lf, object_t *o) {
// undead won't drink
if (getraceclass(lf) == RC_UNDEAD) {
@ -869,6 +905,11 @@ int canpush(lifeform_t *lf, object_t *o, int dir) {
reason = E_FAILED;
return B_FALSE;
}
if (isswimming(lf) || isclimbing(lf)) {
reason = E_FAILED;
return B_FALSE;
}
return B_TRUE;
}
@ -887,6 +928,40 @@ int canquaff(lifeform_t *lf, object_t *o) {
return B_TRUE;
}
int canreach(lifeform_t *lf, lifeform_t *victim, int *reachpenalty) {
enum LFSIZE sizetoreach = SZ_ANY;
// harder to hit flying/levitating enemies, unless you are
// large (ie. tall) enough to reach them.
// if you are smaller than "sizetoreach", you get a penalty
if (!isairborne(lf)) {
switch (isairborne(victim)) {
case F_FLYING:
sizetoreach = SZ_HUGE;
break;
case F_LEVITATING:
sizetoreach = SZ_HUMAN;
break;
default: sizetoreach = SZ_ANY; break;
}
}
if (sizetoreach == SZ_ANY) {
// harder to hit something which is climbing above you
if (isclimbing(victim) && !isclimbing(lf)) {
if (getskill(lf, SK_CLIMBING) != PR_MASTER) {
sizetoreach = SZ_LARGE;
}
}
}
if (sizetoreach == SZ_ANY) {
if (reachpenalty) *reachpenalty = 0;
return B_TRUE;
}
if (reachpenalty) *reachpenalty = (sizetoreach - getlfsize(lf));
return B_FALSE;
}
int cansee(lifeform_t *viewer, lifeform_t *viewee) {
return cansee_real(viewer, viewee, B_TRUE);
}
@ -1264,6 +1339,9 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar
case E_PRONE:
msg("You can't cast spells while prone.");
break;
case E_CLIMBING:
msg("You can't cast spells while climbing.");
break;
case E_SWIMMING:
msg("You can't cast spells while swimming.");
break;
@ -3863,7 +3941,7 @@ int flee(lifeform_t *lf) {
// can we flee via stairs?
stairs = hasobwithflag(lf->cell->obpile, F_CLIMBABLE);
if (stairs) {
if (!usestairs(lf, stairs, B_TRUE)) {
if (!usestairs(lf, stairs, B_TRUE, B_TRUE)) {
if (db) dblog("%s - fleeing via %s", stairs->type->name);
return B_TRUE;
}
@ -6229,6 +6307,9 @@ int getmovespeed(lifeform_t *lf) {
}
char *getmoveverb(lifeform_t *lf) {
if (lfhasflag(lf, F_CLIMBING)) {
return "climb";
}
if (lfhasflag(lf, F_FLYING)) {
return "fly";
} else if (lfhasflag(lf, F_LEVITATING)) {
@ -6244,6 +6325,9 @@ char *getmoveverb(lifeform_t *lf) {
}
char *getmoveverbother(lifeform_t *lf) {
if (lfhasflag(lf, F_CLIMBING)) {
return "climbs";
}
if (lfhasflag(lf, F_FLYING)) {
return "flies";
} else if (lfhasflag(lf, F_LEVITATING)) {
@ -6276,14 +6360,19 @@ lifeform_t *getnearbypeaceful(lifeform_t *lf) {
return NULL;
}
char *getpitverb(lifeform_t *lf, int dir, int onpurpose) {
char *getpitverb(lifeform_t *lf, int dir, int onpurpose, int climb) {
if (lfhasflag(lf, F_FLYING)) {
if (isplayer(lf)) return "fly";
else return "flies";
} else if (onpurpose) { // TODO: check if we are using a rope
} else if (onpurpose) {
if (dir == D_DOWN) {
if (isplayer(lf)) return "jump";
else return "jumps";
if (climb) {
if (isplayer(lf)) return "climb";
else return "climbs";
} else {
if (isplayer(lf)) return "jump";
else return "jumps";
}
} else {
if (isplayer(lf)) return "climb";
else return "climbs";
@ -8787,11 +8876,20 @@ int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) {
if (newdest) *newdest = cell;
}
// solid cells stop lof
// solid cells stop lof UNLESS
// - this is our source cell
// - there is a lf there and this is our target cell
if (loftype & LOF_WALLSTOP) {
if (cell->type->solid) {
reason = E_NOLOF;
return B_FALSE;
if (i == 0) {
// ok
} else if (cell->lf && (i == (numpixels-1)) ) {
// ok
} else {
reason = E_NOLOF;
return B_FALSE;
}
}
// certain objects block lof
blockob = hasobwithflag(cell->obpile, F_BLOCKSLOF);
@ -8812,10 +8910,12 @@ int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) {
}
// if lifeforms don't stop lof, this is now a valid cell.
// if (!(loftype & LOF_LFSSTOP)) {
if (newdest) *newdest = cell;
if (newdest) *newdest = cell;
// }
// lifeforms block lof unless they're in our destination
// lifeforms block lof unless:
// - they're in the first cell (ie the one doing the throwing/firing/shooting/etc)
// - they're in our destination
if (cell->lf && (!isprone(cell->lf)) && (loftype & LOF_LFSSTOP)) {
// if not in first cell...
if (i != 0) {
@ -12278,12 +12378,78 @@ void relinklf(lifeform_t *src, map_t *dst) {
sortlf(dst, src);
}
int startclimbing(lifeform_t *lf, cell_t *where) {
int dir;
dir = getdirtowards(lf->cell, where, lf, B_FALSE, DT_ORTH);
movelf(lf, where);
setfacing(lf, diropposite(dir));
addflag(lf->flags, F_CLIMBING, B_TRUE, NA, NA, NULL);
int startclimbing(lifeform_t *lf) {
cell_t *where;
char lfname[BUFLEN];
object_t *pit = NULL;
char pitname[BUFLEN];
getlfname(lf, lfname);
where = getcellindir(lf->cell, lf->facing);
pit = hasobwithflagval(where->obpile, F_PIT, D_DOWN, NA, NA, NULL);
if (pit) {
getobname(pit, pitname, 1);
} else {
strcpy(pitname, "");
}
if (pit && isplayer(lf)) {
char ques[BUFLEN], ch;
sprintf(ques, "Climb down %s?",pitname);
ch = askchar(ques, "yn","n", B_TRUE, B_FALSE);
if (ch != 'y') {
msg("Cancelled.");
return B_TRUE;
}
}
taketime(lf, getmovespeed(lf));
// climbing down a pit?
if (pit) {
int mod;
// move there.
movelf(lf, where);
// make a skill check.
mod = (countadjwalls(where)+1)/2;
if (skillcheck(lf, SC_CLIMB, 20, mod)) {
// if you pass, safely move down the pit.
// usestairs() will announce this.
usestairs(lf, pit, B_TRUE, B_TRUE);
} else {
// you will fall...
if (isplayer(lf)) {
msg("You lose your footing!");
}
}
} else {
if (skillcheck(lf, SC_CLIMB, getcellclimbdifficulty(where), 0)) {
// announce
if (isplayer(lf)) {
msg("You climb onto %s %s.", needan(where->type->name) ? "an" : "a", where->type->name);
} else if (cansee(player, lf)) {
if (haslos(player, where)) {
msg("%s climbs onto %s %s.", lfname, needan(where->type->name) ? "an" : "a", where->type->name);
} else {
msg("%s climbs out of view.", lfname);
}
}
// change facing BEFORE moving, so that we don't reveal cells on the other
// side of the wall
setfacing(lf, diropposite(lf->facing));
movelf(lf, where);
addflag(lf->flags, F_CLIMBING, B_TRUE, NA, NA, NULL);
} else {
if (isplayer(lf)) {
msg("You try to climb onto %s, but fail.", where->type->name);
} else if (cansee(player, lf)) {
msg("%s tries to start climbing, but fails.", lfname);
}
return B_TRUE;
}
}
return B_FALSE;
}
@ -13680,9 +13846,10 @@ void startlfturn(lifeform_t *lf) {
// either use up stamina, or gain it
if (lfhasflag(lf, F_SPRINTING)) {
modstamina(lf, -1.5);
} else if (isswimming(lf)) {
} else if (isswimming(lf) && isplayer(lf)) {
int lossamt;
// lose stamina based on swimming skill
// players lose stamina based on swimming skill
// monsters don't.
switch (getskill(lf, SK_SWIMMING)) {
case PR_INEPT: lossamt = 3; break;
case PR_NOVICE: lossamt = 1; break;
@ -13693,6 +13860,19 @@ void startlfturn(lifeform_t *lf) {
case PR_MASTER: lossamt = 0; break;
}
if (lossamt) modstamina(lf, -lossamt);
} else if (isclimbing(lf) && isplayer(lf)) {
int lossamt;
// players lose stamina based on climbing skill
// monsters don't lose stamina to climb.
switch (getskill(lf, SK_CLIMBING)) {
case PR_INEPT: lossamt = 2; break;
case PR_NOVICE: lossamt = 1; break;
case PR_BEGINNER: lossamt = 1; break;
case PR_MASTER: lossamt = 0; break;
default:
lossamt = 0.5; break;
}
if (lossamt) modstamina(lf, -lossamt);
} else {
// if we didn't move last turn, regenerate stamina.
if (!killflagsofid(lf->flags, F_MOVED)) {
@ -14040,6 +14220,16 @@ void startlfturn(lifeform_t *lf) {
// IMPORTANT: any potentially damaging effects have to go after here...
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// if you run out of stamina while climbing, you fall
if (isclimbing(lf)) {
if ((getstamina(lf) <= 0) || isburdened(lf) || lfhasflag(lf, F_GRAVBOOSTED)) {
stopclimbing(lf, B_FALSE);
if (isdead(lf)) return;
}
}
f = hasflag(lf->flags, F_POISONED);
if (f) {
// chance of fighting it off - gets easier over time.
@ -14531,7 +14721,10 @@ int stopclimbing(lifeform_t *lf, int onpurpose) {
}
movelf(lf, c);
killflagsofid(lf->flags, F_CLIMBING);
if (!onpurpose) {
if (onpurpose) {
if (isplayer(lf)) msg("You drop down to the ground.");
taketime(lf, getmovespeed(lf));
} else {
fall(lf, NULL, B_TRUE);
}
return B_FALSE;
@ -14855,7 +15048,7 @@ void timeeffectslf(lifeform_t *lf) {
}
if (willfall) {
usestairs(lf, o, B_FALSE);
usestairs(lf, o, B_FALSE, B_FALSE);
donesomething = B_TRUE;
o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL);
}
@ -14893,7 +15086,6 @@ int tryclimb(lifeform_t *lf, cell_t *where, char *towhat) {
// train climbing
practice(lf, SK_CLIMBING, 1);
// continue...
} else {
// you fall.
if (isplayer(lf)) {
@ -15182,7 +15374,7 @@ int useability(lifeform_t *lf, enum OBTYPE aid, lifeform_t *who, cell_t *where)
}
int usestairs(lifeform_t *lf, object_t *o, int onpurpose) {
int usestairs(lifeform_t *lf, object_t *o, int onpurpose, int climb) {
flag_t *f;
map_t *curmap;
map_t *newmap;
@ -15261,6 +15453,9 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) {
if (tryclimb(lf, obcell, buf)) {
// failed
return B_TRUE;
} else {
// success
climb = B_TRUE;
}
}
}
@ -15280,7 +15475,7 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) {
f = hasflag(o->flags, F_PIT);
if (isplayer(lf) || cansee(player, lf)) {
msg("%s %s %s the %s...", lfname, getpitverb(lf, dir,onpurpose), getdirname(dir), noprefix(obname));
msg("%s %s %s the %s...", lfname, getpitverb(lf, dir,onpurpose, climb), getdirname(dir), noprefix(obname));
// move cursor to msgwindow while we create the new level...
if (isplayer(lf)) wrefresh(msgwin);
}

8
lf.h
View File

@ -31,6 +31,7 @@ int calcxprace(enum RACE rid);
void callguards(lifeform_t *caller, lifeform_t *victim);
int canattack(lifeform_t *lf);
int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost);
int canclimb(lifeform_t *lf, enum ERROR *reason);
int candrink(lifeform_t *lf, object_t *o);
int caneat(lifeform_t *lf, object_t *o);
int canhear(lifeform_t *lf, cell_t *c, int volume);
@ -40,6 +41,7 @@ int canpickup(lifeform_t *lf, object_t *o, int amt);
int canpolymorphto(enum RACE rid);
int canpush(lifeform_t *lf, object_t *o, int dir);
int canquaff(lifeform_t *lf, object_t *o);
int canreach(lifeform_t *lf, lifeform_t *victim, int *reachpenalty);
int cansee(lifeform_t *viewer, lifeform_t *viewee);
int cansee_real(lifeform_t *viewer, lifeform_t *viewee, int uselos);
int cansleep(lifeform_t *lf);
@ -179,7 +181,7 @@ int getmovespeed(lifeform_t *lf);
char *getmoveverb(lifeform_t *lf);
char *getmoveverbother(lifeform_t *lf);
lifeform_t *getnearbypeaceful(lifeform_t *lf);
char *getpitverb(lifeform_t *lf, int dir, int onpurpose);
char *getpitverb(lifeform_t *lf, int dir, int onpurpose, int climb);
char *getlfname(lifeform_t *lf, char *buf);
char *real_getlfname(lifeform_t *lf, char *buf, int usevis);
char *getlfnamea(lifeform_t *lf, char *buf);
@ -340,7 +342,7 @@ int recruit(lifeform_t *lf);
void refreshlevelabilities(lifeform_t *lf);
void relinklf(lifeform_t *src, map_t *dst);
int rest(lifeform_t *lf, int onpurpose);
int startclimbing(lifeform_t *lf, cell_t *where);
int startclimbing(lifeform_t *lf);
int startresting(lifeform_t *lf, int willtrain);
int rollattr(enum ATTRBRACKET bracket);
int rollstat(lifeform_t *lf, enum ATTRIB attr);
@ -389,7 +391,7 @@ void unsummon(lifeform_t *lf, int vanishobs);
int unweild(lifeform_t *lf, object_t *o);
int useability(lifeform_t *lf, enum OBTYPE aid, lifeform_t *who, cell_t *where);
int useringofmiracles(lifeform_t *lf, int charges);
int usestairs(lifeform_t *lf, object_t *o, int onpurpose);
int usestairs(lifeform_t *lf, object_t *o, int onpurpose, int climb);
int validateraces(void);
int wear(lifeform_t *lf, object_t *o);
int weild(lifeform_t *lf, object_t *o);

14
map.c
View File

@ -1288,6 +1288,20 @@ cell_t *getcellat(map_t *map, int x, int y) {
return map->cell[y*map->w + x];
}
int getcellclimbdifficulty(cell_t *c) {
int diff = 12;
switch (c->type->material->id) {
case MT_GLASS: diff = 26; break;
case MT_DRAGONWOOD: diff = 20; break;
case MT_METAL: diff = 20; break;
case MT_WAX: diff = 10; break;
case MT_WOOD: diff = 8; break;
case MT_PLANT: diff = 8; break;
default: diff = 12; break;
}
return diff;
}
int getcelldist(cell_t *src, cell_t *dst) {
double xd,yd;
// use pythag

1
map.h
View File

@ -21,6 +21,7 @@ int fix_reachability(map_t *m);
int fix_unreachable_cell(cell_t *badcell);
void floodfill(cell_t *startcell);
cell_t *getcellat(map_t *map, int x, int y);
int getcellclimbdifficulty(cell_t *c);
int getcelldist(cell_t *src, cell_t *dst);
int getcelldistorth(cell_t *src, cell_t *dst);
void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer);

197
move.c
View File

@ -291,19 +291,75 @@ int cellwalkable(lifeform_t *lf, cell_t *cell, enum ERROR *error) {
if (error) *error = E_OFFMAP;
return B_FALSE;
}
if (cell->type->solid) {
// mover is noncorporeal?
if (lf && lfhasflag(lf, F_NONCORPOREAL)) {
if (error) *error = E_WALLINWAY; // ok but still set reason
// ok
} else if (cell->lf && (cell->lf != lf)) { // someone (else) in the wall?
if (error) *error = E_LFINWAY; // ok but still set reason
//return B_FALSE;
// ok
} else {
if (error) *error = E_WALLINWAY;
if (lf && isclimbing(lf)) {
cell_t *rightcell,*leftcell,*frontcell;
int rightdir,leftdir,i;
// climbing rules: you can climb into a cell if:
// - it is directly to your side
// - the target cell (beside you) is a wall
// - the cell in FRONT (ie. dir you are facing) of the target cell
// is NOT a wall
// OR
// - it is straight forward (ie. off the wall)
rightdir = lf->facing;
leftdir = lf->facing;
for (i = 0; i < 2; i++) {
if (++rightdir > DC_NW) rightdir = DC_N;
if (--leftdir < DC_N) leftdir = DC_NW;
}
rightcell = getcellindir(lf->cell, rightdir);
leftcell = getcellindir(lf->cell, leftdir);
frontcell = getcellindir(lf->cell, lf->facing);
if ((cell != rightcell) && (cell != leftcell) && (cell != frontcell)) {
// not left/right/front
if (error) *error = E_BADCLIMBDIR;
return B_FALSE;
}
if (cell == frontcell) {
// in front
if (cell->type->solid) {
if (error) *error = E_BADCLIMBDIR;
return B_FALSE;
} else if (!cell->lf) {
// in front and not solid, and no lf there
if (error) *error = E_STOPCLIMBING;
return B_TRUE;
}
} else {
// to the side
if (cell->type->solid) {
cell_t *sidefrontcell;
sidefrontcell = getcellindir(cell, lf->facing);
if (!sidefrontcell || sidefrontcell->type->solid) {
// cell in front of target cell is solid
if (error) *error = E_BADCLIMBDIR;
return B_FALSE;
}
} else {
// to the side and not solid
if (error) *error = E_BADCLIMBDIR;
return B_FALSE;
}
}
// if okay, now drop through to below code which checks
// whether there are any lfs or objects in the way
} else {
// not climbing
if (cell->type->solid) {
// mover is noncorporeal?
if (lf && lfhasflag(lf, F_NONCORPOREAL)) {
if (error) *error = E_WALLINWAY; // ok but still set reason
// ok
} else if (cell->lf && (cell->lf != lf)) { // someone (else) in the wall?
if (error) *error = E_LFINWAY; // ok but still set reason
//return B_FALSE;
// ok
} else {
if (error) *error = E_WALLINWAY;
return B_FALSE;
}
}
}
// must check for lf before checking for impassable objects,
@ -321,6 +377,7 @@ int cellwalkable(lifeform_t *lf, cell_t *cell, enum ERROR *error) {
if (error) *error = E_LFINWAY;
return B_FALSE;
}
for (o = cell->obpile->first ; o ; o = o->next) {
if (isimpassableob(o, lf)) {
if (lf) {
@ -1395,7 +1452,9 @@ int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) {
// this is the equivilant of losing the same amount of stamina which we
// would regenerate, only it avoids constantly redrawing the status
// bar every single move.
if (isplayer(lf)) addflag(lf->flags, F_MOVED, B_TRUE, NA, NA, NULL);
if (isplayer(lf)) {
addflag(lf->flags, F_MOVED, B_TRUE, NA, NA, NULL);
}
if (!onpurpose || !isplayer(lf)) {
dontclearmsg = B_TRUE;
@ -1608,19 +1667,17 @@ int opendoor(lifeform_t *lf, object_t *o) {
return B_TRUE;
}
if (!canopendoors(lf)) {
if (isplayer(lf)) {
msg("You have no hands with which to open the door!");
} else {
// ai will try to break down doors
if (gettargetlf(lf)) {
attackcell(lf, doorcell, B_TRUE);
return B_FALSE;
}
if (lf) {
if (isclimbing(lf)) {
if (isplayer(lf)) msg("You can't open doors while climbing!");
return B_TRUE;
} else if (isswimming(lf)) {
if (isplayer(lf)) msg("You can't open doors while swimming!");
return B_TRUE;
}
return B_TRUE;
}
f = hasflag(o->flags, F_OPEN);
if (f) {
if (lf && isplayer(lf)) {
@ -1629,6 +1686,19 @@ int opendoor(lifeform_t *lf, object_t *o) {
return B_TRUE;
} else {
if (lf) {
if (!canopendoors(lf)) {
if (isplayer(lf)) {
msg("You have no hands with which to open the door!");
} else {
// ai will try to break down doors
if (gettargetlf(lf)) {
attackcell(lf, doorcell, B_TRUE);
return B_FALSE;
}
}
return B_TRUE;
}
if (isplayer(lf)) {
int dir;
cell_t *pastdoorcell;
@ -1985,6 +2055,28 @@ int initiatemove(lifeform_t *lf, cell_t *cell, int *didmsg) {
if (cell && cell->lf && !canswapwith(lf, cell->lf)) attacking = B_TRUE;
// climbing along a wall?
if (isplayer(lf) && isclimbing(lf)) {
if (cell != getcellindir(lf->cell, lf->facing)) { // not dropping off the wall
if (!getstamina(lf)) {
// this shouldn't be able to happen, since if you run out
// of stamina while on a wall you'll fall off during startlfturn().
msg("You are too tired to climb!");
reason = E_OK;
return B_TRUE;
} else {
// must pass a skill check to keep climbing!
if (!skillcheck(lf, SC_CLIMB, getcellclimbdifficulty(cell), 0)) {
msg("You lose your footing!");
stopclimbing(lf, B_FALSE);
reason = E_OK;
return B_TRUE;
}
modstamina(lf, -1);
}
}
}
// too tired? (if we're attacking, leave the 'too tired' message to
// the attack code)
if (!attacking && !getstamina(lf)) {
@ -2391,9 +2483,9 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) {
// warn before moving onto dangerous cells
if (onpurpose && isplayer(lf) && !lfhasflag(lf, F_SNEAK)) {
char ques[BUFLEN];
char ch;
if (cell && celldangerous(lf, cell, B_TRUE, &errcode)) {
char ques[BUFLEN];
char ch;
if ((errcode == E_AVOIDOB) && rdata) {
char obname[BUFLEN];
object_t *avoidob;
@ -2408,6 +2500,13 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) {
return B_TRUE;
}
}
if (isclimbing(lf) && (cell != getcellindir(lf->cell, lf->facing)) && (getstamina(lf) - 1 <= 0)) {
snprintf(ques, BUFLEN, "Climbing further will exhaust you - continue?");
ch = askchar(ques, "yn","n", B_TRUE, B_FALSE);
if (ch != 'y') {
return B_TRUE;
}
}
}
moveok = B_FALSE;
@ -2456,29 +2555,33 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) {
}
}
// now move to new cell
moveto(lf, cell, rndmove ? B_FALSE : onpurpose, dontclearmsg);
if (errcode == E_STOPCLIMBING) {
stopclimbing(lf, B_TRUE);
} else {
// now move to new cell
moveto(lf, cell, rndmove ? B_FALSE : onpurpose, dontclearmsg);
// take some time
if (onpurpose) {
// strafing sideways/backwards takes longer
if (strafe && !lfhasflag(lf, F_AWARENESS)) {
switch (reldir) {
case RD_SIDEWAYS: howlong = pctof(125, howlong); break;
case RD_BACKWARDS: howlong = pctof(150, howlong); break;
case RD_FORWARDS:
if (hasactivespell(lf, OT_S_TAILWIND)) {
// faster
howlong = pctof(75, howlong);
}
break;
default:
break;
// take some time
if (onpurpose) {
// strafing sideways/backwards takes longer
if (strafe && !lfhasflag(lf, F_AWARENESS)) {
switch (reldir) {
case RD_SIDEWAYS: howlong = pctof(125, howlong); break;
case RD_BACKWARDS: howlong = pctof(150, howlong); break;
case RD_FORWARDS:
if (hasactivespell(lf, OT_S_TAILWIND)) {
// faster
howlong = pctof(75, howlong);
}
break;
default:
break;
}
}
limit(&howlong, SP_GODLIKE, NA);
taketime(lf, howlong);
if (!rndmove && !strafe) setfacing(lf, dir); // face the way we moved
}
limit(&howlong, SP_GODLIKE, NA);
taketime(lf, howlong);
if (!rndmove && !strafe) setfacing(lf, dir); // face the way we moved
}
// attached lfs or lfs you have grabbed will move the same direction if they can
@ -2534,12 +2637,17 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) {
// train
if (isswimming(lf)) {
practice(lf, SK_SWIMMING, 1);
} else if (isclimbing(lf)) {
practice(lf, SK_CLIMBING, 1);
}
} else { // ie !moveok
object_t *inway = NULL;
int door, dooropen;
reason = errcode;
switch (errcode) {
case E_BADCLIMBDIR:
if (isplayer(lf)) msg("You can't climb in that direction.");
break;
case E_OFFMAP:
if (lf->cell->map->region->rtype->id == RG_WORLDMAP) {
// cope with nonorthogonal!
@ -2586,6 +2694,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) {
}
break;
case E_DOORINWAY:
// can't open doors while climbing
inway = (object_t *)rdata;
door = isdoor(inway, &dooropen);
if (door && !dooropen) {

53
spell.c
View File

@ -555,48 +555,23 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef
taketime(user, getactspeed(user)*2);
} else if (abilid == OT_A_CLIMB) {
if (lfhasflag(user, F_CLIMBING)) {
// stop climbing
stopclimbing(user, B_TRUE);
taketime(user, getmovespeed(user));
return B_FALSE;
}
if (isprone(user)) {
if (isplayer(user)) msg("You will need to stand up first!");
return B_TRUE;
}
// ask for direction
if (!targcell) {
int dirch,dir;
dirch = askchar("Climb wall in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if ((dirch == '-') || !dirch) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE;
}
dir = chartodir(dirch);
if (dir == D_NONE) {
targcell = user->cell;
} else {
targcell = getcellindir(user->cell, dir);
enum ERROR why;
if (!canclimb(user, &why)) {
if (isplayer(user)){
switch (why) {
case E_CLIMBING: msg("You are already climbing!"); break;
case E_CANTMOVE: msg("You can't move!"); break;
case E_BADCLIMBDIR: msg("There is no wall to climb in front of you!"); break;
case E_LFINWAY: msg("Something is in the way!"); break;
case E_SWIMMING: msg("You can't climb while swimming!"); break;
case E_TOOHEAVY: msg("Your load is too heavy to climb with!"); break;
default: msg("For some reason, you can't climb."); break;
}
}
}
if (!targcell) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE;
}
}
if (!targcell->type->solid) {
if (isplayer(user)) msg("There is no wall there!");
return B_TRUE;
}
if (targcell->lf) {
if (isplayer(user)) msg("There is no wall there!");
return B_TRUE;
}
startclimbing(user, targcell);
taketime(user, getmovespeed(user));
startclimbing(user);
} else if (abilid == OT_A_COOK) {
object_t *water,*o;
race_t *r = NULL;