Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
(Flicking the big red switch, pushing the big red button)
This is the actual initial commit. The project has been going for a
while, and this is it going on to github.
State is alpha, playable but unfinished.
  • Loading branch information
lkingsford committed Oct 24, 2014
1 parent 989c50e commit e54f8a4
Show file tree
Hide file tree
Showing 42 changed files with 8,380 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -3,7 +3,7 @@ Atlas Warriors

Atlas Warriors is a Roguelike Game primarily developed by Lachlan Kingsford of www.nerdygentleman.com.

You can financially support the development of this project at the moment by paypal donation to lachlan@nerdygentleman.com or bitcoin donation to 14PgnsEcgqgSrCFxTjVvczfu5Fyh85hjbU
You can financially support the development of this project at the moment by pledgie at https://pledgie.com/campaigns/27179 , paypal donation to lachlan@nerdygentleman.com or bitcoin donation to 14PgnsEcgqgSrCFxTjVvczfu5Fyh85hjbU

The game is designed from the outset to be in some ways a minimalist roguelike, taking inspiration from Brogue. Every monster must fight differently and have a unique purpose.

Expand Down
81 changes: 81 additions & 0 deletions docs/Bugs.txt
@@ -0,0 +1,81 @@
IMMEDIATE TO DO LIST:
[x] Fix messages
[x] Fix Unarmed DV bug
[x] Implement Special Dragon Weapons
[x] Implement Shield Fire Blocking
[x] Implement Bandits
[-] Implement Goliaths - Bugged?
[x] Implement Zombies
[x] Implement Orcs
[ ] Implement Assassins
[x] Implement Necromancer
[-] Implement Warlord
[ ] Finish Victory Screen

To Add
- Sound
- Make burning enemy look better
- Make blunt weapons stun loner on crit?
- Assassins go invisible next to walls, and improve AI accordingly
- Give best weapon for PC skills if they defeat the dragon on 9
- Let shields do a bit fire blocking
- Smaller dragons in addition
- Monsters should gain HP and damage when they start at higher hitpoints
- Axes shouldn't gain as much DV with levels
- Shields have 1.5(?)x effectiveness when standing still
- Shields have 2(?)x effectiveness when retreated from the attack
- Dagger counterattack adds extra blank line of message
- Counterattack/Parry message order needs to be sorted
- Give bandits weapons
- Add zombie DV reduce effect
- Necromancers are not implemented
- Make healers focus on Warlord if on same level
- Make the door to the emerald court shut when you walk through it
- Win when you kill the warlord
- Unarmed skll doesn't contribute to DV
- Warload death doesn't look fantastic

Questions
- Should dragons have a winged leap - something that has maybe a 10 turn
cooldown, and costs a little MP to jump 5 spaces or something?
I don't think so
- How should multiple weapons be handled?
Presently:
Attack takes as long as slowest weapon
Both weapons attack at once, with a two weapon skill
Options:
Can only wield 1 at a time, or 1 and a dagger
Can weild multiple weapons, but only attack with one at a time, maybe with 3/4 speed?
Can wield multiple weapons, but each weapon only takes as long as it takes
- Should critters be scattered as opposed to starting in the same place?
- My original thought with goliaths was to have them
charge, but I can't think of a good way to make that work
- Should Critters follow more cleverly, or roam? At the moment, they blindly
move towards the nearest enemy

To Refactor
- Seperate the interface from the game as much as possible. This is not done
nearly at all now
- Move the special tile thing out of tryMove to seperate cell classes

Bugs to fix
- Shields can't affect ToHit of weapons when equipped
- Up stairs should be more randomised
- map.ChooseItem can't choose the least likely item
- Equipping an item to left hand while holding a 2 handed item holds both items
- Some enemies (particularly in a line) seen to disappear
- Animations display when out of sight
- Flames shoot behind as well
- Flames animation needs to store previous state and draw it if no fire there anymore
- Orcs won't do any melee at all, and can be easily cornered. They're still fun
(I think)
- There is some circumstance where moveTo in BUGGYBIT00 in orc.py is [(,)] rather then (,,,).
I'm not sure why it is. When it is that, it crashes (rightfully so). There is a bandaid
fix on it at the moment, but it needs to be looked at. There is probably some path of
execution in which moveTo = moveTo[1] is never triggered.

Poential changes:
- Check if the run away code (in Bandit.py for instance) could survive getting the maximum value
to run away rather then the nearest that's at least 5 away
- Bandit AI is slow
- Zombie DV mod is in character.py. I think this is too slow and should be moved.
54 changes: 54 additions & 0 deletions src/Assassin.py
@@ -0,0 +1,54 @@
from enemy import *
import random

