From fa9071ab69336650daf398a8b6206fc25e5473b4 Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Sat, 21 Jan 2012 00:35:04 +0000 Subject: [PATCH] - [+] bug: mammoan warrior with spear not agile enough to use starting weapon! - [+] new option when chatting: - [+] "care to trade knowledge?" - [+] works if: - [+] you have a skill which they don't know (or a spell which they don't know) - [+] otherwise "there is nothing you can teach me." - [+] AND ONE OF - [+] works if they know a spell which you don't know (but are able to learn) - [+] works if they have the same skills as you, but better, - [+] works if they have the a skill which you don't, AND they are >= adept - [+] otherwise "i fear there is nothing useful i can teach you" - [+] TEST - [+] can only do this once per person ("i have nothign more i can teach you") - [+] if high speech, you can pick what to learn. otherwise it's random. - [+] bug: human rogue asking to learn 'summon weapon' but not skilled in summoning!!! - [+] TEST AGAIN! - [+] speech skill determines what you can ask people about! - [+] nov:any dangers? - [+] beg:any useful items around? - [+] ad:trade knowledge? - [+] sk:join me? - [+] (but make skillchecks harder otherwise by the time you get the rigth speech skill level, you'll always pass!) --- data.c | 12 +-- defs.h | 12 +++ io.c | 24 ++++-- lf.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- lf.h | 5 +- spell.c | 4 +- 6 files changed, 286 insertions(+), 21 deletions(-) diff --git a/data.c b/data.c index a7c9d03..24dd639 100644 --- a/data.c +++ b/data.c @@ -11549,12 +11549,12 @@ void initskills(void) { addskilldesc(SK_SHIELDS, PR_EXPERT, "^gShield accuracy penalties are reduced by 5.^n", B_FALSE); addskilldesc(SK_SHIELDS, PR_MASTER, "^gShield accuracy penalties are reduced by 6.^n", B_FALSE); addskill(SK_SPEECH, "Negotiation", "Your skill at haggling prices, or swaying others through speech.", 50); - addskilldesc(SK_SPEECH, PR_NOVICE, "^gShop item prices are reduced by 5%.^n", B_FALSE); - addskilldesc(SK_SPEECH, PR_BEGINNER, "^gShop item prices are reduced by 10%.^n", B_FALSE); - addskilldesc(SK_SPEECH, PR_ADEPT, "^gShop item prices are reduced by 15%.^n", B_FALSE); - addskilldesc(SK_SPEECH, PR_SKILLED, "^gShop item prices are reduced by 20%.^n", B_FALSE); - addskilldesc(SK_SPEECH, PR_EXPERT, "^gShop item prices are reduced by 25%.^n", B_FALSE); - addskilldesc(SK_SPEECH, PR_MASTER, "^gShop item prices are reduced by 30%.^n", B_FALSE); + addskilldesc(SK_SPEECH, PR_INEPT, "- Each skill level reduces shop prices by 5%.", B_TRUE); + addskilldesc(SK_SPEECH, PR_NOVICE, "^gYou can now question people about items on the current level.", B_TRUE); + addskilldesc(SK_SPEECH, PR_BEGINNER, "^gYou can now question people about nearby traps or monsters.", B_TRUE); + addskilldesc(SK_SPEECH, PR_ADEPT, "^gYou can now trade knowledge and spells with other people.", B_TRUE); + addskilldesc(SK_SPEECH, PR_SKILLED, "^gYou can now persuade people to join to as followers.", B_TRUE); + addskilldesc(SK_SPEECH, PR_EXPERT, "^gYou can now choose which skills to learn from people.", B_TRUE); addskill(SK_PERCEPTION, "Perception", "Your ability to notice hidden details, from simple footprints to sinister traps.", 50); addskilldesc(SK_PERCEPTION, PR_INEPT, "- At higher levels this skill will also let you obscure your own tracks.", B_TRUE); addskilldesc(SK_PERCEPTION, PR_NOVICE, "^gYou can now see footprints.^n", B_TRUE); diff --git a/defs.h b/defs.h index ee48b39..6e0bb17 100644 --- a/defs.h +++ b/defs.h @@ -281,6 +281,11 @@ enum RELATIVEDIR { RD_SIDEWAYS, }; +enum TRADEINFOTYPE { + TI_SKILL, + TI_SPELL, +}; + /////////////////////////////////////// // STRINGS /////////////////////////////////////// @@ -482,6 +487,11 @@ enum SAYPHRASE { SP_RECRUIT_DECLINE_WONTPAY, SP_SORRY, SP_THANKS, + SP_TRADEINFO_ACCEPT, + SP_TRADEINFO_CANCEL, + SP_TRADEINFO_DECLINE_ALREADYDONE, + SP_TRADEINFO_DECLINE_OTHERBAD, + SP_TRADEINFO_DECLINE_PLAYERBAD, }; enum SPEECHVOL { @@ -2437,6 +2447,8 @@ enum FLAG { F_DONEDARKMSG, // tells the game not to say 'it is very dark here' F_DONELISTEN, // supress further 'you hear xx' messages this turn. // lifeform flags / lf flags / monster flags + F_DONEKNOWLEDGETRADE, // you've already traded knowledge with this + // person. F_FOLLOWTIME, // v0 = how long will ai chase you for? defaults to // DEF_AIFOLLOWTIME. F_BEHAVIOUR, // textual field describing special behaviour for this diff --git a/io.c b/io.c index 203d4f7..1bdd71c 100644 --- a/io.c +++ b/io.c @@ -3725,17 +3725,25 @@ void docomms(lifeform_t *lf) { } else if (ishirable(lf) ) { if (lfhasflag(lf, F_ISPRISONER)) { addchoice(&prompt, 'j', "Join me, and I will help you escape.", NULL, NULL, NULL); - } else { + } else if (getskill(player, SK_SPEECH) >= PR_SKILLED) { addchoice(&prompt, 'j', "Join me on my quest!", NULL, NULL, NULL); } } - // TODO: allow you to ask this of allies, but only on the level they started on. - if (!areallies(player, lf) && !isgod(lf)) { - if (ispeaceful(lf) && cantalk(lf)) { + if (!isgod(lf) && ispeaceful(lf) && cantalk(lf)) { + enum SKILLLEVEL slev; + slev = getskill(player, SK_SPEECH); + if (slev >= PR_NOVICE) { addchoice(&prompt, 'i', "What can you tell me about this area?", NULL, NULL, NULL); + } + if (slev >= PR_BEGINNER) { addchoice(&prompt, 'x', "Any dangers nearby that I should look out for?", NULL, NULL, NULL); } + if (!areallies(player, lf)) { + if (slev >= PR_ADEPT) { + addchoice(&prompt, 'k', "Care to trade knowledge?", NULL, NULL, NULL); + } + } } if (areenemies(player, lf)) { @@ -3973,7 +3981,7 @@ void docomms(lifeform_t *lf) { break; case 'i': msg("You say \"What can you tell me about this area?\" to %s.", lfname); - if (askforinfo(lf)) { + if (askforinfo(lf, 2)) { genareaknowledge(lf->flags, 0); docomms_areainfo(lfname, lf->flags, lf); } @@ -3987,6 +3995,10 @@ void docomms(lifeform_t *lf) { } recruit(lf); break; + case 'k': // trade Knowledge + msg("You say \"Care to trade knowledge?\" to %s.", lfname); + tradeknowledge(lf); + break; case 'm': // mercy msg("You say \"Have mercy!\" to %s.", lfname); if (islowhp(player) && @@ -4104,7 +4116,7 @@ void docomms(lifeform_t *lf) { break; case 'x': msg("You say \"Any dangers nearby that I should look out for?\" to %s.", lfname); - if (askforinfo(lf)) { + if (askforinfo(lf, 4)) { genareaknowledge(lf->flags, 0); docomms_areadangers(lfname, lf->flags, lf); } diff --git a/lf.c b/lf.c index caf46da..043171a 100644 --- a/lf.c +++ b/lf.c @@ -8264,6 +8264,93 @@ char *getskilllevelname(enum SKILLLEVEL sl) { } +// get a list of of skills which teacher could teach student. +// returns # of potential skills found. +int getteachableskills(lifeform_t *teacher, lifeform_t *student, int *info, enum TRADEINFOTYPE *tradetype, int *ninfo ) { + flag_t *retflag[MAXCANDIDATES], *f; + int nretflags, i; + + *ninfo = 0; + // 'teacher' can teach a skill to 'student' if: + // - teacher and student both know the skill, and teacher's skill level is higher. + // OR + // - only teacher has the skill, and teacher's skill level is >= adept. + getflags(teacher->flags, retflag, &nretflags, F_HASSKILL, F_NONE); + for (i = 0; i < nretflags; i++) { + enum SKILLLEVEL studentlev,teacherlev; + enum SKILL sid; + int teachable = B_FALSE; + f = retflag[i]; + sid = f->val[0]; + teacherlev = f->val[1]; + studentlev = getskill(student, sid); + if (studentlev && (teacherlev > studentlev) && !ismaxedskill(student, sid)) { + teachable = B_TRUE; + } else if (!studentlev && (teacherlev >= PR_ADEPT)) { + teachable = B_TRUE; + } + if (teachable) { + info[*ninfo] = sid; + tradetype[*ninfo] = TI_SKILL; + (*ninfo)++; + } + } + + // 'teacher' can teach a magic spell (oc_spell) to 'student' if: + // - teacher can will/cast the spell. + // AND + // - student can't already will/cast the spell + // AND + // - student is skilled in at least one of the spell's schools. + // AND + // - student is a high enough level to cast the spell. + // (use existing code from spellbooks!) + getflags(teacher->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE); + for (i = 0; i < nretflags; i++) { + objecttype_t *spell; + enum OBTYPE spellid; + enum SPELLSCHOOL knownschool; + int teachable = B_FALSE; + f = retflag[i]; + spellid = f->val[0]; + spell = findot(spellid); + if (spell->obclass->id != OC_SPELL) continue; + + // student skilled in one of the spell's schools? + knownschool = getspellschoolknown(student, spellid); + if (getskill(student, getschoolskill(knownschool))) { + int mpneeded; + if (!cancast(student, spellid, &mpneeded) && (getspellpower(student, spellid) > 0)) { + if (getmaxmp(student) >= mpneeded) { + teachable = B_TRUE; + } + } + } + + if (teachable) { + info[*ninfo] = spellid; + tradetype[*ninfo] = TI_SPELL; + (*ninfo)++; + } + } + return *ninfo; +} + +char *gettradeinfoname(int what, enum TRADEINFOTYPE tradetype, char *buf) { + if (tradetype == TI_SKILL) { + skill_t *sk; + sk = findskill(what); + strcpy(buf, sk->name); + } else { // ie. spell + objecttype_t *ot; + ot = findot(what); + sprintf(buf, "the spell '%s'", ot->name); + } + return buf; +} + + + // get throw speed (ie. damage multiplier) // based on strength. // @@ -11801,7 +11888,7 @@ int armourfits(lifeform_t *lf, object_t *o, enum ERROR *reason) { } // returns TRUE if 'lf' agrees to share knowledge with the player -int askforinfo(lifeform_t *lf) { +int askforinfo(lifeform_t *lf, int diffmod) { if (lfhasflag(lf, F_NOINFO)) { // refusing to give info sayphrase(lf, SP_INFO_REFUSE_AGAIN, SV_TALK, NA, NULL); @@ -11826,7 +11913,7 @@ int askforinfo(lifeform_t *lf) { int result; int difficulty; - difficulty = 20 + ((gethitdice(player) - gethitdice(lf))*2); + difficulty = 20 + diffmod + ((gethitdice(player) - gethitdice(lf))*2); if (real_skillcheck(player, SC_SPEECH, difficulty, 0, &result)) { askingprice = 0; // passed - free! @@ -11962,7 +12049,8 @@ void autoskill(lifeform_t *lf) { } for (o = lf->pack->first ; o ; o = o->next) { - if (isweapon(o) && canweild(lf, o)) { + //if (isweapon(o) && canweild(lf, o)) { + if (isweapon(o)) { sk = getobskill(o); if (sk && !getskill(lf, sk->id)) { giveskilllev(lf, sk->id, slev); @@ -11970,7 +12058,7 @@ void autoskill(lifeform_t *lf) { // monsters:increase stats to match attribn requirements for starting // weapon. - if (!isplayer(lf)) { + //if (!isplayer(lf)) { getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; @@ -11978,7 +12066,7 @@ void autoskill(lifeform_t *lf) { setattr(lf, f->val[0], f->val[1]); } } - } + //} nweps++; } if (isfirearm(o) && canweild(lf, o)) { @@ -14177,7 +14265,9 @@ int recruit(lifeform_t *lf) { int result; int difficulty; int minmult,maxmult; - difficulty = 25 + ((gethitdice(player) - gethitdice(lf))*2); + // since you have to be at least speech=4(skilled) to ask someone to + // join, add +8 to difficulty (pr_skilled * 2) + difficulty = 25 + 8 + ((gethitdice(player) - gethitdice(lf))*2); if (real_skillcheck(player, SC_SPEECH, difficulty, 0, &result)) { minmult = 10; maxmult = 20; @@ -14613,7 +14703,8 @@ int sayphrase(lifeform_t *lf, enum SAYPHRASE what, int volume, int val0, char *t int i,rv = B_FALSE; char buf[BUFLEN]; char buf2[BUFLEN]; - char *p; + char buf3[BUFLEN]; + char *p,*p2; race_t *r; switch (what) { case SP_ALLY_ATTACK: @@ -14863,6 +14954,61 @@ int sayphrase(lifeform_t *lf, enum SAYPHRASE what, int volume, int val0, char *t } rv = say(lf, buf, volume); break; + case SP_TRADEINFO_ACCEPT: + p = text; + p2 = buf2; + while (*p != '^') { + *p2 = *p; + p++; p2++; + } + *p2 = '\0'; + p++; + // next one... + p2 = buf3; + while (*p != '\0') { + *p2 = *p; + p++; p2++; + } + *p2 = '\0'; + switch (rnd(1,2)) { + case 1: snprintf(buf, BUFLEN, "I'll train you in %s...", buf3); break; + case 2: snprintf(buf, BUFLEN, "I can teach you %s...", buf3); break; + } + strcat(buf, "if you teach me "); + strcat(buf, buf2); + + rv = say(lf, buf, volume); + break; + case SP_TRADEINFO_CANCEL: + switch (rnd(1,2)) { + case 1: snprintf(buf, BUFLEN, "Maybe another time, then."); break; + case 2: snprintf(buf, BUFLEN, "Fair enough."); break; + } + rv = say(lf, buf, volume); + break; + case SP_TRADEINFO_DECLINE_ALREADYDONE: + switch (rnd(1,3)) { + case 1: snprintf(buf, BUFLEN, "Thank you, but I have enough knowledge for now."); break; + case 2: snprintf(buf, BUFLEN, "I'm done with teaching for the moment."); break; + case 3: snprintf(buf, BUFLEN, "I'm sick of learning at the moment."); break; + } + rv = say(lf, buf, volume); + break; + case SP_TRADEINFO_DECLINE_OTHERBAD: + switch (rnd(1,2)) { + case 1: snprintf(buf, BUFLEN, "Thank you, but there is nothing I could teach you."); break; + case 2: snprintf(buf, BUFLEN, "I fear there is nothing you could learn from me."); break; + } + rv = say(lf, buf, volume); + break; + case SP_TRADEINFO_DECLINE_PLAYERBAD: + switch (rnd(1,3)) { + case 1: snprintf(buf, BUFLEN, "You teach ME something? I think not."); break; + case 2: snprintf(buf, BUFLEN, "Sorry, I don't believe there is anything you could teach me."); break; + case 3: snprintf(buf, BUFLEN, "No thank you, there's nothing I need from you."); break; + } + rv = say(lf, buf, volume); + break; default: break; } @@ -17413,6 +17559,96 @@ void timeeffectslf(lifeform_t *lf) { notime = B_FALSE; } + +// returns TRUE if lf declines to teach. +int tradeknowledge(lifeform_t *lf) { + int poss[MAXCANDIDATES], fromplayer, toplayer; + enum TRADEINFOTYPE tradetype[MAXCANDIDATES],fromplayertype, toplayertype; + int nposs = 0,sel; + char buf[BUFLEN],tradetext[BUFLEN],lfname[BUFLEN],ch; + char fromplayertext[BUFLEN],toplayertext[BUFLEN]; + + getlfname(lf, lfname); + + // already traded? + if (lfhasflag(lf, F_DONEKNOWLEDGETRADE)) { + sayphrase(lf, SP_TRADEINFO_DECLINE_ALREADYDONE, SV_TALK, NA, NULL); + return B_TRUE; + } + + // does the player have a skill which lf needs? + getteachableskills(player, lf, poss, tradetype, &nposs); + if (nposs) { + sel = rnd(0,nposs-1); + fromplayer = poss[sel]; + fromplayertype = tradetype[sel]; + } else { + sayphrase(lf, SP_TRADEINFO_DECLINE_PLAYERBAD, SV_TALK, NA, NULL); + return B_TRUE; + } + // does lf have a skill which the player needs? + getteachableskills(lf, player, poss, tradetype, &nposs); + if (nposs) { + if (getskill(player, SK_SPEECH) >= PR_EXPERT) { + int i; + // you can pick which one to learn. + snprintf(buf, BUFLEN, "What would you like to learn from %s?",lfname); + initprompt(&prompt, buf); + ch = 'a'; + for (i = 0; i < nposs; i++) { + gettradeinfoname(poss[i], tradetype[i], buf); + addchoice(&prompt, ch++, buf, NULL, NULL, NULL); + } + addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); + ch = getchoice(&prompt); + if (ch == '-') { + sayphrase(lf, SP_TRADEINFO_CANCEL, SV_TALK, NA, NULL); + return B_TRUE; + } else { + sel = ch - 'a'; + } + } else { + sel = rnd(0,nposs-1); + } + toplayer = poss[sel]; + toplayertype = tradetype[sel]; + } else { + sayphrase(lf, SP_TRADEINFO_DECLINE_OTHERBAD, SV_TALK, NA, NULL); + return B_TRUE; + } + + // at this point we can do a valid trade. make the offer. + // encode skill/spell names into a single string of the form: + // fromplayer^toplayer + gettradeinfoname(fromplayer, fromplayertype, fromplayertext); + gettradeinfoname(toplayer, toplayertype, toplayertext); + sprintf(tradetext, "%s^%s",fromplayertext,toplayertext); + + sayphrase(lf, SP_TRADEINFO_ACCEPT, SV_TALK, NA, tradetext); + more(); + // confirm. + sprintf(buf, "Learn %s from %s", toplayertext, lfname); + ch = askchar(buf, "yn","y", B_TRUE, B_FALSE); + if (ch == 'y') { + // lf learns skill + if (fromplayertype == TI_SKILL) { + giveskill(lf, fromplayer); + } else { // ie. spell + sprintf(buf, "pw:%d;", getspellpower(lf, fromplayer)); + addflag(lf->flags, F_CANCAST, fromplayer, NA, NA, buf); + } + // player learns skill + if (toplayertype == TI_SKILL) { + giveskill(player, toplayer); + } else { // ie. spell + addflag(player->flags, F_CANCAST, toplayer, NA, NA, NULL); + } + // can't trade knowledge anymore + addflag(lf->flags, F_DONEKNOWLEDGETRADE, B_TRUE, NA, NA, NULL); + } + return B_FALSE; +} + // return B_TRUE on failure. int tryclimb(lifeform_t *lf, cell_t *where, char *towhat) { // if you have a rope or there's an adjacent wall, you can try diff --git a/lf.h b/lf.h index c797fdc..a49cc8c 100644 --- a/lf.h +++ b/lf.h @@ -18,7 +18,7 @@ void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o); int areallies(lifeform_t *lf1, lifeform_t *lf2); int areenemies(lifeform_t *lf1, lifeform_t *lf2); int armourfits(lifeform_t *lf, object_t *o, enum ERROR *reason); -int askforinfo(lifeform_t *lf); +int askforinfo(lifeform_t *lf, int diffmod); int askforpayment(lifeform_t *shk, lifeform_t *lf); char *assignnpcname(lifeform_t *lf); void autoskill(lifeform_t *lf); @@ -241,6 +241,8 @@ float getstatmod(lifeform_t *lf, enum ATTRIB att); char *getskilldesc(enum SKILL id ); char *getskillname(enum SKILL id ); char *getskilllevelname(enum SKILLLEVEL sl); +int getteachableskills(lifeform_t *teacher, lifeform_t *student, int *info, enum TRADEINFOTYPE *tradetype, int *ninfo ); +char *gettradeinfoname(int what, enum TRADEINFOTYPE tradetype, char *buf); int getthrowspeed(lifeform_t *lf); int getturnspeed(lifeform_t *lf); void getwantdistance(lifeform_t *lf, lifeform_t *victim, int *min, int *max, int attacking); @@ -418,6 +420,7 @@ int takeoff(lifeform_t *lf, object_t *o); void taketime(lifeform_t *lf, long howlong); int throwat(lifeform_t *thrower, object_t *o, cell_t *where); void timeeffectslf(lifeform_t *lf); +int tradeknowledge(lifeform_t *lf); int tryclimb(lifeform_t *lf, cell_t *where, char *towhat); int touch(lifeform_t *lf, object_t *o); void turntoface(lifeform_t *lf, cell_t *dstcell); diff --git a/spell.c b/spell.c index 2405e2f..3d31be3 100644 --- a/spell.c +++ b/spell.c @@ -10887,6 +10887,9 @@ char *getspellname(enum OBTYPE spellid, lifeform_t *lf, char *buf) { return buf; } +// at what power level COULD lf cast 'spellid' ? +// note: doesn't matter if the lf doesn't actually have f_cancast / f_canwill, +// in this case the function will return the POTENTIAL power. int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { int power = 0; int spelllev; @@ -10896,7 +10899,6 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { int boost; flag_t *f; - if (db) { objecttype_t *ot; ot = findot(spellid);