-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathgame.py
185 lines (159 loc) · 6.82 KB
/
game.py
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import random
import math
from enums import RoundType
from events import events
class Game:
def __init__(self, owner_name, owner_id, title: str):
self.owner_name = owner_name
self.owner_id = owner_id
self.title = title
self.has_started = False
# Player data
self.players = {}
self.players_available_to_act = set()
self.players_dead_today = []
self.total_players_alive = 0
# Round counting
self.day = 1
self.days_since_last_event = 0
self.consecutive_rounds_without_deaths = 0
# Round order control
self.bloodbath_passed = False
self.day_passed = False
self.fallen_passed = False
self.night_passed = False
@property
def players_sorted(self):
l = list(self.players.values())
l.sort()
return l
def add_player(self, new_player):
if new_player.name in self.players:
return False
self.players[new_player.name] = new_player
return True
def remove_player(self, name):
if name in self.players:
del self.players[name]
return True
return False
def start(self):
self.total_players_alive = len(self.players)
self.has_started = True
def step(self):
if self.total_players_alive is 1:
self.has_started = False
for p in self.players.values():
if p.alive is True:
return {'winner': p.name, 'district': p.district}
if self.total_players_alive is 0:
self.has_started = False
return {'allDead': True}
if self.night_passed:
self.day += 1
self.days_since_last_event += 1
self.day_passed = False
self.fallen_passed = False
self.night_passed = False
feast_chance = 100 * (math.pow(self.days_since_last_event, 2) / 55.0) + (9.0 / 55.0)
fatality_factor = random.randint(2, 4) + self.consecutive_rounds_without_deaths
if self.day is 1 and not self.bloodbath_passed:
step_type = RoundType.BLOODBATH
fatality_factor += 2
self.bloodbath_passed = True
elif not self.day_passed and random.randint(0, 100) < feast_chance:
step_type = RoundType.FEAST
self.days_since_last_event = 0
fatality_factor += 2
elif self.days_since_last_event > 0 and random.randint(1, 20) is 1:
step_type = RoundType.ARENA
self.days_since_last_event = 0
fatality_factor += 1
elif not self.day_passed:
step_type = RoundType.DAY
self.day_passed = True
elif self.day_passed and not self.fallen_passed:
step_type = RoundType.FALLEN
self.fallen_passed = True
else:
step_type = RoundType.NIGHT
self.night_passed = True
self.players_available_to_act = {p for p in self.players.values() if p.alive is True}
event = None
if step_type is RoundType.FALLEN:
messages = []
for p in self.players_dead_today:
messages.append("☠️ {0} | District {1}".format(p, p.district))
else:
if step_type is RoundType.ARENA:
event = events['arena'][random.randint(0, len(events['arena']) - 1)]
else:
event = events[step_type.value]
dead_players_now = len(self.players) - self.total_players_alive
messages = self.__generate_messages(fatality_factor, event)
if len(self.players) - self.total_players_alive == dead_players_now:
self.consecutive_rounds_without_deaths += 1
else:
self.consecutive_rounds_without_deaths = 0
summary = {
'day': self.day,
'roundType': step_type.value,
'messages': messages,
'footer': "Tributes Remaining: {0}/{1} | Host: {2}"
.format(self.total_players_alive, len(self.players), self.owner_name)
}
if step_type is RoundType.FALLEN:
summary['title'] = "{0} | {1}".format(self.title, "Fallen Tributes {0}".format(self.day))
if len(self.players_dead_today) > 1:
summary['description'] = "{0} cannon shots can be heard in the distance.".format(
len(self.players_dead_today))
self.players_dead_today.clear()
elif len(self.players_dead_today) is 1:
summary['description'] = "1 cannon shot can be heard in the distance."
self.players_dead_today.clear()
else:
summary['description'] = "No cannon shots are heard."
summary['color'] = 0xaaaaaa
else:
summary['title'] = "{0} | {1}".format(self.title, event['title'].format(self.day))
summary['description'] = event['description']
summary['color'] = event['color']
return summary
def __generate_messages(self, fatality_factor, event):
messages = []
while len(self.players_available_to_act) > 0:
f = random.randint(0, 10)
if f < fatality_factor and self.total_players_alive > 1:
# time to die
action = random.choice(event['fatal'])
if action['killed'] is list and len(action['killed']) >= self.total_players_alive:
# must have one player remaining
continue
else:
# live to see another round
action = random.choice(event['nonfatal'])
tributes = action['tributes']
if tributes > len(self.players_available_to_act):
# not enough tributes for this action
continue
p = random.choice(tuple(self.players_available_to_act))
self.players_available_to_act.remove(p)
active_players = [p]
extra_tributes = tributes - 1
while extra_tributes > 0:
p = random.choice(tuple(self.players_available_to_act))
self.players_available_to_act.remove(p)
active_players.append(p)
extra_tributes -= 1
msg = action['msg'].format(*active_players)
if action.get('killed') is not None:
if action.get('killer') is not None:
for kr in action['killer']:
active_players[kr].kills += len(action['killed'])
for kd in action['killed']:
active_players[kd].alive = False
self.players_dead_today.append(active_players[kd])
self.total_players_alive -= 1
active_players[kd].cause_of_death = msg
messages.append(msg)
return messages