aboutsummaryrefslogtreecommitdiff
path: root/internal/game/hacking.go
blob: dfefd5c0a00f667fde692c1ab36876f193d175e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package game

import (
	"fmt"
	"strings"

	"thehouseoficarus/internal/net"
	"thehouseoficarus/internal/player"
)

type HackingMinigame interface {
	Init(level int) string
	HandleInput(input string) (output string, done bool, won bool)
	Name() string
}

type XPBonuser interface {
	BonusXP() int
}

type HackingSession struct {
	Minigame   HackingMinigame
	TerminalID string
	Level      int
	ReqLevel   int
	Started    bool
}

type TerminalDef struct {
	ObjectID   string
	Level      int
	Minigame   func() HackingMinigame
	BaseXPWin  int
	BaseXPLose int
	Name       string
}

var terminalDefs = map[string]TerminalDef{
	"terminal_basic": {
		ObjectID:   "terminal_basic",
		Level:      1,
		Minigame:   func() HackingMinigame { return NewWumpusGame() },
		BaseXPWin:  50,
		BaseXPLose: 10,
		Name:       "Hunt the Wumpus",
	},
	"terminal_advanced": {
		ObjectID:   "terminal_advanced",
		Level:      20,
		Minigame:   func() HackingMinigame { return NewMastermindGame() },
		BaseXPWin:  150,
		BaseXPLose: 30,
		Name:       "Code Breaker",
	},
	"terminal_secure": {
		ObjectID:   "terminal_secure",
		Level:      40,
		Minigame:   func() HackingMinigame { return NewLiarsDiceGame() },
		BaseXPWin:  300,
		BaseXPLose: 50,
		Name:       "Signal Bluff",
	},
}

func (g *Game) handleHackingInput(sess *net.Session, input string) {
	p, ok := sess.Player.(*player.Player)
	if !ok {
		sess.State = net.StateGame
		return
	}

	hs, ok := g.hackingStates[p.Name]
	if !ok {
		sess.State = net.StateGame
		g.writePrompt(sess)
		return
	}

	input = strings.TrimSpace(input)
	lower := strings.ToLower(input)

	if lower == "jack out" || lower == "quit" || lower == "disconnect" {
		g.endHacking(sess, p, false, false)
		return
	}

	hs.Started = true
	output, done, won := hs.Minigame.HandleInput(input)

	sess.WriteLine(output)

	if done {
		g.endHacking(sess, p, true, won)
		return
	}

	sess.Write("\nhack> ")
}

func (g *Game) endHacking(sess *net.Session, p *player.Player, completed bool, won bool) {
	hs, ok := g.hackingStates[p.Name]
	if !ok {
		sess.State = net.StateGame
		g.writePrompt(sess)
		return
	}

	tDef := terminalDefs[hs.TerminalID]
	var xp int

	if completed && won {
		xp = hackingXP(tDef.BaseXPWin, hs.Level, hs.ReqLevel)
		sess.WriteLine(fmt.Sprintf("\nConnection terminated. Contract complete."))
	} else if completed {
		xp = hackingXP(tDef.BaseXPLose, hs.Level, hs.ReqLevel)
		sess.WriteLine(fmt.Sprintf("\nConnection lost."))
	} else if hs.Started {
		xp = hackingXP(tDef.BaseXPLose, hs.Level, hs.ReqLevel)
		sess.WriteLine("\nYou jack out of the terminal.")
	} else {
		sess.WriteLine("\nYou jack out of the terminal.")
	}

	if won {
		if b, ok := hs.Minigame.(XPBonuser); ok {
			bonus := b.BonusXP()
			if bonus > 0 {
				xp += hackingXP(bonus, hs.Level, hs.ReqLevel)
			}
		}
	}

	if xp > 0 {
		if newLevel := p.AddSkillXP(player.Hacking, xp); newLevel > 0 {
			sess.WriteLine(g.colorize(sess, "level_up",
				fmt.Sprintf("*** You are now level %d hacking! ***", newLevel)))
		}
		if p.OptionBool("xp_drops") {
			sess.WriteLine(g.colorize(sess, "xp",
				fmt.Sprintf("(+%dxp %s)", xp, player.SkillAbbr[player.Hacking])))
		}
		g.AccountStore.SaveCharacter(p)
	}

	delete(g.hackingStates, p.Name)
	p.ActionState = nil
	sess.State = net.StateGame
	g.writePrompt(sess)
}

func hackingXP(baseXP int, playerLevel int, reqLevel int) int {
	multiplier := 1.0 + 0.01*float64(playerLevel-reqLevel)
	if multiplier < 1.0 {
		multiplier = 1.0
	}
	return int(float64(baseXP) * multiplier)
}