diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 63070bb..28f6ffd 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -6,7 +6,7 @@ This document has two parts: codebase (every file, every function, every type, every global) as it exists on the `modern-rogue` branch. - **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. --- @@ -887,7 +887,7 @@ Declares `rd_score(SCORE*)` / `wr_score(SCORE*)` (implemented in save.c). # Part 2 — The Go Port Design -Module: **`sneak.berlin/go/rogue`** +Module: **`git.eeqj.de/sneak/rgoue`** ## 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: ``` -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 game/ package game — the port, one .go file per .c file game.go RogueGame struct, NewGame, Run (main.c) diff --git a/TODO.md b/TODO.md index 2edeccd..1496067 100644 --- a/TODO.md +++ b/TODO.md @@ -29,14 +29,19 @@ Refactor ground rules: # Next Step -Refactor step 3 (branch refactor/object-fields): un-overload Object -fields — split Object.Arm into ArmorClass/Charges/GoldValue; parse -damage dice ("1x4/1x2") once into a DiceSpec type at table definition -instead of per swing; save-format update with round-trip test -migration. +Adopt the house Go linting standards: copy .golangci.yml from the +prompts repo and bring game/, term/, and cmd/ lint-clean (the port is +greenfield code, so no exemptions apply). # 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): ObjectKind separates item category from map glyph (Object.Type byte → Kind ObjectKind with Glyph()); PotionKind/ScrollKind/RingKind/ @@ -71,48 +76,45 @@ migration. # Future Steps -1. Adopt the house Go linting standards: copy .golangci.yml from the - 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 +1. Refactor step 4: method renames, movement/world subsystem (doMove→moveHero, beTrapped→springTrap, rndmove→randomStep, doRooms/doPassages/doMaze→digRooms/digPassages/digMaze, chgStr→changeStrength, ...); remove the goto/label flows in doMove, 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, getItem→promptPackItem returning (obj, ok), getDir→promptDirection); int status codes (attack returning -1) become named results. Two or 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 onto *Player; monster/object list management and map queries consolidate onto *Level; RogueGame keeps turn orchestration and 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, 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); replace the gameEnd panic unwind with error-based turn results where 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. -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 playthrough (descend past level 5, use potions, scrolls, zapping, save/restore). Fix any panics, message mismatches, or divergences 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 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). -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. -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 constants; open design questions are resize policy, gameplay 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. diff --git a/cmd/rogue/main.go b/cmd/rogue/main.go index e6fe426..90febac 100644 --- a/cmd/rogue/main.go +++ b/cmd/rogue/main.go @@ -13,8 +13,8 @@ import ( "syscall" "time" - "sneak.berlin/go/rogue/game" - "sneak.berlin/go/rogue/term" + "git.eeqj.de/sneak/rgoue/game" + "git.eeqj.de/sneak/rgoue/term" ) func main() { diff --git a/game/command.go b/game/command.go index 061418a..d3a4b3a 100644 --- a/game/command.go +++ b/game/command.go @@ -410,7 +410,7 @@ func (g *RogueGame) wizardCommand(ch byte) { g.turnSee(p.On(SenseMonsters)) case CTRL('~'): if item := g.getItem("charge", KindWand); item != nil { - item.SetCharges(10000) + item.Charges = 10000 } case CTRL('I'): for range 9 { @@ -427,7 +427,7 @@ func (g *RogueGame) wizardCommand(ch byte) { obj = newObject() obj.Kind = KindArmor obj.Which = int(ArmorPlateMail) - obj.Arm = -5 + obj.ArmorClass = -5 obj.Flags.Set(Known) obj.Count = 1 p.CurArmor = obj diff --git a/game/dice.go b/game/dice.go new file mode 100644 index 0000000..5548360 --- /dev/null +++ b/game/dice.go @@ -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() +} diff --git a/game/dice_test.go b/game/dice_test.go new file mode 100644 index 0000000..df08051 --- /dev/null +++ b/game/dice_test.go @@ -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)) + } + } +} diff --git a/game/effects_test.go b/game/effects_test.go index faecadd..0bc86db 100644 --- a/game/effects_test.go +++ b/game/effects_test.go @@ -72,11 +72,11 @@ func TestReadEnchantArmor(t *testing.T) { ch := give(g, scr) g.scr.term.(*testTerm).input = []byte{ch} - before := g.Player.CurArmor.Arm + before := g.Player.CurArmor.ArmorClass 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", - 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.Delta = Coord{X: 1, Y: 0} // aim at the monster - charges := stick.Charges() + charges := stick.Charges g.doZap() if !tp.On(Slowed) { 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") } } diff --git a/game/fight.go b/game/fight.go index 96263c7..ebf22c7 100644 --- a/game/fight.go +++ b/game/fight.go @@ -1,10 +1,6 @@ package game -import ( - "fmt" - "strconv" - "strings" -) +import "strconv" // 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 p.Flags.Set(Held) 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 { g.death('F') } @@ -322,34 +318,34 @@ func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool p := &g.Player att := &thatt.Stats def := &thdef.Stats - var cp string + var attacks DiceSpec var hplus, dplus int if weap == nil { - cp = att.Dmg + attacks = att.Dmg } else { hplus = weap.HPlus dplus = weap.DPlus if weap == p.CurWeapon { if p.IsRing(Left, RingIncreaseDamage) { - dplus += p.CurRing[Left].Arm + dplus += p.CurRing[Left].Bonus } else if p.IsRing(Left, RingDexterity) { - hplus += p.CurRing[Left].Arm + hplus += p.CurRing[Left].Bonus } if p.IsRing(Right, RingIncreaseDamage) { - dplus += p.CurRing[Right].Arm + dplus += p.CurRing[Right].Bonus } 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 weap.Flags.Has(Missile) && p.CurWeapon != nil && WeaponKind(p.CurWeapon.Which) == weap.Launch { - cp = weap.HurlDmg + attacks = weap.HurlDmg hplus += p.CurWeapon.HPlus dplus += p.CurWeapon.DPlus } 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) { hplus += 4 } - defArm := def.Arm + defArm := def.ArmorClass if def == &p.Stats { if p.CurArmor != nil { - defArm = p.CurArmor.Arm + defArm = p.CurArmor.ArmorClass } if p.IsRing(Left, RingProtection) { - defArm -= p.CurRing[Left].Arm + defArm -= p.CurRing[Left].Bonus } if p.IsRing(Right, RingProtection) { - defArm -= p.CurRing[Right].Arm + defArm -= p.CurRing[Right].Bonus } } didHit := false - for cp != "" { - ndice := cAtoi(cp) - xi := strings.IndexByte(cp, 'x') - if xi < 0 { - break - } - cp = cp[xi+1:] - nsides := cAtoi(cp) + for _, atk := range attacks { 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] if damage > 0 { def.HP -= damage } didHit = true } - si := strings.IndexByte(cp, '/') - if si < 0 { - break - } - cp = cp[si+1:] } return didHit } @@ -531,7 +515,7 @@ func (g *RogueGame) killed(tp *Monster, pr bool) { case 'F': p.Flags.Clear(Held) p.VfHit = 0 - g.Monsters['F'-'A'].Stats.Dmg = "000x0" + g.Monsters['F'-'A'].Stats.Dmg = dice("000x0") case 'L': pos, ok := g.fallpos(tp.Pos) if ok { @@ -540,9 +524,9 @@ func (g *RogueGame) killed(tp *Monster, pr bool) { if ok && g.Depth >= g.MaxDepth { gold := newObject() gold.Kind = KindGold - gold.SetGoldVal(g.goldCalc()) + gold.GoldValue = g.goldCalc() 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) } diff --git a/game/fight_test.go b/game/fight_test.go index 4d10e80..52e93dd 100644 --- a/game/fight_test.go +++ b/game/fight_test.go @@ -23,8 +23,8 @@ func spawnAdjacent(g *RogueGame, typ byte) *Monster { func TestRollEmParsesMultiAttackDice(t *testing.T) { g := mkGame(t, 42) - att := &Creature{Stats: Stats{Str: 16, Lvl: 20, Dmg: "1x4/1x4/1x4"}} - def := &Creature{Stats: Stats{Arm: 10, HP: 1000}} + att := &Creature{Stats: Stats{Str: 16, Lvl: 20, Dmg: dice("1x4/1x4/1x4")}} + def := &Creature{Stats: Stats{ArmorClass: 10, HP: 1000}} def.Flags.Set(Awake) // With attacker level 20 vs armor 10, swing always hits // (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of diff --git a/game/init.go b/game/init.go index 8830f56..c964539 100644 --- a/game/init.go +++ b/game/init.go @@ -20,7 +20,7 @@ func (g *RogueGame) initPlayer() { obj = newObject() obj.Kind = KindArmor obj.Which = int(ArmorRingMail) - obj.Arm = aClass[ArmorRingMail] - 1 + obj.ArmorClass = aClass[ArmorRingMail] - 1 obj.Flags.Set(Known) obj.Count = 1 p.CurArmor = obj diff --git a/game/io.go b/game/io.go index 5fea9e4..a0a0ade 100644 --- a/game/io.go +++ b/game/io.go @@ -142,9 +142,9 @@ func (g *RogueGame) status() { p := &g.Player // If nothing has changed since the last status, don't bother. - temp := p.Stats.Arm + temp := p.Stats.ArmorClass 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 && s.pur == p.Purse && s.arm == temp && s.str == p.Stats.Str && diff --git a/game/misc.go b/game/misc.go index b4706c5..b6efa00 100644 --- a/game/misc.go +++ b/game/misc.go @@ -279,10 +279,10 @@ func (g *RogueGame) chgStr(amt int) { addStr(&p.Stats.Str, amt) comp := p.Stats.Str if p.IsRing(Left, RingAddStrength) { - addStr(&comp, -p.CurRing[Left].Arm) + addStr(&comp, -p.CurRing[Left].Bonus) } if p.IsRing(Right, RingAddStrength) { - addStr(&comp, -p.CurRing[Right].Arm) + addStr(&comp, -p.CurRing[Right].Bonus) } if comp > p.MaxStats.Str { p.MaxStats.Str = comp diff --git a/game/monsters.go b/game/monsters.go index b80e15b..f3131b9 100644 --- a/game/monsters.go +++ b/game/monsters.go @@ -54,7 +54,7 @@ func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) { tp.Stats.Lvl = mp.Stats.Lvl + levAdd tp.Stats.MaxHP = g.roll(tp.Stats.Lvl, 8) 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.Str = mp.Stats.Str tp.Stats.Exp = mp.Stats.Exp + levAdd*10 + expAdd(tp) @@ -183,10 +183,10 @@ func (g *RogueGame) save(which int) bool { p := &g.Player if which == VsMagic { if p.IsRing(Left, RingProtection) { - which -= p.CurRing[Left].Arm + which -= p.CurRing[Left].Bonus } if p.IsRing(Right, RingProtection) { - which -= p.CurRing[Right].Arm + which -= p.CurRing[Right].Bonus } } return g.saveThrow(which, &p.Stats) diff --git a/game/move.go b/game/move.go index c7a60dd..a7609f1 100644 --- a/game/move.go +++ b/game/move.go @@ -241,7 +241,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind { p.Flags.Clear(Awake) g.msg("a strange white mist envelops you and you fall asleep") 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) if p.Stats.HP <= 0 { g.msg("an arrow killed you") @@ -263,7 +263,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind { g.teleport() g.mvaddch(tc.Y, tc.X, Trap) 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") } else { 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). func (g *RogueGame) rustArmor(arm *Object) { if arm == nil || arm.Kind != KindArmor || arm.ArmorKind() == ArmorLeather || - arm.Arm >= 9 { + arm.ArmorClass >= 9 { return } @@ -331,7 +331,7 @@ func (g *RogueGame) rustArmor(arm *Object) { g.msg("the rust vanishes instantly") } } else { - arm.Arm++ + arm.ArmorClass++ if !g.Options.Terse { g.msg("your armor appears to be weaker now. Oh my!") } else { diff --git a/game/newlevel.go b/game/newlevel.go index 54a2592..cccc7ee 100644 --- a/game/newlevel.go +++ b/game/newlevel.go @@ -111,9 +111,9 @@ func (g *RogueGame) putThings() { if g.Depth >= AmuletLevel && !g.HasAmulet { obj := newObject() attachObj(&g.Level.Objects, obj) - obj.Damage = "0x0" - obj.HurlDmg = "0x0" - obj.Arm = 11 + obj.Damage = dice("0x0") + obj.HurlDmg = dice("0x0") + obj.ArmorClass = 11 obj.Kind = KindAmulet // Put it somewhere obj.Pos, _ = g.findFloor(nil, 0, false) diff --git a/game/object.go b/game/object.go index 8e4d2b6..79ec9df 100644 --- a/game/object.go +++ b/game/object.go @@ -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 // floor or ride in a pack. type Object struct { - Kind ObjectKind // what kind of object it is (o_type) - Pos Coord // where it lives on the screen - Text string // what it says if you read it - Launch WeaponKind // what you need to launch it (noWeapon if none) - PackCh byte // what character it is in the pack - Damage string // damage if used like sword - HurlDmg string // damage if thrown - Count int // count for plural objects - Which int // which object of a type it is (index for the Kind's table) - HPlus int // plusses to hit - DPlus int // plusses to damage - Arm int // armor protection — overloaded as in C (see Charges/GoldVal) - Flags ObjFlags - Group int // group number for this object - Label string // label for object + Kind ObjectKind // what kind of object it is (o_type) + Pos Coord // where it lives on the screen + Text string // what it says if you read it + Launch WeaponKind // what you need to launch it (noWeapon if none) + PackCh byte // what character it is in the pack + Damage DiceSpec // damage if used like sword + HurlDmg DiceSpec // damage if thrown + Count int // count for plural objects + Which int // which object of a type it is (index for the Kind's table) + HPlus int // plusses to hit + DPlus int // plusses to damage + ArmorClass int // armor protection (armor only) + Charges int // charges remaining (wands and staffs only) + GoldValue int // worth (gold piles only) + 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 @@ -117,18 +120,6 @@ func newObject() *Object { 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. func (o *Object) PotionKind() PotionKind { return PotionKind(o.Which) } diff --git a/game/pack.go b/game/pack.go index 85e3178..6f2f932 100644 --- a/game/pack.go +++ b/game/pack.go @@ -250,7 +250,7 @@ func (g *RogueGame) pickUp(ch byte) { if obj == nil { return } - g.money(obj.GoldVal()) + g.money(obj.GoldValue) detachObj(&g.Level.Objects, obj) p.Room.GoldVal = 0 default: diff --git a/game/potions.go b/game/potions.go index d949870..a9ba020 100644 --- a/game/potions.go +++ b/game/potions.go @@ -153,19 +153,19 @@ func (g *RogueGame) quaff() { } case PotionRestoreStrength: 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) { - addStr(&p.Stats.Str, -p.CurRing[Right].Arm) + addStr(&p.Stats.Str, -p.CurRing[Right].Bonus) } if p.Stats.Str < p.MaxStats.Str { p.Stats.Str = p.MaxStats.Str } 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) { - 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") case PotionBlindness: @@ -212,7 +212,7 @@ func (g *RogueGame) doPot(kind PotionKind, knowit bool) { func (o *Object) isMagic() bool { switch o.Kind { case KindArmor: - return o.Flags.Has(Protected) || o.Arm != aClass[o.Which] + return o.Flags.Has(Protected) || o.ArmorClass != aClass[o.Which] case KindWeapon: return o.HPlus != 0 || o.DPlus != 0 case KindPotion, KindScroll, KindWand, KindRing, KindAmulet: diff --git a/game/rings.go b/game/rings.go index a151a83..cede65f 100644 --- a/game/rings.go +++ b/game/rings.go @@ -49,7 +49,7 @@ func (g *RogueGame) ringOn() { // Calculate the effect it has on the poor guy. switch obj.RingKind() { case RingAddStrength: - g.chgStr(obj.Arm) + g.chgStr(obj.Bonus) case RingSeeInvisible: g.invisOn() case RingAggravateMonsters: @@ -168,7 +168,7 @@ func ringNum(g *RogueGame, obj *Object) string { } switch obj.RingKind() { case RingProtection, RingAddStrength, RingIncreaseDamage, RingDexterity: - return fmt.Sprintf(" [%s]", num(obj.Arm, 0, Ring)) + return fmt.Sprintf(" [%s]", num(obj.Bonus, 0, Ring)) } return "" } diff --git a/game/rip.go b/game/rip.go index 8a1cd95..7d488dc 100644 --- a/game/rip.go +++ b/game/rip.go @@ -124,8 +124,8 @@ func (g *RogueGame) totalWinner() { obj.Flags.Set(Known) case KindArmor: worth = it.Armors[obj.Which].Worth - worth += (9 - obj.Arm) * 100 - worth += 10 * (aClass[obj.Which] - obj.Arm) + worth += (9 - obj.ArmorClass) * 100 + worth += 10 * (aClass[obj.Which] - obj.ArmorClass) obj.Flags.Set(Known) case KindScroll: op := &it.Scrolls[obj.Which] @@ -146,8 +146,8 @@ func (g *RogueGame) totalWinner() { worth = op.Worth if obj.RingKind() == RingAddStrength || obj.RingKind() == RingIncreaseDamage || obj.RingKind() == RingProtection || obj.RingKind() == RingDexterity { - if obj.Arm > 0 { - worth += obj.Arm * 100 + if obj.Bonus > 0 { + worth += obj.Bonus * 100 } else { worth = 10 } @@ -160,7 +160,7 @@ func (g *RogueGame) totalWinner() { case KindWand: op := &it.Sticks[obj.Which] worth = op.Worth - worth += 20 * obj.Charges() + worth += 20 * obj.Charges if !obj.Flags.Has(Known) { worth /= 2 } diff --git a/game/rooms.go b/game/rooms.go index 51f391d..5a48c13 100644 --- a/game/rooms.go +++ b/game/rooms.go @@ -88,7 +88,7 @@ func (g *RogueGame) doRooms() { if g.rnd(2) == 0 && (!g.HasAmulet || g.Depth >= g.MaxDepth) { gold := newObject() rp.GoldVal = g.goldCalc() - gold.SetGoldVal(rp.GoldVal) + gold.GoldValue = rp.GoldVal rp.Gold, _ = g.findFloorIn(rp, 0, false) gold.Pos = rp.Gold g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold) diff --git a/game/save.go b/game/save.go index 8d555bf..0d02cf4 100644 --- a/game/save.go +++ b/game/save.go @@ -13,8 +13,10 @@ import ( // saveFormatVersion identifies the snapshot layout; it changes whenever a // field rename or retype would make gob silently mis-decode an older -// save ("go2": Object.Type became Kind ObjectKind). -const saveFormatVersion = Release + "-go2" +// save ("go2": Object.Type became Kind ObjectKind; "go3": Object.Arm split +// 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 // (kind, index) pairs). diff --git a/game/save_test.go b/game/save_test.go index 8e39c05..ef18d70 100644 --- a/game/save_test.go +++ b/game/save_test.go @@ -15,7 +15,7 @@ func TestSaveRestoreRoundTrip(t *testing.T) { g.HasAmulet = true g.Items.Potions[PotionHealing].Know = true 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 { g.Level.Monsters[0].Flags.Set(Awake) g.Level.Monsters[0].Dest = &g.Player.Pos @@ -48,7 +48,7 @@ func TestSaveRestoreRoundTrip(t *testing.T) { if h.Items.Scrolls[ScrollMagicMapping].Guess != "map???" { 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") } if h.Rng.Seed != g.Rng.Seed { diff --git a/game/scrolls.go b/game/scrolls.go index a643212..59b76e0 100644 --- a/game/scrolls.go +++ b/game/scrolls.go @@ -42,7 +42,7 @@ func (g *RogueGame) readScroll() { g.msg("your hands begin to glow %s", g.pickColor("red")) case ScrollEnchantArmor: if p.CurArmor != nil { - p.CurArmor.Arm-- + p.CurArmor.ArmorClass-- p.CurArmor.Flags.Clear(Cursed) g.msg("your armor glows %s for a moment", g.pickColor("silver")) } diff --git a/game/sticks.go b/game/sticks.go index 55bdd5e..dc0345b 100644 --- a/game/sticks.go +++ b/game/sticks.go @@ -16,7 +16,7 @@ func (g *RogueGame) doZap() { g.msg("you can't zap with that!") return } - if obj.Charges() == 0 { + if obj.Charges == 0 { g.msg("nothing happens") return } @@ -111,7 +111,7 @@ func (g *RogueGame) doZap() { g.Items.Sticks[WandMagicMissile].Know = true bolt := newObject() 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.DPlus = 1 bolt.Flags = Missile @@ -167,7 +167,7 @@ func (g *RogueGame) doZap() { g.Items.Sticks[obj.Which].Know = true case WandNothing: } - obj.SetCharges(obj.Charges() - 1) + obj.Charges-- } // 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.Kind = KindWeapon bolt.Which = int(WeaponFlame) - bolt.HurlDmg = "6x6" + bolt.HurlDmg = dice("6x6") bolt.HPlus = 100 bolt.DPlus = 0 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). func (g *RogueGame) fixStick(cur *Object) { if g.Items.WandType[cur.Which] == "staff" { - cur.Damage = "2x3" + cur.Damage = dice("2x3") } else { - cur.Damage = "1x1" + cur.Damage = dice("1x1") } - cur.HurlDmg = "1x1" + cur.HurlDmg = dice("1x1") switch cur.WandKind() { case WandLight: - cur.SetCharges(g.rnd(10) + 10) + cur.Charges = g.rnd(10) + 10 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 "" } 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) } diff --git a/game/tables.go b/game/tables.go index 4581ad6..87b6877 100644 --- a/game/tables.go +++ b/game/tables.go @@ -6,7 +6,7 @@ package game // change during play) are cloned into RogueGame.Items by NewGame. // 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. var aClass = [NumArmorTypes]int{ @@ -47,32 +47,32 @@ var invTName = []string{"Overwrite", "Slow", "Clear"} // rolled from the level at creation time. var monsterTable = [26]MonsterKind{ /* Name CARRY FLAGS str exp lvl arm hp dmg */ - {"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, "0x0/0x0", 0}}, - {"bat", 0, Flying, Stats{10, 1, 1, 3, 1, "1x2", 0}}, - {"centaur", 15, 0, Stats{10, 17, 4, 4, 1, "1x2/1x5/1x5", 0}}, - {"dragon", 100, Mean, Stats{10, 5000, 10, -1, 1, "1x8/1x8/3x10", 0}}, - {"emu", 0, Mean, Stats{10, 2, 1, 7, 1, "1x2", 0}}, - {"venus flytrap", 0, Mean, Stats{10, 80, 8, 3, 1, "%%%x0", 0}}, - {"griffin", 20, Mean | Flying | Regenerates, Stats{10, 2000, 13, 2, 1, "4x3/3x5", 0}}, - {"hobgoblin", 0, Mean, Stats{10, 3, 1, 5, 1, "1x8", 0}}, - {"ice monster", 0, 0, Stats{10, 5, 1, 9, 1, "0x0", 0}}, - {"jabberwock", 70, 0, Stats{10, 3000, 15, 6, 1, "2x12/2x4", 0}}, - {"kestrel", 0, Mean | Flying, Stats{10, 1, 1, 7, 1, "1x4", 0}}, - {"leprechaun", 0, 0, Stats{10, 10, 3, 8, 1, "1x1", 0}}, - {"medusa", 40, Mean, Stats{10, 200, 8, 2, 1, "3x4/3x4/2x5", 0}}, - {"nymph", 100, 0, Stats{10, 37, 3, 9, 1, "0x0", 0}}, - {"orc", 15, Greedy, Stats{10, 5, 1, 6, 1, "1x8", 0}}, - {"phantom", 0, Invisible, Stats{10, 120, 8, 3, 1, "4x4", 0}}, - {"quagga", 0, Mean, Stats{10, 15, 3, 3, 1, "1x5/1x5", 0}}, - {"rattlesnake", 0, Mean, Stats{10, 9, 2, 3, 1, "1x6", 0}}, - {"snake", 0, Mean, Stats{10, 2, 1, 5, 1, "1x3", 0}}, - {"troll", 50, Regenerates | Mean, Stats{10, 120, 6, 4, 1, "1x8/1x8/2x6", 0}}, - {"black unicorn", 0, Mean, Stats{10, 190, 7, -2, 1, "1x9/1x9/2x9", 0}}, - {"vampire", 20, Regenerates | Mean, Stats{10, 350, 8, 1, 1, "1x10", 0}}, - {"wraith", 0, 0, Stats{10, 55, 5, 4, 1, "1x6", 0}}, - {"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, "4x4", 0}}, - {"yeti", 30, 0, Stats{10, 50, 4, 6, 1, "1x6/1x6", 0}}, - {"zombie", 0, Mean, Stats{10, 6, 2, 8, 1, "1x8", 0}}, + {"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, dice("0x0/0x0"), 0}}, + {"bat", 0, Flying, Stats{10, 1, 1, 3, 1, dice("1x2"), 0}}, + {"centaur", 15, 0, Stats{10, 17, 4, 4, 1, dice("1x2/1x5/1x5"), 0}}, + {"dragon", 100, Mean, Stats{10, 5000, 10, -1, 1, dice("1x8/1x8/3x10"), 0}}, + {"emu", 0, Mean, Stats{10, 2, 1, 7, 1, dice("1x2"), 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, dice("4x3/3x5"), 0}}, + {"hobgoblin", 0, Mean, Stats{10, 3, 1, 5, 1, dice("1x8"), 0}}, + {"ice monster", 0, 0, Stats{10, 5, 1, 9, 1, dice("0x0"), 0}}, + {"jabberwock", 70, 0, Stats{10, 3000, 15, 6, 1, dice("2x12/2x4"), 0}}, + {"kestrel", 0, Mean | Flying, Stats{10, 1, 1, 7, 1, dice("1x4"), 0}}, + {"leprechaun", 0, 0, Stats{10, 10, 3, 8, 1, dice("1x1"), 0}}, + {"medusa", 40, Mean, Stats{10, 200, 8, 2, 1, dice("3x4/3x4/2x5"), 0}}, + {"nymph", 100, 0, Stats{10, 37, 3, 9, 1, dice("0x0"), 0}}, + {"orc", 15, Greedy, Stats{10, 5, 1, 6, 1, dice("1x8"), 0}}, + {"phantom", 0, Invisible, Stats{10, 120, 8, 3, 1, dice("4x4"), 0}}, + {"quagga", 0, Mean, Stats{10, 15, 3, 3, 1, dice("1x5/1x5"), 0}}, + {"rattlesnake", 0, Mean, Stats{10, 9, 2, 3, 1, dice("1x6"), 0}}, + {"snake", 0, Mean, Stats{10, 2, 1, 5, 1, dice("1x3"), 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, dice("1x9/1x9/2x9"), 0}}, + {"vampire", 20, Regenerates | Mean, Stats{10, 350, 8, 1, 1, dice("1x10"), 0}}, + {"wraith", 0, 0, Stats{10, 55, 5, 4, 1, dice("1x6"), 0}}, + {"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, dice("4x4"), 0}}, + {"yeti", 30, 0, Stats{10, 50, 4, 6, 1, dice("1x6/1x6"), 0}}, + {"zombie", 0, Mean, Stats{10, 6, 2, 8, 1, dice("1x8"), 0}}, } // 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. const ( 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)" ) diff --git a/game/things.go b/game/things.go index fc90159..bf15d9d 100644 --- a/game/things.go +++ b/game/things.go @@ -69,11 +69,11 @@ func (g *RogueGame) invName(obj *Object, drop bool) string { case KindArmor: sp := it.Armors[which].Name 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 { pb.WriteString("protection ") } - fmt.Fprintf(&pb, "%d]", 10-obj.Arm) + fmt.Fprintf(&pb, "%d]", 10-obj.ArmorClass) } else { pb.WriteString(sp) } @@ -83,7 +83,7 @@ func (g *RogueGame) invName(obj *Object, drop bool) string { case KindAmulet: pb.WriteString("The Amulet of Yendor") case KindGold: - fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldVal()) + fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldValue) } out := pb.String() @@ -168,7 +168,7 @@ func (g *RogueGame) dropCheck(obj *Object) bool { p.CurRing[hand] = nil switch obj.RingKind() { case RingAddStrength: - g.chgStr(-obj.Arm) + g.chgStr(-obj.Bonus) case RingSeeInvisible: g.unsee(0) 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). func (g *RogueGame) newThing() *Object { cur := newObject() - cur.Damage = "0x0" - cur.HurlDmg = "0x0" - cur.Arm = 11 + cur.Damage = dice("0x0") + cur.HurlDmg = dice("0x0") + cur.ArmorClass = 11 cur.Count = 1 // 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: cur.Kind = KindArmor cur.Which = pickOne(g, g.Items.Armors[:]) - cur.Arm = aClass[cur.Which] + cur.ArmorClass = aClass[cur.Which] if r := g.rnd(100); r < 20 { cur.Flags.Set(Cursed) - cur.Arm += g.rnd(3) + 1 + cur.ArmorClass += g.rnd(3) + 1 } else if r < 28 { - cur.Arm -= g.rnd(3) + 1 + cur.ArmorClass -= g.rnd(3) + 1 } case 5: cur.Kind = KindRing cur.Which = pickOne(g, g.Items.Rings[:]) switch cur.RingKind() { case RingAddStrength, RingProtection, RingDexterity, RingIncreaseDamage: - if cur.Arm = g.rnd(3); cur.Arm == 0 { - cur.Arm = -1 + if cur.Bonus = g.rnd(3); cur.Bonus == 0 { + cur.Bonus = -1 cur.Flags.Set(Cursed) } case RingAggravateMonsters, RingTeleportation: diff --git a/game/types.go b/game/types.go index 47c3bcc..302875a 100644 --- a/game/types.go +++ b/game/types.go @@ -374,13 +374,13 @@ type Coord struct { // Stats describes a fighting being (rogue.h struct stats). type Stats struct { - Str int // strength (s_str; 3..31) - Exp int // experience - Lvl int // level of mastery - Arm int // armor class - HP int // hit points (s_hpt) - Dmg string // damage dice, e.g. "1x4/1x2" - MaxHP int + Str int // strength (s_str; 3..31) + Exp int // experience + Lvl int // level of mastery + ArmorClass int // armor class (s_arm) + HP int // hit points (s_hpt) + Dmg DiceSpec // damage dice, e.g. "1x4/1x2" + MaxHP int } // Room describes a room or passage network (rogue.h struct room). diff --git a/game/weapons.go b/game/weapons.go index f539107..235ef67 100644 --- a/game/weapons.go +++ b/game/weapons.go @@ -123,20 +123,20 @@ func (g *RogueGame) wield() { // initWeaps is the weapons.c init_dam[] table. var initWeaps = [NumWeaponTypes]struct { - dam string // damage when wielded - hrl string // damage when thrown + dam DiceSpec // damage when wielded + hrl DiceSpec // damage when thrown launch WeaponKind // launching weapon flags ObjFlags }{ - {"2x4", "1x3", noWeapon, 0}, // WeaponMace - {"3x4", "1x2", noWeapon, 0}, // Long sword - {"1x1", "1x1", noWeapon, 0}, // WeaponBow - {"1x1", "2x3", WeaponBow, Stackable | Missile}, // WeaponArrow - {"1x6", "1x4", noWeapon, Missile}, // WeaponDagger - {"4x4", "1x2", noWeapon, 0}, // 2h sword - {"1x1", "1x3", noWeapon, Stackable | Missile}, // WeaponDart - {"1x2", "2x4", noWeapon, Stackable | Missile}, // Shuriken - {"2x3", "1x6", noWeapon, Missile}, // WeaponSpear + {dice("2x4"), dice("1x3"), noWeapon, 0}, // WeaponMace + {dice("3x4"), dice("1x2"), noWeapon, 0}, // Long sword + {dice("1x1"), dice("1x1"), noWeapon, 0}, // WeaponBow + {dice("1x1"), dice("2x3"), WeaponBow, Stackable | Missile}, // WeaponArrow + {dice("1x6"), dice("1x4"), noWeapon, Missile}, // WeaponDagger + {dice("4x4"), dice("1x2"), noWeapon, 0}, // 2h sword + {dice("1x1"), dice("1x3"), noWeapon, Stackable | Missile}, // WeaponDart + {dice("1x2"), dice("2x4"), noWeapon, Stackable | Missile}, // Shuriken + {dice("2x3"), dice("1x6"), noWeapon, Missile}, // WeaponSpear } // initWeapon sets up a new weapon (weapons.c init_weapon). diff --git a/game/wizard.go b/game/wizard.go index b2079b6..8223b6d 100644 --- a/game/wizard.go +++ b/game/wizard.go @@ -38,12 +38,12 @@ func (g *RogueGame) createObj() { obj.HPlus += g.rnd(3) + 1 } } else { - obj.Arm = aClass[obj.Which] + obj.ArmorClass = aClass[obj.Which] if bless == '-' { - obj.Arm += g.rnd(3) + 1 + obj.ArmorClass += g.rnd(3) + 1 } if bless == '+' { - obj.Arm -= g.rnd(3) + 1 + obj.ArmorClass -= g.rnd(3) + 1 } } case obj.Kind == KindRing: @@ -54,9 +54,9 @@ func (g *RogueGame) createObj() { g.Msgs.Mpos = 0 if bless == '-' { obj.Flags.Set(Cursed) - obj.Arm = -1 + obj.Bonus = -1 } else { - obj.Arm = g.rnd(2) + 1 + obj.Bonus = g.rnd(2) + 1 } case RingAggravateMonsters, RingTeleportation: obj.Flags.Set(Cursed) @@ -67,7 +67,7 @@ func (g *RogueGame) createObj() { g.msg("how much?") buf := "" if g.getStr(&buf, g.scr.Std) == Norm { - obj.SetGoldVal(cAtoi(buf)) + obj.GoldValue = cAtoi(buf) } } g.addPack(obj, false) @@ -169,7 +169,7 @@ func (g *RogueGame) teleport() { if p.On(Held) { p.Flags.Clear(Held) p.VfHit = 0 - g.Monsters['F'-'A'].Stats.Dmg = "000x0" + g.Monsters['F'-'A'].Stats.Dmg = dice("000x0") } g.NoMove = 0 g.Count = 0 diff --git a/go.mod b/go.mod index e297093..14e66b0 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,11 @@ -module sneak.berlin/go/rogue +module git.eeqj.de/sneak/rgoue go 1.25.7 +require github.com/gdamore/tcell/v2 v2.13.10 + require ( 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/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.38.0 // indirect diff --git a/term/tcell.go b/term/tcell.go index c748122..5f3cbc8 100644 --- a/term/tcell.go +++ b/term/tcell.go @@ -11,7 +11,7 @@ import ( "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