-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathplayer.py
349 lines (293 loc) · 11.3 KB
/
player.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
from .observer import Observable
from .utils import todict
from .items import Item, convert_item_from_json, convert_items_from_json
from aqt.utils import showText
import math
import datetime
class InventoryItem():
def __init__(self, item: Item, amount: int, position: int):
self.item = item
self.amount = amount
self.position = position
class Inventory():
MAX_INVENTORY_SIZE = 20
def __init__(self):
self.items = list([])
def is_full(self):
return len(self.items) >= Inventory.MAX_INVENTORY_SIZE
def receive_item(self, item: Item, amount: int, force_index = None):
if self.is_full():
return self
empty_slot = self.get_empty_slot()
if force_index is not None:
empty_slot = force_index
if item.groupable is False:
self.items.append(InventoryItem(item, 1, empty_slot))
return self
alreadyExist = False
for inventory_item in self.items:
if inventory_item is not None and inventory_item.item.code == item.code:
inventory_item.amount += 1
alreadyExist = True
if not alreadyExist:
self.items.append(InventoryItem(item, amount, empty_slot))
return self
def get_empty_slot(self) -> int:
for i in range(Inventory.MAX_INVENTORY_SIZE):
item = self.get_item_by_index(i)
if item is None:
return i
return None
def get_inventory_item_by_id(self, id):
for item in self.items:
if item is not None and item.item.id == id:
return item.item
return None
def get_item_by_index(self, index: int):
items = list(filter(lambda i: i.position == index, self.items))
if len(items) > 0:
return items[0]
return None
def get_index_by_item(self, item: Item):
for index, inventory_item in enumerate(self.items):
if inventory_item.item.id == item.id:
return index
return -1
def remove_item(self, item: Item):
for index, inventory_item in enumerate(self.items):
if inventory_item is not None and inventory_item.item.id == item.id:
inventory_item.amount -= 1
if inventory_item.amount <= 0:
del self.items[index]
return True
return False
def fromJSON(self, data):
for inventory_item in data["items"]:
if inventory_item is not None:
item = convert_item_from_json(inventory_item["item"])
self.items.append(InventoryItem(item, inventory_item["amount"], inventory_item["position"]))
return self
class Player(Observable):
MAX_STREAK = 36
STREAK_DATE_FORMAT = "%Y-%m-%d"
def __init__(self, nickname="", level=1, exp=0, inventory: Inventory = Inventory(), stats=None):
super().__init__()
self.nickname = nickname
self.level = level
self.exp = exp
self.inventory = inventory
self.streak_history = []
self.available_points_to_distribute = 0
if stats is None:
self.stats = {
"curr_hp": 10,
"max_hp": 10,
"strength": 2,
"defense": 2,
"curr_energy": 10,
"max_energy": 10,
"bonus_xp": 0,
"streak": 0
}
else:
self.stats = stats
self.equipments = {
"left_hand": None,
"right_hand": None,
"head": None,
"body": None,
"legs": None,
"acessory": None,
"skill_1": None,
"skill_2": None,
"skill_3": None,
"skill_4": None,
"skill_5": None,
}
# return the exp necessary to a level
# https://bulbapedia.bulbagarden.net/wiki/Experience
# Function https://www.wolframalpha.com/input?i=4+*+pow%28x%2C+3%29+%2F+5
def calculate_exp_by_level(self, level: int) -> float:
return 4 * pow(level, 3) / 5
def calculate_percentage_exp_to_next_level(self) -> float:
exp_to_next_level = self.calculate_exp_by_level(self.level+1)
exp_to_curr_level = self.calculate_exp_by_level(self.level)
current_exp = self.exp
return (current_exp-exp_to_curr_level) * 100 / (exp_to_next_level-exp_to_curr_level)
def get_daily_streak_status(self, date: datetime.date) -> str:
if len(self.streak_history) == 0:
return "ok"
last = datetime.datetime.strptime(
self.streak_history[-1], Player.STREAK_DATE_FORMAT).date()
today = date
yesterday = today - datetime.timedelta(days=1)
if today == last:
return "ignore"
elif yesterday == last:
return "ok"
else:
return "break"
def update_daily_streak(self, today_reference: datetime.date):
status = self.get_daily_streak_status(today_reference)
if status == "ok":
self.daily_streak()
elif status == "break":
self.reset_daily_streak()
elif status == "ignore":
pass
self.streak_history.append(
today_reference.strftime(Player.STREAK_DATE_FORMAT))
self.emit("change")
def daily_streak(self):
self.stats["streak"] += 1
if self.stats["streak"] > Player.MAX_STREAK:
self.stats["streak"] = Player.MAX_STREAK
self.stats["bonus_xp"] = self.stats["streak"] * 0.2
self.emit("change")
return self
def reset_daily_streak(self):
self.stats["streak"] = 0
self.stats["bonus_xp"] = 0
self.streak_history = []
self.emit("change")
return self
# just inverse the function above
def calculate_level_by_exp(self, exp: float) -> int:
return math.floor(((5 * exp) / 4)**(1/3))
def update_level(self):
levelByExp = self.calculate_level_by_exp(self.exp)
# if levelByExp is not the same as the current level, update
# the available_points_to_distribute with the difference
if levelByExp != self.level and levelByExp > self.level:
self.available_points_to_distribute += levelByExp - self.level
self.level = levelByExp
self.emit("change")
return self
def increase_exp_by_ease(self, ease: int):
self.increase_exp((7 * (ease * 1.1)) * (1 + self.stats["bonus_xp"]))
def increase_exp(self, value):
self.exp += value
self.update_level()
return self
def decrease_exp(self, value):
if self.exp - value <= 0:
self.exp = 0
else:
self.exp -= value
self.update_level()
return self
def equip(self, id):
item = self.inventory.get_inventory_item_by_id(id)
if len(item.body_parts) > 0:
empty = []
for bodyPart in item.body_parts:
if not self.equipments[bodyPart]:
empty.append(bodyPart)
firstBodyPartToUse = item.body_parts[0]
if len(empty) > 0:
firstBodyPartToUse = empty[0]
self.unequip(firstBodyPartToUse)
self.equipments[firstBodyPartToUse] = item
self.inventory.remove_item(item)
self.emit("change")
return {
"body_part": firstBodyPartToUse,
"item": item
}
return None
def unequip(self, bodyPart):
item = self.equipments[bodyPart]
if item:
self.inventory.receive_item(item, 1)
self.equipments[bodyPart] = None
self.emit("change")
return item
return None
def destroy_item(self, item):
for key in self.equipments:
if self.equipments[key] == item:
self.equipments[key] = None
break
self.inventory.remove_item(item)
self.emit("change")
self.emit("destroy_item")
return self
def receive_loot(self, items):
for loot in items:
self.inventory.receive_item(loot["item"], loot["amount"])
self.emit("change")
return self
def get_stats(self):
stats = self.stats.copy()
for key in self.equipments:
if self.equipments[key]:
stats = self.equipments[key].apply_each_modifier(stats)
return stats
def resolve_hp_value(self, value):
finalValue = self.stats["curr_hp"] + value
if finalValue > self.stats["max_hp"]:
return self.stats["max_hp"]
elif finalValue < 0:
return 0
else:
return finalValue
def consume_item(self, id):
item = self.inventory.get_inventory_item_by_id(id)
stats = self.stats.copy()
newStats = item.apply_each_modifier(stats)
self.set_stats(newStats)
self.inventory.remove_item(item)
self.emit("change")
return self
def receive_item(self, item, amount):
self.inventory.receive_item(item, amount)
self.emit("change")
return self
def get_items(self):
return list(filter(lambda i: i is not None, self.inventory.items))
def set_stats(self, stats):
self.stats = {
"curr_hp": self.resolve_hp_value(stats["curr_hp"]),
"max_hp": stats["max_hp"],
"strength": stats["strength"],
"defense": stats["defense"],
"curr_energy": stats["curr_energy"],
"max_energy": stats["max_energy"],
"bonus_xp": stats["bonus_xp"],
"streak": stats["streak"],
}
self.emit("change")
return self
def set_stats_by_points(self,stats, used_points):
self.available_points_to_distribute -= used_points
self.set_stats(stats)
self.emit("change")
def receive_damage(self, damage):
self.stats["curr_hp"] = self.resolve_hp_value(-damage)
self.emit("change")
return self
def toJSON(self):
return todict(self)
def fromJSON(self, data):
self.nickname = data["nickname"]
self.level = data["level"]
self.exp = data["exp"]
self.available_points_to_distribute = data["available_points_to_distribute"]
self.inventory = Inventory().fromJSON(data["inventory"])
self.stats = data["stats"]
self.streak_history = data["streak_history"]
self.equipments = {
"left_hand": convert_item_from_json(data["equipments"]["left_hand"]),
"right_hand": convert_item_from_json(data["equipments"]["right_hand"]),
"head": convert_item_from_json(data["equipments"]["head"]),
"body": convert_item_from_json(data["equipments"]["body"]),
"legs": convert_item_from_json(data["equipments"]["legs"]),
"acessory": convert_item_from_json(data["equipments"]["acessory"]),
"skill_1": convert_item_from_json(data["equipments"]["skill_1"]),
"skill_2": convert_item_from_json(data["equipments"]["skill_2"]),
"skill_3": convert_item_from_json(data["equipments"]["skill_3"]),
"skill_4": convert_item_from_json(data["equipments"]["skill_4"]),
"skill_5": convert_item_from_json(data["equipments"]["skill_5"]),
}
self.update_level()
return self