/* Melnorme strategy: - Wander around continuously until hero comes near - When hero is in room stop and turn purple - When hero left the room start to wander and turn green Returns ms elapsed, -2 if the monster dies. */ int shMonster::doMelnorme () { int elapsed; int result = -1; int retries = 3; int myRoom = Level->getRoomID (mX, mY); while (-1 == result) { if (!retries--) { return 200; } /* Melnorme have no designated shop. They are willing to trade if you enter their room. This also allows them to relocate. */ if (Level->getRoomID (Hero.mX, Hero.mY) == myRoom and Level->getRoomID (mX, mY) != 0) { if (mGlyph.mColor == kGreen and !isHostile ()) { /* Greet hero who just entered the room. */ if (Hero.tryToTranslate (this)) { char *buf = GetBuf (); int rnd; switch (RNG (3)) { case 0: /* hello, heroname/title/racename */ rnd = RNG (4); strncpy (buf, rnd == 0 ? Hero.mName : rnd == 1 ? Hero.mProfession->mName : rnd == 2 ? Hero.getTitle () : Hero.myIlk ()->mName, SHBUFLEN); if (rnd == 0) buf[0] = toupper (buf[0]); break; case 1: /* hello, heroname the Janitor */ snprintf (buf, SHBUFLEN, "%s the %s", Hero.mName, !RNG (3) ? Hero.mProfession->mName : !RNG (2) ? Hero.getTitle () : Hero.myIlk ()->mName); buf[0] = toupper (buf[0]); break; case 2: /* hello, earthling Detective */ snprintf (buf, SHBUFLEN, "%s %s", Hero.myIlk ()->mName, !RNG (2) ? Hero.mProfession->mName : Hero.getTitle ()); break; } switch (RNG (4)) { case 0: I->p ("I bid you a formal welcome, %s.", buf); break; case 1: I->p ("Hello, %s.", buf); break; case 2: I->p ("How nice to see you, %s.", buf); break; case 3: /* Use just the capitalized name because greetings could be too long otherwise. */ strncpy (buf, Hero.mName, SHBUFLEN); buf[0] = toupper (buf[0]); I->p ("I had itchy pods recently, %s" ", and here you are!", buf); break; } } else { I->p ("%s mumbles something in coarse voice.", THE (this)); } } mGlyph.mColor = kMagenta; mGlyph.mTileX = 3; /* Hero was greeted properly. Is this first encounter? */ if (!Hero.getStoryFlag ("Met Melnorme")) { Hero.setStoryFlag ("Met Melnorme", 1); I->p ("Press '%s' to request services.", I->getKeyForCommand (shInterface::kPay)); } } else { mGlyph.mColor = kGreen; mGlyph.mTileX = 2; } switch (mTactic) { case kNewEnemy: mStrategy = kWander; mTactic = kNewEnemy; mGlyph.mColor = kNavy; mGlyph.mTileX = 4; return doWander (); case kMoveTo: result = doMoveTo (); continue; case kReady: /* Usually stay where you are but occassionally move to new square inside this room. */ if (myRoom and !RNG (6)) { mDestX = RNG (mMelnorme.mSX, mMelnorme.mEX); mDestY = RNG (mMelnorme.mSY, mMelnorme.mEY); if (!mLevel->isOccupied (mDestX, mDestY)) { elapsed = doQuickMoveTo (); if (-1 == elapsed) elapsed = 800; return elapsed; } return RNG (600, 1000); /* Path blocked. Wait. */ /* !myRoom == Warp out of corridors eagerly. */ } else if ((!myRoom or !RNG (40)) and mGlyph.mColor == kGreen) { int newx = mX, newy = mY, tries = 3; while (tries--) { mLevel->findUnoccupiedSquare (&newx, &newy); if (!mLevel->isOccupied (newx, newy) and !mLevel->isInShop (newx, newy) and /* Do not accept corridors. */ (myRoom = Level->getRoomID (newx, newy))) { transport (newx, newy, 100, 1); /* Update location data. */ Level->getRoomDimensions (myRoom, &mMelnorme.mSX, &mMelnorme.mSY, &mMelnorme.mEX, &mMelnorme.mEY); /* Exclude walls from room coordinates. */ ++mMelnorme.mSX; ++mMelnorme.mSY; --mMelnorme.mEX; --mMelnorme.mEY; return FULLTURN; } } return FULLTURN; } else { return FULLTURN; } default: mTactic = kReady; debug.log ("Unexpected Melnorme tactic!"); } } return FULLTURN; }
int BasicMonster::action() { if ((usesMeleeWeapons() || usesRangedWeapons()) && inventory.size() > 0) { useBestWeapon(); } int time; bool seen = seePlayer(); if (shouldFlee()) { time = doFlee(); if (time > 0) return time; } if (attitude == ATTITUDE_HOSTILE && seen) { int sqdist = Point::sqlen(world.player->getCreature()->getPos() - position); Weapon* weapon = getMainWeapon(); if (weapon == NULL) weapon = &baseWeapon; int range = weapon->getRange(); // Ranged attack if (range > 1 && sqdist <= range*range) { time = doRangedAttack(weapon); if (time > 0) return time; } // Special attacks (e.g. dragon breath) time = doSpecialAttack(); if (time > 0) return time; // Melee attack if (sqdist <= 2) { time = doMeleeAttack(weapon); if (time > 0) return time; } // Run towards player / tactical movement time = doChargePlayer(); if (time > 0) return time; } // Random walk, search for items, etc. time = doWander(); return time > 0 ? time : 10; }
/* doctor strategy: wait around for hero to request services; occasionally advertise returns ms elapsed, -2 if the monster dies */ int shMonster::doDoctor () { int elapsed; int res = -1; int retry = 3; while (-1 == res) { if (!retry--) { return 200; } switch (mTactic) { case kNewEnemy: mStrategy = kWander; mTactic = kNewEnemy; return doWander (); case kMoveTo: res = doMoveTo (); continue; case kReady: if (Level->getRoomID (mX, mY) != mDoctor.mRoomID) { /* somehow, we're not in our hospital! */ mDestX = mDoctor.mHomeX; mDestY = mDoctor.mHomeY; if (setupPath ()) { mTactic = kMoveTo; continue; } else { return 2000; } } if (canSee (&Hero) and mDoctor.mRoomID == Level->getRoomID (Hero.mX, Hero.mY)) { if (0 == RNG (12)) { /* move to an empty square near the home square */ mDestX = mDoctor.mHomeX; mDestY = mDoctor.mHomeY; if (!mLevel -> findAdjacentUnoccupiedSquare (&mDestX, &mDestY)) { elapsed = doQuickMoveTo (); if (-1 == elapsed) elapsed = 800; return elapsed; } return RNG (300, 1000); /* nowhere to go, let's wait... */ } else if (0 == RNG (50)) { const char *quips[] = { "\"Dammit, %s! I'm a docbot, not %s!\"", "\"Your second amputation is half off!\"", "\"Galaxy-class health care services!\"", "\"I've been through much experience, you're dealing with an expert!\"" }; const unsigned int NUMQUIPS = sizeof (quips) / sizeof (char *); const char *nouns[] = { "a magician", "a psychiatrist", "a bartender", "a bricklayer", "a mechanic", "a priest", "a lawyer", "a prostitute", "a toaster oven", "a warbot" }; const unsigned int NUMNOUNS = sizeof (nouns) / sizeof (char *); if (Hero.tryToTranslate (this)) { I->p (quips[RNG (NUMQUIPS)], Hero.mName, nouns[RNG (NUMNOUNS)]); Hero.interrupt (); } return RNG (500, 1000); } return RNG (500, 1000); } else { return RNG (800, 1600); } case kFight: case kShoot: mTactic = kReady; debug.log ("Unexpected doctor tactic!"); } } return RNG (300, 1000); /* keep on lurking */ }