X-Git-Url: https://plomlompom.com/repos/?p=plomrogue2-experiments;a=blobdiff_plain;f=new%2Fplomrogue%2Fgame.py;h=d20713cd61fa219d387808ef3c2e9bdbd8ac4fff;hp=13b40024cf3909eda619367af5b034aaf9b2c046;hb=8f2dc382612c0684fd9a75e60c23561a1859cb8f;hpb=1fbd3687acb5edcdf043abfed92ec022d342f807 diff --git a/new/plomrogue/game.py b/new/plomrogue/game.py index 13b4002..d20713c 100755 --- a/new/plomrogue/game.py +++ b/new/plomrogue/game.py @@ -8,11 +8,12 @@ from plomrogue.commands import (cmd_GEN_WORLD, cmd_GET_GAMESTATE, cmd_GET_PICKABLE_ITEMS, cmd_MAP_SIZE, cmd_TERRAIN_LINE, cmd_PLAYER_ID, cmd_TURN, cmd_SWITCH_PLAYER, cmd_SAVE) -from plomrogue.mapping import MapGeometryHex, Map, YX +from plomrogue.mapping import MapGeometryHex, MapChunk, YX from plomrogue.parser import Parser from plomrogue.io import GameIO from plomrogue.misc import quote -from plomrogue.things import Thing, ThingMonster, ThingHuman, ThingFood +from plomrogue.things import (Thing, ThingMonster, ThingHuman, ThingFood, + ThingAnimate) import random @@ -93,6 +94,7 @@ class Game(GameBase): self.player_id = 0 self.player_is_alive = True self.maps = {} + self.max_map_awakeness = 100 self.rand = PRNGod(0) def get_string_options(self, string_option_type): @@ -112,6 +114,7 @@ class Game(GameBase): self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_)) self.io.send('THING_POS %s %s' % (thing.id_, view_pos)) + self.io.send('PLAYER_ID ' + str(self.player_id)) self.io.send('TURN ' + str(self.turn)) visible_map = self.player.get_visible_map() self.io.send('VISIBLE_MAP %s %s' % (visible_map.size, @@ -203,44 +206,109 @@ class Game(GameBase): return 0 return self.things[-1].id_ + 1 - def get_map(self, map_pos, create_unfound=True): + def get_map(self, map_pos): if not (map_pos in self.maps and self.maps[map_pos].size == self.map_size): - if create_unfound: - self.maps[map_pos] = Map(self.map_size) - for pos in self.maps[map_pos]: - self.maps[map_pos][pos] = '.' - else: - return None + self.maps[map_pos] = MapChunk(self.map_size) + self.maps[map_pos].awake = self.max_map_awakeness + for pos in self.maps[map_pos]: + self.maps[map_pos][pos] = '.' return self.maps[map_pos] def proceed_to_next_player_turn(self): """Run game world turns until player can decide their next step. - Iterates through all non-player things, on each step - furthering them in their tasks (and letting them decide new - ones if they finish). The iteration order is: first all things - that come after the player in the world things list, then - (after incrementing the world turn) all that come before the - player; then the player's .proceed() is run, and if it does - not finish his task, the loop starts at the beginning. Once - the player's task is finished, or the player is dead, the loop - breaks. + All things and processes inside the player's reality bubble + are worked through. Things are furthered in their tasks and, + if finished, decide new ones. The iteration order is: first + all things that come after the player in the world things + list, then (after incrementing the world turn) all that come + before the player; then the player's .proceed() is run. + + Next, parts of the game world are put to sleep or woken up + based on how close they are to the player's position, or how + short ago the player visited them. + + If the player's last task is finished at the end of the loop, + it breaks; otherwise it starts again. """ - while True: - player_i = self.things.index(self.player) + + def proceed_world(): for thing in self.things[player_i+1:]: thing.proceed() self.turn += 1 - for pos in self.maps[YX(0,0)]: - if self.maps[YX(0,0)][pos] == '.' and \ - len(self.things_at_pos((YX(0,0), pos))) == 0 and \ - self.rand.random() > 0.999: - self.add_thing_at('food', (YX(0,0), pos)) + for map_pos in self.maps: + if self.maps[map_pos].awake: + for pos in self.maps[map_pos]: + if self.rand.random() > 0.999 and \ + self.maps[map_pos][pos] == '.' and \ + len(self.things_at_pos((map_pos, pos))) == 0: + self.add_thing_at('food', (map_pos, pos)) for thing in self.things[:player_i]: thing.proceed() self.player.proceed(is_AI=False) + + def reality_bubble(): + + def regenerate_chunk_from_map_stats(map_): + import math + max_stat = self.max_map_awakeness + for t_type in map_.stats: + stat = map_.stats[t_type] + to_create = stat['population'] // max_stat + mod_created = int(self.rand.randint(0, max_stat - 1) < + (stat['population'] % max_stat)) + to_create = (stat['population'] // max_stat) + mod_created + if to_create == 0: + continue + average_health = None + if stat['health'] > 0: + average_health = math.ceil(stat['health'] / + stat['population']) + for i in range(to_create): + t = self.add_thing_at_random(map_pos, t_type) + if average_health: + t.health = average_health + #if hasattr(t, 'health'): + # print('DEBUG create', t.type_, t.health) + + for map_pos in self.maps: + m = self.maps[map_pos] + if map_pos in self.player.close_maps: + + # Newly inside chunks are regenerated from .stats. + if not m.awake: + #print('DEBUG regen stats', map_pos, m.stats) + regenerate_chunk_from_map_stats(m) + + # Inside chunks are set to max .awake and don't collect + # stats. + m.awake = self.max_map_awakeness + m.stats = {} + + # Outside chunks grow distant through .awake decremention. + # They collect .stats until they fall asleep – then any things + # inside are disappeared. + elif m.awake > 0: + m.awake -= 1 + for t in self.things: + if t.position[0] == map_pos: + if not t.type_ in m.stats: + m.stats[t.type_] = {'population': 0, + 'health': 0} + m.stats[t.type_]['population'] += 1 + if isinstance(t, ThingAnimate): + m.stats[t.type_]['health'] += t.health + if not m.awake: + del self.things[self.things.index(t)] + #if not m.awake: + # print('DEBUG sleep stats', map_pos, m.stats) + + while True: + player_i = self.things.index(self.player) + proceed_world() + reality_bubble() if self.player.task is None or not self.player_is_alive: break @@ -250,34 +318,43 @@ class Game(GameBase): self.things += [t] return t - def make_new_world(self, yx, seed): - - def add_thing_at_random(type_): - while True: - new_pos = (YX(0,0), - YX(self.rand.randint(0, yx.y - 1), - self.rand.randint(0, yx.x - 1))) - if self.maps[new_pos[0]][new_pos[1]] != '.': - continue - if len(self.things_at_pos(new_pos)) > 0: - continue - return self.add_thing_at(type_, new_pos) - + def add_thing_at_random(self, big_yx, type_): + while True: + new_pos = (big_yx, + YX(self.rand.randint(0, self.map_size.y - 1), + self.rand.randint(0, self.map_size.x - 1))) + if self.maps[new_pos[0]][new_pos[1]] != '.': + continue + if len(self.things_at_pos(new_pos)) > 0: + continue + return self.add_thing_at(type_, new_pos) + + def make_map_chunk(self, big_yx): + map_ = self.get_map(big_yx) + for pos in map_: + map_[pos] = self.rand.choice(('.', '.', '.', '~', 'x')) + self.add_thing_at_random(big_yx, 'monster') + self.add_thing_at_random(big_yx, 'monster') + self.add_thing_at_random(big_yx, 'monster') + self.add_thing_at_random(big_yx, 'monster') + self.add_thing_at_random(big_yx, 'monster') + self.add_thing_at_random(big_yx, 'monster') + self.add_thing_at_random(big_yx, 'monster') + self.add_thing_at_random(big_yx, 'monster') + self.add_thing_at_random(big_yx, 'food') + self.add_thing_at_random(big_yx, 'food') + self.add_thing_at_random(big_yx, 'food') + self.add_thing_at_random(big_yx, 'food') + + def make_new_world(self, size, seed): self.things = [] self.rand.seed(seed) self.turn = 0 self.maps = {} - self.map_size = yx - map_ = self.get_map(YX(0,0)) - for pos in map_: - map_[pos] = self.rand.choice(('.', '.', '.', '~', 'x')) - player = add_thing_at_random('human') + self.map_size = size + self.make_map_chunk(YX(0,0)) + player = self.add_thing_at_random(YX(0,0), 'human') + player.surroundings # To help initializing reality bubble, see + # comment on ThingAnimate._position_set self.player_id = player.id_ - add_thing_at_random('monster') - add_thing_at_random('monster') - add_thing_at_random('food') - add_thing_at_random('food') - add_thing_at_random('food') - add_thing_at_random('food') return 'success' -