guns.gno
4.48 Kb · 193 lines
1package engine
2
3// GunType identifies a weapon model.
4type GunType string
5
6const (
7 // NoGun marks an empty gun slot.
8 NoGun GunType = ""
9 // Colt45 is the one-handed six-shooter every character starts with.
10 Colt45 GunType = "colt45"
11)
12
13// GunSpec is the static data of a gun model (rules section 10 and the
14// WEAPON LISTS).
15type GunSpec struct {
16 Name string
17 Capacity int
18 OneHanded bool
19 Impact byte // its line of the IMPACT TABLE (see Impact)
20 Wielding int // its wielding factor: how far it can be thrown (16.21, 17.2)
21 Striking byte // its line of the STRIKING TABLE (see Striking)
22}
23
24// Spec returns the model's static data.
25func (t GunType) Spec() GunSpec {
26 switch t {
27 case Colt45:
28 return GunSpec{Name: "Colt 45", Capacity: 6, OneHanded: true, Impact: 'B', Wielding: 4, Striking: 'B'}
29 }
30 return GunSpec{}
31}
32
33// GunLocation is where a character keeps a gun (the boxes of the sheet).
34type GunLocation int
35
36const (
37 Holstered GunLocation = iota
38 GunHand
39 OtherHand
40 BothHands
41)
42
43// Hand codes used in plan strings: 0 = gun hand, 1 = other hand, 2 = both
44// hands; HandGround, "g" in a gun move, is the hex the character stands in.
45const (
46 HandGun = 0
47 HandOther = 1
48 HandBoth = 2
49 HandGround = 3
50)
51
52// HandLocation maps a plan hand code to the location it designates.
53func HandLocation(code int) (GunLocation, bool) {
54 switch code {
55 case HandGun:
56 return GunHand, true
57 case HandOther:
58 return OtherHand, true
59 case HandBoth:
60 return BothHands, true
61 }
62 return 0, false
63}
64
65// String returns the location name used in JSON views.
66func (l GunLocation) String() string {
67 switch l {
68 case Holstered:
69 return "holstered"
70 case GunHand:
71 return "gun_hand"
72 case OtherHand:
73 return "other_hand"
74 case BothHands:
75 return "both_hands"
76 }
77 return "?"
78}
79
80// Gun is one weapon carried by a character. ID identifies it for the whole
81// game (plans refer to it), even among several guns of the same model.
82type Gun struct {
83 ID int
84 Type GunType
85 Location GunLocation
86 Cocked bool
87 Shells int
88 Jammed bool // every shell crossed off; fires again once completely reloaded (13.31)
89 Exploded bool // cannot fire for the rest of the game (13.31)
90}
91
92// CanFireFrom reports whether the gun is in a box it can shoot from (13.1):
93// a one-handed gun in the gun hand or both hands, a two-handed one in both
94// hands.
95func (g Gun) CanFireFrom() bool {
96 if g.Type == NoGun {
97 return false
98 }
99 if g.Type.Spec().OneHanded {
100 return g.Location == GunHand || g.Location == BothHands
101 }
102 return g.Location == BothHands
103}
104
105// MaxGuns is how many guns a character can carry (hands and holsters).
106const MaxGuns = 3
107
108// Guns is a character's weapons; empty slots have Type NoGun.
109type Guns [MaxGuns]Gun
110
111// Find returns the slot of the gun with the given id, or -1.
112func (g Guns) Find(id int) int {
113 for i, gun := range g {
114 if gun.Type != NoGun && gun.ID == id {
115 return i
116 }
117 }
118 return -1
119}
120
121// NextID is a free id for a gun added to this character.
122func (g Guns) NextID() int {
123 n := 0
124 for _, gun := range g {
125 if gun.Type != NoGun && gun.ID > n {
126 n = gun.ID
127 }
128 }
129 return n + 1
130}
131
132// At returns the slot of the gun held at a location, or -1.
133func (g Guns) At(loc GunLocation) int {
134 for i, gun := range g {
135 if gun.Type != NoGun && gun.Location == loc {
136 return i
137 }
138 }
139 return -1
140}
141
142// Firing returns the slot of the gun held in a box it can shoot from
143// (rule 11.3), or -1.
144func (g Guns) Firing() int {
145 for i, gun := range g {
146 if gun.CanFireFrom() {
147 return i
148 }
149 }
150 return -1
151}
152
153// Blocked reports whether a gun other than the one in slot except holds
154// loc, or a hand box overlapping it: BOTH HANDS takes the gun hand and the
155// other hand, and either hand is taken by a gun in BOTH HANDS.
156func (g Guns) Blocked(loc GunLocation, except int) bool {
157 for i, gun := range g {
158 if i == except || gun.Type == NoGun || gun.Location == Holstered {
159 continue
160 }
161 if gun.Location == loc || gun.Location == BothHands || loc == BothHands {
162 return true
163 }
164 }
165 return false
166}
167
168// Free returns an empty slot, or -1 when the character carries MaxGuns.
169func (g Guns) Free() int {
170 for i, gun := range g {
171 if gun.Type == NoGun {
172 return i
173 }
174 }
175 return -1
176}
177
178// Count is the number of guns carried.
179func (g Guns) Count() int {
180 n := 0
181 for _, gun := range g {
182 if gun.Type != NoGun {
183 n++
184 }
185 }
186 return n
187}
188
189// startingGuns is the fixed loadout at game creation: a loaded, uncocked
190// Colt 45 in the holster, gun 1.
191func startingGuns() Guns {
192 return Guns{{ID: 1, Type: Colt45, Location: Holstered, Cocked: false, Shells: 6}}
193}