From 06cf19e7c94ad77141ec059946754a512da4edbf Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Wed, 2 Nov 2011 22:34:15 +0000 Subject: [PATCH] - [+] 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 --- ai.c | 39 ++++++--- attack.c | 41 ++++++---- data.c | 19 ++++- defs.h | 3 + flag.c | 1 + io.c | 29 +++++-- lf.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++++------ lf.h | 8 +- map.c | 14 ++++ map.h | 1 + move.c | 197 +++++++++++++++++++++++++++++++++++---------- spell.c | 53 ++++-------- 12 files changed, 498 insertions(+), 148 deletions(-) diff --git a/ai.c b/ai.c index 3d59889..2bee2b1 100644 --- a/ai.c +++ b/ai.c @@ -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; diff --git a/attack.c b/attack.c index 5859e2b..6854958 100644 --- a/attack.c +++ b/attack.c @@ -3,6 +3,7 @@ #include #include #include +#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); diff --git a/data.c b/data.c index 466742d..0788f09 100644 --- a/data.c +++ b/data.c @@ -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); diff --git a/defs.h b/defs.h index f08cf7f..9830963 100644 --- a/defs.h +++ b/defs.h @@ -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, }; diff --git a/flag.c b/flag.c index 4fdd81a..e5c9188 100644 --- a/flag.c +++ b/flag.c @@ -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: diff --git a/io.c b/io.c index 7ed86ad..e4fc7d5 100644 --- a/io.c +++ b/io.c @@ -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)); diff --git a/lf.c b/lf.c index a3ef641..0ea5ea4 100644 --- a/lf.c +++ b/lf.c @@ -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); } diff --git a/lf.h b/lf.h index 9343960..3b62826 100644 --- a/lf.h +++ b/lf.h @@ -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); diff --git a/map.c b/map.c index 7c2a37b..f6c36dc 100644 --- a/map.c +++ b/map.c @@ -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 diff --git a/map.h b/map.h index 87c8559..713c231 100644 --- a/map.h +++ b/map.h @@ -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); diff --git a/move.c b/move.c index 6fb95c3..18fafd3 100644 --- a/move.c +++ b/move.c @@ -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) { diff --git a/spell.c b/spell.c index 6425f9e..9cb16ce 100644 --- a/spell.c +++ b/spell.c @@ -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;