pfyon
06-26-2011, 11:15 AM
I finally got back to the work I started a couple of years ago with NPCs talking in languages, and here it is. I always thought languages were underused in classic EQ since outside the pvp servers (I think?), it pretty much had no use. Every NPC spoke in common and players just upped their language skills for fun, for the most part
This code does a few things. One part allows players to gain skillups in languages from say, group, guild, and shout (normally it`s just group chat I think). This lets you gain skillups both from players speaking in public channels in a language other than common, but also by listening to NPCs talking in say. I made say skillup at 10% the rate of other channels to help discourage spamming hail to NPCs to get skillups. It isn`t very effective. A better solution would be to have a max number of language skillups per time period (eg 5 a minute).
The code also changes quest::say() so it checks the base race of the NPC and chooses a language appropriate for the race.
Both the functions are set with rules in the DB and default to false in the code.
Diffs:
client.cpp
1108,1121c1108
< ^M
< /*^M
< * 0 -- Guild^M
< * 2 -- Group^M
< * 3 -- Shout^M
< * 4 -- Auction^M
< * 5 -- OOC^M
< * 6 -- Broadcast^M
< * 7 -- Tell^M
< * 8 -- say^M
< * 11 -- GMSay^M
< * 15 -- Raid^M
< */^M
< ^M
---
> ^M
1125,1149c1112,1114
< ^M
< // guild, group, shout, say in unmastered language, check for skillup^M
< ^M
< if(RuleB(Character, LearnLanguagesFromChannels))^M
< {^M
< if((chan_num == 8) && (ListenerSkill < 100))^M
< {^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() ))^M
< {^M
< //We want it to be harder to skillup using say than group/guild/shout^M
< CheckLanguageSkillIncrease(language, lang_skill, true);^M
< }^M
< } else if ((chan_num == 0 || chan_num == 2 || chan_num == 3) && (ListenerSkill < 100))^M
< {^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
< {^M
< CheckLanguageSkillIncrease(language, lang_skill, false);^M
< }^M
< }^M
< } else^M
< {^M
< if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
< CheckLanguageSkillIncrease(language, lang_skill);^M
< }^M
---
> if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up^M
> if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
> CheckLanguageSkillIncrease(language, lang_skill);^M
2272c2237
< void Client::CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill, bool hard) {^M
---
> void Client::CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill) {^M
2282,2287d2246
< //Channel is a 'hard' one to master, since we don't want people spamming stuff to npcs for skillups^M
< if(hard)^M
< {^M
< Chance *= 0.1;^M
< }^M
< ^M
5929,5935c5888
< }^M
< ^M
< int Client::GetLanguageSkill(int skill_id)^M
< {^M
< if(skill_id > 25 || skill_id < 0) return 0;^M
< return m_pp.languages[skill_id];^M
< }^M
---
> }
\ No newline at end of file
client.h
584d583
< int GetLanguageSkill(int skill_id);^M
594c593
< void CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill, bool hard = false);^M
---
> void CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill);^M
ruletypes.h
92d91
< RULE_BOOL ( Character, LearnLanguagesFromChannels, false) // If true, allows players to gain skillups for languages heard in say, shout, guild, and group. Say is increased at 10% the chance of guild/group to discourage spamming of NPCs for skillups^M
321d319
< RULE_BOOL ( NPC, EnableNPCRacialLanguages, false) //Causes calls to quest::say to use a language that makes sense for the race of the npc talking (eg dragon for dragons, elvish for elves, etc)^M
mob.cpp
19d18
< #include "../common/languages.h"^M
5127,5570d5078
< int8 Mob::GetRacialLanguage()^M
< {^M
< switch(this->GetBaseRace())^M
< {^M
< //Common tongue races^M
< // Fall through to default for these for slight performance improvement^M
< /*case 1: //Human^M
< case 7: //"Half Elf"^M
< case 13: //Aviak^M
< case 14: //werewolf^M
< case 16: //centaur^M
< case 17: //golem^M
< case 20: //doppleganger^M
< case 22: //beetle^M
< case 24: //fish^M
< case 28: //fungusman^M
< case 30: //"Gasbag"^M
< case 31: //"Gelatinous Cube"^M
< case 32: //"Ghost"^M
< case 33: //"Ghoul"^M
< case 34: //"Giant Bat"^M
< case 35: //"Giant Eel"^M
< case 36: //"Giant Rat"^M
< case 37: //"Giant Snake"^M
< case 38: //"Giant Spider"^M
< case 41: //"Gorilla"^M
< case 42: //"Wolf"^M
< case 43: //"Bear"^M
< case 44: //"Freeport Guards"^M
< case 45: //"Demi Lich"^M
< case 47: //"Griffin"^M
< case 50: //"Lion"^M
< case 52: //"Mimic"^M
< case 53: //"Minotaur"^M
< case 55: //"Human Beggar"^M
< case 58: //"Solusek Ro"^M
< case 60: //"Skeleton"^M
< case 61: //"Shark"^M
< case 65: //"Vampire"^M
< case 63: //"Tiger"^M
< case 67: //"Highpass Citizen"^M
< case 68: //"Tentacle"^M
< case 69: //"Will 'O Wisp"^M
< case 70: //"Zombie"^M
< case 71: //"Qeynos Citizen"^M
< case 72: //"Ship"^M
< case 73: //"Launch"^M
< case 74: //"Piranha"^M
< case 75: //"Elemental"^M
< case 76: //"Puma"^M
< case 77: //"Neriak Citizen"^M
< case 78: //"Erudite Citizen"^M
< case 79: //"Bixie"^M
< case 80: //"Reanimated Hand"^M
< case 81: //"Rivervale Citizen"^M
< case 82: //"Scarecrow"^M
< case 83: //"Skunk"^M
< case 84: //"Snake Elemental"^M
< case 86: //"Sphinx"^M
< case 87: //"Armadillo"^M
< case 91: //"Alligator"^M
< case 96: //"Cockatrice"^M
< case 97: //"Daisy Man"^M
< case 99: //"Denizen"^M
< case 100: //"Dervish"^M
< case 101: //"Efreeti"^M
< case 103: //"Kedge"^M
< case 104: //"Leech"^M
< case 105: //"Swordfish"^M
< case 106: //"Felguard"^M
< case 107: //"Mammoth"^M
< case 108: //"Eye of Zomm"^M
< case 109: //"Wasp"^M
< case 110: //"Mermaid"^M
< case 111: //"Harpie"^M
< case 114: //"Ghost Ship"^M
< case 115: //"Clam"^M
< case 116: //"Sea Horse"^M
< case 119: //"Sabertooth Cat"^M
< case 120: //"Wolf Elemental"^M
< case 121: //"Gorgon"^M
< case 123: //"Innoruuk"^M
< case 124: //"Unicorn"^M
< case 125: //"Pegasus"^M
< case 126: //"Djinn"^M
< case 127: //"Invisible Man"^M
< case 129: //"Scorpion"^M
< case 133: //"Lycanthrope"^M
< case 134: //"Mosquito"^M
< case 135: //"Rhino"^M
< case 138: //"Yeti"^M
< case 140: //"Forest Giant"^M
< case 141: //"Boat"^M
< case 142: //"UNKNOWN RACE"^M
< case 143: //"UNKNOWN RACE"^M
< case 144: //"Burynai"^M
< case 145: //"Goo"^M
< case 148: //"Kunark Fish"^M
< case 149: //"Iksar Scorpion"^M
< case 150: //"Erollisi"^M
< case 156: //"Ratman"^M
< case 159: //"Devourer"^M
< case 162: //"Man Eating Plant"^M
< case 163: //"Raptor"^M
< case 167: //"Succulent"^M
< case 168: //"Flying Monkey"^M
< case 169: //"Brontotherium"^M
< case 170: //"Snow Dervish"^M
< case 171: //"Dire Wolf"^M
< case 172: //"Manticore"^M
< case 173: //"Totem"^M
< case 175: //"Enchanted Armor"^M
< case 176: //"Snow Bunny"^M
< case 177: //"Walrus"^M
< case 178: //"Rock-gem Men"^M
< case 179: //"UNKNOWN RACE"^M
< case 180: //"UNKNOWN RACE"^M
< case 185: //"Hag"^M
< case 186: //"Hippogriff"^M
< case 187: //"Siren"^M
< case 190: //"Ottermen"^M
< case 191: //"Walrus Man"^M
< case 193: //"Abhorent"^M
< case 194: //"Sea Turtle"^M
< case 197: //"Ronnie Test"^M
< case 199: //"ShikNar"^M
< case 200: //"Rockhopper"^M
< case 201: //"Underbulk"^M
< case 202: //"Grimling"^M
< case 203: //"Vacuum Worm"^M
< case 204: //"Evan Test"^M
< case 206: //"Owlbear"^M
< case 207: //"Rhino Beetle"^M
< case 208: //"Vampyre"^M
< case 209: //"Earth Elemental"^M
< case 210: //"Air Elemental"^M
< case 211: //"Water Elemental"^M
< case 212: //"Fire Elemental"^M
< case 213: //"Wetfang Minnow"^M
< case 215: //"Tegi"^M
< case 216: //"Horse"^M
< case 218: //"Fungal Fiend"^M
< case 219: //"Vampire Volatalis"^M
< case 220: //"StoneGrabber"^M
< case 221: //"Scarlet Cheetah"^M
< case 222: //"Zelniak"^M
< case 223: //"Lightcrawler"^M
< case 225: //"Sunflower"^M
< case 226: //"Sun Revenant"^M
< case 227: //"Shrieker"^M
< case 228: //"Galorian"^M
< case 229: //"Netherbian"^M
< case 230: //"Akheva"^M
< case 231: //"Spire Spirit"^M
< case 232: //"Sonic Wolf"^M
< case 233: //"Ground Shaker"^M
< case 235: //"Mutant Humanoid"^M
< case 236: //"Seru"^M
< case 237: //"Recuso"^M
< case 240: //"Teleport Man"^M
< case 241: //"Lujein"^M
< case 242: //"Naiad"^M
< case 243: //"Nymph"^M
< case 244: //"Ent"^M
< case 245: //"Fly Man"^M
< case 246: //"Tarew Marr"^M
< case 247: //"Sol Ro"^M
< case 250: //"Spectral Banshee"^M
< case 251: //"Guard of Justice"^M
< case 252: //"UNKNOWN RACE"^M
< case 253: //"Disease Boss"^M
< case 254: //"Sol Ro Guard"^M
< case 258: //"Vegerog"^M
< case 259: //"Crocodile"^M
< case 260: //"Bat"^M
< case 261: //"Slarghilug"^M
< case 262: //"Tranquilion"^M
< case 263: //"Tin Soldier"^M
< case 271: //"Water Mephit"^M
< case 272: //"Stormrider"^M
< case 278: //"Karana"^M
< case 284: //"Fennin Ro"^M
< case 291: //"Air Mephit"^M
< case 292: //"Earth Mephit"^M
< case 293: //"Fire Mephit"^M
< case 295: //"Zebuxoruk"^M
< case 296: //"Mithaniel Marr"^M
< case 298: //"The Rathe"^M
< case 299: //"Xegony"^M
< case 301: //"Test Object"^M
< case 302: //"Crab"^M
< case 303: //"Phoenix"^M
< case 304: //"PoP Dragon"^M
< case 305: //"PoP Bear"^M
< case 306: //"Storm Taarid"^M
< case 307: //"Storm Satuur"^M
< case 308: //"Storm Kuraaln"^M
< case 309: //"Storm Volaas"^M
< case 310: //"Storm Mana"^M
< case 311: //"Storm Fire"^M
< case 312: //"Storm Celestial"^M
< case 313: //"War Wraith"^M
< case 314: //"Wrulon"^M
< case 315: //"Kraken"^M
< case 316: //"Poison Frog"^M
< case 317: //"Queztocoatal"^M
< case 318: //"Valorian"^M
< case 319: //"War Boar"^M
< case 320: //"PoP Efreeti"^M
< case 321: //"War Boar Unarmored"^M
< case 322: //"Black Knight"^M
< case 323: //"Animated Armor"^M
< case 326: //"Arachnid"^M
< case 327: //"Crystal Spider"^M
< case 328: //"Zeb Cage"^M
< case 329: //"BoT Portal"^M
< case 334: //"Spectre Pirate Boss"^M
< case 335: //"Pirate Boss"^M
< case 336: //"Pirate Dark Shaman"^M
< case 337: //"Pirate Officer"^M
< case 341: //"Human Pirate"^M
< case 343: //"Poison Dart Frog"^M
< case 345: //"Luggald Land"^M
< case 346: //"Luggald Armored"^M
< case 347: //"Luggald Robed"^M
< case 351: //"Chosen Warrior"^M
< case 352: //"Chosen Wizard"^M
< case 353: //"Veksar"^M
< case 354: //"Greater Veksar"^M
< case 355: //"Veksar Boss"^M
< case 356: //"Chokadai"^M
< case 357: //"Undead Chokadai"^M
< case 358: //"Undead Veksar"^M
< case 359: //"Vampire Lesser"^M
< case 360: //"Vampire Elite"^M
< case 362: //"Bone Golem"^M
< case 363: //"Synarcana"^M
< case 365: //"Vampire Master"^M
< case 367: //"Skeleton New"^M
< case 368: //"Mummy New"^M
< case 370: //"Insect"^M
< case 372: //"Dervish New"^M
< case 374: //"Golem New"^M
< case 376: //"Box"^M
< case 377: //"Barrel"^M
< case 378: //"Chest"^M
< case 379: //"Vase"^M
< case 380: //"Table"^M
< case 381: //"Weapons Rack"^M
< case 382: //"Coffin"^M
< case 383: //"Bones"^M
< case 384: //"Jokester"^M
< case 385: //"Talosian Nihil"^M
< case 386: //"Talosian Exile"^M
< case 387: //"Talosian Golem"^M
< case 388: //"Talosian Wolf"^M
< case 389: //"Talosian Amphibian"^M
< case 390: //"Talosian Mountain Beast"^M
< case 391: //"Talosian Trilobyte"^M
< case 392: //"Invader War Hound"^M
< case 393: //"Invader Elite Centaur"^M
< case 394: //"Invader Lamia"^M
< case 395: //"Invader Cyclops"^M
< case 396: //"Kyv"^M
< case 397: //"Invader Soldier"^M
< case 398: //"Invader Brute"^M
< case 399: //"Invader Force Commander"^M
< case 400: //"Invader Lieutenant Boss"^M
< case 401: //"Invader War Beast"^M
< case 402: //"Invader Soldier Elite"^M
< case 403: //"UNKNOWN RACE"^M
< case 404: //"Discord Ship"^M
< return LANG_COMMON_TONGUE;*/^M
< //Barbarian^M
< case 2: //"Barbarian"^M
< case 90: //"Halas Citizen"^M
< case 151: //"Tribunal"^M
< case 256: //"New Tribunal"^M
< return LANG_BARBARIAN;^M
< //Erudite^M
< case 3: //"Erudite"^M
< case 118: //"Erudite Ghost"^M
< case 342: //"Erudite Pirate"^M
< return LANG_ERUDIAN;^M
< //Elvish^M
< case 4: //"Wood Elf"^M
< case 5: //"High Elf"^M
< case 64: //"Treant"^M
< case 98: //"Elf Vampire"^M
< case 112: //"Fayguard"^M
< case 181: //"Yak Man"^M
< case 182: //"Faun"^M
< case 364: //"Sand Elf"^M
< return LANG_ELVISH;^M
< //Dark Elvish^M
< case 6: //"Dark Elf"^M
< case 339: //"Dark Elf Pirate"^M
< return LANG_DARK_ELVISH;^M
< //Dwarvish^M
< case 8: //"Dwarf"^M
< case 94: //"Kaladim Citizen"^M
< case 117: //"Ghost Dwarf"^M
< case 183: //"Coldain"^M
< return LANG_DWARVISH;^M
< //Troll^M
< case 9: //"Troll"^M
< case 92: //"Grobb Citizen"^M
< case 95: //"Cazic Thule"^M
< case 331: //"Troll Buccaneer"^M
< case 332: //"Troll Freebooter"^M
< case 333: //"Troll Sea Rover"^M
< case 344: //"Troll Zombie"^M
< return LANG_TROLL;^M
< //Ogre^M
< case 10: //"Ogre"^M
< case 18: //"Giant / Cyclops"^M
< case 66: //"Rallos Zek"^M
< case 93: //"Oggok Citizen"^M
< case 188: //"Frost Giant"^M
< case 189: //"Storm Giant"^M
< case 288: //"New Rallos Zek"^M
< case 289: //"Vallon Zek"^M
< case 290: //"Tallon Zek"^M
< case 325: //"Rallos Zek Minion"^M
< case 340: //"Ogre Pirate"^M
< return LANG_OGRE;^M
< //Halfling^M
< case 11: //"Halfling"^M
< case 153: //"Bristlebane"^M
< return LANG_HALFLING;^M
< //Gnomish^M
< case 12: //"Gnome"^M
< case 88: //"Clockwork Gnome"^M
< case 192: //"Clockwork Dragon"^M
< case 248: //"Clockwork Golem"^M
< case 249: //"Clockwork Brain"^M
< case 273: //"Junk Beast"^M
< case 274: //"Broken Clockwork"^M
< case 275: //"Giant Clockwork"^M
< case 276: //"Clockwork Beetle"^M
< case 338: //"Gnome Pirate"^M
< return LANG_GNOMISH;^M
< case 15: //"Brownie"^M
< case 25: //"Fairy"^M
< case 56: //"Pixie"^M
< case 113: //"Drixie"^M
< case 154: //"Fay Drake"^M
< return LANG_FAERIE;^M
< case 19: //"Trakenon"^M
< case 198: //"Prismatic Dragon"^M
< return LANG_ELDER_DRAGON;^M
< case 21: //"Evil Eye"^M
< case 29: //"Gargoyle"^M
< case 46: //"Imp"^M
< case 57: //"Dracnid"^M
< case 85: //"Spectre"^M
< case 152: //"Bertoxxulous"^M
< case 174: //"Cold Spectre"^M
< case 214: //"Thought Horror"^M
< case 224: //"Shade"^M
< case 255: //"New Bertox"^M
< case 257: //"Terris Thule"^M
< case 264: //"Nightmare Wraith"^M
< case 265: //"Malarian"^M
< case 266: //"Knight of Pestilence"^M
< case 267: //"Lepertoloth"^M
< case 268: //"Bubonian Boss"^M
< case 269: //"Bubonian Underling"^M
< case 270: //"Pusling"^M
< case 279: //"Blood Raven"^M
< case 280: //"Nightmare Gargoyle"^M
< case 281: //"Mouths of Insanity"^M
< case 282: //"Skeletal Horse"^M
< case 283: //"Saryn"^M
< case 285: //"Tormentor"^M
< case 286: //"Necro Priest"^M
< case 287: //"Nightmare"^M
< case 294: //"Nightmare Mephit"^M
< case 297: //"Undead Knight"^M
< case 300: //"Fiend"^M
< case 324: //"Undead Footman"^M
< case 373: //"Shadow Creatue"^M
< case 375: //"Evil Eye New"^M
< return LANG_DARK_SPEECH;^M
< case 26: //"Old Froglok"^M
< case 27: //"Old Froglok Ghoul"^M
< case 102: //"Old Froglok Tadpole"^M
< case 277: //"Nightmare Goblin"^M
< case 330: //"Froglok"^M
< case 348: //"Froglok Mount"^M
< case 349: //"Froglok Skeleton"^M
< case 350: //"Undead Froglok"^M
< case 371: //"Froglok Ghost"^M
< return LANG_FROGLOK;^M
< case 39: //"Gnoll"^M
< return LANG_GNOLL;^M
< case 40: //"Goblin"^M
< case 48: //"Kobold"^M
< case 59: //"Bloodgills"^M
< case 137: //"Kunark Goblin"^M
< case 369: //"Goblin New"^M
< return LANG_GOBLIN;^M
< case 49: //"Lava Dragon"^M
< case 89: //"Drake"^M
< case 122: //"Dragon Skeleton"^M
< case 157: //"Wyvern"^M
< case 158: //"Wurm"^M
< case 165: //"Water Dragon"^M
< case 184: //"Velious Dragons"^M
< case 195: //"Black and White Dragons"^M
< case 196: //"Ghost Dragon"^M
< return LANG_DRAGON;^M
< case 51: //"Lizard Man"^M
< case 128: //"Iksar"^M
< case 131: //"Sarnak"^M
< case 132: //"Draglock"^M
< case 136: //"Xalgoz"^M
< case 139: //"Iksar Citizen"^M
< case 146: //"Spectral Sarnak"^M
< case 147: //"Spectral Iksar"^M
< case 155: //"Sarnak Skeleton"^M
< case 160: //"Iksar Golem"^M
< case 161: //"Iksar Skeleton"^M
< case 164: //"Sarnak Golem"^M
< case 166: //"Iksar Hand"^M
< case 217: //"Shissar"^M
< return LANG_LIZARDMAN;^M
< case 54: //"Orc"^M
< case 361: //"Rujakian Orc"^M
< case 366: //"Rujakian Orc Elite"^M
< return LANG_ORCISH;^M
< case 62: //"Tunare"^M
< return LANG_ELDER_ELVISH;^M
< case 130: //"Vah Shir"^M
< case 205: //"Kahli Shah"^M
< case 234: //"Vah Shir Skeleton"^M
< case 238: //"Vah Shir King"^M
< case 239: //"Vah Shir Guard"^M
< return LANG_VAH_SHIR;^M
< default:^M
< return LANG_COMMON_TONGUE;^M
< }^M
< }^M
< ^M
5579c5087
< }^M
---
> }
\ No newline at end of file
mob.h
609,610d604
< int8 GetRacialLanguage();^M
< ^M
questmgr.cpp
212,241c212,217
< void QuestManager::say(const char *str)^M
< {^M
< //EnableNPCRacialLanguages seems to be mutually exclusive with the quest journal^M
< if(RuleB(NPC, EnableNPCRacialLanguages) && initiator)^M
< {^M
< int8 owner_language = owner->GetRacialLanguage(); ^M
< if(initiator->IsClient())^M
< {^M
< //If they aren't clients, they can talk all they want^M
< if(initiator->GetLanguageSkill(owner_language) > 50)^M
< {^M
< quest_manager.say(str, owner_language);^M
< } else^M
< {^M
< quest_manager.say("I'm sorry, I don't understand a word you're saying.", owner_language);^M
< }^M
< } else^M
< {^M
< quest_manager.say(str, owner_language);^M
< }^M
< } else^M
< {^M
< if(RuleB(NPC, EnableNPCQuestJournal) && initiator)^M
< {^M
< owner->QuestJournalledSay(initiator, str);^M
< } else^M
< {^M
< owner->Say(str);^M
< }^M
< }^M
---
> void QuestManager::say(const char *str) {^M
> ^M
> if(RuleB(NPC, EnableNPCQuestJournal) && initiator)^M
> owner->QuestJournalledSay(initiator, str);^M
> else^M
> owner->Say(str);^M
2609c2585
< }^M
---
> }
\ No newline at end of file
Optional SQL:
INSERT INTO rule_values (ruleset_id, rule_name, rule_value, notes) values (1, 'NPC:EnableNPCRacialLanguages', 0, 'Enables quest::say() to use a race-specific language when called without the second argument');
INSERT INTO rule_values (ruleset_id, rule_name, rule_value, notes) values (1, 'Character:LearnLanguagesFromChannels', 0, 'Enables players to learn languages from say, guild, group, and shout');
This code does a few things. One part allows players to gain skillups in languages from say, group, guild, and shout (normally it`s just group chat I think). This lets you gain skillups both from players speaking in public channels in a language other than common, but also by listening to NPCs talking in say. I made say skillup at 10% the rate of other channels to help discourage spamming hail to NPCs to get skillups. It isn`t very effective. A better solution would be to have a max number of language skillups per time period (eg 5 a minute).
The code also changes quest::say() so it checks the base race of the NPC and chooses a language appropriate for the race.
Both the functions are set with rules in the DB and default to false in the code.
Diffs:
client.cpp
1108,1121c1108
< ^M
< /*^M
< * 0 -- Guild^M
< * 2 -- Group^M
< * 3 -- Shout^M
< * 4 -- Auction^M
< * 5 -- OOC^M
< * 6 -- Broadcast^M
< * 7 -- Tell^M
< * 8 -- say^M
< * 11 -- GMSay^M
< * 15 -- Raid^M
< */^M
< ^M
---
> ^M
1125,1149c1112,1114
< ^M
< // guild, group, shout, say in unmastered language, check for skillup^M
< ^M
< if(RuleB(Character, LearnLanguagesFromChannels))^M
< {^M
< if((chan_num == 8) && (ListenerSkill < 100))^M
< {^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() ))^M
< {^M
< //We want it to be harder to skillup using say than group/guild/shout^M
< CheckLanguageSkillIncrease(language, lang_skill, true);^M
< }^M
< } else if ((chan_num == 0 || chan_num == 2 || chan_num == 3) && (ListenerSkill < 100))^M
< {^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
< {^M
< CheckLanguageSkillIncrease(language, lang_skill, false);^M
< }^M
< }^M
< } else^M
< {^M
< if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
< CheckLanguageSkillIncrease(language, lang_skill);^M
< }^M
---
> if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up^M
> if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
> CheckLanguageSkillIncrease(language, lang_skill);^M
2272c2237
< void Client::CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill, bool hard) {^M
---
> void Client::CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill) {^M
2282,2287d2246
< //Channel is a 'hard' one to master, since we don't want people spamming stuff to npcs for skillups^M
< if(hard)^M
< {^M
< Chance *= 0.1;^M
< }^M
< ^M
5929,5935c5888
< }^M
< ^M
< int Client::GetLanguageSkill(int skill_id)^M
< {^M
< if(skill_id > 25 || skill_id < 0) return 0;^M
< return m_pp.languages[skill_id];^M
< }^M
---
> }
\ No newline at end of file
client.h
584d583
< int GetLanguageSkill(int skill_id);^M
594c593
< void CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill, bool hard = false);^M
---
> void CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill);^M
ruletypes.h
92d91
< RULE_BOOL ( Character, LearnLanguagesFromChannels, false) // If true, allows players to gain skillups for languages heard in say, shout, guild, and group. Say is increased at 10% the chance of guild/group to discourage spamming of NPCs for skillups^M
321d319
< RULE_BOOL ( NPC, EnableNPCRacialLanguages, false) //Causes calls to quest::say to use a language that makes sense for the race of the npc talking (eg dragon for dragons, elvish for elves, etc)^M
mob.cpp
19d18
< #include "../common/languages.h"^M
5127,5570d5078
< int8 Mob::GetRacialLanguage()^M
< {^M
< switch(this->GetBaseRace())^M
< {^M
< //Common tongue races^M
< // Fall through to default for these for slight performance improvement^M
< /*case 1: //Human^M
< case 7: //"Half Elf"^M
< case 13: //Aviak^M
< case 14: //werewolf^M
< case 16: //centaur^M
< case 17: //golem^M
< case 20: //doppleganger^M
< case 22: //beetle^M
< case 24: //fish^M
< case 28: //fungusman^M
< case 30: //"Gasbag"^M
< case 31: //"Gelatinous Cube"^M
< case 32: //"Ghost"^M
< case 33: //"Ghoul"^M
< case 34: //"Giant Bat"^M
< case 35: //"Giant Eel"^M
< case 36: //"Giant Rat"^M
< case 37: //"Giant Snake"^M
< case 38: //"Giant Spider"^M
< case 41: //"Gorilla"^M
< case 42: //"Wolf"^M
< case 43: //"Bear"^M
< case 44: //"Freeport Guards"^M
< case 45: //"Demi Lich"^M
< case 47: //"Griffin"^M
< case 50: //"Lion"^M
< case 52: //"Mimic"^M
< case 53: //"Minotaur"^M
< case 55: //"Human Beggar"^M
< case 58: //"Solusek Ro"^M
< case 60: //"Skeleton"^M
< case 61: //"Shark"^M
< case 65: //"Vampire"^M
< case 63: //"Tiger"^M
< case 67: //"Highpass Citizen"^M
< case 68: //"Tentacle"^M
< case 69: //"Will 'O Wisp"^M
< case 70: //"Zombie"^M
< case 71: //"Qeynos Citizen"^M
< case 72: //"Ship"^M
< case 73: //"Launch"^M
< case 74: //"Piranha"^M
< case 75: //"Elemental"^M
< case 76: //"Puma"^M
< case 77: //"Neriak Citizen"^M
< case 78: //"Erudite Citizen"^M
< case 79: //"Bixie"^M
< case 80: //"Reanimated Hand"^M
< case 81: //"Rivervale Citizen"^M
< case 82: //"Scarecrow"^M
< case 83: //"Skunk"^M
< case 84: //"Snake Elemental"^M
< case 86: //"Sphinx"^M
< case 87: //"Armadillo"^M
< case 91: //"Alligator"^M
< case 96: //"Cockatrice"^M
< case 97: //"Daisy Man"^M
< case 99: //"Denizen"^M
< case 100: //"Dervish"^M
< case 101: //"Efreeti"^M
< case 103: //"Kedge"^M
< case 104: //"Leech"^M
< case 105: //"Swordfish"^M
< case 106: //"Felguard"^M
< case 107: //"Mammoth"^M
< case 108: //"Eye of Zomm"^M
< case 109: //"Wasp"^M
< case 110: //"Mermaid"^M
< case 111: //"Harpie"^M
< case 114: //"Ghost Ship"^M
< case 115: //"Clam"^M
< case 116: //"Sea Horse"^M
< case 119: //"Sabertooth Cat"^M
< case 120: //"Wolf Elemental"^M
< case 121: //"Gorgon"^M
< case 123: //"Innoruuk"^M
< case 124: //"Unicorn"^M
< case 125: //"Pegasus"^M
< case 126: //"Djinn"^M
< case 127: //"Invisible Man"^M
< case 129: //"Scorpion"^M
< case 133: //"Lycanthrope"^M
< case 134: //"Mosquito"^M
< case 135: //"Rhino"^M
< case 138: //"Yeti"^M
< case 140: //"Forest Giant"^M
< case 141: //"Boat"^M
< case 142: //"UNKNOWN RACE"^M
< case 143: //"UNKNOWN RACE"^M
< case 144: //"Burynai"^M
< case 145: //"Goo"^M
< case 148: //"Kunark Fish"^M
< case 149: //"Iksar Scorpion"^M
< case 150: //"Erollisi"^M
< case 156: //"Ratman"^M
< case 159: //"Devourer"^M
< case 162: //"Man Eating Plant"^M
< case 163: //"Raptor"^M
< case 167: //"Succulent"^M
< case 168: //"Flying Monkey"^M
< case 169: //"Brontotherium"^M
< case 170: //"Snow Dervish"^M
< case 171: //"Dire Wolf"^M
< case 172: //"Manticore"^M
< case 173: //"Totem"^M
< case 175: //"Enchanted Armor"^M
< case 176: //"Snow Bunny"^M
< case 177: //"Walrus"^M
< case 178: //"Rock-gem Men"^M
< case 179: //"UNKNOWN RACE"^M
< case 180: //"UNKNOWN RACE"^M
< case 185: //"Hag"^M
< case 186: //"Hippogriff"^M
< case 187: //"Siren"^M
< case 190: //"Ottermen"^M
< case 191: //"Walrus Man"^M
< case 193: //"Abhorent"^M
< case 194: //"Sea Turtle"^M
< case 197: //"Ronnie Test"^M
< case 199: //"ShikNar"^M
< case 200: //"Rockhopper"^M
< case 201: //"Underbulk"^M
< case 202: //"Grimling"^M
< case 203: //"Vacuum Worm"^M
< case 204: //"Evan Test"^M
< case 206: //"Owlbear"^M
< case 207: //"Rhino Beetle"^M
< case 208: //"Vampyre"^M
< case 209: //"Earth Elemental"^M
< case 210: //"Air Elemental"^M
< case 211: //"Water Elemental"^M
< case 212: //"Fire Elemental"^M
< case 213: //"Wetfang Minnow"^M
< case 215: //"Tegi"^M
< case 216: //"Horse"^M
< case 218: //"Fungal Fiend"^M
< case 219: //"Vampire Volatalis"^M
< case 220: //"StoneGrabber"^M
< case 221: //"Scarlet Cheetah"^M
< case 222: //"Zelniak"^M
< case 223: //"Lightcrawler"^M
< case 225: //"Sunflower"^M
< case 226: //"Sun Revenant"^M
< case 227: //"Shrieker"^M
< case 228: //"Galorian"^M
< case 229: //"Netherbian"^M
< case 230: //"Akheva"^M
< case 231: //"Spire Spirit"^M
< case 232: //"Sonic Wolf"^M
< case 233: //"Ground Shaker"^M
< case 235: //"Mutant Humanoid"^M
< case 236: //"Seru"^M
< case 237: //"Recuso"^M
< case 240: //"Teleport Man"^M
< case 241: //"Lujein"^M
< case 242: //"Naiad"^M
< case 243: //"Nymph"^M
< case 244: //"Ent"^M
< case 245: //"Fly Man"^M
< case 246: //"Tarew Marr"^M
< case 247: //"Sol Ro"^M
< case 250: //"Spectral Banshee"^M
< case 251: //"Guard of Justice"^M
< case 252: //"UNKNOWN RACE"^M
< case 253: //"Disease Boss"^M
< case 254: //"Sol Ro Guard"^M
< case 258: //"Vegerog"^M
< case 259: //"Crocodile"^M
< case 260: //"Bat"^M
< case 261: //"Slarghilug"^M
< case 262: //"Tranquilion"^M
< case 263: //"Tin Soldier"^M
< case 271: //"Water Mephit"^M
< case 272: //"Stormrider"^M
< case 278: //"Karana"^M
< case 284: //"Fennin Ro"^M
< case 291: //"Air Mephit"^M
< case 292: //"Earth Mephit"^M
< case 293: //"Fire Mephit"^M
< case 295: //"Zebuxoruk"^M
< case 296: //"Mithaniel Marr"^M
< case 298: //"The Rathe"^M
< case 299: //"Xegony"^M
< case 301: //"Test Object"^M
< case 302: //"Crab"^M
< case 303: //"Phoenix"^M
< case 304: //"PoP Dragon"^M
< case 305: //"PoP Bear"^M
< case 306: //"Storm Taarid"^M
< case 307: //"Storm Satuur"^M
< case 308: //"Storm Kuraaln"^M
< case 309: //"Storm Volaas"^M
< case 310: //"Storm Mana"^M
< case 311: //"Storm Fire"^M
< case 312: //"Storm Celestial"^M
< case 313: //"War Wraith"^M
< case 314: //"Wrulon"^M
< case 315: //"Kraken"^M
< case 316: //"Poison Frog"^M
< case 317: //"Queztocoatal"^M
< case 318: //"Valorian"^M
< case 319: //"War Boar"^M
< case 320: //"PoP Efreeti"^M
< case 321: //"War Boar Unarmored"^M
< case 322: //"Black Knight"^M
< case 323: //"Animated Armor"^M
< case 326: //"Arachnid"^M
< case 327: //"Crystal Spider"^M
< case 328: //"Zeb Cage"^M
< case 329: //"BoT Portal"^M
< case 334: //"Spectre Pirate Boss"^M
< case 335: //"Pirate Boss"^M
< case 336: //"Pirate Dark Shaman"^M
< case 337: //"Pirate Officer"^M
< case 341: //"Human Pirate"^M
< case 343: //"Poison Dart Frog"^M
< case 345: //"Luggald Land"^M
< case 346: //"Luggald Armored"^M
< case 347: //"Luggald Robed"^M
< case 351: //"Chosen Warrior"^M
< case 352: //"Chosen Wizard"^M
< case 353: //"Veksar"^M
< case 354: //"Greater Veksar"^M
< case 355: //"Veksar Boss"^M
< case 356: //"Chokadai"^M
< case 357: //"Undead Chokadai"^M
< case 358: //"Undead Veksar"^M
< case 359: //"Vampire Lesser"^M
< case 360: //"Vampire Elite"^M
< case 362: //"Bone Golem"^M
< case 363: //"Synarcana"^M
< case 365: //"Vampire Master"^M
< case 367: //"Skeleton New"^M
< case 368: //"Mummy New"^M
< case 370: //"Insect"^M
< case 372: //"Dervish New"^M
< case 374: //"Golem New"^M
< case 376: //"Box"^M
< case 377: //"Barrel"^M
< case 378: //"Chest"^M
< case 379: //"Vase"^M
< case 380: //"Table"^M
< case 381: //"Weapons Rack"^M
< case 382: //"Coffin"^M
< case 383: //"Bones"^M
< case 384: //"Jokester"^M
< case 385: //"Talosian Nihil"^M
< case 386: //"Talosian Exile"^M
< case 387: //"Talosian Golem"^M
< case 388: //"Talosian Wolf"^M
< case 389: //"Talosian Amphibian"^M
< case 390: //"Talosian Mountain Beast"^M
< case 391: //"Talosian Trilobyte"^M
< case 392: //"Invader War Hound"^M
< case 393: //"Invader Elite Centaur"^M
< case 394: //"Invader Lamia"^M
< case 395: //"Invader Cyclops"^M
< case 396: //"Kyv"^M
< case 397: //"Invader Soldier"^M
< case 398: //"Invader Brute"^M
< case 399: //"Invader Force Commander"^M
< case 400: //"Invader Lieutenant Boss"^M
< case 401: //"Invader War Beast"^M
< case 402: //"Invader Soldier Elite"^M
< case 403: //"UNKNOWN RACE"^M
< case 404: //"Discord Ship"^M
< return LANG_COMMON_TONGUE;*/^M
< //Barbarian^M
< case 2: //"Barbarian"^M
< case 90: //"Halas Citizen"^M
< case 151: //"Tribunal"^M
< case 256: //"New Tribunal"^M
< return LANG_BARBARIAN;^M
< //Erudite^M
< case 3: //"Erudite"^M
< case 118: //"Erudite Ghost"^M
< case 342: //"Erudite Pirate"^M
< return LANG_ERUDIAN;^M
< //Elvish^M
< case 4: //"Wood Elf"^M
< case 5: //"High Elf"^M
< case 64: //"Treant"^M
< case 98: //"Elf Vampire"^M
< case 112: //"Fayguard"^M
< case 181: //"Yak Man"^M
< case 182: //"Faun"^M
< case 364: //"Sand Elf"^M
< return LANG_ELVISH;^M
< //Dark Elvish^M
< case 6: //"Dark Elf"^M
< case 339: //"Dark Elf Pirate"^M
< return LANG_DARK_ELVISH;^M
< //Dwarvish^M
< case 8: //"Dwarf"^M
< case 94: //"Kaladim Citizen"^M
< case 117: //"Ghost Dwarf"^M
< case 183: //"Coldain"^M
< return LANG_DWARVISH;^M
< //Troll^M
< case 9: //"Troll"^M
< case 92: //"Grobb Citizen"^M
< case 95: //"Cazic Thule"^M
< case 331: //"Troll Buccaneer"^M
< case 332: //"Troll Freebooter"^M
< case 333: //"Troll Sea Rover"^M
< case 344: //"Troll Zombie"^M
< return LANG_TROLL;^M
< //Ogre^M
< case 10: //"Ogre"^M
< case 18: //"Giant / Cyclops"^M
< case 66: //"Rallos Zek"^M
< case 93: //"Oggok Citizen"^M
< case 188: //"Frost Giant"^M
< case 189: //"Storm Giant"^M
< case 288: //"New Rallos Zek"^M
< case 289: //"Vallon Zek"^M
< case 290: //"Tallon Zek"^M
< case 325: //"Rallos Zek Minion"^M
< case 340: //"Ogre Pirate"^M
< return LANG_OGRE;^M
< //Halfling^M
< case 11: //"Halfling"^M
< case 153: //"Bristlebane"^M
< return LANG_HALFLING;^M
< //Gnomish^M
< case 12: //"Gnome"^M
< case 88: //"Clockwork Gnome"^M
< case 192: //"Clockwork Dragon"^M
< case 248: //"Clockwork Golem"^M
< case 249: //"Clockwork Brain"^M
< case 273: //"Junk Beast"^M
< case 274: //"Broken Clockwork"^M
< case 275: //"Giant Clockwork"^M
< case 276: //"Clockwork Beetle"^M
< case 338: //"Gnome Pirate"^M
< return LANG_GNOMISH;^M
< case 15: //"Brownie"^M
< case 25: //"Fairy"^M
< case 56: //"Pixie"^M
< case 113: //"Drixie"^M
< case 154: //"Fay Drake"^M
< return LANG_FAERIE;^M
< case 19: //"Trakenon"^M
< case 198: //"Prismatic Dragon"^M
< return LANG_ELDER_DRAGON;^M
< case 21: //"Evil Eye"^M
< case 29: //"Gargoyle"^M
< case 46: //"Imp"^M
< case 57: //"Dracnid"^M
< case 85: //"Spectre"^M
< case 152: //"Bertoxxulous"^M
< case 174: //"Cold Spectre"^M
< case 214: //"Thought Horror"^M
< case 224: //"Shade"^M
< case 255: //"New Bertox"^M
< case 257: //"Terris Thule"^M
< case 264: //"Nightmare Wraith"^M
< case 265: //"Malarian"^M
< case 266: //"Knight of Pestilence"^M
< case 267: //"Lepertoloth"^M
< case 268: //"Bubonian Boss"^M
< case 269: //"Bubonian Underling"^M
< case 270: //"Pusling"^M
< case 279: //"Blood Raven"^M
< case 280: //"Nightmare Gargoyle"^M
< case 281: //"Mouths of Insanity"^M
< case 282: //"Skeletal Horse"^M
< case 283: //"Saryn"^M
< case 285: //"Tormentor"^M
< case 286: //"Necro Priest"^M
< case 287: //"Nightmare"^M
< case 294: //"Nightmare Mephit"^M
< case 297: //"Undead Knight"^M
< case 300: //"Fiend"^M
< case 324: //"Undead Footman"^M
< case 373: //"Shadow Creatue"^M
< case 375: //"Evil Eye New"^M
< return LANG_DARK_SPEECH;^M
< case 26: //"Old Froglok"^M
< case 27: //"Old Froglok Ghoul"^M
< case 102: //"Old Froglok Tadpole"^M
< case 277: //"Nightmare Goblin"^M
< case 330: //"Froglok"^M
< case 348: //"Froglok Mount"^M
< case 349: //"Froglok Skeleton"^M
< case 350: //"Undead Froglok"^M
< case 371: //"Froglok Ghost"^M
< return LANG_FROGLOK;^M
< case 39: //"Gnoll"^M
< return LANG_GNOLL;^M
< case 40: //"Goblin"^M
< case 48: //"Kobold"^M
< case 59: //"Bloodgills"^M
< case 137: //"Kunark Goblin"^M
< case 369: //"Goblin New"^M
< return LANG_GOBLIN;^M
< case 49: //"Lava Dragon"^M
< case 89: //"Drake"^M
< case 122: //"Dragon Skeleton"^M
< case 157: //"Wyvern"^M
< case 158: //"Wurm"^M
< case 165: //"Water Dragon"^M
< case 184: //"Velious Dragons"^M
< case 195: //"Black and White Dragons"^M
< case 196: //"Ghost Dragon"^M
< return LANG_DRAGON;^M
< case 51: //"Lizard Man"^M
< case 128: //"Iksar"^M
< case 131: //"Sarnak"^M
< case 132: //"Draglock"^M
< case 136: //"Xalgoz"^M
< case 139: //"Iksar Citizen"^M
< case 146: //"Spectral Sarnak"^M
< case 147: //"Spectral Iksar"^M
< case 155: //"Sarnak Skeleton"^M
< case 160: //"Iksar Golem"^M
< case 161: //"Iksar Skeleton"^M
< case 164: //"Sarnak Golem"^M
< case 166: //"Iksar Hand"^M
< case 217: //"Shissar"^M
< return LANG_LIZARDMAN;^M
< case 54: //"Orc"^M
< case 361: //"Rujakian Orc"^M
< case 366: //"Rujakian Orc Elite"^M
< return LANG_ORCISH;^M
< case 62: //"Tunare"^M
< return LANG_ELDER_ELVISH;^M
< case 130: //"Vah Shir"^M
< case 205: //"Kahli Shah"^M
< case 234: //"Vah Shir Skeleton"^M
< case 238: //"Vah Shir King"^M
< case 239: //"Vah Shir Guard"^M
< return LANG_VAH_SHIR;^M
< default:^M
< return LANG_COMMON_TONGUE;^M
< }^M
< }^M
< ^M
5579c5087
< }^M
---
> }
\ No newline at end of file
mob.h
609,610d604
< int8 GetRacialLanguage();^M
< ^M
questmgr.cpp
212,241c212,217
< void QuestManager::say(const char *str)^M
< {^M
< //EnableNPCRacialLanguages seems to be mutually exclusive with the quest journal^M
< if(RuleB(NPC, EnableNPCRacialLanguages) && initiator)^M
< {^M
< int8 owner_language = owner->GetRacialLanguage(); ^M
< if(initiator->IsClient())^M
< {^M
< //If they aren't clients, they can talk all they want^M
< if(initiator->GetLanguageSkill(owner_language) > 50)^M
< {^M
< quest_manager.say(str, owner_language);^M
< } else^M
< {^M
< quest_manager.say("I'm sorry, I don't understand a word you're saying.", owner_language);^M
< }^M
< } else^M
< {^M
< quest_manager.say(str, owner_language);^M
< }^M
< } else^M
< {^M
< if(RuleB(NPC, EnableNPCQuestJournal) && initiator)^M
< {^M
< owner->QuestJournalledSay(initiator, str);^M
< } else^M
< {^M
< owner->Say(str);^M
< }^M
< }^M
---
> void QuestManager::say(const char *str) {^M
> ^M
> if(RuleB(NPC, EnableNPCQuestJournal) && initiator)^M
> owner->QuestJournalledSay(initiator, str);^M
> else^M
> owner->Say(str);^M
2609c2585
< }^M
---
> }
\ No newline at end of file
Optional SQL:
INSERT INTO rule_values (ruleset_id, rule_name, rule_value, notes) values (1, 'NPC:EnableNPCRacialLanguages', 0, 'Enables quest::say() to use a race-specific language when called without the second argument');
INSERT INTO rule_values (ruleset_id, rule_name, rule_value, notes) values (1, 'Character:LearnLanguagesFromChannels', 0, 'Enables players to learn languages from say, guild, group, and shout');