class Assassin(Enemy):
def __init__(self, messageLog, currentMap = None):
super().__init__(messageLog, currentMap)
self.name = "Assassin"
self.levelMod = random.choice([-2,-1,0,0,0,0,0,1,1,2])
self.level = currentMap.level + self.levelMod
self.character = "A"
self.speed = 15
self.hp = self.maxhp = round(6 * (max(1,self.level - 3) ** 0.4))
self.baseDamage = round(8 * (max(1,self.level - 3) ** 0.2))
self.baseToHit = round(10 * (max(1,self.level - 3) ** 0.2))
self.baseToDefend = round(2 * (max(1,self.level - 3) ** 0.2))
self.color = "gray"
# 0 is normal, 1 is badass, 2 is super-badass
#
# Chance of Badass = .10 + 0.04 * level
# Chance of Superbadass = -0.1 + 0.04 * level
self.badass = badass = 1 if random.random() < (.10 + 0.04 * currentMap.level) else 2 if random.random() < (-.2 + 0.04 * currentMap.level) else 0
if (self.badass == 1):
self.name = "Master Assassin"
self.hp = self.maxhp = self.hp * 2
self.baseDamage = self.baseDamage * 2
self.baseToHit = self.baseToHit * 2
self.baseToDefend = self.baseToDefend * 2
self.character = "A"
self.color = "navy"
elif (self.badass == 2):
self.name = "Assassin Mortician"
self.character = "A"
self.hp = self.maxhp = self.hp * 4
self.baseDamage = round(self.baseDamage * 4)
self.baseToHit = self.baseToHit * 4
self.baseToDefend = self.baseToDefend * 4
self.color = "blue"

def danger(self):
return (2 + self.levelMod/2) * ((self.badass + 1) ** 2)

def update(self):
super().update()
try:
nearestEnemy = min([i for i in self.currentMap.characters if i != self and i.team != self.team],
key = lambda i: abs(self.x - i.x) + abs(self.y - i.y))
dx = 0 if nearestEnemy.x == self.x else (-1 if nearestEnemy.x < self.x else 1)
dy = 0 if nearestEnemy.y == self.y else (-1 if nearestEnemy.y < self.y else 1)
self.tryMove(self.x + dx, self.y + dy)

except ValueError:
self.Wait()
self.Wait()

230 changes: 230 additions & 0 deletions src/Bandit.py
@@ -0,0 +1,230 @@
from enemy import *
import random
import Message
import copy

class Bandit(Enemy):
def __init__(self, messageLog, currentMap = None, badass = -1):
super().__init__(messageLog, currentMap)
self.name = "Bandit"
self.levelMod = random.choice([-2,-1,0,0,0,0,0,1,1,2])
self.level = currentMap.level + self.levelMod
self.character = "b"
self.speed = 15
self.hp = self.maxhp = round(5 * (max(1,self.level) ** 0.4))
self.baseDamage = round(3 * (max(1,self.level) ** 0.2))
self.baseToHit = round(4 * (max(1,self.level) ** 0.2))
self.baseToDefend = round(2 * (max(1,self.level) ** 0.2))
self.color = "silver"

# 0 is normal, 1 is badass, 2 is super-badass
#
# Chance of Badass = 0.02 * level
# Chance of Superbadass = -0.1 + 0.02 * level
# Chance of Super^2badass = -0.2 + 0.02 * level
self.badass = badass if badass != -1 else 1 if random.random() < (0.02 * currentMap.level) else 2 if random.random() < (-.1 + 0.02 * currentMap.level) else 3 if random.random() < (-.2 + 0.02 * currentMap.level) else 0
if (self.badass == 1):
self.name = "Experienced Bandit"
self.hp = self.maxhp = self.hp * 2
self.baseDamage = self.baseDamage * 2
self.baseToHit = self.baseToHit * 2
self.baseToDefend = self.baseToDefend * 2
self.character = "B"
self.color = "yellow"
elif (self.badass == 2):
self.name = "Veteran Bandit"
self.character = "B"
self.hp = self.maxhp = self.hp * 4
self.baseDamage = round(self.baseDamage * 4)
self.baseToHit = self.baseToHit * 4
self.baseToDefend = self.baseToDefend * 4
self.color = "red"
elif (self.badass == 3):
self.name = "Bandit Warlord"
self.character = "B"
self.hp = self.maxhp = self.hp * 8
self.baseDamage = round(self.baseDamage * 8)
self.baseToHit = self.baseToHit * 8
self.baseToDefend = self.baseToDefend * 8
self.color = "fuchsia"

# Choose some weapons, and maybe a shield
# Choose weapon.
# weaponType = random.choice([1,2,3,4,5])

if self.level < 3:
weapon = random.choice([1,2,3,4,5])
shieldEquipped = random.random() < .1
if shieldEquipped:
shield = 5

elif self.level < 5:
weapon = random.choice([1,2,3,4,5,7,11,16,17,19,24])
shieldEquipped = random.random() < .3
if shieldEquipped:
shield = random.choice([5,30])
else:
weapon = random.choice([1,2,3,4,5,7,11,16,17,19,24, 8, 12, 21,28])
shieldEquipped = random.random() < .5
if shieldEquipped:
shield = random.choice([5,30,31,32])

self.rightHandEquipped = copy.deepcopy([i for i in self.currentMap.defaultItems if i.ID == weapon][0])
if shieldEquipped:
self.leftHandEquipped = copy.deepcopy([i for i in self.currentMap.defaultItems if i.ID == shield][0])

