mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 16:42:41 +00:00
333 lines
16 KiB
Go
333 lines
16 KiB
Go
package plugin
|
||
|
||
import (
|
||
"fmt"
|
||
"math/rand/v2"
|
||
"strings"
|
||
)
|
||
|
||
// ── St. Guildmore's Memorial Hospital — Nurse Joy Flavor Text ──────────────
|
||
|
||
// nurseJoyFirstVisit is shown once on the player's first hospital visit.
|
||
var nurseJoyFirstVisit = "🏥 **Welcome to St. Guildmore's Memorial Hospital!**\n\n" +
|
||
"We're the official healthcare provider for members of the Adventurer's Guild. " +
|
||
"Even better, we've modeled our pricing after the United States of America, " +
|
||
"which means we offer only the quickest and best care. _whispers_ ...for those who can afford it.\n\n" +
|
||
"Your Guild insurance is on file. Nurse Joy will be right with you.\n\n"
|
||
|
||
var nurseJoyAdmission = []string{
|
||
"_looks up from clipboard with a bright smile_ Hi! Name? Guild card? Oh you're covered under the Collective, that's wonderful! Let me get Nurse Joy to come take a look at you. _is Nurse Joy_",
|
||
"Oh you're with the Guild? That's great, we love Guild members! Very comprehensive plan. Someone will be right with you! _gestures at chairs_ Can I get you anything while you wait? We have water. It's complimentary. The water is complimentary.",
|
||
"_already typing, still cheerful_ Cause of death? Temporary obviously, just for the record! And your Guild card number? Perfect! We'll get you all sorted out. _sorted out does not mean quickly but she means it warmly_",
|
||
"Welcome to St. Guildmore's! You're in such good hands here. _has said this ten thousand times and means it every single time_ Insurance on file? Wonderful. Take a number and we'll be right with you!",
|
||
"_looks up, immediately sympathetic in a clinical way_ Oh you don't look great! That's okay, that's what we're here for. Guild member? Amazing. Nurse Joy will be right out. _is Nurse Joy. will be right out._",
|
||
}
|
||
|
||
var nurseJoyBillDelivery = []string{
|
||
"_slides it across with a smile_ So that's the total before insurance! Your plan is really good though -- the Collective is wonderful. We're going to call them right now. _already reaching for the phone, still smiling_",
|
||
"Here's the full rate! _sets it down cheerfully_ Don't worry about that number, insurance is going to take care of so much of it. So much. Give us just a sec! _hums while dialing_",
|
||
"That's what we call the pre-insurance figure! It's a big number but honestly that's just how we price things here. The Collective will bring it way down. Way down. _wanders off still visibly pleased with how this is going_",
|
||
"_hands it over like she's giving you a gift_ Your Guild plan is really comprehensive -- one of the better ones we see honestly. Let me just get them on the line! _does not register your expression at all_",
|
||
}
|
||
|
||
// nurseJoyAfterInsurance — format with afterInsurance cost.
|
||
// The last entry has two %d placeholders (intentional — she says the number twice).
|
||
var nurseJoyAfterInsurance = []string{
|
||
"_practically skips back in_ Great news! The Collective was amazing -- they covered so much of it. Your portion is only **€%d**! _says \"only\" like she's reporting a minor inconvenience_",
|
||
"_beaming_ Okay so the insurance came through and honestly? Really good result today. You're looking at **€%d** out of pocket! That's after everything they covered. _genuinely proud of this outcome_",
|
||
"_sits across from you, clasps hands_ I have such good news. The Collective took care of the bulk of it. Your share is just **€%d**! I know it sounds like a lot but you should see what it was before. _you have seen what it was before_ Actually you did see it. But still -- this is so much better!",
|
||
"_slides over a new, smaller, still alarming piece of paper_ So they covered everything above your threshold! You just owe **€%d**. _taps it encouragingly_ And then you're all set! Good as new! _has never once considered that **€%d** might be a problem_",
|
||
}
|
||
|
||
var nurseJoyCantAfford = []string{
|
||
"Oh! It looks like your balance isn't quite there today! _signals to the orderlies_ That's okay! We'll get you back to where we found you and you can rest up overnight! _waves as the stretcher is wheeled back out_",
|
||
"Hmm! So the balance isn't quite covering it! No worries at all -- _already nodding to someone behind you_ -- the natural recovery process works just as well! We'll get you back to your ditch! Have a great night!",
|
||
"Oh that's a little short! That's okay! _cheerfully_ We're going to pop you back where you were and you'll be right as rain tomorrow morning! _you are deposited back in the ditch_",
|
||
}
|
||
|
||
var nurseJoyNudge = []string{
|
||
"Hi! This is Nurse Joy at St. Guildmore's Memorial! We noticed you haven't come in yet.\n\n" +
|
||
"We have a bed ready and your insurance is all on file! We even have Derek standing by but that's only relevant if there's a billing issue and I'm sure there won't be!\n\n" +
|
||
"Come in whenever you're ready! We're open! _we are always open_\n\n" +
|
||
"Type `!hospital` to check in.",
|
||
}
|
||
|
||
// nurseJoyHaggleFlavor — Nurse Joy's reaction to the player's CHA haggle roll.
|
||
// Indexed by hospitalHaggleOutcome (haggleFumble=0, haggleFail=1, haggleWin=2, haggleCrit=3).
|
||
var nurseJoyHaggleFlavor = [][]string{
|
||
// haggleFumble — nat 1
|
||
{
|
||
"_you have drooled on the clipboard_ Oh sweetie. _gently dabs at it_ That's okay! We see this all the time. _has not seen this all the time._ Full price, but on the house emotionally!",
|
||
"You opened your mouth and a single, low groan came out. _she nods supportively_ I'll take that as 'fine with the full bill!' Wonderful negotiating!",
|
||
"_you tried to wink and both eyes closed_ Bless. We'll just go with the standard rate, sugar. Don't strain yourself.",
|
||
"You attempted charm. You produced a wet, rattling exhale. _Nurse Joy beams._ \"That's a yes to the full price! Great chat!\"",
|
||
"_you mumbled what you thought was a compliment about her hair_ That was a tube. That was the oxygen tube, honey. Full price!",
|
||
},
|
||
// haggleFail — d20+CHA below DC
|
||
{
|
||
"_smiles, doesn't blink_ I hear you, I do, but our pricing is set by the regional billing council and they are not sentimental people. Full bill it is!",
|
||
"That was a nice try! Really. _pats your hand_ Unfortunately I don't actually have the authority to discount anything, but it's so cute that you asked!",
|
||
"_tilts head sympathetically_ Oh, I would if I could! I really would. _she would not_ Standard rate, sugar.",
|
||
"Mm, no, that one didn't quite land for me. _still smiling_ But you're so brave for trying! Standard pricing applies.",
|
||
},
|
||
// haggleWin — pass
|
||
{
|
||
"_leans in conspiratorially_ You know what? You've been so polite. _types something_ I'm going to push this through as a 'goodwill adjustment.' Half off! Don't tell Derek.",
|
||
"_genuinely charmed_ Oh you're sweet. Okay, okay, I can flag this as a hardship case. Fifty percent off! _winks, has never winked at anyone before_",
|
||
"You know, the system has this little box for 'compassionate review' and I'm just _click_ going to go ahead and check that. There. Half price! Our little secret.",
|
||
"_giggles_ You're trouble. Okay — half off, but only because I like you, and because Derek is on lunch.",
|
||
},
|
||
// haggleCrit — nat 20
|
||
{
|
||
"_completely disarmed_ Oh my god. _puts down clipboard_ I haven't laughed like that on shift in YEARS. You know what? This one's on the house. Derek is going to have a complete meltdown and I do not care.",
|
||
"_dabs at her eye, recovering_ Sir. Ma'am. Whatever-you-are. That was the single most charming thing a corpse has ever said to me. Bill: waived. Walk it off.",
|
||
"_actually blushing_ I — okay. Okay! You know what? Free. It's free. _whispers to the ceiling_ Sorry Derek.",
|
||
"_stares at you for a long moment_ I'm calling it a clinical trial. You're the trial. There is no charge. Get out of here before I change my mind. _winks for real this time_",
|
||
"_to the orderlies_ Tear up the bill. _to you_ You, sugar, are a treasure. Discharge papers, no charge, complimentary water for the road.",
|
||
},
|
||
}
|
||
|
||
var nurseJoyAlive = []string{
|
||
"_looks up from clipboard_ You don't appear to be dead! That's great news! We love when people aren't dead. Come back if that changes! _cheerfully_",
|
||
"_tilts head_ Our records show you're... alive? That's wonderful! We don't actually treat alive people here. It's a whole different department. _waves vaguely_",
|
||
}
|
||
|
||
var nurseJoyAlreadyRevived = "You seem to have recovered on your own! " +
|
||
"That's the body's natural healing process at work. Beautiful thing. " +
|
||
"No charge! _beams_"
|
||
|
||
// ── Room Announcements ───────────────────────────────────────────────────<E29480><E29480>─
|
||
|
||
// hospitalDischargeBillDescriptors are the variants used for the
|
||
// "The bill has been described as ..." sentence in the discharge announcement.
|
||
// Some are bare adjectives ("devastating"); others are full quoted phrases.
|
||
var hospitalDischargeBillDescriptors = []string{
|
||
"\"inhumane but probably normal by USA standards.\"",
|
||
"devastating.",
|
||
"ruinous.",
|
||
"soul-crushing.",
|
||
"\"proof that someone, somewhere, hates me specifically.\"",
|
||
"\"tearing to shreds what little hope I had left for humanity.\"",
|
||
"\"what I imagine my greatest enemies would charge me.\"",
|
||
"\"enough to make me reconsider the whole 'surviving' part.\"",
|
||
}
|
||
|
||
var hospitalDischargeAnnouncePrefix = "🏥 %s has been discharged from St. Guildmore's Memorial Hospital. " +
|
||
"Recovered. Back in action. The bill has been described as %s"
|
||
|
||
var hospitalDitchAnnounce = "🏥 %s was brought into St. Guildmore's on a stretcher. " +
|
||
"They have been returned to the ditch. They'll be fine tomorrow."
|
||
|
||
// ── Itemized Bill Generation ───────────────────────────────────────────────
|
||
|
||
type hospitalBillItem struct {
|
||
Name string
|
||
Note string // optional italicized sub-line
|
||
Min float64 // min fraction of total
|
||
Max float64 // max fraction of total
|
||
}
|
||
|
||
// hospitalBillPool is the pool of possible line items. Each bill picks 8-12.
|
||
var hospitalBillPool = []hospitalBillItem{
|
||
{"Resuscitation services (standard)", "", 0.04, 0.10},
|
||
{"Resuscitation services (premium)", "(performed simultaneously with standard, non-optional)", 0.03, 0.08},
|
||
{"Facility fee", "", 0.02, 0.05},
|
||
{"Facility fee (after hours)", "(you died at 2pm. this is contested. we will not contest it.)", 0.02, 0.04},
|
||
{"Physician consultation", "", 0.03, 0.06},
|
||
{"Physician consultation (second opinion)", "(same physician. different hat.)", 0.03, 0.06},
|
||
{"Gauze", "", 0.01, 0.02},
|
||
{"Gauze (application fee)", "", 0.01, 0.02},
|
||
{"Gauze (removal, future, pre-billed)", "", 0.01, 0.03},
|
||
{"Convenience fee", "(thank you for allowing us to exist)", 0.02, 0.05},
|
||
{"Inconvenience fee", "(deterrent to prevent emergency room abuse or usage in general)", 0.02, 0.05},
|
||
{"Guild membership verification fee", "(your guild card was checked. this takes resources.)", 0.01, 0.03},
|
||
{"Administrative processing", "", 0.01, 0.03},
|
||
{"Spiritual realignment surcharge", "(your soul was briefly elsewhere. retrieval is billable.)", 0.02, 0.04},
|
||
{"Bed occupancy charge", "(per minute, retroactive to estimated time of death)", 0.02, 0.05},
|
||
{"Oxygen (ambient)", "(you breathed hospital air. the air is not free.)", 0.01, 0.03},
|
||
{"Emotional support services", "(Nurse Joy smiled at you. that's a service.)", 0.01, 0.02},
|
||
{"Post-mortem orientation fee", "(the pamphlet you did not read.)", 0.01, 0.02},
|
||
{"Equipment sterilization", "(your gear was near our equipment. proximity counts.)", 0.01, 0.03},
|
||
{"Discharge planning", "(we are already planning your discharge. this costs money.)", 0.01, 0.03},
|
||
}
|
||
|
||
// hospitalHolidaySurcharge is included only on holidays.
|
||
var hospitalHolidaySurcharge = hospitalBillItem{
|
||
"Holiday surcharge", "(applied if death occurs on or adjacent to a recognized holiday)", 0.02, 0.02,
|
||
}
|
||
|
||
// hospitalFrequentCustomer is included when hospitalVisits > 0.
|
||
// It's a "discount" that is actually a positive charge. Because healthcare.
|
||
var hospitalFrequentCustomer = hospitalBillItem{
|
||
"Frequent customer discount", "(thank you for your continued patronage.)", 0.01, 0.02,
|
||
}
|
||
|
||
// generateItemizedBill builds a procedural hospital bill that sums to the given total.
|
||
func generateItemizedBill(total int64, afterInsurance int64, hospitalVisits int, isHoliday bool) string {
|
||
// Shuffle and pick 8-12 items from the pool
|
||
pool := make([]hospitalBillItem, len(hospitalBillPool))
|
||
copy(pool, hospitalBillPool)
|
||
rand.Shuffle(len(pool), func(i, j int) { pool[i], pool[j] = pool[j], pool[i] })
|
||
|
||
count := 8 + rand.IntN(5) // 8-12
|
||
if count > len(pool) {
|
||
count = len(pool)
|
||
}
|
||
items := pool[:count]
|
||
|
||
// Add conditional items
|
||
if isHoliday {
|
||
items = append(items, hospitalHolidaySurcharge)
|
||
}
|
||
if hospitalVisits > 0 {
|
||
items = append(items, hospitalFrequentCustomer)
|
||
}
|
||
|
||
// Assign amounts
|
||
type billLine struct {
|
||
Name string
|
||
Note string
|
||
Amount int64
|
||
}
|
||
|
||
var lines []billLine
|
||
var assigned int64
|
||
|
||
for _, item := range items {
|
||
frac := item.Min + rand.Float64()*(item.Max-item.Min)
|
||
amount := int64(float64(total) * frac)
|
||
if amount < 1 {
|
||
amount = 1
|
||
}
|
||
lines = append(lines, billLine{Name: item.Name, Note: item.Note, Amount: amount})
|
||
assigned += amount
|
||
}
|
||
|
||
// Miscellaneous absorbs the remainder
|
||
misc := total - assigned
|
||
if misc < 0 {
|
||
// Over-assigned — scale everything down proportionally
|
||
scale := float64(total) / float64(assigned)
|
||
assigned = 0
|
||
for i := range lines {
|
||
lines[i].Amount = int64(float64(lines[i].Amount) * scale)
|
||
if lines[i].Amount < 1 {
|
||
lines[i].Amount = 1
|
||
}
|
||
assigned += lines[i].Amount
|
||
}
|
||
misc = total - assigned
|
||
}
|
||
if misc > 0 {
|
||
lines = append(lines, billLine{
|
||
Name: "Miscellaneous",
|
||
Note: "",
|
||
Amount: misc,
|
||
})
|
||
}
|
||
|
||
// Find max name width for alignment
|
||
maxName := 0
|
||
for _, l := range lines {
|
||
if len(l.Name) > maxName {
|
||
maxName = len(l.Name)
|
||
}
|
||
}
|
||
if maxName < 35 {
|
||
maxName = 35
|
||
}
|
||
|
||
// Format
|
||
var sb strings.Builder
|
||
sb.WriteString("```\n")
|
||
sb.WriteString("ST. GUILDMORE'S MEMORIAL HOSPITAL\n")
|
||
sb.WriteString("─────────────────────────────────\n")
|
||
|
||
for _, l := range lines {
|
||
padding := maxName - len(l.Name) + 2
|
||
if padding < 2 {
|
||
padding = 2
|
||
}
|
||
sb.WriteString(l.Name)
|
||
sb.WriteString(strings.Repeat(" ", padding))
|
||
sb.WriteString(formatBillAmount(l.Amount))
|
||
sb.WriteByte('\n')
|
||
if l.Note != "" {
|
||
sb.WriteString(" " + l.Note + "\n")
|
||
}
|
||
}
|
||
|
||
sb.WriteString("─────────────────────────────────\n")
|
||
totalLabel := "TOTAL (before insurance)"
|
||
padding := maxName - len(totalLabel) + 2
|
||
if padding < 2 {
|
||
padding = 2
|
||
}
|
||
sb.WriteString(totalLabel)
|
||
sb.WriteString(strings.Repeat(" ", padding))
|
||
sb.WriteString(formatBillAmount(total))
|
||
sb.WriteByte('\n')
|
||
|
||
insLabel := "Guild Adventurer's Insurance"
|
||
insPadding := maxName - len(insLabel) + 2
|
||
if insPadding < 2 {
|
||
insPadding = 2
|
||
}
|
||
sb.WriteString(insLabel)
|
||
sb.WriteString(strings.Repeat(" ", insPadding))
|
||
sb.WriteString("-" + formatBillAmount(total-afterInsurance))
|
||
sb.WriteByte('\n')
|
||
|
||
sb.WriteString("─────────────────────────────────\n")
|
||
oweLabel := "AMOUNT DUE"
|
||
owePadding := maxName - len(oweLabel) + 2
|
||
if owePadding < 2 {
|
||
owePadding = 2
|
||
}
|
||
sb.WriteString(oweLabel)
|
||
sb.WriteString(strings.Repeat(" ", owePadding))
|
||
sb.WriteString(formatBillAmount(afterInsurance))
|
||
sb.WriteByte('\n')
|
||
sb.WriteString("```")
|
||
|
||
return sb.String()
|
||
}
|
||
|
||
// formatBillAmount formats an int64 as €X,XXX with comma separators.
|
||
func formatBillAmount(n int64) string {
|
||
if n == 0 {
|
||
return "€0"
|
||
}
|
||
neg := n < 0
|
||
if neg {
|
||
n = -n
|
||
}
|
||
|
||
// Format with commas
|
||
s := fmt.Sprintf("%d", n)
|
||
if len(s) <= 3 {
|
||
if neg {
|
||
return "-€" + s
|
||
}
|
||
return "€" + s
|
||
}
|
||
|
||
// Insert commas from right
|
||
var result strings.Builder
|
||
remainder := len(s) % 3
|
||
if remainder > 0 {
|
||
result.WriteString(s[:remainder])
|
||
}
|
||
for i := remainder; i < len(s); i += 3 {
|
||
if result.Len() > 0 {
|
||
result.WriteByte(',')
|
||
}
|
||
result.WriteString(s[i : i+3])
|
||
}
|
||
|
||
if neg {
|
||
return "-€" + result.String()
|
||
}
|
||
return "€" + result.String()
|
||
}
|