combat.py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  1. __filename__ = "combat.py"
  2. __author__ = "Bob Mottram"
  3. __credits__ = ["Bob Mottram"]
  4. __license__ = "AGPL3+"
  5. __version__ = "1.0.0"
  6. __maintainer__ = "Bob Mottram"
  7. __email__ = "bob@freedombone.net"
  8. __status__ = "Production"
  9. #!/usr/bin/python
  10. # -*- coding: utf-8 -*-
  11. from functions import log
  12. from functions import playerInventoryWeight
  13. from functions import stowHands
  14. from functions import prepareSpells
  15. from functions import randomDescription
  16. from functions import decreaseAffinityBetweenPlayers
  17. from random import randint
  18. from copy import deepcopy
  19. from environment import getTemperatureAtCoords
  20. from proficiencies import damageProficiency
  21. from proficiencies import defenseProficiency
  22. from proficiencies import weaponProficiency
  23. from traps import playerIsTrapped
  24. import time
  25. defenseClothing = (
  26. 'clo_chest',
  27. 'clo_head',
  28. 'clo_larm',
  29. 'clo_rarm',
  30. 'clo_lleg',
  31. 'clo_rleg',
  32. 'clo_lwrist',
  33. 'clo_rwrist')
  34. def updateTemporaryIncapacitation(mud, players, isNPC):
  35. """Checks if players are incapacitated by spells and removes them
  36. after the duration has elapsed
  37. """
  38. now = int(time.time())
  39. for p in players:
  40. if players[p]['frozenStart'] != 0:
  41. if now >= players[p]['frozenStart'] + players[p]['frozenDuration']:
  42. players[p]['frozenStart'] = 0
  43. players[p]['frozenDuration'] = 0
  44. players[p]['frozenDescription'] = ""
  45. if not isNPC:
  46. mud.send_message(
  47. p, "<f220>You find that you can move again.<r>\n\n")
  48. def updateTemporaryHitPoints(mud, players, isNPC):
  49. """Updates any hit points added for a temporary period
  50. as the result of a spell
  51. """
  52. now = int(time.time())
  53. for p in players:
  54. if players[p]['tempHitPoints'] == 0:
  55. continue
  56. if players[p]['tempHitPointsStart'] == 0 and \
  57. players[p]['tempHitPointsDuration'] > 0:
  58. players[p]['tempHitPointsStart'] = now
  59. else:
  60. if now > players[p]['tempHitPointsStart'] + \
  61. players[p]['tempHitPointsDuration']:
  62. players[p]['tempHitPoints'] = 0
  63. players[p]['tempHitPointsStart'] = 0
  64. players[p]['tempHitPointsDuration'] = 0
  65. if not isNPC:
  66. mud.send_message(
  67. p, "<f220>Your magical protection expires.<r>\n\n")
  68. def updateTemporaryCharm(mud, players, isNPC):
  69. """Updates any charm added for a temporary period
  70. as the result of a spell
  71. """
  72. now = int(time.time())
  73. for p in players:
  74. if players[p]['tempCharm'] == 0:
  75. continue
  76. if players[p]['tempCharmStart'] == 0 and \
  77. players[p]['tempCharmDuration'] > 0:
  78. players[p]['tempCharmStart'] = now
  79. else:
  80. if now > players[p]['tempCharmStart'] + \
  81. players[p]['tempCharmDuration']:
  82. players[p]['tempCharmStart'] = 0
  83. players[p]['tempCharmDuration'] = 0
  84. if players[p]['affinity'].get(players[p]['tempCharmTarget']):
  85. players[p]['affinity'][players[p]['tempCharmTarget']]-=players[p]['tempCharm']
  86. players[p]['tempCharm'] = 0
  87. if not isNPC:
  88. mud.send_message(
  89. p, "<f220>A charm spell wears off.<r>\n\n")
  90. def playersRest(mud, players):
  91. """Rest restores hit points
  92. """
  93. for p in players:
  94. if players[p]['name'] is not None and players[p]['authenticated'] is not None:
  95. if players[p]['hp'] < players[p]['hpMax'] + \
  96. players[p]['tempHitPoints']:
  97. if randint(0, 100) > 90:
  98. players[p]['hp'] += 1
  99. else:
  100. players[p]['hp'] = players[p]['hpMax'] + \
  101. players[p]['tempHitPoints']
  102. players[p]['restRequired'] = 0
  103. prepareSpells(mud, p, players)
  104. def itemInNPCInventory(npcs, id, itemName, itemsDB):
  105. if len(list(npcs[id]['inv'])) > 0:
  106. itemNameLower = itemName.lower()
  107. for i in list(npcs[id]['inv']):
  108. if itemsDB[int(i)]['name'].lower() == itemNameLower:
  109. return True
  110. return False
  111. def npcUpdateLuck(nid, npcs, items, itemsDB):
  112. """Calculate the luck of an NPC based on what items they are carrying
  113. """
  114. luck = 0
  115. for i in npcs[nid]['inv']:
  116. luck = luck + itemsDB[int(i)]['mod_luc']
  117. npcs[nid]['luc'] = luck
  118. def npcWieldsWeapon(mud, id, nid, npcs, items, itemsDB):
  119. """what is the best weapon which the NPC is carrying?
  120. """
  121. itemID = 0
  122. max_protection = 0
  123. max_damage = 0
  124. if int(npcs[nid]['canWield']) != 0:
  125. for i in npcs[nid]['inv']:
  126. if itemsDB[int(i)]['clo_rhand'] > 0:
  127. if itemsDB[int(i)]['mod_str'] > max_damage:
  128. max_damage = itemsDB[int(i)]['mod_str']
  129. itemID = int(i)
  130. putOnArmor = False
  131. if int(npcs[nid]['canWear']) != 0:
  132. for i in npcs[nid]['inv']:
  133. if itemsDB[int(i)]['clo_chest'] > 0:
  134. if itemsDB[int(i)]['mod_endu'] > max_protection:
  135. max_protection = itemsDB[int(i)]['mod_endu']
  136. itemID = int(i)
  137. putOnArmor = True
  138. # search for any weapons on the floor
  139. pickedUpWeapon = False
  140. itemWeaponIndex = 0
  141. if int(npcs[nid]['canWield']) != 0:
  142. itemsInWorldCopy = deepcopy(items)
  143. for (iid, pl) in list(itemsInWorldCopy.items()):
  144. if itemsInWorldCopy[iid]['room'] == npcs[nid]['room']:
  145. if itemsDB[items[iid]['id']]['weight'] > 0:
  146. if itemsDB[items[iid]['id']]['clo_rhand'] > 0:
  147. if itemsDB[items[iid]['id']]['mod_str'] > max_damage:
  148. itemName = itemsDB[items[iid]['id']]['name']
  149. if not itemInNPCInventory(
  150. npcs, nid, itemName, itemsDB):
  151. max_damage = itemsDB[items[iid]
  152. ['id']]['mod_str']
  153. itemID = int(items[iid]['id'])
  154. itemWeaponIndex = iid
  155. pickedUpWeapon = True
  156. # Search for any armor on the floor
  157. pickedUpArmor = False
  158. itemArmorIndex = 0
  159. if int(npcs[nid]['canWear']) != 0:
  160. for (iid, pl) in list(itemsInWorldCopy.items()):
  161. if itemsInWorldCopy[iid]['room'] == npcs[nid]['room']:
  162. if itemsDB[items[iid]['id']]['weight'] > 0:
  163. if itemsDB[items[iid]['id']]['clo_chest'] > 0:
  164. if itemsDB[items[iid]['id']
  165. ]['mod_endu'] > max_protection:
  166. itemName = itemsDB[items[iid]['id']]['name']
  167. if not itemInNPCInventory(
  168. npcs, nid, itemName, itemsDB):
  169. max_protection = itemsDB[items[iid]
  170. ['id']]['mod_endu']
  171. itemID = int(items[iid]['id'])
  172. itemArmorIndex = iid
  173. pickedUpArmor = True
  174. if itemID > 0:
  175. if putOnArmor:
  176. if npcs[nid]['clo_chest'] != itemID:
  177. npcs[nid]['clo_chest'] = itemID
  178. mud.send_message(
  179. id,
  180. '<f220>' +
  181. npcs[nid]['name'] +
  182. '<r> puts on ' +
  183. itemsDB[itemID]['article'] +
  184. ' ' +
  185. itemsDB[itemID]['name'] +
  186. '\n')
  187. return True
  188. return False
  189. if pickedUpArmor:
  190. if npcs[nid]['clo_chest'] != itemID:
  191. npcs[nid]['inv'].append(str(itemID))
  192. npcs[nid]['clo_chest'] = itemID
  193. del items[itemArmorIndex]
  194. mud.send_message(
  195. id,
  196. '<f220>' +
  197. npcs[nid]['name'] +
  198. '<r> picks up and wears ' +
  199. itemsDB[itemID]['article'] +
  200. ' ' +
  201. itemsDB[itemID]['name'] +
  202. '\n')
  203. return True
  204. return False
  205. if npcs[nid]['clo_rhand'] != itemID:
  206. # Transfer weapon to hand
  207. npcs[nid]['clo_rhand'] = itemID
  208. npcs[nid]['clo_lhand'] = 0
  209. if pickedUpWeapon:
  210. npcs[nid]['inv'].append(str(itemID))
  211. del items[itemWeaponIndex]
  212. mud.send_message(
  213. id,
  214. '<f220>' +
  215. npcs[nid]['name'] +
  216. '<r> picks up ' +
  217. itemsDB[itemID]['article'] +
  218. ' ' +
  219. itemsDB[itemID]['name'] +
  220. '\n')
  221. else:
  222. mud.send_message(
  223. id,
  224. '<f220>' +
  225. npcs[nid]['name'] +
  226. '<r> has drawn their ' +
  227. itemsDB[itemID]['name'] +
  228. '\n')
  229. return True
  230. return False
  231. def npcWearsArmor(id, npcs, itemsDB):
  232. """An NPC puts on armor
  233. """
  234. if len(npcs[id]['inv']) == 0:
  235. return
  236. for c in defenseClothing:
  237. itemID = 0
  238. # what is the best defense which the NPC is carrying?
  239. max_defense = 0
  240. for i in npcs[id]['inv']:
  241. if itemsDB[int(i)][c] > 0:
  242. if itemsDB[int(i)]['mod_str'] == 0:
  243. if itemsDB[int(i)]['mod_endu'] > max_defense:
  244. max_defense = itemsDB[int(i)]['mod_endu']
  245. itemID = int(i)
  246. if itemID > 0:
  247. # Wear the armor
  248. npcs[id][c] = itemID
  249. def weaponDamage(id, players, itemsDB, weaponType, characterClassDB):
  250. """Calculates the amount of damage which a player can do
  251. with weapons held
  252. """
  253. damage = 0
  254. itemID = players[id]['clo_lhand']
  255. if itemID > 0:
  256. damage = damage + itemsDB[itemID]['mod_str']
  257. itemID = players[id]['clo_rhand']
  258. if itemID > 0:
  259. damage = damage + itemsDB[itemID]['mod_str']
  260. # Extra damage based on proficiencies
  261. if damage > 0:
  262. damageProficiency(id, players, weaponType, characterClassDB)
  263. # Total damage inflicted by weapons
  264. return damage
  265. def raceResistance(id, players, racesDB, weaponType):
  266. """How much resistance does the player have to the weapon type
  267. based upon their race
  268. """
  269. resistance = 0
  270. resistParam = 'resist_' + weaponType.lower()
  271. if weaponType.endswith('bow'):
  272. resistParam = 'resist_piercing'
  273. if weaponType.endswith('sling'):
  274. resistParam = 'resist_bludgeoning'
  275. if players[id].get('race'):
  276. race = players[id]['race'].lower()
  277. if racesDB.get(race):
  278. if racesDB[race].get(resistParam):
  279. resistance = racesDB[race][resistParam]
  280. return resistance
  281. def weaponDefense(id, players, itemsDB, racesDB, weaponType, characterClassDB):
  282. """How much defense does a player have due to armor worn?
  283. """
  284. defense = raceResistance(id, players, racesDB, weaponType)
  285. for c in defenseClothing:
  286. itemID = int(players[id][c])
  287. if itemID > 0:
  288. defense = defense + int(itemsDB[itemID]['mod_endu'])
  289. if defense > 0:
  290. defenseProficiency(id, players, characterClassDB)
  291. # Total defense by shields or clothing
  292. return defense
  293. def armorAgility(id, players, itemsDB):
  294. """Modify agility based on armor worn
  295. """
  296. agility = 0
  297. for c in defenseClothing:
  298. itemID = int(players[id][c])
  299. if itemID > 0:
  300. agility = agility + int(itemsDB[itemID]['mod_agi'])
  301. # Total agility for clothing
  302. return agility
  303. def canUseWeapon(id, players, itemsDB, itemID):
  304. if itemID == 0:
  305. return True
  306. lockItemID = itemsDB[itemID]['lockedWithItem']
  307. if lockItemID > 0:
  308. itemName = itemsDB[lockItemID]['name']
  309. for i in list(players[id]['inv']):
  310. if itemsDB[int(i)]['name'] == itemName:
  311. return True
  312. return False
  313. return True
  314. def getWeaponHeld(id, players, itemsDB):
  315. """Returns the type of weapon held, or fists if none is held
  316. """
  317. if players[id]['clo_rhand'] > 0 and players[id]['clo_lhand'] == 0:
  318. # something in right hand
  319. itemID = int(players[id]['clo_rhand'])
  320. if itemsDB[itemID]['mod_str'] > 0:
  321. if len(itemsDB[itemID]['type']) > 0:
  322. return itemID, itemsDB[itemID]['type'], itemsDB[itemID]['rof']
  323. if players[id]['clo_lhand'] > 0 and players[id]['clo_rhand'] == 0:
  324. # something in left hand
  325. itemID = int(players[id]['clo_lhand'])
  326. if itemsDB[itemID]['mod_str'] > 0:
  327. if len(itemsDB[itemID]['type']) > 0:
  328. return itemID, itemsDB[itemID]['type'], itemsDB[itemID]['rof']
  329. if players[id]['clo_lhand'] > 0 and players[id]['clo_rhand'] > 0:
  330. # something in both hands
  331. itemRightID = int(players[id]['clo_rhand'])
  332. itemLeftID = int(players[id]['clo_lhand'])
  333. if randint(0, 1) == 1:
  334. if itemsDB[itemRightID]['mod_str'] > 0:
  335. if len(itemsDB[itemRightID]['type']) > 0:
  336. return itemRightID, itemsDB[itemRightID]['type'], itemsDB[itemRightID]['rof']
  337. if itemsDB[itemLeftID]['mod_str'] > 0:
  338. if len(itemsDB[itemLeftID]['type']) > 0:
  339. return itemLeftID, itemsDB[itemLeftID]['type'], itemsDB[itemLeftID]['rof']
  340. else:
  341. if itemsDB[itemLeftID]['mod_str'] > 0:
  342. if len(itemsDB[itemLeftID]['type']) > 0:
  343. return itemLeftID, itemsDB[itemLeftID]['type'], itemsDB[itemLeftID]['rof']
  344. if itemsDB[itemRightID]['mod_str'] > 0:
  345. if len(itemsDB[itemRightID]['type']) > 0:
  346. return itemRightID, itemsDB[itemRightID]['type'], itemsDB[itemRightID]['rof']
  347. return 0, "fists", 1
  348. def getAttackDescription(weaponType):
  349. """Describes an attack with a given type of weapon. This
  350. Returns both the first person and second person
  351. perspective descriptions
  352. """
  353. weaponType = weaponType.lower()
  354. attackStrings = [
  355. "swing a fist at",
  356. "punch",
  357. "crudely swing a fist at",
  358. "ineptly punch"]
  359. attackDescriptionFirst = attackStrings[randint(0, len(attackStrings) - 1)]
  360. attackStrings = [
  361. "swung a fist at",
  362. "punched",
  363. "crudely swung a fist at",
  364. "ineptly punched"]
  365. attackDescriptionSecond = attackStrings[randint(0, len(attackStrings) - 1)]
  366. if weaponType.startswith("acid"):
  367. attackStrings = ["corrode", "spray", "splash"]
  368. attackDescriptionFirst = attackStrings[randint(
  369. 0, len(attackStrings) - 1)]
  370. attackStrings = ["corroded", "sprayed", "splashed"]
  371. attackDescriptionSecond = attackStrings[randint(
  372. 0, len(attackStrings) - 1)]
  373. if weaponType.startswith("bludg"):
  374. attackStrings = [
  375. "deliver a crushing blow on",
  376. "strike at",
  377. "swing at",
  378. "swing clumsily at",
  379. "strike a blow on"]
  380. attackDescriptionFirst = attackStrings[randint(
  381. 0, len(attackStrings) - 1)]
  382. attackStrings = [
  383. "delivered a crushing blow on",
  384. "struck at",
  385. "swung at",
  386. "swung clumsily at",
  387. "struck a blow on"]
  388. attackDescriptionSecond = attackStrings[randint(
  389. 0, len(attackStrings) - 1)]
  390. if weaponType.startswith("cold"):
  391. attackStrings = ["freeze", "chill"]
  392. attackDescriptionFirst = attackStrings[randint(
  393. 0, len(attackStrings) - 1)]
  394. attackStrings = ["froze", "chilled"]
  395. attackDescriptionSecond = attackStrings[randint(
  396. 0, len(attackStrings) - 1)]
  397. if weaponType.startswith("fire"):
  398. attackStrings = [
  399. "cast a ball a of flame at",
  400. "cast a fireball at",
  401. "cast a burning sphere at"]
  402. attackDescriptionFirst = attackStrings[randint(
  403. 0, len(attackStrings) - 1)]
  404. attackStrings = [
  405. "casted a ball a of flame at",
  406. "casted a fireball at",
  407. "casted a burning sphere at"]
  408. attackDescriptionSecond = attackStrings[randint(
  409. 0, len(attackStrings) - 1)]
  410. if weaponType.startswith("force"):
  411. attackStrings = ["point at", "wave at"]
  412. attackDescriptionFirst = attackStrings[randint(
  413. 0, len(attackStrings) - 1)]
  414. attackStrings = ["pointed at", "waved at"]
  415. attackDescriptionSecond = attackStrings[randint(
  416. 0, len(attackStrings) - 1)]
  417. if weaponType.startswith("lightning"):
  418. attackStrings = [
  419. "cast a bolt of lightning at",
  420. "cast a lightning bolt at"]
  421. attackDescriptionFirst = attackStrings[randint(
  422. 0, len(attackStrings) - 1)]
  423. attackStrings = [
  424. "casted a bolt of lightning at",
  425. "casted a lightning bolt at"]
  426. attackDescriptionSecond = attackStrings[randint(
  427. 0, len(attackStrings) - 1)]
  428. if weaponType.startswith("necro"):
  429. attackStrings = ["whither", "chill"]
  430. attackDescriptionFirst = attackStrings[randint(
  431. 0, len(attackStrings) - 1)]
  432. attackStrings = ["whithered", "chilled"]
  433. attackDescriptionSecond = attackStrings[randint(
  434. 0, len(attackStrings) - 1)]
  435. if weaponType.startswith("pierc"):
  436. attackStrings = ["stab at", "hack at"]
  437. attackDescriptionFirst = attackStrings[randint(
  438. 0, len(attackStrings) - 1)]
  439. attackStrings = ["stabbed at", "hacked at"]
  440. attackDescriptionSecond = attackStrings[randint(
  441. 0, len(attackStrings) - 1)]
  442. if weaponType.startswith("poison"):
  443. attackStrings = ["poison"]
  444. attackDescriptionFirst = attackStrings[randint(
  445. 0, len(attackStrings) - 1)]
  446. attackStrings = ["poisoned"]
  447. attackDescriptionSecond = attackStrings[randint(
  448. 0, len(attackStrings) - 1)]
  449. if weaponType.startswith("psy"):
  450. attackStrings = ["psychically blast", "psychically deplete"]
  451. attackDescriptionFirst = attackStrings[randint(
  452. 0, len(attackStrings) - 1)]
  453. attackStrings = ["psychically blasted", "psychically depleted"]
  454. attackDescriptionSecond = attackStrings[randint(
  455. 0, len(attackStrings) - 1)]
  456. if weaponType.startswith("radiant"):
  457. attackStrings = ["sear", "scorch"]
  458. attackDescriptionFirst = attackStrings[randint(
  459. 0, len(attackStrings) - 1)]
  460. attackStrings = ["seared", "scorched"]
  461. attackDescriptionSecond = attackStrings[randint(
  462. 0, len(attackStrings) - 1)]
  463. if weaponType.startswith("slash"):
  464. attackStrings = [
  465. "cut at",
  466. "cut savagely into",
  467. "slash at",
  468. "swing at",
  469. "swing clumsily at"]
  470. attackDescriptionFirst = attackStrings[randint(
  471. 0, len(attackStrings) - 1)]
  472. attackStrings = [
  473. "cut at",
  474. "cut savagely into",
  475. "slashed at",
  476. "swung at",
  477. "swung clumsily at"]
  478. attackDescriptionSecond = attackStrings[randint(
  479. 0, len(attackStrings) - 1)]
  480. if weaponType.startswith("thunder"):
  481. attackStrings = ["cast a thunderbolt at", "cast a bolt of thunder at"]
  482. attackDescriptionFirst = attackStrings[randint(
  483. 0, len(attackStrings) - 1)]
  484. attackStrings = [
  485. "casted a thunderbolt at",
  486. "casted a bolt of thunder at"]
  487. attackDescriptionSecond = attackStrings[randint(
  488. 0, len(attackStrings) - 1)]
  489. if weaponType.startswith("ranged bow") or \
  490. weaponType.startswith("ranged shortbow") or \
  491. weaponType.startswith("ranged longbow"):
  492. attackStrings = ["fire an arrow at", "release an arrow at"]
  493. attackDescriptionFirst = attackStrings[randint(
  494. 0, len(attackStrings) - 1)]
  495. attackStrings = ["fired an arrow at", "released an arrow at"]
  496. attackDescriptionSecond = attackStrings[randint(
  497. 0, len(attackStrings) - 1)]
  498. if weaponType.startswith("ranged crossbow"):
  499. attackStrings = ["fire a bolt at", "release a bolt at"]
  500. attackDescriptionFirst = attackStrings[randint(
  501. 0, len(attackStrings) - 1)]
  502. attackStrings = ["fired a bolt at", "released a bolt at"]
  503. attackDescriptionSecond = attackStrings[randint(
  504. 0, len(attackStrings) - 1)]
  505. if weaponType.startswith("ranged sling"):
  506. attackStrings = ["sling a rock at"]
  507. attackDescriptionFirst = attackStrings[randint(
  508. 0, len(attackStrings) - 1)]
  509. attackStrings = ["slung a rock at"]
  510. attackDescriptionSecond = attackStrings[randint(
  511. 0, len(attackStrings) - 1)]
  512. if weaponType.startswith("ranged dart"):
  513. attackStrings = ["blow a dart at"]
  514. attackDescriptionFirst = attackStrings[randint(
  515. 0, len(attackStrings) - 1)]
  516. attackStrings = ["blew a dart at"]
  517. attackDescriptionSecond = attackStrings[randint(
  518. 0, len(attackStrings) - 1)]
  519. return attackDescriptionFirst, attackDescriptionSecond
  520. def getTemperatureDifficulty(rm, rooms, mapArea, clouds):
  521. """Returns a difficulty factor based on the ambient
  522. temperature
  523. """
  524. temperature = getTemperatureAtCoords(
  525. rooms[rm]['coords'], rooms, mapArea, clouds)
  526. if temperature > 5:
  527. # Things get difficult when hotter
  528. return int(temperature / 4)
  529. # Things get difficult in snow/ice
  530. return -(temperature - 5)
  531. def attackRoll(luck):
  532. """Did an attack succeed?
  533. """
  534. if luck > 6:
  535. luck = 6
  536. if randint(1 + luck, 10) > 5:
  537. return True
  538. return False
  539. def criticalHit():
  540. """Is this a critical hit (extra damage)?
  541. """
  542. if randint(1, 20) == 20:
  543. return True
  544. return False
  545. def calculateDamage(weapons, defense):
  546. """Returns the amount of damage an attack causes
  547. """
  548. damageDescription = 'damage'
  549. damageValue = weapons
  550. armorClass = defense
  551. if criticalHit():
  552. damageDescription = 'critical damage'
  553. damageValue = damageValue * 2
  554. return damageValue, armorClass, damageDescription
  555. def runFightsBetweenPlayers(
  556. mud,
  557. players,
  558. npcs,
  559. fights,
  560. fid,
  561. itemsDB,
  562. rooms,
  563. maxTerrainDifficulty,
  564. mapArea,
  565. clouds,
  566. racesDB,
  567. characterClassDB):
  568. """A fight between two players
  569. """
  570. s1id = fights[fid]['s1id']
  571. s2id = fights[fid]['s2id']
  572. # In the same room?
  573. if players[s1id]['room'] != players[s2id]['room']:
  574. return
  575. # is the player frozen?
  576. if players[s1id]['frozenStart'] > 0 or players[s1id]['canAttack'] == 0:
  577. mud.send_message(
  578. s2id, randomDescription(
  579. players[s1id]['frozenDescription']) + '\n')
  580. return
  581. if playerIsTrapped(s1id,players,rooms):
  582. return
  583. currRoom = players[s1id]['room']
  584. weightDifficulty = int(playerInventoryWeight(s1id, players, itemsDB) / 20)
  585. temperatureDifficulty = getTemperatureDifficulty(
  586. currRoom, rooms, mapArea, clouds)
  587. terrainDifficulty = rooms[players[s1id]['room']
  588. ]['terrainDifficulty'] * 10 / maxTerrainDifficulty
  589. # Agility of player
  590. if int(time.time()) < players[s1id]['lastCombatAction'] + 10 - players[s1id]['agi'] - armorAgility(
  591. s1id, players, itemsDB) + terrainDifficulty + temperatureDifficulty + weightDifficulty:
  592. return
  593. if players[s2id]['isAttackable'] == 1:
  594. players[s1id]['isInCombat'] = 1
  595. players[s2id]['isInCombat'] = 1
  596. weaponID, weaponType, roundsOfFire = getWeaponHeld(
  597. s1id, players, itemsDB)
  598. if not canUseWeapon(s1id, players, itemsDB, weaponID):
  599. lockItemID = itemsDB[weaponID]['lockedWithItem']
  600. mud.send_message(
  601. s1id,
  602. 'You take aim, but find you have no ' +
  603. itemsDB[lockItemID]['name'].lower() +
  604. '.\n')
  605. mud.send_message(
  606. s2id,
  607. '<f32>' +
  608. players[s1id]['name'] +
  609. '<r> takes aim, but finds they have no ' +
  610. itemsDB[lockItemID]['name'].lower() +
  611. '.\n')
  612. stowHands(s1id, players, itemsDB, mud)
  613. mud.send_message(
  614. s2id,
  615. '<f32>' +
  616. players[s1id]['name'] +
  617. '<r> stows ' +
  618. itemsDB[itemID]['article'] +
  619. ' <b234>' +
  620. itemsDB[itemID]['name'] +
  621. '\n\n')
  622. players[s1id]['lastCombatAction'] = int(time.time())
  623. return
  624. # Do damage to the PC here
  625. if attackRoll(
  626. players[s1id]['luc'] +
  627. weaponProficiency(
  628. s1id,
  629. players,
  630. weaponType,
  631. characterClassDB)):
  632. damageValue, armorClass, damageDescription = calculateDamage(
  633. weaponDamage(
  634. s1id, players, itemsDB, weaponType, characterClassDB), weaponDefense(
  635. s2id, players, itemsDB, racesDB, weaponType, characterClassDB))
  636. if roundsOfFire < 1:
  637. roundsOfFire = 1
  638. attackDescriptionFirst, attackDescriptionSecond = getAttackDescription(
  639. weaponType)
  640. if armorClass <= damageValue:
  641. if players[s1id]['hp'] > 0:
  642. modifierStr = ''
  643. for firingRound in range(roundsOfFire):
  644. modifier = randint(
  645. 0, 10) + (damageValue * roundsOfFire) - armorClass
  646. damagePoints = players[s1id]['str'] + modifier
  647. if damagePoints < 0:
  648. damagePoints = 0
  649. players[s2id]['hp'] = players[s2id]['hp'] - \
  650. damagePoints
  651. if len(modifierStr) == 0:
  652. modifierStr = modifierStr + str(damagePoints)
  653. else:
  654. modifierStr = modifierStr + \
  655. ' + ' + str(damagePoints)
  656. decreaseAffinityBetweenPlayers(
  657. players, s2id, players, s1id)
  658. decreaseAffinityBetweenPlayers(
  659. players, s1id, players, s2id)
  660. mud.send_message(
  661. s1id,
  662. 'You ' +
  663. attackDescriptionFirst +
  664. ' <f32><u>' +
  665. players[s2id]['name'] +
  666. '<r> for <f15><b2> * ' +
  667. modifierStr +
  668. ' *<r> points of ' +
  669. damageDescription +
  670. '.\n')
  671. mud.send_message(
  672. s2id,
  673. '<f32>' +
  674. players[s1id]['name'] +
  675. '<r> has ' +
  676. attackDescriptionSecond +
  677. ' you for <f15><b88> * ' +
  678. modifierStr +
  679. ' *<r> points of ' +
  680. damageDescription +
  681. '.\n')
  682. else:
  683. if players[s1id]['hp'] > 0:
  684. # Attack deflected by armor
  685. mud.send_message(
  686. s1id,
  687. 'You ' +
  688. attackDescriptionFirst +
  689. ' <f32><u>' +
  690. players[s2id]['name'] +
  691. '<r> but their armor deflects it.\n')
  692. mud.send_message(
  693. s2id,
  694. '<f32>' +
  695. players[s1id]['name'] +
  696. '<r> has ' +
  697. attackDescriptionSecond +
  698. ' you but it is deflected by your armor.\n')
  699. else:
  700. players[s1id]['lastCombatAction'] = int(time.time())
  701. mud.send_message(
  702. s1id,
  703. 'You miss trying to hit <f32><u>' +
  704. players[s2id]['name'] +
  705. '\n')
  706. mud.send_message(
  707. s2id,
  708. '<f32><u>' +
  709. players[s1id]['name'] +
  710. '<r> missed while trying to hit you!\n')
  711. players[s1id]['lastCombatAction'] = int(time.time())
  712. else:
  713. mud.send_message(
  714. s1id,
  715. '<f225>Suddenly you stop. It wouldn`t be a good idea to attack <f32>' +
  716. players[s2id]['name'] +
  717. ' at this time.\n')
  718. fightsCopy = deepcopy(fights)
  719. for (fight, pl) in fightsCopy.items():
  720. if fightsCopy[fight]['s1id'] == s1id and fightsCopy[fight]['s2id'] == s2id:
  721. del fights[fight]
  722. def runFightsBetweenPlayerAndNPC(
  723. mud,
  724. players,
  725. npcs,
  726. fights,
  727. fid,
  728. itemsDB,
  729. rooms,
  730. maxTerrainDifficulty,
  731. mapArea,
  732. clouds,
  733. racesDB,
  734. characterClassDB):
  735. """Fight between a player and an NPC
  736. """
  737. s1id = fights[fid]['s1id']
  738. s2id = fights[fid]['s2id']
  739. # In the same room?
  740. if players[s1id]['room'] != npcs[s2id]['room']:
  741. return
  742. # is the player frozen?
  743. if players[s1id]['frozenStart'] > 0 or players[s1id]['canAttack'] == 0:
  744. mud.send_message(
  745. s2id, randomDescription(
  746. players[s1id]['frozenDescription']) + '\n')
  747. return
  748. if playerIsTrapped(s1id,players,rooms):
  749. return
  750. currRoom = players[s1id]['room']
  751. weightDifficulty = int(playerInventoryWeight(s1id, players, itemsDB) / 20)
  752. temperatureDifficulty = getTemperatureDifficulty(
  753. currRoom, rooms, mapArea, clouds)
  754. terrainDifficulty = rooms[players[s1id]['room']
  755. ]['terrainDifficulty'] * 10 / maxTerrainDifficulty
  756. # Agility of player
  757. if int(time.time()) < players[s1id]['lastCombatAction'] + 10 - players[s1id]['agi'] - armorAgility(
  758. s1id, players, itemsDB) + terrainDifficulty + temperatureDifficulty + weightDifficulty:
  759. return
  760. if npcs[s2id]['isAttackable'] == 1:
  761. players[s1id]['isInCombat'] = 1
  762. npcs[s2id]['isInCombat'] = 1
  763. weaponID, weaponType, roundsOfFire = getWeaponHeld(
  764. s1id, players, itemsDB)
  765. if not canUseWeapon(s1id, players, itemsDB, weaponID):
  766. lockItemID = itemsDB[weaponID]['lockedWithItem']
  767. mud.send_message(
  768. s1id,
  769. 'You take aim, but find you have no ' +
  770. itemsDB[lockItemID]['name'].lower() +
  771. '.\n')
  772. stowHands(s1id, players, itemsDB, mud)
  773. players[s1id]['lastCombatAction'] = int(time.time())
  774. return
  775. # Do damage to the NPC here
  776. if attackRoll(
  777. players[s1id]['luc'] +
  778. weaponProficiency(
  779. s1id,
  780. players,
  781. weaponType,
  782. characterClassDB)):
  783. damageValue, armorClass, damageDescription = calculateDamage(
  784. weaponDamage(
  785. s1id, players, itemsDB, weaponType, characterClassDB), weaponDefense(
  786. s2id, npcs, itemsDB, racesDB, weaponType, characterClassDB))
  787. npcWearsArmor(s2id, npcs, itemsDB)
  788. if roundsOfFire < 1:
  789. roundsOfFire = 1
  790. attackDescriptionFirst, attackDescriptionSecond = getAttackDescription(
  791. weaponType)
  792. if armorClass <= damageValue:
  793. if players[s1id]['hp'] > 0:
  794. modifierStr = ''
  795. for firingRound in range(roundsOfFire):
  796. modifier = randint(0, 10) + damageValue - armorClass
  797. damagePoints = players[s1id]['str'] + modifier
  798. if damagePoints < 0:
  799. damagePoints = 0
  800. npcs[s2id]['hp'] = npcs[s2id]['hp'] - damagePoints
  801. if len(modifierStr) == 0:
  802. modifierStr = modifierStr + str(damagePoints)
  803. else:
  804. modifierStr = modifierStr + \
  805. ' + ' + str(damagePoints)
  806. decreaseAffinityBetweenPlayers(npcs, s2id, players, s1id)
  807. decreaseAffinityBetweenPlayers(players, s1id, npcs, s2id)
  808. mud.send_message(
  809. s1id,
  810. 'You ' +
  811. attackDescriptionFirst +
  812. ' <f220>' +
  813. npcs[s2id]['name'] +
  814. '<r> for <b2><f15> * ' +
  815. modifierStr +
  816. ' * <r> points of ' +
  817. damageDescription +
  818. '\n')
  819. else:
  820. if players[s1id]['hp'] > 0:
  821. # Attack deflected by armor
  822. mud.send_message(
  823. s1id,
  824. 'You ' +
  825. attackDescriptionFirst +
  826. ' <f32><u>' +
  827. npcs[s2id]['name'] +
  828. '<r> but their armor deflects it.\n')
  829. else:
  830. players[s1id]['lastCombatAction'] = int(time.time())
  831. mud.send_message(
  832. s1id,
  833. 'You miss <f220>' +
  834. npcs[s2id]['name'] +
  835. '<r> completely!\n')
  836. players[s1id]['lastCombatAction'] = int(time.time())
  837. else:
  838. mud.send_message(
  839. s1id,
  840. '<f225>Suddenly you stop. It wouldn`t be a good idea to attack <u><f21>' +
  841. npcs[s2id]['name'] +
  842. '<r> at this time.\n')
  843. fightsCopy = deepcopy(fights)
  844. for (fight, pl) in fightsCopy.items():
  845. if fightsCopy[fight]['s1id'] == s1id and fightsCopy[fight]['s2id'] == s2id:
  846. del fights[fight]
  847. def runFightsBetweenNPCAndPlayer(
  848. mud,
  849. players,
  850. npcs,
  851. fights,
  852. fid,
  853. items,
  854. itemsDB,
  855. rooms,
  856. maxTerrainDifficulty,
  857. mapArea,
  858. clouds,
  859. racesDB,
  860. characterClassDB):
  861. """Fight between NPC and player
  862. """
  863. s1id = fights[fid]['s1id']
  864. s2id = fights[fid]['s2id']
  865. # In the same room?
  866. if npcs[s1id]['room'] != players[s2id]['room']:
  867. return
  868. # is the player frozen?
  869. if npcs[s1id]['frozenStart'] > 0:
  870. mud.send_message(
  871. s2id,
  872. '<f220>' +
  873. npcs[s1id]['name'] +
  874. "<r> tries to attack but can't move\n")
  875. return
  876. currRoom = npcs[s1id]['room']
  877. weightDifficulty = int(playerInventoryWeight(s1id, npcs, itemsDB) / 20)
  878. temperatureDifficulty = getTemperatureDifficulty(
  879. currRoom, rooms, mapArea, clouds)
  880. terrainDifficulty = rooms[players[s2id]['room']
  881. ]['terrainDifficulty'] * 10 / maxTerrainDifficulty
  882. # Agility of NPC
  883. if int(time.time()) < npcs[s1id]['lastCombatAction'] + 10 - npcs[s1id]['agi'] - armorAgility(
  884. s1id, npcs, itemsDB) + terrainDifficulty + temperatureDifficulty + weightDifficulty:
  885. return
  886. npcs[s1id]['isInCombat'] = 1
  887. players[s2id]['isInCombat'] = 1
  888. npcUpdateLuck(s1id, npcs, items, itemsDB)
  889. if npcWieldsWeapon(mud, s2id, s1id, npcs, items, itemsDB):
  890. return
  891. weaponID, weaponType, roundsOfFire = getWeaponHeld(s1id, npcs, itemsDB)
  892. # Do the damage to PC here
  893. if attackRoll(npcs[s1id]['luc'] +
  894. weaponProficiency(s1id, npcs, weaponType, characterClassDB)):
  895. damageValue, armorClass, damageDescription = calculateDamage(
  896. weaponDamage(
  897. s1id, npcs, itemsDB, weaponType, characterClassDB), weaponDefense(
  898. s2id, players, itemsDB, racesDB, weaponType, characterClassDB))
  899. if roundsOfFire < 1:
  900. roundsOfFire = 1
  901. attackDescriptionFirst, attackDescriptionSecond = getAttackDescription(
  902. weaponType)
  903. if armorClass <= damageValue:
  904. if npcs[s1id]['hp'] > 0:
  905. modifierStr = ''
  906. for firingRound in range(roundsOfFire):
  907. modifier = randint(
  908. 0, 10) + damageValue - armorClass - npcs[s1id]['tempHitPoints']
  909. damagePoints = npcs[s1id]['str'] + modifier
  910. if damagePoints < 0:
  911. damagePoints = 0
  912. players[s2id]['hp'] = players[s2id]['hp'] - damagePoints
  913. if len(modifierStr) == 0:
  914. modifierStr = modifierStr + str(damagePoints)
  915. else:
  916. modifierStr = modifierStr + ' + ' + str(damagePoints)
  917. decreaseAffinityBetweenPlayers(npcs, s1id, players, s2id)
  918. decreaseAffinityBetweenPlayers(players, s2id, npcs, s1id)
  919. mud.send_message(
  920. s2id,
  921. '<f220>' +
  922. npcs[s1id]['name'] +
  923. '<r> has ' +
  924. attackDescriptionSecond +
  925. ' you for <f15><b88> * ' +
  926. modifierStr +
  927. ' * <r> points of ' +
  928. damageDescription +
  929. '.\n')
  930. else:
  931. mud.send_message(
  932. s2id,
  933. '<f220>' +
  934. npcs[s1id]['name'] +
  935. '<r> has ' +
  936. attackDescriptionSecond +
  937. ' you but it is deflected by your armor.\n')
  938. else:
  939. npcs[s1id]['lastCombatAction'] = int(time.time())
  940. mud.send_message(
  941. s2id,
  942. '<f220>' +
  943. npcs[s1id]['name'] +
  944. '<r> has missed you completely!\n')
  945. npcs[s1id]['lastCombatAction'] = int(time.time())
  946. def runFights(
  947. mud,
  948. players,
  949. npcs,
  950. fights,
  951. items,
  952. itemsDB,
  953. rooms,
  954. maxTerrainDifficulty,
  955. mapArea,
  956. clouds,
  957. racesDB,
  958. characterClassDB):
  959. """Handles fights
  960. """
  961. for (fid, pl) in list(fights.items()):
  962. # PC -> PC
  963. if fights[fid]['s1type'] == 'pc' and fights[fid]['s2type'] == 'pc':
  964. runFightsBetweenPlayers(
  965. mud,
  966. players,
  967. npcs,
  968. fights,
  969. fid,
  970. itemsDB,
  971. rooms,
  972. maxTerrainDifficulty,
  973. mapArea,
  974. clouds,
  975. racesDB,
  976. characterClassDB)
  977. # PC -> NPC
  978. elif fights[fid]['s1type'] == 'pc' and fights[fid]['s2type'] == 'npc':
  979. runFightsBetweenPlayerAndNPC(
  980. mud,
  981. players,
  982. npcs,
  983. fights,
  984. fid,
  985. itemsDB,
  986. rooms,
  987. maxTerrainDifficulty,
  988. mapArea,
  989. clouds,
  990. racesDB,
  991. characterClassDB)
  992. # NPC -> PC
  993. elif fights[fid]['s1type'] == 'npc' and fights[fid]['s2type'] == 'pc':
  994. runFightsBetweenNPCAndPlayer(
  995. mud,
  996. players,
  997. npcs,
  998. fights,
  999. fid,
  1000. items,
  1001. itemsDB,
  1002. rooms,
  1003. maxTerrainDifficulty,
  1004. mapArea,
  1005. clouds,
  1006. racesDB,
  1007. characterClassDB)
  1008. # NPC -> NPC
  1009. elif fights[fid]['s1type'] == 'npc' and fights[fid]['s2type'] == 'npc':
  1010. test = 1