package game import ( "fmt" "os" "time" ) // rip.c — the fun ends: death or a total win. // // The C functions here call exit(). One game run is one process, so the // port does the same: myExit restores the terminal and exits directly. // myExit leaves the process properly (main.c my_exit): it restores the // terminal and ends the process. Every C caller exited with status 0. func (g *RogueGame) myExit() { g.scr.Fini() os.Exit(0) } // death does something really fun when he dies (rip.c death). func (g *RogueGame) death(monst byte) { p := &g.Player p.Purse -= p.Purse / 10 g.clear() killer := g.killname(monst, false) if !g.Options.Tombstone { g.scr.Std.MvPrintwf(NumLines-2, 0, "Killed by ") if monst != 's' && monst != 'h' { g.printw("a%s ", vowelstr(killer)) } g.printw("%s with %d gold", killer, p.Purse) } else { year := time.Now().Year() for i, line := range g.data.ripArt { g.scr.Std.MvAddStr(8+i, 0, line) } g.mvaddstr(17, center(killer), killer) if monst == 's' || monst == 'h' { g.mvaddch(16, 32, ' ') } else { g.mvaddstr(16, 33, vowelstr(killer)) } g.mvaddstr(14, center(g.Whoami), g.Whoami) au := fmt.Sprintf("%d Au", p.Purse) g.mvaddstr(15, center(au), au) g.mvaddstr(18, 26, fmt.Sprintf("%4d", year)) } g.mvaddstr(NumLines-1, 0, "[Press return to continue]") g.refresh() flags := 0 if g.HasAmulet { flags = 3 } g.score(p.Purse, flags, monst) g.waitFor('\n') g.myExit() } // center returns the column to center the given string on the tombstone // (rip.c center). func center(str string) int { return 28 - (len(str)+1)/2 } // totalWinner is the code for a winner (rip.c total_winner). func (g *RogueGame) totalWinner() { p := &g.Player g.clear() g.standout() banner := []string{ " ", " @ @ @ @ @ @@@ @ @ ", " @ @ @@ @@ @ @ @ @ ", " @ @ @@@ @ @ @ @ @ @@@ @@@@ @@@ @ @@@ @ ", " @@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ", " @ @ @ @ @ @ @ @@@@ @ @ @@@@@ @ @ @ ", " @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @ @ ", " @@@ @@@ @@ @ @ @ @@@@ @@@@ @@@ @@@ @@ @ ", " ", " Congratulations, you have made it to the light of day! ", } for i, line := range banner { g.scr.Std.MvAddStr(i, 0, line) } g.standend() g.scr.Std.MvAddStr(10, 0, "You have joined the elite ranks of those who have escaped the") g.scr.Std.MvAddStr(11, 0, "Dungeons of Doom alive. You journey home and sell all your loot at") g.scr.Std.MvAddStr(12, 0, "a great profit and are admitted to the Fighters' Guild.") g.mvaddstr(NumLines-1, 0, "--Press space to continue--") g.refresh() g.waitFor(' ') g.clear() g.mvaddstr(0, 0, " Worth Item") g.move(1, 0) oldpurse := p.Purse line := 1 for _, obj := range p.Pack { worth := g.objectWorth(obj) g.scr.Std.MvPrintwf(line, 0, "%c) %5d %s", obj.PackCh, worth, g.inventoryName(obj, false)) line++ p.Purse += worth } g.scr.Std.MvPrintwf(line, 0, " %5d Gold Pieces ", oldpurse) g.refresh() g.score(p.Purse, 2, ' ') g.myExit() } // objectWorth appraises one pack item on the way out, marking it known // (the switch of rip.c total_winner). func (g *RogueGame) objectWorth(obj *Object) int { it := &g.Items worth := 0 switch obj.Kind { case KindFood: worth = 2 * obj.Count case KindWeapon: worth = it.Weapons[obj.Which].Worth worth *= 3*(obj.HPlus+obj.DPlus) + obj.Count obj.Flags.Set(Known) case KindArmor: worth = it.Armors[obj.Which].Worth worth += (9 - obj.ArmorClass) * 100 worth += 10 * (g.data.aClass[obj.Which] - obj.ArmorClass) obj.Flags.Set(Known) case KindScroll: worth = loreWorth(&it.Scrolls[obj.Which], obj.Count) case KindPotion: worth = loreWorth(&it.Potions[obj.Which], obj.Count) case KindRing: worth = g.ringWorth(obj) case KindWand: worth = g.wandWorth(obj) case KindAmulet: worth = 1000 } if worth < 0 { worth = 0 } return worth } // loreWorth appraises a scroll or potion, halved when unidentified, and // identifies it (rip.c total_winner). func loreWorth(op *ObjInfo, count int) int { worth := op.Worth * count if !op.Know { worth /= 2 } op.Know = true return worth } // ringWorth appraises a ring: bonus rings gain by their bonus, cursed // ones are junk (rip.c total_winner). func (g *RogueGame) ringWorth(obj *Object) int { op := &g.Items.Rings[obj.Which] worth := op.Worth if obj.RingKind() == RingAddStrength || obj.RingKind() == RingIncreaseDamage || obj.RingKind() == RingProtection || obj.RingKind() == RingDexterity { if obj.Bonus > 0 { worth += obj.Bonus * 100 } else { worth = 10 } } if !obj.Flags.Has(Known) { worth /= 2 } obj.Flags.Set(Known) op.Know = true return worth } // wandWorth appraises a wand or staff by its charges (rip.c // total_winner). func (g *RogueGame) wandWorth(obj *Object) int { op := &g.Items.Sticks[obj.Which] worth := op.Worth worth += 20 * obj.Charges if !obj.Flags.Has(Known) { worth /= 2 } obj.Flags.Set(Known) op.Know = true return worth } // killname converts a code to a monster name (rip.c killname). func (g *RogueGame) killname(monst byte, doart bool) string { var ( sp string article bool ) if isUpper(monst) { sp = g.Monsters[monst-'A'].Name article = true } else { sp = "Wally the Wonder Badger" article = false for _, hp := range g.data.killnameTable { if hp.Ch == monst { sp = hp.Desc article = hp.Print break } } } if doart && article { return "a" + vowelstr(sp) + " " + sp } return sp } // DeathDemo implements the -d command line option (main.c): burn some // random numbers to break patterns, then die a random death. It does not // return — death exits the process. func (g *RogueGame) DeathDemo() { dnum := g.rnd(100) for dnum--; dnum > 0; dnum-- { g.rnd(100) } g.Player.Purse = g.rnd(100) + 1 g.Depth = g.rnd(100) + 1 g.death(g.deathMonst()) } // deathMonst returns a monster appropriate for a random death (rip.c // death_monst). func (g *RogueGame) deathMonst() byte { poss := []byte{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'h', 'd', 's', ' ', // generates the "Wally the Wonder Badger" message } return poss[g.rnd(len(poss))] }