# Mode 0 is inactive
# Mode 1 is active
# Mode 2 is on patrol
# Mode 3 is running away
# Mode 4 is berserk. If health is extremely low, go nuts and
# just rush attack. Will also berserk if all exits
# are blocked

self.mode = random.choice([0,0,0,2])

room1 = room2 = random.choice(self.currentMap.Rooms)
while room1 == room2:
room2 = random.choice(self.currentMap.Rooms)
self.patrolRoute = [(round(room1.w / 2) + room1.x,(round(room1.h / 2) + room1.y)), (round(room2.w / 2) + room2.x,(round(room2.h / 2) + room2.y))]
self.patrolRouteI = 0
self.currentTarget = self.patrolRoute[self.patrolRouteI]


self.runAwayAt = (self.maxhp / 4) * random.choice([0,1,2])

self.teamMates = []

def danger(self):
return (2 + self.levelMod/2) * ((self.badass + 1) ** 2)

def update(self):
super().update()

# Nearest enemy is naive and is simply approximate - I don't use pathfinding
enemies = [i for i in self.currentMap.characters if i != self and i.team != self.team]
if len(enemies)>0:
nearestEnemy = min(enemies,
key = lambda i: abs(self.x - i.x) + abs(self.y - i.y))
else:
self.Wait()
return

# Find if any enemies in room
# This could be an eloquent one liner, but I think that this is actually more clear
#
# I think this is a slow(ish) chunk of code but it's better then it used to be...
myRooms = self.currentMap.GetRooms(self.x, self.y)
inRoom = [i for i in self.currentMap.characters\
if (i.team != self.team) and\
len(self.currentMap.Map[self.x][self.y].rooms & self.currentMap.Map[i.x][i.y].rooms) > 0]


moveTo = None
activateTeam = False
# This update is in two parts.
# The first picks if a mode needs to change
# The second is to performs its action

if self.mode == 0:
# Inactive. Do nothing unless enemy moves into room,
# in which case go active
if len(inRoom) > 0:
self.mode = 1
else:
moveTo = None

# NOTE: No elif - just if. I want to go active straight away if
# located
if self.mode == 2:
# Walk from the centre of room1 to the centre of room2.
# If an enemy is found, blow whistle to make all teammates active
# and go active

if len(inRoom) > 0:
self.mode = 1
activateTeam = True

else:
if self.x == self.currentTarget[0] and self.y == self.currentTarget[1]:
# Swap targets
self.patrolRouteI += 1
self.currentTarget = self.patrolRoute[self.patrolRouteI % len(self.patrolRoute)]

#print (self.x, ' ', self.y, ' ', self.currentTarget)
moveTo = self.GetRoute(self.currentTarget)
#print (moveTo)
if moveTo == []:
moveTo = None
if moveTo != None:
moveTo = moveTo[1]

if self.mode == 1:
# Active. Move towards nearest enemy.
if len(inRoom) > 0:
nearestEnemy = min([i for i in inRoom if i != self and i.team != self.team],\
key = lambda i: abs(self.x - i.x) + abs(self.y - i.y))

moveTo = self.GetRoute((nearestEnemy.x, nearestEnemy.y))
if moveTo == []:
moveTo = None
if moveTo != None:
moveTo = moveTo[1]

if self.hp < self.runAwayAt:
moveTo = self.GetNearest(lambda i: (max(abs(i[0]-nearestEnemy.x),abs(i[1]-nearestEnemy.y))) > 5)
if moveTo == []:
self.mode = 4
self.messageLog.append(Message.Message(self.name + " turns to run away but is blocked!"))
self.messageLog.append(Message.Message(self.name + " decides to fight for his life!"))
else:
self.messageLog.append(Message.Message(self.name + " turns to run away!"))
self.mode = 3


if self.mode == 3:
# Try to run away
if self.hp < 3:
self.mode = 4
self.messageLog.append(Message.Message(self.name + " decides to fight for his life!"))

# This might be better as a max function rather then a find first which satisfies,
# but I'm concerned about speed
moveTo = self.GetNearest(lambda i: (max(abs(i[0]-nearestEnemy.x),abs(i[1]-nearestEnemy.y))) > 5)
if moveTo == []:
moveTo = None
# Go nuts if can't run away

if moveTo == None:
self.mode = 4
self.messageLog.append(Message.Message(self.name + " decides to fight for his life!"))
else:
moveTo = moveTo[1]

if self.mode == 4:
# Go nuts. Just try to kill.
if len(inRoom) > 0:
nearestEnemy = min([i for i in inRoom if i != self and i.team != self.team],\
key = lambda i: abs(self.x - i.x) + abs(self.y - i.y))

moveTo = self.GetRoute((nearestEnemy.x, nearestEnemy.y))
if moveTo == []:
moveTo = None
if moveTo != None:
moveTo = moveTo[1]

if moveTo == None:
self.Wait()
else:
if len(moveTo) > 0:
self.tryMove(moveTo[0], moveTo[1])
else:
self.Wait()

if activateTeam:
activateTeam = False
self.messageLog.append(Message.Message(self.name + " loudly blows a tiny whistle!"))
for i in self.teamMates:
i.mode = 1


0 comments on commit e54f8a4

Please sign in to comment.