Merge refactor/object-fields (refactor step 3 + module rename)
This commit is contained in:
@@ -6,7 +6,7 @@ This document has two parts:
|
|||||||
codebase (every file, every function, every type, every global) as it exists
|
codebase (every file, every function, every type, every global) as it exists
|
||||||
on the `modern-rogue` branch.
|
on the `modern-rogue` branch.
|
||||||
- **Part 2 — The Go Port Design**: the object-oriented architecture for the Go
|
- **Part 2 — The Go Port Design**: the object-oriented architecture for the Go
|
||||||
port (`sneak.berlin/go/rogue`), including how every C construct maps onto Go
|
port (`git.eeqj.de/sneak/rgoue`), including how every C construct maps onto Go
|
||||||
types and methods.
|
types and methods.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -887,7 +887,7 @@ Declares `rd_score(SCORE*)` / `wr_score(SCORE*)` (implemented in save.c).
|
|||||||
|
|
||||||
# Part 2 — The Go Port Design
|
# Part 2 — The Go Port Design
|
||||||
|
|
||||||
Module: **`sneak.berlin/go/rogue`**
|
Module: **`git.eeqj.de/sneak/rgoue`**
|
||||||
|
|
||||||
## 1. Goals and principles
|
## 1. Goals and principles
|
||||||
|
|
||||||
@@ -929,7 +929,7 @@ branches; this branch is Go-only. The tcell wrapper ended up in its own
|
|||||||
`term` package so that `game` itself has no third-party imports:
|
`term` package so that `game` itself has no third-party imports:
|
||||||
|
|
||||||
```
|
```
|
||||||
go.mod module sneak.berlin/go/rogue
|
go.mod module git.eeqj.de/sneak/rgoue
|
||||||
cmd/rogue/main.go package main — flag parsing, constructs RogueGame, calls Run
|
cmd/rogue/main.go package main — flag parsing, constructs RogueGame, calls Run
|
||||||
game/ package game — the port, one .go file per .c file
|
game/ package game — the port, one .go file per .c file
|
||||||
game.go RogueGame struct, NewGame, Run (main.c)
|
game.go RogueGame struct, NewGame, Run (main.c)
|
||||||
|
|||||||
42
TODO.md
42
TODO.md
@@ -29,14 +29,19 @@ Refactor ground rules:
|
|||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Refactor step 3 (branch refactor/object-fields): un-overload Object
|
Adopt the house Go linting standards: copy .golangci.yml from the
|
||||||
fields — split Object.Arm into ArmorClass/Charges/GoldValue; parse
|
prompts repo and bring game/, term/, and cmd/ lint-clean (the port is
|
||||||
damage dice ("1x4/1x2") once into a DiceSpec type at table definition
|
greenfield code, so no exemptions apply).
|
||||||
instead of per swing; save-format update with round-trip test
|
|
||||||
migration.
|
|
||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-07-06 Module base path updated to git.eeqj.de/sneak/rgoue
|
||||||
|
(go.mod, term/ and cmd/ imports, ARCHITECTURE.md, version string).
|
||||||
|
- 2026-07-06 Refactor step 3 (refactor/object-fields): Object.Arm split
|
||||||
|
into ArmorClass/Charges/GoldValue/Bonus (rings); Stats.Arm →
|
||||||
|
ArmorClass; damage strings parsed once into DiceSpec at table
|
||||||
|
definition (ParseDice keeps C roll_em parse semantics, incl. "%%%x0"
|
||||||
|
and "000x0" edge cases, regression-tested); save format 5.4.4-go3.
|
||||||
- 2026-07-06 Refactor step 2 (refactor/typed-kinds, b940cfc):
|
- 2026-07-06 Refactor step 2 (refactor/typed-kinds, b940cfc):
|
||||||
ObjectKind separates item category from map glyph (Object.Type byte
|
ObjectKind separates item category from map glyph (Object.Type byte
|
||||||
→ Kind ObjectKind with Glyph()); PotionKind/ScrollKind/RingKind/
|
→ Kind ObjectKind with Glyph()); PotionKind/ScrollKind/RingKind/
|
||||||
@@ -71,48 +76,45 @@ migration.
|
|||||||
|
|
||||||
# Future Steps
|
# Future Steps
|
||||||
|
|
||||||
1. Adopt the house Go linting standards: copy .golangci.yml from the
|
1. Refactor step 4: method renames, movement/world subsystem
|
||||||
prompts repo and bring game/, term/, and cmd/ lint-clean (the port
|
|
||||||
is greenfield code, so no exemptions apply).
|
|
||||||
2. Refactor step 4: method renames, movement/world subsystem
|
|
||||||
(doMove→moveHero, beTrapped→springTrap, rndmove→randomStep,
|
(doMove→moveHero, beTrapped→springTrap, rndmove→randomStep,
|
||||||
doRooms/doPassages/doMaze→digRooms/digPassages/digMaze,
|
doRooms/doPassages/doMaze→digRooms/digPassages/digMaze,
|
||||||
chgStr→changeStrength, ...); remove the goto/label flows in doMove,
|
chgStr→changeStrength, ...); remove the goto/label flows in doMove,
|
||||||
dispatch, and saveGame in favor of loops and helpers.
|
dispatch, and saveGame in favor of loops and helpers.
|
||||||
3. Refactor step 5: method renames, items/combat/UI subsystems
|
2. Refactor step 5: method renames, items/combat/UI subsystems
|
||||||
(invName→inventoryName, rollEm→rollAttacks, doPot→applyPotionFuse,
|
(invName→inventoryName, rollEm→rollAttacks, doPot→applyPotionFuse,
|
||||||
getItem→promptPackItem returning (obj, ok), getDir→promptDirection);
|
getItem→promptPackItem returning (obj, ok), getDir→promptDirection);
|
||||||
int status codes (attack returning -1) become named results. Two or
|
int status codes (attack returning -1) become named results. Two or
|
||||||
three commits, one subsystem each.
|
three commits, one subsystem each.
|
||||||
4. Refactor step 6: extract types from the god object — MessageLine
|
3. Refactor step 6: extract types from the god object — MessageLine
|
||||||
owns the msg/addmsg/endmsg machinery; pack/inventory operations move
|
owns the msg/addmsg/endmsg machinery; pack/inventory operations move
|
||||||
onto *Player; monster/object list management and map queries
|
onto *Player; monster/object list management and map queries
|
||||||
consolidate onto *Level; RogueGame keeps turn orchestration and
|
consolidate onto *Level; RogueGame keeps turn orchestration and
|
||||||
cross-system effects only.
|
cross-system effects only.
|
||||||
5. Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap
|
4. Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap
|
||||||
switches become per-kind handler tables of small named methods,
|
switches become per-kind handler tables of small named methods,
|
||||||
keeping effect order and RNG call sequence identical.
|
keeping effect order and RNG call sequence identical.
|
||||||
6. Refactor step 8: constructor and style pass per the house
|
5. Refactor step 8: constructor and style pass per the house
|
||||||
styleguide — game.New(game.Params{...}) replacing NewGame(Config);
|
styleguide — game.New(game.Params{...}) replacing NewGame(Config);
|
||||||
replace the gameEnd panic unwind with error-based turn results where
|
replace the gameEnd panic unwind with error-based turn results where
|
||||||
feasible; 77-column wrap sweep.
|
feasible; 77-column wrap sweep.
|
||||||
7. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the
|
6. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the
|
||||||
post-refactor names; add the C name → Go name rename table.
|
post-refactor names; add the C name → Go name rename table.
|
||||||
8. Playtest hardening pass: play several full games with the tcell
|
7. Playtest hardening pass: play several full games with the tcell
|
||||||
binary and extend run_test.go to script a deeper multi-level
|
binary and extend run_test.go to script a deeper multi-level
|
||||||
playthrough (descend past level 5, use potions, scrolls, zapping,
|
playthrough (descend past level 5, use potions, scrolls, zapping,
|
||||||
save/restore). Fix any panics, message mismatches, or divergences
|
save/restore). Fix any panics, message mismatches, or divergences
|
||||||
from the C behavior that this uncovers, with regression tests.
|
from the C behavior that this uncovers, with regression tests.
|
||||||
9. Verify the seed-compatibility claim against the C reference on
|
8. Verify the seed-compatibility claim against the C reference on
|
||||||
c-master: same seed, same dungeon, same item tables, for several
|
c-master: same seed, same dungeon, same item tables, for several
|
||||||
seeds.
|
seeds.
|
||||||
10. Broaden unit test coverage where playtesting finds thin spots
|
9. Broaden unit test coverage where playtesting finds thin spots
|
||||||
(rings, sticks, wizard commands).
|
(rings, sticks, wizard commands).
|
||||||
11. Tag a release once a full game (Amulet retrieval and score entry)
|
10. Tag a release once a full game (Amulet retrieval and score entry)
|
||||||
completes without defects.
|
completes without defects.
|
||||||
12. Full-terminal-size support (deferred by explicit decision
|
11. Full-terminal-size support (deferred by explicit decision
|
||||||
2026-07-06): per-game dungeon dimensions instead of the 80x24
|
2026-07-06): per-game dungeon dimensions instead of the 80x24
|
||||||
constants; open design questions are resize policy, gameplay
|
constants; open design questions are resize policy, gameplay
|
||||||
tuning at larger sizes, and a --classic 80x24 mode.
|
tuning at larger sizes, and a --classic 80x24 mode.
|
||||||
13. Note: this repo is exempt from the standard policy scaffold. Do not
|
12. Note: this repo is exempt from the standard policy scaffold. Do not
|
||||||
add Makefile, Dockerfile, or REPO_POLICIES.md.
|
add Makefile, Dockerfile, or REPO_POLICIES.md.
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"sneak.berlin/go/rogue/game"
|
"git.eeqj.de/sneak/rgoue/game"
|
||||||
"sneak.berlin/go/rogue/term"
|
"git.eeqj.de/sneak/rgoue/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ func (g *RogueGame) wizardCommand(ch byte) {
|
|||||||
g.turnSee(p.On(SenseMonsters))
|
g.turnSee(p.On(SenseMonsters))
|
||||||
case CTRL('~'):
|
case CTRL('~'):
|
||||||
if item := g.getItem("charge", KindWand); item != nil {
|
if item := g.getItem("charge", KindWand); item != nil {
|
||||||
item.SetCharges(10000)
|
item.Charges = 10000
|
||||||
}
|
}
|
||||||
case CTRL('I'):
|
case CTRL('I'):
|
||||||
for range 9 {
|
for range 9 {
|
||||||
@@ -427,7 +427,7 @@ func (g *RogueGame) wizardCommand(ch byte) {
|
|||||||
obj = newObject()
|
obj = newObject()
|
||||||
obj.Kind = KindArmor
|
obj.Kind = KindArmor
|
||||||
obj.Which = int(ArmorPlateMail)
|
obj.Which = int(ArmorPlateMail)
|
||||||
obj.Arm = -5
|
obj.ArmorClass = -5
|
||||||
obj.Flags.Set(Known)
|
obj.Flags.Set(Known)
|
||||||
obj.Count = 1
|
obj.Count = 1
|
||||||
p.CurArmor = obj
|
p.CurArmor = obj
|
||||||
|
|||||||
58
game/dice.go
Normal file
58
game/dice.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Rogue expresses damage as strings like "1x4/3x6": one attack rolling
|
||||||
|
// 1d4 and a second rolling 3d6. The C code re-parsed these with atoi on
|
||||||
|
// every swing (fight.c roll_em); the port parses them once, at table
|
||||||
|
// definition or item creation, into a DiceSpec.
|
||||||
|
|
||||||
|
// DiceRoll is one attack's dice: Count rolls of a Sides-sided die.
|
||||||
|
type DiceRoll struct {
|
||||||
|
Count int
|
||||||
|
Sides int
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiceSpec is the sequence of attacks a damage string described.
|
||||||
|
type DiceSpec []DiceRoll
|
||||||
|
|
||||||
|
// ParseDice parses a C damage string with the exact semantics of the
|
||||||
|
// roll_em loop: leading digits (C atoi) before and after each 'x', attacks
|
||||||
|
// separated by '/'. Junk like the bestiary's "%%%x0" placeholder parses as
|
||||||
|
// a single 0x0 attack, as it did in C.
|
||||||
|
func ParseDice(s string) DiceSpec {
|
||||||
|
var spec DiceSpec
|
||||||
|
for s != "" {
|
||||||
|
count := cAtoi(s)
|
||||||
|
xi := strings.IndexByte(s, 'x')
|
||||||
|
if xi < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s = s[xi+1:]
|
||||||
|
spec = append(spec, DiceRoll{Count: count, Sides: cAtoi(s)})
|
||||||
|
si := strings.IndexByte(s, '/')
|
||||||
|
if si < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s = s[si+1:]
|
||||||
|
}
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
|
||||||
|
// dice is the table-definition shorthand for ParseDice.
|
||||||
|
func dice(s string) DiceSpec { return ParseDice(s) }
|
||||||
|
|
||||||
|
// String renders the spec back in the classic "NxM/NxM" form.
|
||||||
|
func (d DiceSpec) String() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
for i, r := range d {
|
||||||
|
if i > 0 {
|
||||||
|
sb.WriteByte('/')
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&sb, "%dx%d", r.Count, r.Sides)
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
45
game/dice_test.go
Normal file
45
game/dice_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// ParseDice must keep the exact semantics of the C roll_em parse loop,
|
||||||
|
// including its junk-tolerant edges: the bestiary placeholder "%%%x0" and
|
||||||
|
// the flytrap reset "000x0" both mean a single 0x0 attack.
|
||||||
|
func TestParseDice(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
in string
|
||||||
|
want string
|
||||||
|
len int
|
||||||
|
}{
|
||||||
|
{"1x4", "1x4", 1},
|
||||||
|
{"1x2/1x5/1x5", "1x2/1x5/1x5", 3},
|
||||||
|
{"2x12/2x4", "2x12/2x4", 2},
|
||||||
|
{"0x0", "0x0", 1},
|
||||||
|
{"000x0", "0x0", 1},
|
||||||
|
{"%%%x0", "0x0", 1},
|
||||||
|
{"", "", 0},
|
||||||
|
{"3", "", 0}, // no 'x': C parses nothing
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
got := ParseDice(c.in)
|
||||||
|
if len(got) != c.len || got.String() != c.want {
|
||||||
|
t.Errorf("ParseDice(%q) = %v (len %d), want %q (len %d)",
|
||||||
|
c.in, got, len(got), c.want, c.len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The bestiary and weapon tables must parse to at least one attack each so
|
||||||
|
// every creature and weapon actually swings.
|
||||||
|
func TestTablesHaveDice(t *testing.T) {
|
||||||
|
for i, m := range monsterTable {
|
||||||
|
if len(m.Stats.Dmg) == 0 {
|
||||||
|
t.Errorf("monster %c (%s) has no attacks", 'A'+i, m.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for w, iw := range initWeaps {
|
||||||
|
if len(iw.dam) == 0 || len(iw.hrl) == 0 {
|
||||||
|
t.Errorf("weapon %v has empty dice", WeaponKind(w))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -72,11 +72,11 @@ func TestReadEnchantArmor(t *testing.T) {
|
|||||||
ch := give(g, scr)
|
ch := give(g, scr)
|
||||||
g.scr.term.(*testTerm).input = []byte{ch}
|
g.scr.term.(*testTerm).input = []byte{ch}
|
||||||
|
|
||||||
before := g.Player.CurArmor.Arm
|
before := g.Player.CurArmor.ArmorClass
|
||||||
g.readScroll()
|
g.readScroll()
|
||||||
if g.Player.CurArmor.Arm != before-1 {
|
if g.Player.CurArmor.ArmorClass != before-1 {
|
||||||
t.Errorf("enchant armor: AC %d -> %d, want %d",
|
t.Errorf("enchant armor: AC %d -> %d, want %d",
|
||||||
before, g.Player.CurArmor.Arm, before-1)
|
before, g.Player.CurArmor.ArmorClass, before-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,12 +139,12 @@ func TestZapSlowMonster(t *testing.T) {
|
|||||||
g.scr.term.(*testTerm).input = []byte{ch}
|
g.scr.term.(*testTerm).input = []byte{ch}
|
||||||
g.Delta = Coord{X: 1, Y: 0} // aim at the monster
|
g.Delta = Coord{X: 1, Y: 0} // aim at the monster
|
||||||
|
|
||||||
charges := stick.Charges()
|
charges := stick.Charges
|
||||||
g.doZap()
|
g.doZap()
|
||||||
if !tp.On(Slowed) {
|
if !tp.On(Slowed) {
|
||||||
t.Error("slow monster wand did not slow")
|
t.Error("slow monster wand did not slow")
|
||||||
}
|
}
|
||||||
if stick.Charges() != charges-1 {
|
if stick.Charges != charges-1 {
|
||||||
t.Error("zap did not use a charge")
|
t.Error("zap did not use a charge")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import "strconv"
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fight.c — all the fighting gets done here.
|
// fight.c — all the fighting gets done here.
|
||||||
|
|
||||||
@@ -245,7 +241,7 @@ func (g *RogueGame) attack(mp *Monster) int {
|
|||||||
// Venus Flytrap stops the poor guy from moving
|
// Venus Flytrap stops the poor guy from moving
|
||||||
p.Flags.Set(Held)
|
p.Flags.Set(Held)
|
||||||
p.VfHit++
|
p.VfHit++
|
||||||
g.Monsters['F'-'A'].Stats.Dmg = fmt.Sprintf("%dx1", p.VfHit)
|
g.Monsters['F'-'A'].Stats.Dmg = DiceSpec{{Count: p.VfHit, Sides: 1}}
|
||||||
if p.Stats.HP--; p.Stats.HP <= 0 {
|
if p.Stats.HP--; p.Stats.HP <= 0 {
|
||||||
g.death('F')
|
g.death('F')
|
||||||
}
|
}
|
||||||
@@ -322,34 +318,34 @@ func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool
|
|||||||
p := &g.Player
|
p := &g.Player
|
||||||
att := &thatt.Stats
|
att := &thatt.Stats
|
||||||
def := &thdef.Stats
|
def := &thdef.Stats
|
||||||
var cp string
|
var attacks DiceSpec
|
||||||
var hplus, dplus int
|
var hplus, dplus int
|
||||||
if weap == nil {
|
if weap == nil {
|
||||||
cp = att.Dmg
|
attacks = att.Dmg
|
||||||
} else {
|
} else {
|
||||||
hplus = weap.HPlus
|
hplus = weap.HPlus
|
||||||
dplus = weap.DPlus
|
dplus = weap.DPlus
|
||||||
if weap == p.CurWeapon {
|
if weap == p.CurWeapon {
|
||||||
if p.IsRing(Left, RingIncreaseDamage) {
|
if p.IsRing(Left, RingIncreaseDamage) {
|
||||||
dplus += p.CurRing[Left].Arm
|
dplus += p.CurRing[Left].Bonus
|
||||||
} else if p.IsRing(Left, RingDexterity) {
|
} else if p.IsRing(Left, RingDexterity) {
|
||||||
hplus += p.CurRing[Left].Arm
|
hplus += p.CurRing[Left].Bonus
|
||||||
}
|
}
|
||||||
if p.IsRing(Right, RingIncreaseDamage) {
|
if p.IsRing(Right, RingIncreaseDamage) {
|
||||||
dplus += p.CurRing[Right].Arm
|
dplus += p.CurRing[Right].Bonus
|
||||||
} else if p.IsRing(Right, RingDexterity) {
|
} else if p.IsRing(Right, RingDexterity) {
|
||||||
hplus += p.CurRing[Right].Arm
|
hplus += p.CurRing[Right].Bonus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cp = weap.Damage
|
attacks = weap.Damage
|
||||||
if hurl {
|
if hurl {
|
||||||
if weap.Flags.Has(Missile) && p.CurWeapon != nil &&
|
if weap.Flags.Has(Missile) && p.CurWeapon != nil &&
|
||||||
WeaponKind(p.CurWeapon.Which) == weap.Launch {
|
WeaponKind(p.CurWeapon.Which) == weap.Launch {
|
||||||
cp = weap.HurlDmg
|
attacks = weap.HurlDmg
|
||||||
hplus += p.CurWeapon.HPlus
|
hplus += p.CurWeapon.HPlus
|
||||||
dplus += p.CurWeapon.DPlus
|
dplus += p.CurWeapon.DPlus
|
||||||
} else if weap.Launch < 0 {
|
} else if weap.Launch < 0 {
|
||||||
cp = weap.HurlDmg
|
attacks = weap.HurlDmg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,40 +354,28 @@ func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool
|
|||||||
if !thdef.Flags.Has(Awake) {
|
if !thdef.Flags.Has(Awake) {
|
||||||
hplus += 4
|
hplus += 4
|
||||||
}
|
}
|
||||||
defArm := def.Arm
|
defArm := def.ArmorClass
|
||||||
if def == &p.Stats {
|
if def == &p.Stats {
|
||||||
if p.CurArmor != nil {
|
if p.CurArmor != nil {
|
||||||
defArm = p.CurArmor.Arm
|
defArm = p.CurArmor.ArmorClass
|
||||||
}
|
}
|
||||||
if p.IsRing(Left, RingProtection) {
|
if p.IsRing(Left, RingProtection) {
|
||||||
defArm -= p.CurRing[Left].Arm
|
defArm -= p.CurRing[Left].Bonus
|
||||||
}
|
}
|
||||||
if p.IsRing(Right, RingProtection) {
|
if p.IsRing(Right, RingProtection) {
|
||||||
defArm -= p.CurRing[Right].Arm
|
defArm -= p.CurRing[Right].Bonus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
didHit := false
|
didHit := false
|
||||||
for cp != "" {
|
for _, atk := range attacks {
|
||||||
ndice := cAtoi(cp)
|
|
||||||
xi := strings.IndexByte(cp, 'x')
|
|
||||||
if xi < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
cp = cp[xi+1:]
|
|
||||||
nsides := cAtoi(cp)
|
|
||||||
if g.swing(att.Lvl, defArm, hplus+strPlus[att.Str]) {
|
if g.swing(att.Lvl, defArm, hplus+strPlus[att.Str]) {
|
||||||
proll := g.roll(ndice, nsides)
|
proll := g.roll(atk.Count, atk.Sides)
|
||||||
damage := dplus + proll + addDam[att.Str]
|
damage := dplus + proll + addDam[att.Str]
|
||||||
if damage > 0 {
|
if damage > 0 {
|
||||||
def.HP -= damage
|
def.HP -= damage
|
||||||
}
|
}
|
||||||
didHit = true
|
didHit = true
|
||||||
}
|
}
|
||||||
si := strings.IndexByte(cp, '/')
|
|
||||||
if si < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
cp = cp[si+1:]
|
|
||||||
}
|
}
|
||||||
return didHit
|
return didHit
|
||||||
}
|
}
|
||||||
@@ -531,7 +515,7 @@ func (g *RogueGame) killed(tp *Monster, pr bool) {
|
|||||||
case 'F':
|
case 'F':
|
||||||
p.Flags.Clear(Held)
|
p.Flags.Clear(Held)
|
||||||
p.VfHit = 0
|
p.VfHit = 0
|
||||||
g.Monsters['F'-'A'].Stats.Dmg = "000x0"
|
g.Monsters['F'-'A'].Stats.Dmg = dice("000x0")
|
||||||
case 'L':
|
case 'L':
|
||||||
pos, ok := g.fallpos(tp.Pos)
|
pos, ok := g.fallpos(tp.Pos)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -540,9 +524,9 @@ func (g *RogueGame) killed(tp *Monster, pr bool) {
|
|||||||
if ok && g.Depth >= g.MaxDepth {
|
if ok && g.Depth >= g.MaxDepth {
|
||||||
gold := newObject()
|
gold := newObject()
|
||||||
gold.Kind = KindGold
|
gold.Kind = KindGold
|
||||||
gold.SetGoldVal(g.goldCalc())
|
gold.GoldValue = g.goldCalc()
|
||||||
if g.save(VsMagic) {
|
if g.save(VsMagic) {
|
||||||
gold.Arm += g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc()
|
gold.GoldValue += g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc()
|
||||||
}
|
}
|
||||||
attachObj(&tp.Pack, gold)
|
attachObj(&tp.Pack, gold)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ func spawnAdjacent(g *RogueGame, typ byte) *Monster {
|
|||||||
|
|
||||||
func TestRollEmParsesMultiAttackDice(t *testing.T) {
|
func TestRollEmParsesMultiAttackDice(t *testing.T) {
|
||||||
g := mkGame(t, 42)
|
g := mkGame(t, 42)
|
||||||
att := &Creature{Stats: Stats{Str: 16, Lvl: 20, Dmg: "1x4/1x4/1x4"}}
|
att := &Creature{Stats: Stats{Str: 16, Lvl: 20, Dmg: dice("1x4/1x4/1x4")}}
|
||||||
def := &Creature{Stats: Stats{Arm: 10, HP: 1000}}
|
def := &Creature{Stats: Stats{ArmorClass: 10, HP: 1000}}
|
||||||
def.Flags.Set(Awake)
|
def.Flags.Set(Awake)
|
||||||
// With attacker level 20 vs armor 10, swing always hits
|
// With attacker level 20 vs armor 10, swing always hits
|
||||||
// (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of
|
// (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func (g *RogueGame) initPlayer() {
|
|||||||
obj = newObject()
|
obj = newObject()
|
||||||
obj.Kind = KindArmor
|
obj.Kind = KindArmor
|
||||||
obj.Which = int(ArmorRingMail)
|
obj.Which = int(ArmorRingMail)
|
||||||
obj.Arm = aClass[ArmorRingMail] - 1
|
obj.ArmorClass = aClass[ArmorRingMail] - 1
|
||||||
obj.Flags.Set(Known)
|
obj.Flags.Set(Known)
|
||||||
obj.Count = 1
|
obj.Count = 1
|
||||||
p.CurArmor = obj
|
p.CurArmor = obj
|
||||||
|
|||||||
@@ -142,9 +142,9 @@ func (g *RogueGame) status() {
|
|||||||
p := &g.Player
|
p := &g.Player
|
||||||
|
|
||||||
// If nothing has changed since the last status, don't bother.
|
// If nothing has changed since the last status, don't bother.
|
||||||
temp := p.Stats.Arm
|
temp := p.Stats.ArmorClass
|
||||||
if p.CurArmor != nil {
|
if p.CurArmor != nil {
|
||||||
temp = p.CurArmor.Arm
|
temp = p.CurArmor.ArmorClass
|
||||||
}
|
}
|
||||||
if s.init && s.hp == p.Stats.HP && s.exp == p.Stats.Exp &&
|
if s.init && s.hp == p.Stats.HP && s.exp == p.Stats.Exp &&
|
||||||
s.pur == p.Purse && s.arm == temp && s.str == p.Stats.Str &&
|
s.pur == p.Purse && s.arm == temp && s.str == p.Stats.Str &&
|
||||||
|
|||||||
@@ -279,10 +279,10 @@ func (g *RogueGame) chgStr(amt int) {
|
|||||||
addStr(&p.Stats.Str, amt)
|
addStr(&p.Stats.Str, amt)
|
||||||
comp := p.Stats.Str
|
comp := p.Stats.Str
|
||||||
if p.IsRing(Left, RingAddStrength) {
|
if p.IsRing(Left, RingAddStrength) {
|
||||||
addStr(&comp, -p.CurRing[Left].Arm)
|
addStr(&comp, -p.CurRing[Left].Bonus)
|
||||||
}
|
}
|
||||||
if p.IsRing(Right, RingAddStrength) {
|
if p.IsRing(Right, RingAddStrength) {
|
||||||
addStr(&comp, -p.CurRing[Right].Arm)
|
addStr(&comp, -p.CurRing[Right].Bonus)
|
||||||
}
|
}
|
||||||
if comp > p.MaxStats.Str {
|
if comp > p.MaxStats.Str {
|
||||||
p.MaxStats.Str = comp
|
p.MaxStats.Str = comp
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) {
|
|||||||
tp.Stats.Lvl = mp.Stats.Lvl + levAdd
|
tp.Stats.Lvl = mp.Stats.Lvl + levAdd
|
||||||
tp.Stats.MaxHP = g.roll(tp.Stats.Lvl, 8)
|
tp.Stats.MaxHP = g.roll(tp.Stats.Lvl, 8)
|
||||||
tp.Stats.HP = tp.Stats.MaxHP
|
tp.Stats.HP = tp.Stats.MaxHP
|
||||||
tp.Stats.Arm = mp.Stats.Arm - levAdd
|
tp.Stats.ArmorClass = mp.Stats.ArmorClass - levAdd
|
||||||
tp.Stats.Dmg = mp.Stats.Dmg
|
tp.Stats.Dmg = mp.Stats.Dmg
|
||||||
tp.Stats.Str = mp.Stats.Str
|
tp.Stats.Str = mp.Stats.Str
|
||||||
tp.Stats.Exp = mp.Stats.Exp + levAdd*10 + expAdd(tp)
|
tp.Stats.Exp = mp.Stats.Exp + levAdd*10 + expAdd(tp)
|
||||||
@@ -183,10 +183,10 @@ func (g *RogueGame) save(which int) bool {
|
|||||||
p := &g.Player
|
p := &g.Player
|
||||||
if which == VsMagic {
|
if which == VsMagic {
|
||||||
if p.IsRing(Left, RingProtection) {
|
if p.IsRing(Left, RingProtection) {
|
||||||
which -= p.CurRing[Left].Arm
|
which -= p.CurRing[Left].Bonus
|
||||||
}
|
}
|
||||||
if p.IsRing(Right, RingProtection) {
|
if p.IsRing(Right, RingProtection) {
|
||||||
which -= p.CurRing[Right].Arm
|
which -= p.CurRing[Right].Bonus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return g.saveThrow(which, &p.Stats)
|
return g.saveThrow(which, &p.Stats)
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
|
|||||||
p.Flags.Clear(Awake)
|
p.Flags.Clear(Awake)
|
||||||
g.msg("a strange white mist envelops you and you fall asleep")
|
g.msg("a strange white mist envelops you and you fall asleep")
|
||||||
case TrapArrow:
|
case TrapArrow:
|
||||||
if g.swing(p.Stats.Lvl-1, p.Stats.Arm, 1) {
|
if g.swing(p.Stats.Lvl-1, p.Stats.ArmorClass, 1) {
|
||||||
p.Stats.HP -= g.roll(1, 6)
|
p.Stats.HP -= g.roll(1, 6)
|
||||||
if p.Stats.HP <= 0 {
|
if p.Stats.HP <= 0 {
|
||||||
g.msg("an arrow killed you")
|
g.msg("an arrow killed you")
|
||||||
@@ -263,7 +263,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
|
|||||||
g.teleport()
|
g.teleport()
|
||||||
g.mvaddch(tc.Y, tc.X, Trap)
|
g.mvaddch(tc.Y, tc.X, Trap)
|
||||||
case TrapDart:
|
case TrapDart:
|
||||||
if !g.swing(p.Stats.Lvl+1, p.Stats.Arm, 1) {
|
if !g.swing(p.Stats.Lvl+1, p.Stats.ArmorClass, 1) {
|
||||||
g.msg("a small dart whizzes by your ear and vanishes")
|
g.msg("a small dart whizzes by your ear and vanishes")
|
||||||
} else {
|
} else {
|
||||||
p.Stats.HP -= g.roll(1, 4)
|
p.Stats.HP -= g.roll(1, 4)
|
||||||
@@ -322,7 +322,7 @@ func (g *RogueGame) rndmove(who *Creature) Coord {
|
|||||||
// aren't wearing a magic ring (move.c rust_armor).
|
// aren't wearing a magic ring (move.c rust_armor).
|
||||||
func (g *RogueGame) rustArmor(arm *Object) {
|
func (g *RogueGame) rustArmor(arm *Object) {
|
||||||
if arm == nil || arm.Kind != KindArmor || arm.ArmorKind() == ArmorLeather ||
|
if arm == nil || arm.Kind != KindArmor || arm.ArmorKind() == ArmorLeather ||
|
||||||
arm.Arm >= 9 {
|
arm.ArmorClass >= 9 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ func (g *RogueGame) rustArmor(arm *Object) {
|
|||||||
g.msg("the rust vanishes instantly")
|
g.msg("the rust vanishes instantly")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arm.Arm++
|
arm.ArmorClass++
|
||||||
if !g.Options.Terse {
|
if !g.Options.Terse {
|
||||||
g.msg("your armor appears to be weaker now. Oh my!")
|
g.msg("your armor appears to be weaker now. Oh my!")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -111,9 +111,9 @@ func (g *RogueGame) putThings() {
|
|||||||
if g.Depth >= AmuletLevel && !g.HasAmulet {
|
if g.Depth >= AmuletLevel && !g.HasAmulet {
|
||||||
obj := newObject()
|
obj := newObject()
|
||||||
attachObj(&g.Level.Objects, obj)
|
attachObj(&g.Level.Objects, obj)
|
||||||
obj.Damage = "0x0"
|
obj.Damage = dice("0x0")
|
||||||
obj.HurlDmg = "0x0"
|
obj.HurlDmg = dice("0x0")
|
||||||
obj.Arm = 11
|
obj.ArmorClass = 11
|
||||||
obj.Kind = KindAmulet
|
obj.Kind = KindAmulet
|
||||||
// Put it somewhere
|
// Put it somewhere
|
||||||
obj.Pos, _ = g.findFloor(nil, 0, false)
|
obj.Pos, _ = g.findFloor(nil, 0, false)
|
||||||
|
|||||||
@@ -94,21 +94,24 @@ func (k ObjectKind) MergesInPack() bool {
|
|||||||
// Object is the _o arm of the C THING union: anything that can lie on the
|
// Object is the _o arm of the C THING union: anything that can lie on the
|
||||||
// floor or ride in a pack.
|
// floor or ride in a pack.
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Kind ObjectKind // what kind of object it is (o_type)
|
Kind ObjectKind // what kind of object it is (o_type)
|
||||||
Pos Coord // where it lives on the screen
|
Pos Coord // where it lives on the screen
|
||||||
Text string // what it says if you read it
|
Text string // what it says if you read it
|
||||||
Launch WeaponKind // what you need to launch it (noWeapon if none)
|
Launch WeaponKind // what you need to launch it (noWeapon if none)
|
||||||
PackCh byte // what character it is in the pack
|
PackCh byte // what character it is in the pack
|
||||||
Damage string // damage if used like sword
|
Damage DiceSpec // damage if used like sword
|
||||||
HurlDmg string // damage if thrown
|
HurlDmg DiceSpec // damage if thrown
|
||||||
Count int // count for plural objects
|
Count int // count for plural objects
|
||||||
Which int // which object of a type it is (index for the Kind's table)
|
Which int // which object of a type it is (index for the Kind's table)
|
||||||
HPlus int // plusses to hit
|
HPlus int // plusses to hit
|
||||||
DPlus int // plusses to damage
|
DPlus int // plusses to damage
|
||||||
Arm int // armor protection — overloaded as in C (see Charges/GoldVal)
|
ArmorClass int // armor protection (armor only)
|
||||||
Flags ObjFlags
|
Charges int // charges remaining (wands and staffs only)
|
||||||
Group int // group number for this object
|
GoldValue int // worth (gold piles only)
|
||||||
Label string // label for object
|
Bonus int // magic bonus (rings only: protection, add strength, ...)
|
||||||
|
Flags ObjFlags
|
||||||
|
Group int // group number for this object
|
||||||
|
Label string // label for object
|
||||||
}
|
}
|
||||||
|
|
||||||
// newObject is list.c new_item() for objects: a zeroed Object with the
|
// newObject is list.c new_item() for objects: a zeroed Object with the
|
||||||
@@ -117,18 +120,6 @@ func newObject() *Object {
|
|||||||
return &Object{Launch: noWeapon}
|
return &Object{Launch: noWeapon}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Charges is the o_charges alias for Arm on wands and staffs.
|
|
||||||
func (o *Object) Charges() int { return o.Arm }
|
|
||||||
|
|
||||||
// SetCharges stores a charge count in the overloaded Arm field.
|
|
||||||
func (o *Object) SetCharges(n int) { o.Arm = n }
|
|
||||||
|
|
||||||
// GoldVal is the o_goldval alias for Arm on gold piles.
|
|
||||||
func (o *Object) GoldVal() int { return o.Arm }
|
|
||||||
|
|
||||||
// SetGoldVal stores a gold value in the overloaded Arm field.
|
|
||||||
func (o *Object) SetGoldVal(n int) { o.Arm = n }
|
|
||||||
|
|
||||||
// PotionKind returns Which as a potion kind; valid only for KindPotion.
|
// PotionKind returns Which as a potion kind; valid only for KindPotion.
|
||||||
func (o *Object) PotionKind() PotionKind { return PotionKind(o.Which) }
|
func (o *Object) PotionKind() PotionKind { return PotionKind(o.Which) }
|
||||||
|
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ func (g *RogueGame) pickUp(ch byte) {
|
|||||||
if obj == nil {
|
if obj == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.money(obj.GoldVal())
|
g.money(obj.GoldValue)
|
||||||
detachObj(&g.Level.Objects, obj)
|
detachObj(&g.Level.Objects, obj)
|
||||||
p.Room.GoldVal = 0
|
p.Room.GoldVal = 0
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -153,19 +153,19 @@ func (g *RogueGame) quaff() {
|
|||||||
}
|
}
|
||||||
case PotionRestoreStrength:
|
case PotionRestoreStrength:
|
||||||
if p.IsRing(Left, RingAddStrength) {
|
if p.IsRing(Left, RingAddStrength) {
|
||||||
addStr(&p.Stats.Str, -p.CurRing[Left].Arm)
|
addStr(&p.Stats.Str, -p.CurRing[Left].Bonus)
|
||||||
}
|
}
|
||||||
if p.IsRing(Right, RingAddStrength) {
|
if p.IsRing(Right, RingAddStrength) {
|
||||||
addStr(&p.Stats.Str, -p.CurRing[Right].Arm)
|
addStr(&p.Stats.Str, -p.CurRing[Right].Bonus)
|
||||||
}
|
}
|
||||||
if p.Stats.Str < p.MaxStats.Str {
|
if p.Stats.Str < p.MaxStats.Str {
|
||||||
p.Stats.Str = p.MaxStats.Str
|
p.Stats.Str = p.MaxStats.Str
|
||||||
}
|
}
|
||||||
if p.IsRing(Left, RingAddStrength) {
|
if p.IsRing(Left, RingAddStrength) {
|
||||||
addStr(&p.Stats.Str, p.CurRing[Left].Arm)
|
addStr(&p.Stats.Str, p.CurRing[Left].Bonus)
|
||||||
}
|
}
|
||||||
if p.IsRing(Right, RingAddStrength) {
|
if p.IsRing(Right, RingAddStrength) {
|
||||||
addStr(&p.Stats.Str, p.CurRing[Right].Arm)
|
addStr(&p.Stats.Str, p.CurRing[Right].Bonus)
|
||||||
}
|
}
|
||||||
g.msg("hey, this tastes great. It make you feel warm all over")
|
g.msg("hey, this tastes great. It make you feel warm all over")
|
||||||
case PotionBlindness:
|
case PotionBlindness:
|
||||||
@@ -212,7 +212,7 @@ func (g *RogueGame) doPot(kind PotionKind, knowit bool) {
|
|||||||
func (o *Object) isMagic() bool {
|
func (o *Object) isMagic() bool {
|
||||||
switch o.Kind {
|
switch o.Kind {
|
||||||
case KindArmor:
|
case KindArmor:
|
||||||
return o.Flags.Has(Protected) || o.Arm != aClass[o.Which]
|
return o.Flags.Has(Protected) || o.ArmorClass != aClass[o.Which]
|
||||||
case KindWeapon:
|
case KindWeapon:
|
||||||
return o.HPlus != 0 || o.DPlus != 0
|
return o.HPlus != 0 || o.DPlus != 0
|
||||||
case KindPotion, KindScroll, KindWand, KindRing, KindAmulet:
|
case KindPotion, KindScroll, KindWand, KindRing, KindAmulet:
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (g *RogueGame) ringOn() {
|
|||||||
// Calculate the effect it has on the poor guy.
|
// Calculate the effect it has on the poor guy.
|
||||||
switch obj.RingKind() {
|
switch obj.RingKind() {
|
||||||
case RingAddStrength:
|
case RingAddStrength:
|
||||||
g.chgStr(obj.Arm)
|
g.chgStr(obj.Bonus)
|
||||||
case RingSeeInvisible:
|
case RingSeeInvisible:
|
||||||
g.invisOn()
|
g.invisOn()
|
||||||
case RingAggravateMonsters:
|
case RingAggravateMonsters:
|
||||||
@@ -168,7 +168,7 @@ func ringNum(g *RogueGame, obj *Object) string {
|
|||||||
}
|
}
|
||||||
switch obj.RingKind() {
|
switch obj.RingKind() {
|
||||||
case RingProtection, RingAddStrength, RingIncreaseDamage, RingDexterity:
|
case RingProtection, RingAddStrength, RingIncreaseDamage, RingDexterity:
|
||||||
return fmt.Sprintf(" [%s]", num(obj.Arm, 0, Ring))
|
return fmt.Sprintf(" [%s]", num(obj.Bonus, 0, Ring))
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
10
game/rip.go
10
game/rip.go
@@ -124,8 +124,8 @@ func (g *RogueGame) totalWinner() {
|
|||||||
obj.Flags.Set(Known)
|
obj.Flags.Set(Known)
|
||||||
case KindArmor:
|
case KindArmor:
|
||||||
worth = it.Armors[obj.Which].Worth
|
worth = it.Armors[obj.Which].Worth
|
||||||
worth += (9 - obj.Arm) * 100
|
worth += (9 - obj.ArmorClass) * 100
|
||||||
worth += 10 * (aClass[obj.Which] - obj.Arm)
|
worth += 10 * (aClass[obj.Which] - obj.ArmorClass)
|
||||||
obj.Flags.Set(Known)
|
obj.Flags.Set(Known)
|
||||||
case KindScroll:
|
case KindScroll:
|
||||||
op := &it.Scrolls[obj.Which]
|
op := &it.Scrolls[obj.Which]
|
||||||
@@ -146,8 +146,8 @@ func (g *RogueGame) totalWinner() {
|
|||||||
worth = op.Worth
|
worth = op.Worth
|
||||||
if obj.RingKind() == RingAddStrength || obj.RingKind() == RingIncreaseDamage ||
|
if obj.RingKind() == RingAddStrength || obj.RingKind() == RingIncreaseDamage ||
|
||||||
obj.RingKind() == RingProtection || obj.RingKind() == RingDexterity {
|
obj.RingKind() == RingProtection || obj.RingKind() == RingDexterity {
|
||||||
if obj.Arm > 0 {
|
if obj.Bonus > 0 {
|
||||||
worth += obj.Arm * 100
|
worth += obj.Bonus * 100
|
||||||
} else {
|
} else {
|
||||||
worth = 10
|
worth = 10
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ func (g *RogueGame) totalWinner() {
|
|||||||
case KindWand:
|
case KindWand:
|
||||||
op := &it.Sticks[obj.Which]
|
op := &it.Sticks[obj.Which]
|
||||||
worth = op.Worth
|
worth = op.Worth
|
||||||
worth += 20 * obj.Charges()
|
worth += 20 * obj.Charges
|
||||||
if !obj.Flags.Has(Known) {
|
if !obj.Flags.Has(Known) {
|
||||||
worth /= 2
|
worth /= 2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func (g *RogueGame) doRooms() {
|
|||||||
if g.rnd(2) == 0 && (!g.HasAmulet || g.Depth >= g.MaxDepth) {
|
if g.rnd(2) == 0 && (!g.HasAmulet || g.Depth >= g.MaxDepth) {
|
||||||
gold := newObject()
|
gold := newObject()
|
||||||
rp.GoldVal = g.goldCalc()
|
rp.GoldVal = g.goldCalc()
|
||||||
gold.SetGoldVal(rp.GoldVal)
|
gold.GoldValue = rp.GoldVal
|
||||||
rp.Gold, _ = g.findFloorIn(rp, 0, false)
|
rp.Gold, _ = g.findFloorIn(rp, 0, false)
|
||||||
gold.Pos = rp.Gold
|
gold.Pos = rp.Gold
|
||||||
g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold)
|
g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold)
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ import (
|
|||||||
|
|
||||||
// saveFormatVersion identifies the snapshot layout; it changes whenever a
|
// saveFormatVersion identifies the snapshot layout; it changes whenever a
|
||||||
// field rename or retype would make gob silently mis-decode an older
|
// field rename or retype would make gob silently mis-decode an older
|
||||||
// save ("go2": Object.Type became Kind ObjectKind).
|
// save ("go2": Object.Type became Kind ObjectKind; "go3": Object.Arm split
|
||||||
const saveFormatVersion = Release + "-go2"
|
// into ArmorClass/Charges/GoldValue/Bonus, damage strings became
|
||||||
|
// DiceSpec).
|
||||||
|
const saveFormatVersion = Release + "-go3"
|
||||||
|
|
||||||
// destRef encodes a monster's chase target (state.c rs_write_thing's
|
// destRef encodes a monster's chase target (state.c rs_write_thing's
|
||||||
// (kind, index) pairs).
|
// (kind, index) pairs).
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
|
|||||||
g.HasAmulet = true
|
g.HasAmulet = true
|
||||||
g.Items.Potions[PotionHealing].Know = true
|
g.Items.Potions[PotionHealing].Know = true
|
||||||
g.Items.Scrolls[ScrollMagicMapping].Guess = "map???"
|
g.Items.Scrolls[ScrollMagicMapping].Guess = "map???"
|
||||||
g.Monsters['F'-'A'].Stats.Dmg = "3x1" // mutated bestiary must survive
|
g.Monsters['F'-'A'].Stats.Dmg = dice("3x1") // mutated bestiary must survive
|
||||||
if len(g.Level.Monsters) > 0 {
|
if len(g.Level.Monsters) > 0 {
|
||||||
g.Level.Monsters[0].Flags.Set(Awake)
|
g.Level.Monsters[0].Flags.Set(Awake)
|
||||||
g.Level.Monsters[0].Dest = &g.Player.Pos
|
g.Level.Monsters[0].Dest = &g.Player.Pos
|
||||||
@@ -48,7 +48,7 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
|
|||||||
if h.Items.Scrolls[ScrollMagicMapping].Guess != "map???" {
|
if h.Items.Scrolls[ScrollMagicMapping].Guess != "map???" {
|
||||||
t.Error("scroll guess lost")
|
t.Error("scroll guess lost")
|
||||||
}
|
}
|
||||||
if h.Monsters['F'-'A'].Stats.Dmg != "3x1" {
|
if h.Monsters['F'-'A'].Stats.Dmg.String() != "3x1" {
|
||||||
t.Error("mutated bestiary lost")
|
t.Error("mutated bestiary lost")
|
||||||
}
|
}
|
||||||
if h.Rng.Seed != g.Rng.Seed {
|
if h.Rng.Seed != g.Rng.Seed {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func (g *RogueGame) readScroll() {
|
|||||||
g.msg("your hands begin to glow %s", g.pickColor("red"))
|
g.msg("your hands begin to glow %s", g.pickColor("red"))
|
||||||
case ScrollEnchantArmor:
|
case ScrollEnchantArmor:
|
||||||
if p.CurArmor != nil {
|
if p.CurArmor != nil {
|
||||||
p.CurArmor.Arm--
|
p.CurArmor.ArmorClass--
|
||||||
p.CurArmor.Flags.Clear(Cursed)
|
p.CurArmor.Flags.Clear(Cursed)
|
||||||
g.msg("your armor glows %s for a moment", g.pickColor("silver"))
|
g.msg("your armor glows %s for a moment", g.pickColor("silver"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func (g *RogueGame) doZap() {
|
|||||||
g.msg("you can't zap with that!")
|
g.msg("you can't zap with that!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if obj.Charges() == 0 {
|
if obj.Charges == 0 {
|
||||||
g.msg("nothing happens")
|
g.msg("nothing happens")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ func (g *RogueGame) doZap() {
|
|||||||
g.Items.Sticks[WandMagicMissile].Know = true
|
g.Items.Sticks[WandMagicMissile].Know = true
|
||||||
bolt := newObject()
|
bolt := newObject()
|
||||||
bolt.Kind = KindGold // C set o_type='*': draws a '*' and is not a weapon
|
bolt.Kind = KindGold // C set o_type='*': draws a '*' and is not a weapon
|
||||||
bolt.HurlDmg = "1x4"
|
bolt.HurlDmg = dice("1x4")
|
||||||
bolt.HPlus = 100
|
bolt.HPlus = 100
|
||||||
bolt.DPlus = 1
|
bolt.DPlus = 1
|
||||||
bolt.Flags = Missile
|
bolt.Flags = Missile
|
||||||
@@ -167,7 +167,7 @@ func (g *RogueGame) doZap() {
|
|||||||
g.Items.Sticks[obj.Which].Know = true
|
g.Items.Sticks[obj.Which].Know = true
|
||||||
case WandNothing:
|
case WandNothing:
|
||||||
}
|
}
|
||||||
obj.SetCharges(obj.Charges() - 1)
|
obj.Charges--
|
||||||
}
|
}
|
||||||
|
|
||||||
// drain does the drain-hit-points-from-player schtick (sticks.c drain).
|
// drain does the drain-hit-points-from-player schtick (sticks.c drain).
|
||||||
@@ -213,7 +213,7 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {
|
|||||||
bolt := newObject()
|
bolt := newObject()
|
||||||
bolt.Kind = KindWeapon
|
bolt.Kind = KindWeapon
|
||||||
bolt.Which = int(WeaponFlame)
|
bolt.Which = int(WeaponFlame)
|
||||||
bolt.HurlDmg = "6x6"
|
bolt.HurlDmg = dice("6x6")
|
||||||
bolt.HPlus = 100
|
bolt.HPlus = 100
|
||||||
bolt.DPlus = 0
|
bolt.DPlus = 0
|
||||||
g.Items.Weapons[WeaponFlame].Name = name
|
g.Items.Weapons[WeaponFlame].Name = name
|
||||||
@@ -322,17 +322,17 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {
|
|||||||
// fixStick sets up a new wand or staff (sticks.c fix_stick).
|
// fixStick sets up a new wand or staff (sticks.c fix_stick).
|
||||||
func (g *RogueGame) fixStick(cur *Object) {
|
func (g *RogueGame) fixStick(cur *Object) {
|
||||||
if g.Items.WandType[cur.Which] == "staff" {
|
if g.Items.WandType[cur.Which] == "staff" {
|
||||||
cur.Damage = "2x3"
|
cur.Damage = dice("2x3")
|
||||||
} else {
|
} else {
|
||||||
cur.Damage = "1x1"
|
cur.Damage = dice("1x1")
|
||||||
}
|
}
|
||||||
cur.HurlDmg = "1x1"
|
cur.HurlDmg = dice("1x1")
|
||||||
|
|
||||||
switch cur.WandKind() {
|
switch cur.WandKind() {
|
||||||
case WandLight:
|
case WandLight:
|
||||||
cur.SetCharges(g.rnd(10) + 10)
|
cur.Charges = g.rnd(10) + 10
|
||||||
default:
|
default:
|
||||||
cur.SetCharges(g.rnd(5) + 3)
|
cur.Charges = g.rnd(5) + 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ func chargeStr(g *RogueGame, obj *Object) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if g.Options.Terse {
|
if g.Options.Terse {
|
||||||
return fmt.Sprintf(" [%d]", obj.Charges())
|
return fmt.Sprintf(" [%d]", obj.Charges)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(" [%d charges]", obj.Charges())
|
return fmt.Sprintf(" [%d charges]", obj.Charges)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package game
|
|||||||
// change during play) are cloned into RogueGame.Items by NewGame.
|
// change during play) are cloned into RogueGame.Items by NewGame.
|
||||||
|
|
||||||
// initStats is the C INIT_STATS: the player's starting statistics.
|
// initStats is the C INIT_STATS: the player's starting statistics.
|
||||||
var initStats = Stats{Str: 16, Exp: 0, Lvl: 1, Arm: 10, HP: 12, Dmg: "1x4", MaxHP: 12}
|
var initStats = Stats{Str: 16, Exp: 0, Lvl: 1, ArmorClass: 10, HP: 12, Dmg: dice("1x4"), MaxHP: 12}
|
||||||
|
|
||||||
// aClass is extern.c a_class[]: armor class for each armor type.
|
// aClass is extern.c a_class[]: armor class for each armor type.
|
||||||
var aClass = [NumArmorTypes]int{
|
var aClass = [NumArmorTypes]int{
|
||||||
@@ -47,32 +47,32 @@ var invTName = []string{"Overwrite", "Slow", "Clear"}
|
|||||||
// rolled from the level at creation time.
|
// rolled from the level at creation time.
|
||||||
var monsterTable = [26]MonsterKind{
|
var monsterTable = [26]MonsterKind{
|
||||||
/* Name CARRY FLAGS str exp lvl arm hp dmg */
|
/* Name CARRY FLAGS str exp lvl arm hp dmg */
|
||||||
{"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, "0x0/0x0", 0}},
|
{"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, dice("0x0/0x0"), 0}},
|
||||||
{"bat", 0, Flying, Stats{10, 1, 1, 3, 1, "1x2", 0}},
|
{"bat", 0, Flying, Stats{10, 1, 1, 3, 1, dice("1x2"), 0}},
|
||||||
{"centaur", 15, 0, Stats{10, 17, 4, 4, 1, "1x2/1x5/1x5", 0}},
|
{"centaur", 15, 0, Stats{10, 17, 4, 4, 1, dice("1x2/1x5/1x5"), 0}},
|
||||||
{"dragon", 100, Mean, Stats{10, 5000, 10, -1, 1, "1x8/1x8/3x10", 0}},
|
{"dragon", 100, Mean, Stats{10, 5000, 10, -1, 1, dice("1x8/1x8/3x10"), 0}},
|
||||||
{"emu", 0, Mean, Stats{10, 2, 1, 7, 1, "1x2", 0}},
|
{"emu", 0, Mean, Stats{10, 2, 1, 7, 1, dice("1x2"), 0}},
|
||||||
{"venus flytrap", 0, Mean, Stats{10, 80, 8, 3, 1, "%%%x0", 0}},
|
{"venus flytrap", 0, Mean, Stats{10, 80, 8, 3, 1, dice("%%%x0"), 0}},
|
||||||
{"griffin", 20, Mean | Flying | Regenerates, Stats{10, 2000, 13, 2, 1, "4x3/3x5", 0}},
|
{"griffin", 20, Mean | Flying | Regenerates, Stats{10, 2000, 13, 2, 1, dice("4x3/3x5"), 0}},
|
||||||
{"hobgoblin", 0, Mean, Stats{10, 3, 1, 5, 1, "1x8", 0}},
|
{"hobgoblin", 0, Mean, Stats{10, 3, 1, 5, 1, dice("1x8"), 0}},
|
||||||
{"ice monster", 0, 0, Stats{10, 5, 1, 9, 1, "0x0", 0}},
|
{"ice monster", 0, 0, Stats{10, 5, 1, 9, 1, dice("0x0"), 0}},
|
||||||
{"jabberwock", 70, 0, Stats{10, 3000, 15, 6, 1, "2x12/2x4", 0}},
|
{"jabberwock", 70, 0, Stats{10, 3000, 15, 6, 1, dice("2x12/2x4"), 0}},
|
||||||
{"kestrel", 0, Mean | Flying, Stats{10, 1, 1, 7, 1, "1x4", 0}},
|
{"kestrel", 0, Mean | Flying, Stats{10, 1, 1, 7, 1, dice("1x4"), 0}},
|
||||||
{"leprechaun", 0, 0, Stats{10, 10, 3, 8, 1, "1x1", 0}},
|
{"leprechaun", 0, 0, Stats{10, 10, 3, 8, 1, dice("1x1"), 0}},
|
||||||
{"medusa", 40, Mean, Stats{10, 200, 8, 2, 1, "3x4/3x4/2x5", 0}},
|
{"medusa", 40, Mean, Stats{10, 200, 8, 2, 1, dice("3x4/3x4/2x5"), 0}},
|
||||||
{"nymph", 100, 0, Stats{10, 37, 3, 9, 1, "0x0", 0}},
|
{"nymph", 100, 0, Stats{10, 37, 3, 9, 1, dice("0x0"), 0}},
|
||||||
{"orc", 15, Greedy, Stats{10, 5, 1, 6, 1, "1x8", 0}},
|
{"orc", 15, Greedy, Stats{10, 5, 1, 6, 1, dice("1x8"), 0}},
|
||||||
{"phantom", 0, Invisible, Stats{10, 120, 8, 3, 1, "4x4", 0}},
|
{"phantom", 0, Invisible, Stats{10, 120, 8, 3, 1, dice("4x4"), 0}},
|
||||||
{"quagga", 0, Mean, Stats{10, 15, 3, 3, 1, "1x5/1x5", 0}},
|
{"quagga", 0, Mean, Stats{10, 15, 3, 3, 1, dice("1x5/1x5"), 0}},
|
||||||
{"rattlesnake", 0, Mean, Stats{10, 9, 2, 3, 1, "1x6", 0}},
|
{"rattlesnake", 0, Mean, Stats{10, 9, 2, 3, 1, dice("1x6"), 0}},
|
||||||
{"snake", 0, Mean, Stats{10, 2, 1, 5, 1, "1x3", 0}},
|
{"snake", 0, Mean, Stats{10, 2, 1, 5, 1, dice("1x3"), 0}},
|
||||||
{"troll", 50, Regenerates | Mean, Stats{10, 120, 6, 4, 1, "1x8/1x8/2x6", 0}},
|
{"troll", 50, Regenerates | Mean, Stats{10, 120, 6, 4, 1, dice("1x8/1x8/2x6"), 0}},
|
||||||
{"black unicorn", 0, Mean, Stats{10, 190, 7, -2, 1, "1x9/1x9/2x9", 0}},
|
{"black unicorn", 0, Mean, Stats{10, 190, 7, -2, 1, dice("1x9/1x9/2x9"), 0}},
|
||||||
{"vampire", 20, Regenerates | Mean, Stats{10, 350, 8, 1, 1, "1x10", 0}},
|
{"vampire", 20, Regenerates | Mean, Stats{10, 350, 8, 1, 1, dice("1x10"), 0}},
|
||||||
{"wraith", 0, 0, Stats{10, 55, 5, 4, 1, "1x6", 0}},
|
{"wraith", 0, 0, Stats{10, 55, 5, 4, 1, dice("1x6"), 0}},
|
||||||
{"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, "4x4", 0}},
|
{"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, dice("4x4"), 0}},
|
||||||
{"yeti", 30, 0, Stats{10, 50, 4, 6, 1, "1x6/1x6", 0}},
|
{"yeti", 30, 0, Stats{10, 50, 4, 6, 1, dice("1x6/1x6"), 0}},
|
||||||
{"zombie", 0, Mean, Stats{10, 6, 2, 8, 1, "1x8", 0}},
|
{"zombie", 0, Mean, Stats{10, 6, 2, 8, 1, dice("1x8"), 0}},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base ObjInfo tables (extern.c). These are templates: NewGame copies them
|
// Base ObjInfo tables (extern.c). These are templates: NewGame copies them
|
||||||
@@ -323,5 +323,5 @@ var helpStr = []helpEntry{
|
|||||||
// the Go save format does not use them.
|
// the Go save format does not use them.
|
||||||
const (
|
const (
|
||||||
Release = "5.4.4"
|
Release = "5.4.4"
|
||||||
Version = "rogue (sneak.berlin/go/rogue port of rogueforge 5.4.4)"
|
Version = "rogue (git.eeqj.de/sneak/rgoue port of rogueforge 5.4.4)"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -69,11 +69,11 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
|
|||||||
case KindArmor:
|
case KindArmor:
|
||||||
sp := it.Armors[which].Name
|
sp := it.Armors[which].Name
|
||||||
if obj.Flags.Has(Known) {
|
if obj.Flags.Has(Known) {
|
||||||
fmt.Fprintf(&pb, "%s %s [", num(aClass[which]-obj.Arm, 0, Armor), sp)
|
fmt.Fprintf(&pb, "%s %s [", num(aClass[which]-obj.ArmorClass, 0, Armor), sp)
|
||||||
if !g.Options.Terse {
|
if !g.Options.Terse {
|
||||||
pb.WriteString("protection ")
|
pb.WriteString("protection ")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&pb, "%d]", 10-obj.Arm)
|
fmt.Fprintf(&pb, "%d]", 10-obj.ArmorClass)
|
||||||
} else {
|
} else {
|
||||||
pb.WriteString(sp)
|
pb.WriteString(sp)
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
|
|||||||
case KindAmulet:
|
case KindAmulet:
|
||||||
pb.WriteString("The Amulet of Yendor")
|
pb.WriteString("The Amulet of Yendor")
|
||||||
case KindGold:
|
case KindGold:
|
||||||
fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldVal())
|
fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
out := pb.String()
|
out := pb.String()
|
||||||
@@ -168,7 +168,7 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
|
|||||||
p.CurRing[hand] = nil
|
p.CurRing[hand] = nil
|
||||||
switch obj.RingKind() {
|
switch obj.RingKind() {
|
||||||
case RingAddStrength:
|
case RingAddStrength:
|
||||||
g.chgStr(-obj.Arm)
|
g.chgStr(-obj.Bonus)
|
||||||
case RingSeeInvisible:
|
case RingSeeInvisible:
|
||||||
g.unsee(0)
|
g.unsee(0)
|
||||||
g.Extinguish(DUnsee)
|
g.Extinguish(DUnsee)
|
||||||
@@ -180,9 +180,9 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
|
|||||||
// newThing returns a new random thing for the dungeon (things.c new_thing).
|
// newThing returns a new random thing for the dungeon (things.c new_thing).
|
||||||
func (g *RogueGame) newThing() *Object {
|
func (g *RogueGame) newThing() *Object {
|
||||||
cur := newObject()
|
cur := newObject()
|
||||||
cur.Damage = "0x0"
|
cur.Damage = dice("0x0")
|
||||||
cur.HurlDmg = "0x0"
|
cur.HurlDmg = dice("0x0")
|
||||||
cur.Arm = 11
|
cur.ArmorClass = 11
|
||||||
cur.Count = 1
|
cur.Count = 1
|
||||||
|
|
||||||
// Decide what kind of object it will be; if we haven't had food for a
|
// Decide what kind of object it will be; if we haven't had food for a
|
||||||
@@ -219,20 +219,20 @@ func (g *RogueGame) newThing() *Object {
|
|||||||
case 4:
|
case 4:
|
||||||
cur.Kind = KindArmor
|
cur.Kind = KindArmor
|
||||||
cur.Which = pickOne(g, g.Items.Armors[:])
|
cur.Which = pickOne(g, g.Items.Armors[:])
|
||||||
cur.Arm = aClass[cur.Which]
|
cur.ArmorClass = aClass[cur.Which]
|
||||||
if r := g.rnd(100); r < 20 {
|
if r := g.rnd(100); r < 20 {
|
||||||
cur.Flags.Set(Cursed)
|
cur.Flags.Set(Cursed)
|
||||||
cur.Arm += g.rnd(3) + 1
|
cur.ArmorClass += g.rnd(3) + 1
|
||||||
} else if r < 28 {
|
} else if r < 28 {
|
||||||
cur.Arm -= g.rnd(3) + 1
|
cur.ArmorClass -= g.rnd(3) + 1
|
||||||
}
|
}
|
||||||
case 5:
|
case 5:
|
||||||
cur.Kind = KindRing
|
cur.Kind = KindRing
|
||||||
cur.Which = pickOne(g, g.Items.Rings[:])
|
cur.Which = pickOne(g, g.Items.Rings[:])
|
||||||
switch cur.RingKind() {
|
switch cur.RingKind() {
|
||||||
case RingAddStrength, RingProtection, RingDexterity, RingIncreaseDamage:
|
case RingAddStrength, RingProtection, RingDexterity, RingIncreaseDamage:
|
||||||
if cur.Arm = g.rnd(3); cur.Arm == 0 {
|
if cur.Bonus = g.rnd(3); cur.Bonus == 0 {
|
||||||
cur.Arm = -1
|
cur.Bonus = -1
|
||||||
cur.Flags.Set(Cursed)
|
cur.Flags.Set(Cursed)
|
||||||
}
|
}
|
||||||
case RingAggravateMonsters, RingTeleportation:
|
case RingAggravateMonsters, RingTeleportation:
|
||||||
|
|||||||
@@ -374,13 +374,13 @@ type Coord struct {
|
|||||||
|
|
||||||
// Stats describes a fighting being (rogue.h struct stats).
|
// Stats describes a fighting being (rogue.h struct stats).
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
Str int // strength (s_str; 3..31)
|
Str int // strength (s_str; 3..31)
|
||||||
Exp int // experience
|
Exp int // experience
|
||||||
Lvl int // level of mastery
|
Lvl int // level of mastery
|
||||||
Arm int // armor class
|
ArmorClass int // armor class (s_arm)
|
||||||
HP int // hit points (s_hpt)
|
HP int // hit points (s_hpt)
|
||||||
Dmg string // damage dice, e.g. "1x4/1x2"
|
Dmg DiceSpec // damage dice, e.g. "1x4/1x2"
|
||||||
MaxHP int
|
MaxHP int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Room describes a room or passage network (rogue.h struct room).
|
// Room describes a room or passage network (rogue.h struct room).
|
||||||
|
|||||||
@@ -123,20 +123,20 @@ func (g *RogueGame) wield() {
|
|||||||
|
|
||||||
// initWeaps is the weapons.c init_dam[] table.
|
// initWeaps is the weapons.c init_dam[] table.
|
||||||
var initWeaps = [NumWeaponTypes]struct {
|
var initWeaps = [NumWeaponTypes]struct {
|
||||||
dam string // damage when wielded
|
dam DiceSpec // damage when wielded
|
||||||
hrl string // damage when thrown
|
hrl DiceSpec // damage when thrown
|
||||||
launch WeaponKind // launching weapon
|
launch WeaponKind // launching weapon
|
||||||
flags ObjFlags
|
flags ObjFlags
|
||||||
}{
|
}{
|
||||||
{"2x4", "1x3", noWeapon, 0}, // WeaponMace
|
{dice("2x4"), dice("1x3"), noWeapon, 0}, // WeaponMace
|
||||||
{"3x4", "1x2", noWeapon, 0}, // Long sword
|
{dice("3x4"), dice("1x2"), noWeapon, 0}, // Long sword
|
||||||
{"1x1", "1x1", noWeapon, 0}, // WeaponBow
|
{dice("1x1"), dice("1x1"), noWeapon, 0}, // WeaponBow
|
||||||
{"1x1", "2x3", WeaponBow, Stackable | Missile}, // WeaponArrow
|
{dice("1x1"), dice("2x3"), WeaponBow, Stackable | Missile}, // WeaponArrow
|
||||||
{"1x6", "1x4", noWeapon, Missile}, // WeaponDagger
|
{dice("1x6"), dice("1x4"), noWeapon, Missile}, // WeaponDagger
|
||||||
{"4x4", "1x2", noWeapon, 0}, // 2h sword
|
{dice("4x4"), dice("1x2"), noWeapon, 0}, // 2h sword
|
||||||
{"1x1", "1x3", noWeapon, Stackable | Missile}, // WeaponDart
|
{dice("1x1"), dice("1x3"), noWeapon, Stackable | Missile}, // WeaponDart
|
||||||
{"1x2", "2x4", noWeapon, Stackable | Missile}, // Shuriken
|
{dice("1x2"), dice("2x4"), noWeapon, Stackable | Missile}, // Shuriken
|
||||||
{"2x3", "1x6", noWeapon, Missile}, // WeaponSpear
|
{dice("2x3"), dice("1x6"), noWeapon, Missile}, // WeaponSpear
|
||||||
}
|
}
|
||||||
|
|
||||||
// initWeapon sets up a new weapon (weapons.c init_weapon).
|
// initWeapon sets up a new weapon (weapons.c init_weapon).
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ func (g *RogueGame) createObj() {
|
|||||||
obj.HPlus += g.rnd(3) + 1
|
obj.HPlus += g.rnd(3) + 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
obj.Arm = aClass[obj.Which]
|
obj.ArmorClass = aClass[obj.Which]
|
||||||
if bless == '-' {
|
if bless == '-' {
|
||||||
obj.Arm += g.rnd(3) + 1
|
obj.ArmorClass += g.rnd(3) + 1
|
||||||
}
|
}
|
||||||
if bless == '+' {
|
if bless == '+' {
|
||||||
obj.Arm -= g.rnd(3) + 1
|
obj.ArmorClass -= g.rnd(3) + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case obj.Kind == KindRing:
|
case obj.Kind == KindRing:
|
||||||
@@ -54,9 +54,9 @@ func (g *RogueGame) createObj() {
|
|||||||
g.Msgs.Mpos = 0
|
g.Msgs.Mpos = 0
|
||||||
if bless == '-' {
|
if bless == '-' {
|
||||||
obj.Flags.Set(Cursed)
|
obj.Flags.Set(Cursed)
|
||||||
obj.Arm = -1
|
obj.Bonus = -1
|
||||||
} else {
|
} else {
|
||||||
obj.Arm = g.rnd(2) + 1
|
obj.Bonus = g.rnd(2) + 1
|
||||||
}
|
}
|
||||||
case RingAggravateMonsters, RingTeleportation:
|
case RingAggravateMonsters, RingTeleportation:
|
||||||
obj.Flags.Set(Cursed)
|
obj.Flags.Set(Cursed)
|
||||||
@@ -67,7 +67,7 @@ func (g *RogueGame) createObj() {
|
|||||||
g.msg("how much?")
|
g.msg("how much?")
|
||||||
buf := ""
|
buf := ""
|
||||||
if g.getStr(&buf, g.scr.Std) == Norm {
|
if g.getStr(&buf, g.scr.Std) == Norm {
|
||||||
obj.SetGoldVal(cAtoi(buf))
|
obj.GoldValue = cAtoi(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.addPack(obj, false)
|
g.addPack(obj, false)
|
||||||
@@ -169,7 +169,7 @@ func (g *RogueGame) teleport() {
|
|||||||
if p.On(Held) {
|
if p.On(Held) {
|
||||||
p.Flags.Clear(Held)
|
p.Flags.Clear(Held)
|
||||||
p.VfHit = 0
|
p.VfHit = 0
|
||||||
g.Monsters['F'-'A'].Stats.Dmg = "000x0"
|
g.Monsters['F'-'A'].Stats.Dmg = dice("000x0")
|
||||||
}
|
}
|
||||||
g.NoMove = 0
|
g.NoMove = 0
|
||||||
g.Count = 0
|
g.Count = 0
|
||||||
|
|||||||
5
go.mod
5
go.mod
@@ -1,10 +1,11 @@
|
|||||||
module sneak.berlin/go/rogue
|
module git.eeqj.de/sneak/rgoue
|
||||||
|
|
||||||
go 1.25.7
|
go 1.25.7
|
||||||
|
|
||||||
|
require github.com/gdamore/tcell/v2 v2.13.10
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/encoding v1.0.1 // indirect
|
github.com/gdamore/encoding v1.0.1 // indirect
|
||||||
github.com/gdamore/tcell/v2 v2.13.10 // indirect
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
|
|
||||||
"sneak.berlin/go/rogue/game"
|
"git.eeqj.de/sneak/rgoue/game"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tcell renders game Windows on a tcell screen and turns key events into
|
// Tcell renders game Windows on a tcell screen and turns key events into
|
||||||
|
|||||||
Reference in New Issue
Block a user