Script: q_post_inc
// q_post_inc.nss — Hobbit Post shared helpers (roadmap: hobbit-post)
//
// T1 daily delivery quest, journal tag "hobbit_post". Posco Whitfoot, the
// Hobbiton post-master, hands out one sealed parcel per real day; the
// addressee rotates with the game calendar (GetCalendarDay() % 5 + 1):
//
// 1 Daddy Twofoot Hobbiton (same area as Posco)
// 2 Old Noakes Bywater
// 3 the Keeper of the Green Dragon Green Dragon Inn (door off Hobbiton)
// 4 Salvia Brockhouse Whitfurrow Inn
// 5 Milo Burrows Whitfurrow Inn
//
// State lives entirely on the parcel item (tag QP_TAG, blueprint
// q_post_parcel — plot + cursed, so it can't be sold, dropped or traded):
// POST_DAY int 1..5 — the addressee on the label (baked at accept time,
// so a calendar flip mid-delivery can't strand it)
// POST_MIS int 0/1 — misdelivery: the label is wrong; the labelled
// recipient refuses and points at the true one,
// QP_TrueRec(label) = (label % 5) + 1, who pays extra
// POST_T0 int — real-world accept epoch (QCD_Now); the speed bonus
// pays if delivery lands within QP_FAST_SECS of it
//
// Daily gating is quest_cd_inc's calendar reset: QCD_IsDoneToday gates the
// offer, QCD_Stamp fires in QP_Pay on delivery. Custom tokens 6340 (name),
// 6341 (place), 6342 (time to reset) are set by the StartingConditionals.
//
// Rewards (QP_Pay): 60 gp base, +40 gp if fast, +60 gp and a small random
// parcel-loot item if misdelivered, +50 XP always.
#include "quest_cd_inc"
const string QP_TAG = "q_post_parcel"; // item tag AND blueprint resref
const string QP_QUEST = "hobbit_post"; // questcddb key + journal tag
const int QP_FAST_SECS = 300; // speed-bonus window (5 real min)
const int QP_MIS_PCT = 8; // % chance the label is wrong
const int QP_GOLD_BASE = 60;
const int QP_GOLD_FAST = 40;
const int QP_GOLD_MIS = 60;
const int QP_XP = 50;
// Today's addressee index, 1..5.
int QP_TodayIdx()
{
return (GetCalendarDay() % 5) + 1;
}
// Who a mislabelled parcel really belongs to.
int QP_TrueRec(int nLabel)
{
return (nLabel % 5) + 1;
}
string QP_Name(int n)
{
switch (n)
{
case 1: return "Daddy Twofoot";
case 2: return "Old Noakes";
case 3: return "the Keeper of the Green Dragon";
case 4: return "Salvia Brockhouse";
case 5: return "Milo Burrows";
}
return "somebody in the Shire";
}
string QP_Place(int n)
{
switch (n)
{
case 1: return "here in Hobbiton, under the Hill";
case 2: return "out along the Bywater road";
case 3: return "behind the bar of the Green Dragon Inn";
case 4: return "in the common room of the Whitfurrow Inn";
case 5: return "in the common room of the Whitfurrow Inn";
}
return "somewhere in the Shire";
}
// The parcel the PC is carrying, or OBJECT_INVALID.
object QP_Parcel(object oPC)
{
return GetItemPossessedBy(oPC, QP_TAG);
}
// Label on the carried parcel (1..5), or 0 when no parcel.
int QP_Label(object oPC)
{
object oParcel = QP_Parcel(oPC);
if (!GetIsObjectValid(oParcel)) return 0;
return GetLocalInt(oParcel, "POST_DAY");
}
// TRUE when the carried parcel is mislabelled.
int QP_Mis(object oPC)
{
object oParcel = QP_Parcel(oPC);
return GetIsObjectValid(oParcel) && GetLocalInt(oParcel, "POST_MIS");
}
// --- Recipient StartingConditional cores (r = recipient index 1..5) ---
// Normal delivery: parcel labelled for r and the label is honest.
int QP_CanAccept(object oPC, int r)
{
return QP_Label(oPC) == r && !QP_Mis(oPC);
}
// Mislabelled: parcel says r, but it isn't theirs. Sets tokens 6340/6341 to
// the TRUE recipient so the refusal line can point the player onward.
int QP_IsMislabel(object oPC, int r)
{
if (QP_Label(oPC) != r || !QP_Mis(oPC)) return FALSE;
int nTrue = QP_TrueRec(r);
SetCustomToken(6340, QP_Name(nTrue));
SetCustomToken(6341, QP_Place(nTrue));
return TRUE;
}
// True recipient of a mislabelled parcel.
int QP_IsTrue(object oPC, int r)
{
int nLabel = QP_Label(oPC);
return nLabel > 0 && QP_Mis(oPC) && QP_TrueRec(nLabel) == r;
}
// --- Payout — ActionTaken on every accepting entry (normal + true-recipient).
// Guarded so it can never pay twice in a day or without a parcel in hand.
void QP_Pay(object oPC)
{
object oParcel = QP_Parcel(oPC);
if (!GetIsObjectValid(oParcel)) return;
int bMis = GetLocalInt(oParcel, "POST_MIS");
int nT0 = GetLocalInt(oParcel, "POST_T0");
DestroyObject(oParcel);
if (QCD_IsDoneToday(oPC, QP_QUEST)) return;
QCD_Stamp(oPC, QP_QUEST);
int nElapsed = QCD_Now() - nT0;
int bFast = (nT0 > 0 && nElapsed >= 0 && nElapsed <= QP_FAST_SECS);
int nGold = QP_GOLD_BASE;
if (bFast) nGold += QP_GOLD_FAST;
if (bMis) nGold += QP_GOLD_MIS;
GiveGoldToCreature(oPC, nGold);
GiveXPToCreature(oPC, QP_XP);
AddJournalQuestEntry(QP_QUEST, 2, oPC, FALSE, FALSE, TRUE);
if (bFast)
FloatingTextStringOnCreature(
"Swift delivery! The seal was still warm -- bonus pay.", oPC, FALSE);
// Misdelivered parcels turn up with a little something extra inside.
if (bMis)
{
switch (d3())
{
case 1: CreateItemOnObject("frogpint", oPC, 1); break;
case 2: CreateItemOnObject("nw_it_mpotion003", oPC, 1); break;
case 3: CreateItemOnObject("nw_it_mpotion021", oPC, 1); break;
}
FloatingTextStringOnCreature(
"The rightful owner tips you for untangling the post.", oPC, FALSE);
}
}