Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

fire.gno

11.76 Kb · 267 lines
  1package engine
  2
  3import (
  4	"strconv"
  5	"strings"
  6)
  7
  8// HitLoc is a hit location on a FIRE card's hit chart (rule 13.5).
  9type HitLoc string
 10
 11const (
 12	Miss     HitLoc = "-"
 13	Bullseye HitLoc = "BE" // the shooter picks the location (13.7)
 14	LegHit   HitLoc = "LEG"
 15	GunHit   HitLoc = "GUN"  // the gun arm
 16	ArmHit   HitLoc = "ARM"  // the other arm
 17	SideHit  HitLoc = "SIDE" // a flesh wound
 18	BodyHit  HitLoc = "BODY"
 19	CritHit  HitLoc = "CRIT"
 20	VitalHit HitLoc = "VITAL"
 21	HeadHit  HitLoc = "HEAD"
 22)
 23
 24// Target statuses a FIRE card may list (rule 13.6), as the letters of a
 25// card's status string.
 26const (
 27	StatusMove = 'M' // played a foot action or dropped this turn (13.61)
 28	StatusRun  = 'R' // played a Run this turn (13.62)
 29	StatusDown = 'D' // is DOWN (13.63)
 30	StatusHead = 'H' // head counter shot at (13.64, not played)
 31)
 32
 33// Band is an inclusive range of aim times or ranges; Hi < 0 is open-ended.
 34type Band struct{ Lo, Hi int }
 35
 36// Contains reports whether n falls in the band.
 37func (b Band) Contains(n int) bool { return n >= b.Lo && (b.Hi < 0 || n <= b.Hi) }
 38
 39// Chart is the hit chart of a FIRE card: a hit location per aim-time row
 40// and range column.
 41type Chart struct {
 42	Cols []Band
 43	Rows []ChartRow
 44}
 45
 46// ChartRow is one aim-time row of a Chart.
 47type ChartRow struct {
 48	Aim   Band
 49	Cells []HitLoc
 50}
 51
 52// Lookup is the preliminary hit location for an aim time and a range; off
 53// the chart is a miss (rule 13.5).
 54func (c *Chart) Lookup(aim, rng int) HitLoc {
 55	col := -1
 56	for i, b := range c.Cols {
 57		if b.Contains(rng) {
 58			col = i
 59			break
 60		}
 61	}
 62	if col < 0 {
 63		return Miss
 64	}
 65	for _, r := range c.Rows {
 66		if r.Aim.Contains(aim) {
 67			return r.Cells[col]
 68		}
 69	}
 70	return Miss
 71}
 72
 73// Lesser is the hit the card's Target Status entries name: the chart's hit
 74// location other than a bullseye (every card has exactly one).
 75func (c *Chart) Lesser() HitLoc {
 76	for _, r := range c.Rows {
 77		for _, h := range r.Cells {
 78			if h != Miss && h != Bullseye {
 79				return h
 80			}
 81		}
 82	}
 83	return Miss
 84}
 85
 86// Malfunction is the upper block of cards 101-108 (rule 13.3): whether the
 87// shot misfires per shell type, and the gun's fate on a second malfunction
 88// drawn after a misfire (13.31).
 89type Malfunction struct {
 90	Storebought bool // misfire with storebought shells
 91	Handloaded  bool // misfire with handloaded shells (the Colt 45's)
 92	CapAndBall  bool // misfire with cap-and-ball shells
 93	Second      string
 94}
 95
 96// Second-draw penalties.
 97const (
 98	GunJams     = "jams"     // all shells crossed off; cannot fire until reloaded
 99	GunExplodes = "explodes" // cannot fire for the rest of the game
100)
101
102// fireCard is the transcription of a FIRE card's upper block: the hit chart
103// as "<range bands>|<aim band>:<cell per column>|..." (bands "n", "n-m",
104// "n+" or "n-m+") and the Target Status letters (M move, R run, D down,
105// H head), each of which turns the card's lesser hit into a miss and, for
106// M and R, a bullseye into the lesser hit.
107type fireCard struct {
108	chart, status string
109}
110
111// fireCards are cards 1 to 100, from the card images (assets/result_card_NNN.png).
112var fireCards = [100]fireCard{
113	{"0,1-5,6-12+|2-5:BE,-,-|6-8:BE,SIDE,-|9+:BE,BE,SIDE", "H"},         // 1
114	{"0,1-5,6-9|3-6:BE,-,-|7:BE,SIDE,-|8+:BE,BE,SIDE", "M"},             // 2
115	{"0-1,2-4|1-3:SIDE,-|4+:BE,SIDE", "H"},                              // 3
116	{"0-2,3-7|1-6:SIDE,-|7+:BE,SIDE", "H"},                              // 4
117	{"0-5,6-8|2-4:SIDE,-|5+:BE,SIDE", "D"},                              // 5
118	{"0,1|3-4:BE,-|5-7:BE,SIDE|8+:BE,BE", "H"},                          // 6
119	{"0,1-3|1-4:SIDE,-|5:BE,SIDE|6+:BE,BE", "H"},                        // 7
120	{"0,1-4|2-6:BE,-|7+:BE,SIDE", ""},                                   // 8
121	{"0-1,2-6|1-5:SIDE,-|6+:BE,SIDE", "D"},                              // 9
122	{"0-3,4-5|2-6:SIDE,-|7-8:BE,-|9+:BE,SIDE", "H"},                     // 10
123	{"0-2,3-6|4-8:SIDE,-|9+:BE,BE", ""},                                 // 11
124	{"0-1,2-3,4-5|3-4:SIDE,SIDE,-|5-8:BE,SIDE,SIDE|9+:BE,BE,SIDE", "D"}, // 12
125	{"0,1-3,4-7|6-8:BE,SIDE,-|9+:BE,BE,SIDE", ""},                       // 13
126	{"0-7|4-6:SIDE|7+:BE", "M"},                                         // 14
127	{"0,1,2-5|1-2:BE,-,-|3-7:BE,BE,-|8-9:BE,BE,SIDE", "D"},              // 15
128	{"0-4|1-3:SIDE|4+:BE", "D"},                                         // 16
129	{"0-5,6-11|8:SIDE,-|9+:BE,BE", ""},                                  // 17
130	{"0-6|3-5:SIDE|6+:BE", ""},                                          // 18
131	{"0,1|3-4:BE,-|5-7:BE,BODY|8+:BE,BE", ""},                           // 19
132	{"0,1-5,6-12+|2-5:BE,-,-|6-8:BE,BODY,-|9+:BE,BE,BODY", ""},          // 20
133	{"0-1,2,3|1-2:BODY,-,-|3-8:BE,BODY,-|9+:BE,BE,BODY", ""},            // 21
134	{"0,1-3|2-6:BODY,-|7+:BE,BE", ""},                                   // 22
135	{"0-3,4-7|2-7:BODY,-|8+:BE,BODY", ""},                               // 23
136	{"0-4,5-10|4-6:BODY,-|7:BE,BODY|8+:BE,BE", ""},                      // 24
137	{"0-5,6-8|2-4:BODY,-|5+:BE,BODY", "D"},                              // 25
138	{"0-6|3-5:BODY|6+:BE", ""},                                          // 26
139	{"0-1,2,3-5|1-2:BE,BODY,BODY|3-4:BE,BE,BODY|5+:BE,BE,BE", "R"},      // 27
140	{"0-2,3-9|2-8:BODY,-|9+:BE,BE", ""},                                 // 28
141	{"0,1-4|2-6:BE,-|7+:BE,BODY", "H"},                                  // 29
142	{"0-1,2,3|8:BE,BODY,-|9+:BE,BE,BODY", ""},                           // 30
143	{"0,1-3|1-4:BODY,-|5:BE,BODY|6+:BE,BE", "H"},                        // 31
144	{"0-5,6-11|8:BODY,-|9+:BE,BE", ""},                                  // 32
145	{"0-1,2-3,4|2-7:BE,-,-|8:BE,BODY,-|9+:BE,BE,BODY", "H"},             // 33
146	{"0,1-2,3-6|1-2:BODY,-,-|3-7:BE,BODY,-|8+:BE,BE,BODY", "H"},         // 34
147	{"0,1-4,5-8|1-5:BE,-,-|6-8:BE,BODY,-|9+:BE,BE,BODY", "H"},           // 35
148	{"0,1-5,6-9|3-6:BE,-,-|7:BE,BODY,-|8+:BE,BE,BODY", "H"},             // 36
149	{"0,1-2,3-4|1-4:BODY,-,-|5+:BE,BE,BODY", "DH"},                      // 37
150	{"0-4|3-7:BODY|8+:BE", "D"},                                         // 38
151	{"0-1,2-3,4-5|3-4:BODY,BODY,-|5-8:BE,BODY,BODY|9+:BE,BE,BODY", "D"}, // 39
152	{"0-2,3-7|1-6:BODY,-|7+:BE,BODY", "MH"},                             // 40
153	{"0-1,2-6|1-5:BODY,-|6+:BE,BODY", "H"},                              // 41
154	{"0-1,2,3|8:BE,BODY,-|9+:BE,BE,BODY", ""},                           // 42
155	{"0-2,3-4|5+:BE,BODY", "H"},                                         // 43
156	{"0,1-4|2-6:BE,-|7+:BE,CRIT", "R"},                                  // 44
157	{"0-3,4-9|1-5:CRIT,-|6+:BE,CRIT", "DH"},                             // 45
158	{"0,1-2,3-6|1-2:CRIT,-,-|3-7:BE,CRIT,-|8+:BE,BE,CRIT", "DH"},        // 46
159	{"0-5,6-11|2-7:CRIT,-|8+:BE,CRIT", ""},                              // 47
160	{"0-1,2-3|4-7:CRIT,-|8+:BE,BE", "M"},                                // 48
161	{"0,1,2-4|1-2:BE,-,-|3+:BE,BE,CRIT", "D"},                           // 49
162	{"0-1,2-5|3-7:CRIT,-|8+:BE,CRIT", "H"},                              // 50
163	{"0-3,4-6|5+:BE,CRIT", "MD"},                                        // 51
164	{"0-1,2-3,4|2-3:BE,-,-|4-8:BE,CRIT,-|9+:BE,BE,CRIT", "D"},           // 52
165	{"0-3,4-5|2-6:CRIT,-|7-8:BE,-|9+:BE,CRIT", "H"},                     // 53
166	{"0,1-3|2-6:CRIT,-|7+:BE,BE", "M"},                                  // 54
167	{"0-1,2,3|1-2:CRIT,-,-|3-8:BE,CRIT,-|9+:BE,BE,CRIT", "M"},           // 55
168	{"0,1-3,4-7|6-8:BE,CRIT,-|9+:BE,BE,CRIT", "R"},                      // 56
169	{"0-2,3-9|2-8:CRIT,-|9+:BE,BE", "M"},                                // 57
170	{"0-7|4-6:CRIT|7+:BE", "H"},                                         // 58
171	{"0,1-2,3-6|1-4:VITAL,-,-|5-8:BE,BE,-|9+:BE,BE,BE", "M"},            // 59
172	{"0-2,3,4-7|2-5:BE,VITAL,-|6-8:BE,VITAL,VITAL|9+:BE,BE,BE", "R"},    // 60
173	{"0-1,2,3-4|1-2:BE,VITAL,-|3+:BE,VITAL,VITAL", "M"},                 // 61
174	{"0-1,2,3-5|3:VITAL,-,-|4-8:BE,VITAL,-|9+:BE,BE,VITAL", "DH"},       // 62
175	{"0-2,3-4|2-4:VITAL,-|5-6:VITAL,VITAL|7+:BE,VITAL", "DH"},           // 63
176	{"0-1,2-3,4-7|1-5:VITAL,-,-|6-7:VITAL,VITAL,-|8+:BE,BE,VITAL", "H"}, // 64
177	{"0-1,2,3-6|4-6:VITAL,-,-|7-8:BE,VITAL,VITAL|9+:BE,BE,VITAL", "M"},  // 65
178	{"0-1,2-5|1-5:BE,-|6-8:BE,HEAD|9+:BE,BE", "M"},                      // 66
179	{"0,1-4,5-8|1-5:BE,-,-|6-8:BE,HEAD,-|9+:BE,BE,HEAD", ""},            // 67
180	{"0-1,2-6|3:HEAD,-|4-6:BE,-|7+:BE,HEAD", "R"},                       // 68
181	{"0-2,3-5|1-6:HEAD,-|7-8:BE,-|9+:BE,HEAD", "R"},                     // 69
182	{"0-3|4-7:HEAD|8+:BE", ""},                                          // 70
183	{"0,1-4|2-6:BE,-|7+:BE,HEAD", ""},                                   // 71
184	{"0-1,2-4|1-3:HEAD,-|4+:BE,HEAD", "M"},                              // 72
185	{"0-2,3-5,6+|1-4:BE,LEG,LEG|5+:BE,BE,LEG", "H"},                     // 73
186	{"0-5,6-11|2-7:LEG,-|8+:BE,LEG", "RH"},                              // 74
187	{"0-3,4-6|5+:BE,LEG", "MD"},                                         // 75
188	{"0-3,4-9|1-5:LEG,-|6+:BE,LEG", "MH"},                               // 76
189	{"0-2,3-4|2:BE,-|3+:BE,LEG", "MH"},                                  // 77
190	{"0-2,3-4|5+:BE,LEG", "H"},                                          // 78
191	{"0-4|1-3:LEG|4+:BE", "DH"},                                         // 79
192	{"0-4,5-10|4-6:LEG,-|7:BE,LEG|8+:BE,BE", "DH"},                      // 80
193	{"0,1-2|1-3:GUN,-|4-7:BE,GUN|8+:BE,BE", ""},                         // 81
194	{"0-3,4-5,6-7|3-8:BE,GUN,-|9+:BE,BE,BE", "M"},                       // 82
195	{"0-1,2-8|2-7:GUN,-|8+:BE,BE", "R"},                                 // 83
196	{"0-1|3-8:GUN|9+:BE", ""},                                           // 84
197	{"0-3,4-5|5-8:GUN,-|9+:BE,GUN", ""},                                 // 85
198	{"0-3,4-5|1-6:GUN,-|7+:BE,GUN", "MD"},                               // 86
199	{"0-2,3-6|1-5:GUN,-|6-7:BE,-|8+:BE,GUN", "R"},                       // 87
200	{"0-1,2-3|5-6:BE,-|7+:BE,GUN", ""},                                  // 88
201	{"0-4|2-8:GUN|9+:BE", "R"},                                          // 89
202	{"0-2,3-6|4+:BE,GUN", "RD"},                                         // 90
203	{"0,1-2|1-3:ARM,-|4-7:BE,ARM|8+:BE,BE", "H"},                        // 91
204	{"0-3,4-5,6-7|3-8:BE,ARM,-|9+:BE,BE,BE", "MH"},                      // 92
205	{"0-1,2-8|2-7:ARM,-|8+:BE,BE", "RH"},                                // 93
206	{"0-1|3-8:ARM|9+:BE", "H"},                                          // 94
207	{"0-3,4-5|5-8:ARM,-|9+:BE,ARM", "H"},                                // 95
208	{"0-3,4-7|1-6:ARM,-|8+:BE,ARM", "MDH"},                              // 96
209	{"0-2,3-6|1-5:ARM,-|6-7:BE,-|8+:BE,ARM", "RH"},                      // 97
210	{"0-1,2-3|5-6:BE,-|7+:BE,ARM", "H"},                                 // 98
211	{"0-4|2-8:ARM|9+:BE", "RH"},                                         // 99
212	{"0-2,3-6|4+:BE,ARM", "RDH"},                                        // 100
213}
214
215// malfunctions are the blocks of cards 101 to 108.
216var malfunctions = [8]Malfunction{
217	{false, false, true, GunJams},    // 101
218	{false, false, true, GunJams},    // 102
219	{false, false, true, GunJams},    // 103
220	{true, true, true, GunJams},      // 104
221	{true, true, true, GunJams},      // 105
222	{true, true, true, GunJams},      // 106
223	{false, true, true, GunExplodes}, // 107
224	{false, true, true, GunExplodes}, // 108
225}
226
227func parseChart(s string) *Chart {
228	parts := strings.Split(s, "|")
229	c := &Chart{}
230	for _, b := range strings.Split(parts[0], ",") {
231		c.Cols = append(c.Cols, parseBand(b))
232	}
233	for _, row := range parts[1:] {
234		i := strings.IndexByte(row, ':')
235		r := ChartRow{Aim: parseBand(row[:i])}
236		for _, cell := range strings.Split(row[i+1:], ",") {
237			r.Cells = append(r.Cells, HitLoc(cell))
238		}
239		if len(r.Cells) != len(c.Cols) {
240			panic("fire chart row/column mismatch: " + s)
241		}
242		c.Rows = append(c.Rows, r)
243	}
244	return c
245}
246
247func parseBand(s string) Band {
248	open := strings.HasSuffix(s, "+")
249	s = strings.TrimSuffix(s, "+")
250	lo, hi := s, s
251	if i := strings.IndexByte(s, '-'); i >= 0 {
252		lo, hi = s[:i], s[i+1:]
253	}
254	b := Band{Lo: atoi(lo), Hi: atoi(hi)}
255	if open {
256		b.Hi = -1
257	}
258	return b
259}
260
261func atoi(s string) int {
262	n, err := strconv.Atoi(s)
263	if err != nil {
264		panic("fire chart: bad number " + strconv.Quote(s))
265	}
266	return n
267}