mirror of
https://github.com/ProgramSnail/python_curses_game_2021.git
synced 2025-12-06 10:08:43 +00:00
commit
bb1f0b5118
4 changed files with 478 additions and 0 deletions
1
proj/__init__.py
Normal file
1
proj/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
import main
|
||||
411
proj/main.py
Normal file
411
proj/main.py
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
import curses
|
||||
import random
|
||||
import copy
|
||||
|
||||
from var_config import *
|
||||
|
||||
|
||||
def begin_curses():
|
||||
stdscr = curses.initscr()
|
||||
curses.noecho()
|
||||
curses.cbreak()
|
||||
curses.curs_set(0)
|
||||
stdscr.keypad(True)
|
||||
#stdscr.nodelay(True)
|
||||
|
||||
curses.start_color()
|
||||
|
||||
curses.init_pair(SIMPLE_COLOR, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||
curses.init_pair(ACTIVE_COLOR, curses.COLOR_BLACK, curses.COLOR_WHITE)
|
||||
curses.init_pair(WALL_COLOR, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||
curses.init_pair(GOLD_COLOR, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
||||
curses.init_pair(WEAPON_COLOR, curses.COLOR_CYAN, curses.COLOR_BLACK)
|
||||
curses.init_pair(HIGHLIGHTED_COLOR, curses.COLOR_CYAN, curses.COLOR_BLACK)
|
||||
curses.init_pair(PLAYER_COLOR, curses.COLOR_BLACK, curses.COLOR_WHITE)
|
||||
curses.init_pair(DOOR_COLOR, curses.COLOR_BLUE, curses.COLOR_BLACK)
|
||||
curses.init_pair(MONSTER_COLOR, curses.COLOR_RED, curses.COLOR_BLACK)
|
||||
curses.init_pair(ADD_HEALTH_COLOR, curses.COLOR_GREEN, curses.COLOR_BLACK)
|
||||
curses.init_pair(SCORE_COLOR, curses.COLOR_GREEN, curses.COLOR_BLACK)
|
||||
curses.init_pair(ACTIVE_WEAPON_COLOR, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
||||
|
||||
return stdscr
|
||||
|
||||
|
||||
def end_curses(stdscr):
|
||||
#stdscr.nodelay(False)
|
||||
stdscr.keypad(False)
|
||||
curses.curs_set(1)
|
||||
curses.nocbreak()
|
||||
curses.echo()
|
||||
curses.endwin()
|
||||
|
||||
|
||||
|
||||
def gen_map_step(map):
|
||||
tmp_map = copy.deepcopy(map)
|
||||
for i in range(1, len(map) - 1):
|
||||
for j in range(1, len(map[i]) - 1):
|
||||
x = 0
|
||||
for d in EXTENDED_DIRECTIONS:
|
||||
if tmp_map[i + d[0]][j + d[1]][0] == WALL:
|
||||
x += 1
|
||||
if x < 2 or x >= 4:
|
||||
map[i][j] = [EMPTY, SIMPLE_COLOR]
|
||||
elif x >= 3 and x <= 3:
|
||||
map[i][j] = [WALL, WALL_COLOR]
|
||||
|
||||
|
||||
def generate_map(map, sz_x, sz_y, map_symbols,
|
||||
automata_iterations, wall_rand_range,
|
||||
wall_rand_gen):
|
||||
rand_sum = 0
|
||||
for symbol in map_symbols:
|
||||
rand_sum += symbol[1]
|
||||
|
||||
for i in range(sz_x):
|
||||
map.append([])
|
||||
for j in range(sz_y):
|
||||
if i == 0 or j == 0 or i == sz_x - 1 or j == sz_y - 1:
|
||||
map[i].append([WALL, WALL_COLOR])
|
||||
else:
|
||||
if random.randint(1, wall_rand_range) <= wall_rand_gen:
|
||||
map[i].append([WALL, WALL_COLOR])
|
||||
else:
|
||||
map[i].append([EMPTY, SIMPLE_COLOR])
|
||||
|
||||
for i in range(automata_iterations):
|
||||
gen_map_step(map)
|
||||
|
||||
for i in range(1, len(map) - 1):
|
||||
for j in range(1, len(map[i]) - 1):
|
||||
if (map[i][j][0] == EMPTY):
|
||||
x = random.randint(1, rand_sum)
|
||||
for symbol in map_symbols:
|
||||
x -= symbol[1]
|
||||
if x <= 0:
|
||||
map[i][j] = symbol[0]
|
||||
break
|
||||
|
||||
|
||||
|
||||
def map_mark_dfs(map, i, j):
|
||||
if map[i][j][0] == WALL or map[i][j][0] == MARK:
|
||||
return []
|
||||
|
||||
positions = [(i, j)]
|
||||
|
||||
map[i][j] = (MARK, SIMPLE_COLOR)
|
||||
for d in DIRECTIONS:
|
||||
# walls on all sides, then no check needed there
|
||||
new_positions = map_mark_dfs(map, i + d[0], j + d[1])
|
||||
for pos in new_positions:
|
||||
positions.append(pos)
|
||||
|
||||
return positions
|
||||
|
||||
|
||||
def generate_doors(map):
|
||||
doors = []
|
||||
tmp_map = copy.deepcopy(map)
|
||||
|
||||
for i in range(len(tmp_map)):
|
||||
for j in range(len(tmp_map[i])):
|
||||
if tmp_map[i][j][0] == EMPTY:
|
||||
map_mark_dfs(tmp_map, i, j)
|
||||
map[i][j] = [DOOR, DOOR_COLOR]
|
||||
doors.append([i, j])
|
||||
|
||||
return doors
|
||||
|
||||
|
||||
def random_direction():
|
||||
k = random.randint(0, 3)
|
||||
return [((k % 2) * 2 - 1) * (k // 2),
|
||||
((k % 2) * 2 - 1) * (1 - k // 2)]
|
||||
|
||||
|
||||
def generate_monsters(map, rand_range, rand_monster):
|
||||
monsters = []
|
||||
for i in range(len(map)):
|
||||
for j in range(len(map[i])):
|
||||
if map[i][j][0] == EMPTY and \
|
||||
random.randint(1, rand_range) <= rand_monster:
|
||||
monsters.append([[i, j], random_direction()])
|
||||
|
||||
return monsters
|
||||
|
||||
|
||||
def move_monsters(map, monsters):
|
||||
for i in range(len(monsters)):
|
||||
pos = [0, 0]
|
||||
pos[0] = monsters[i][0][0] + monsters[i][1][0]
|
||||
pos[1] = monsters[i][0][1] + monsters[i][1][1]
|
||||
if map[pos[0]][pos[1]][0] != WALL:
|
||||
monsters[i][0] = pos
|
||||
pass
|
||||
else:
|
||||
monsters[i][1] = random_direction()
|
||||
pass
|
||||
|
||||
|
||||
def damage_player(monsters, player_state):
|
||||
for m in monsters:
|
||||
if abs(player_state[P_POS][0] - m[0][0]) + abs(player_state[P_POS][1] - m[0][1]) <= 1:
|
||||
player_state[P_HEALTH] -= MONSTER_DAMAGE
|
||||
pass
|
||||
|
||||
|
||||
def game_step(map, monsters, player_state, doors):
|
||||
#if player_pos == door for any door ?
|
||||
move_monsters(map, monsters)
|
||||
damage_player(monsters, player_state)
|
||||
pass
|
||||
|
||||
|
||||
def draw(stdscr, map, monsters, active_weapon, player_state):
|
||||
stdscr.addstr(0, 0, str(player_state))
|
||||
|
||||
info_str = [
|
||||
"Health: " + str(player_state[P_HEALTH]),
|
||||
"Weapon: " + str(player_state[P_WEAPON]),
|
||||
"Gold: " + str(player_state[P_GOLD]),
|
||||
"Score: " + str(player_state[P_SCORE]),
|
||||
"",
|
||||
"Help:",
|
||||
"",
|
||||
"ARROWS - move",
|
||||
"",
|
||||
"WASD - use",
|
||||
"weapon",
|
||||
"",
|
||||
"QE - move",
|
||||
"through doors",
|
||||
"",
|
||||
"You must",
|
||||
"collect",
|
||||
"all score",
|
||||
"",
|
||||
"% - weapon",
|
||||
"# - wall",
|
||||
"@ - score",
|
||||
"M - monster",
|
||||
"P - player",
|
||||
"> - door",
|
||||
"$ - money",
|
||||
"+ - add health"
|
||||
]
|
||||
|
||||
for i in range(len(info_str)):
|
||||
stdscr.addstr(MAP_POS_Y - 1 + i, 0, info_str[i],
|
||||
curses.color_pair(HIGHLIGHTED_COLOR))
|
||||
|
||||
for i in range(len(map)):
|
||||
stdscr.addstr(MAP_POS_Y - 1, i + MAP_POS_X,
|
||||
'-', curses.color_pair(SIMPLE_COLOR))
|
||||
stdscr.addstr(len(map[0]) + MAP_POS_Y, i + MAP_POS_X,
|
||||
'-', curses.color_pair(SIMPLE_COLOR))
|
||||
|
||||
for i in range(len(map[0])): # ?? better ??
|
||||
stdscr.addstr(i + MAP_POS_Y, MAP_POS_X - 1,
|
||||
'|', curses.color_pair(SIMPLE_COLOR))
|
||||
stdscr.addstr(i + MAP_POS_Y, len(map) + MAP_POS_X,
|
||||
'|', curses.color_pair(SIMPLE_COLOR))
|
||||
|
||||
for i in range(len(map)):
|
||||
for j in range(len(map[i])):
|
||||
stdscr.addstr(j + MAP_POS_Y, i + MAP_POS_X,
|
||||
map[i][j][0], curses.color_pair(map[i][j][1]))
|
||||
|
||||
for m in monsters:
|
||||
stdscr.addstr(m[0][1] + MAP_POS_Y, m[0][0] + MAP_POS_X,
|
||||
MONSTER, curses.color_pair(MONSTER_COLOR))
|
||||
|
||||
for w in active_weapon:
|
||||
stdscr.addstr(w[0][1] + MAP_POS_Y, w[0][0] + MAP_POS_X,
|
||||
ACTIVE_WEAPON, curses.color_pair(ACTIVE_WEAPON_COLOR))
|
||||
|
||||
stdscr.addstr(MAP_POS_Y + player_state[P_POS][1],
|
||||
MAP_POS_X + player_state[P_POS][0], PLAYER,
|
||||
curses.color_pair(PLAYER_COLOR))
|
||||
|
||||
|
||||
def input(stdscr): # not work
|
||||
m = [0, 0]
|
||||
key = ""
|
||||
|
||||
try:
|
||||
key = stdscr.getkey()
|
||||
if key == MOVE_UP_ACTION:
|
||||
m[1] -= 1
|
||||
elif key == MOVE_DOWN_ACTION:
|
||||
m[1] += 1
|
||||
elif key == MOVE_LEFT_ACTION:
|
||||
m[0] -= 1
|
||||
elif key == MOVE_RIGHT_ACTION:
|
||||
m[0] += 1
|
||||
except:
|
||||
pass
|
||||
# no input
|
||||
|
||||
return (m, key)
|
||||
pass
|
||||
|
||||
|
||||
def move_player(map, player_state, pos_change):
|
||||
p = copy.deepcopy(player_state[0])
|
||||
|
||||
p[0] += pos_change[0]
|
||||
p[1] += pos_change[1]
|
||||
|
||||
x = map[p[0]][p[1]][0]
|
||||
|
||||
if x != WALL:
|
||||
player_state[0] = p
|
||||
if x == ADD_HEALTH:
|
||||
player_state[P_HEALTH] += 1
|
||||
elif x == WEAPON:
|
||||
player_state[P_WEAPON] += 1
|
||||
elif x == GOLD:
|
||||
player_state[P_GOLD] += 1
|
||||
elif x == SCORE:
|
||||
player_state[P_SCORE] += 1
|
||||
|
||||
if x != WALL and x != DOOR:
|
||||
map[p[0]][p[1]] = [EMPTY, SIMPLE_COLOR]
|
||||
|
||||
|
||||
def use_weapon(map, active_weapon, player_state, direction):
|
||||
if player_state[P_WEAPON] <= 0:
|
||||
return
|
||||
p = copy.deepcopy(player_state[P_POS])
|
||||
p[0] += direction[0]
|
||||
p[1] += direction[1]
|
||||
if map[p[0]][p[1]][0] == WALL:
|
||||
if p[0] != 0 and p[1] != 0 and p[0] != len(map) - 1 and p[1] != len(map[0]):
|
||||
map[p[0]][p[1]] = [EMPTY, SIMPLE_COLOR]
|
||||
player_state[P_WEAPON] -= 1
|
||||
else:
|
||||
active_weapon.append([p, copy.deepcopy(direction)])
|
||||
player_state[P_WEAPON] -= 1
|
||||
|
||||
|
||||
def player_actions(map, doors, active_weapon, player_state, player_action):
|
||||
k = -1
|
||||
if player_action == ATTACK_UP_ACTION:
|
||||
k = 0
|
||||
elif player_action == ATTACK_DOWN_ACTION:
|
||||
k = 1
|
||||
elif player_action == ATTACK_LEFT_ACTION:
|
||||
k = 2
|
||||
elif player_action == ATTACK_RIGHT_ACTION:
|
||||
k = 3
|
||||
|
||||
if k >= 0:
|
||||
use_weapon(map, active_weapon, player_state, DIRECTIONS[k])
|
||||
|
||||
if not doors[player_state[P_ACTIVE_DOOR]]:
|
||||
return
|
||||
|
||||
if player_action == NEXT_DOOR_ACTION:
|
||||
player_state[P_ACTIVE_DOOR] += 1
|
||||
player_state[P_ACTIVE_DOOR] %= len(doors)
|
||||
player_state[P_POS] = copy.deepcopy(
|
||||
doors[player_state[P_ACTIVE_DOOR]])
|
||||
elif player_action == PREV_DOOR_ACTION:
|
||||
player_state[P_ACTIVE_DOOR] = (player_state[P_ACTIVE_DOOR]
|
||||
+ len(doors) - 1) % len(doors)
|
||||
player_state[P_POS] = copy.deepcopy(
|
||||
doors[player_state[P_ACTIVE_DOOR]])
|
||||
|
||||
|
||||
def move_weapon(map, active_weapon, player_state): # needed to test
|
||||
remove_list = []
|
||||
for i in range(len(active_weapon)):
|
||||
p = copy.deepcopy(active_weapon[i][0])
|
||||
p[0] += active_weapon[i][1][0]
|
||||
p[1] += active_weapon[i][1][1]
|
||||
if map[p[0]][p[1]][0] == WALL or \
|
||||
map[p[0]][p[1]][0] == DOOR:
|
||||
p = active_weapon[i][0]
|
||||
if map[p[0]][p[1]][0] == SCORE:
|
||||
player_state[P_SCORE] += 1
|
||||
map[p[0]][p[1]] = [WALL, WALL_COLOR]
|
||||
remove_list.append(i)
|
||||
else:
|
||||
active_weapon[i][0] = p
|
||||
|
||||
for i in remove_list:
|
||||
active_weapon.remove(active_weapon[i])
|
||||
|
||||
|
||||
def calc_max_score(map):
|
||||
max_score = 0
|
||||
for row in map:
|
||||
for cell in row:
|
||||
if cell[0] == SCORE:
|
||||
max_score += 1
|
||||
return max_score
|
||||
|
||||
|
||||
def main():
|
||||
map_symbols = [
|
||||
[[EMPTY, SIMPLE_COLOR], 90],
|
||||
[[GOLD, GOLD_COLOR], 2],
|
||||
[[WEAPON, WEAPON_COLOR], 4],
|
||||
[[ADD_HEALTH, ADD_HEALTH_COLOR], 2],
|
||||
[[SCORE, SCORE_COLOR], 2]
|
||||
]
|
||||
|
||||
stdscr = begin_curses()
|
||||
|
||||
map_size = [40, 20]
|
||||
|
||||
active_weapon = [] # positions of active weapon
|
||||
|
||||
map = []
|
||||
|
||||
generate_map(map, map_size[0], map_size[1], map_symbols, 20, 100, 45)
|
||||
|
||||
doors = generate_doors(map)
|
||||
|
||||
monsters = generate_monsters(map, 100, 3)
|
||||
|
||||
max_score = calc_max_score(map)
|
||||
|
||||
player_state = [doors[0], 10, 2, 0, 0, 0]
|
||||
# pos = [x, y], lives, weapon, money, active door, score
|
||||
|
||||
main_loop_exception = False
|
||||
|
||||
player_win = False
|
||||
|
||||
try:
|
||||
while(True):
|
||||
stdscr.clear()
|
||||
game_step(map, monsters, player_state, doors)
|
||||
draw(stdscr, map, monsters, active_weapon, player_state)
|
||||
stdscr.refresh()
|
||||
pos_change, player_action = input(stdscr)
|
||||
move_player(map, player_state, pos_change)
|
||||
player_actions(map, doors, active_weapon, player_state, player_action)
|
||||
for i in range(WEAPON_SPEED):
|
||||
move_weapon(map, active_weapon, player_state)
|
||||
if player_state[P_HEALTH] <= 0:
|
||||
break
|
||||
if player_state[P_SCORE] == max_score:
|
||||
player_win = True
|
||||
break # win
|
||||
except:
|
||||
main_loop_exception = True
|
||||
|
||||
end_curses(stdscr)
|
||||
|
||||
if main_loop_exception:
|
||||
print("Error during main loop")
|
||||
elif player_win:
|
||||
print("You win!")
|
||||
else:
|
||||
print("You lose(")
|
||||
|
||||
if (__name__ == "__main__"):
|
||||
main()
|
||||
65
proj/var_config.py
Normal file
65
proj/var_config.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
MAP_POS_X = 16
|
||||
MAP_POS_Y = 2
|
||||
|
||||
WEAPON_SPEED = 1
|
||||
|
||||
PLAYER = 'P'
|
||||
MONSTER = 'M'
|
||||
EMPTY = ' '
|
||||
DOOR = '>'
|
||||
MARK = '!'
|
||||
WALL = '#'
|
||||
GOLD = '$'
|
||||
WEAPON = '/'
|
||||
ACTIVE_WEAPON = '.'
|
||||
ADD_HEALTH = '+'
|
||||
SCORE = '@'
|
||||
|
||||
SIMPLE_COLOR = 1
|
||||
ACTIVE_COLOR = 2
|
||||
WALL_COLOR = 3
|
||||
GOLD_COLOR = 4
|
||||
WEAPON_COLOR = 5
|
||||
HIGHLIGHTED_COLOR = 6
|
||||
PLAYER_COLOR = 7
|
||||
DOOR_COLOR = 8
|
||||
MONSTER_COLOR = 9
|
||||
ADD_HEALTH_COLOR = 10
|
||||
SCORE_COLOR = 11
|
||||
ACTIVE_WEAPON_COLOR = 12
|
||||
|
||||
MONSTER_DAMAGE = 1
|
||||
|
||||
P_POS = 0
|
||||
P_HEALTH = 1
|
||||
P_WEAPON = 2
|
||||
P_GOLD = 3
|
||||
P_ACTIVE_DOOR = 4
|
||||
P_SCORE = 5
|
||||
|
||||
MOVE_UP_ACTION = "KEY_UP"
|
||||
MOVE_DOWN_ACTION = "KEY_DOWN"
|
||||
MOVE_LEFT_ACTION = "KEY_LEFT"
|
||||
MOVE_RIGHT_ACTION = "KEY_RIGHT"
|
||||
|
||||
ATTACK_UP_ACTION = 'w'
|
||||
ATTACK_DOWN_ACTION = 's'
|
||||
ATTACK_LEFT_ACTION = 'a'
|
||||
ATTACK_RIGHT_ACTION = 'd'
|
||||
|
||||
NEXT_DOOR_ACTION = 'q'
|
||||
PREV_DOOR_ACTION = 'e'
|
||||
|
||||
DIRECTIONS = [
|
||||
[0, -1],
|
||||
[0, 1],
|
||||
[-1, 0],
|
||||
[1, 0]
|
||||
]
|
||||
|
||||
EXTENDED_DIRECTIONS = DIRECTIONS + [
|
||||
[1, 1],
|
||||
[1, -1],
|
||||
[-1, 1],
|
||||
[-1, -1]
|
||||
]
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
curses
|
||||
Loading…
Add table
Add a link
Reference in a new issue