mirror of
https://github.com/prosolis/gogobee.git
synced 2026-09-14 10:51:09 +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 ───────────────────────────────────────────────────��─
|
|
|
|
// 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()
|
|
}
|