1
Fork 0
mirror of https://github.com/Steffo99/lihzahrd.git synced 2024-11-25 01:24:21 +00:00

Added parsing for bestiary and journey mode powers.

Journey mode powers could use some convenience functions to map and from
the values shown in the game.
This commit is contained in:
Dale Floer 2020-05-30 22:45:24 -07:00
parent 76944e5c63
commit a7827e2e8e
9 changed files with 162 additions and 12 deletions

View file

@ -1,5 +1,5 @@
import lihzahrd import lihzahrd
world = lihzahrd.World.create_from_file("test-1404.wld") world = lihzahrd.World.create_from_file("test-1404j.wld")
breakpoint() breakpoint()

View file

@ -1,5 +1,5 @@
from .world import World from .world import World
from . import chests, fileutils, header, npcs, pressureplates, signs, tileentities, tiles, townmanager from . import bestiary, chests, fileutils, header, journeypowers, npcs, pressureplates, signs, tileentities, tiles, townmanager
__all__ = ["World", "chests", "fileutils", "header", "npcs", "pressureplates", "signs", "tileentities", "tiles", __all__ = ["World", "bestiary", "chests", "fileutils", "header", "journeypowers", "npcs", "pressureplates", "signs", "tileentities", "tiles",
"townmanager"] "townmanager"]

3
lihzahrd/bestiary/__init__.py Executable file
View file

@ -0,0 +1,3 @@
from .bestiary import Bestiary
__all__ = ["Bestiary"]

23
lihzahrd/bestiary/bestiary.py Executable file
View file

@ -0,0 +1,23 @@
import typing
class Bestiary:
"""A bestiary entry."""
__slots__ = "npc_chats_count", "npc_chats_data", "npc_kill_count","npc_kill_data", "npc_sighting_count", "npc_sighting_data"
def __init__(self,
npc_chats_count: int,
npc_chats_data: typing.Dict[str, int],
npc_kill_count: int,
npc_kill_data: typing.List[str],
npc_sighting_count: int,
npc_sighting_data: typing.List[str],):
self.npc_chats_count: int = npc_chats_count
self.npc_chats_data: typing.Dict[str, int] = npc_chats_data
self.npc_kill_count: int = npc_kill_count
self.npc_kill_data: typing.List[str] = npc_kill_data
self.npc_sighting_count: int = npc_sighting_count
self.npc_sighting_data: typing.List[str] = npc_sighting_data
def __repr__(self):
return f"<Bestiary entry: chats={self.npc_chats_count}: {self.npc_chats_data}, kills={self.npc_kill_count}: {self.npc_kill_data}, sightings={self.npc_sighting_count}: {self.npc_sighting_data}>"

View file

@ -11,6 +11,8 @@ class Pointers:
tile_entities: int, tile_entities: int,
pressure_plates: int, pressure_plates: int,
town_manager: int, town_manager: int,
bestiary: int,
journey_powers: int,
footer: int, footer: int,
*unknown): *unknown):
self.file_format: int = 0 self.file_format: int = 0
@ -22,5 +24,7 @@ class Pointers:
self.tile_entities: int = tile_entities self.tile_entities: int = tile_entities
self.pressure_plates: int = pressure_plates self.pressure_plates: int = pressure_plates
self.town_manager: int = town_manager self.town_manager: int = town_manager
self.bestiary: int = bestiary
self.journey_powers: int = journey_powers
self.footer: int = footer self.footer: int = footer
self.unknown: typing.List[int] = list(unknown) self.unknown: typing.List[int] = list(unknown)

View file

@ -0,0 +1,3 @@
from .journeypowers import JourneyPowers
__all__ = ["JourneyPowers"]

View file

@ -0,0 +1,45 @@
import typing
class JourneyPowers:
"""Journey mode powers settings. Spawn rate does not appear to be stored in the world."""
__slots__ = "freeze_time", "god_mode", "time_rate", "freeze_rain", "freeze_wind", "far_placement_range", "difficulty", "freeze_biome_spread", "spawn_rate"
def __init__(self,
freeze_time: typing.Optional[bool] = None,
god_mode: typing.Optional[bool] = None,
time_rate: typing.Optional[float] = None,
freeze_rain: typing.Optional[bool] = None,
freeze_wind: typing.Optional[bool] = None,
far_placement_range: typing.Optional[bool] = None,
difficulty: typing.Optional[float] = None,
freeze_biome_spread: typing.Optional[bool] = None,):
self.freeze_time: bool = freeze_time
"""Can time be frozen."""
self.god_mode: bool = god_mode
"""Can god mode be enabled."""
self.time_rate: float = time_rate
"""How fast does time go, 1x to 24x. Value ranges from 0.0 to 1.0."""
self.freeze_rain: bool = freeze_rain
"""Can the rain change."""
self.freeze_wind: bool = freeze_wind
"""Can the wind speed and direction change."""
self.far_placement_range: bool = far_placement_range
"""Can players place blocks further than normal."""
self.difficulty: float = difficulty
"""Enemy difficulty scaling, 0.5x to 3x. Value ranges from 0.0 to 1.0."""
self.freeze_biome_spread: bool = freeze_biome_spread
"""Can evil biomes & the hallow spread."""
def __repr__(self):
return f"<JourneyPowers: freeze_time={self.freeze_time}, god_mode={self.god_mode}, freeze_rain={self.freeze_rain}," \
f"freeze_wind={self.freeze_wind}, far_placement={self.far_placement_range}, biome_spread={self.freeze_biome_spread}," \
f"time_rate={self.time_rate}, difficulty={self.difficulty}>"

View file

@ -6,13 +6,14 @@ from ..fileutils import Coordinates
class NPC: class NPC:
"""A NPC somewhere in the world.""" """A NPC somewhere in the world."""
__slots__ = "type", "name", "position", "home" __slots__ = "type", "name", "position", "home", "variation_index"
def __init__(self, def __init__(self,
type_: EntityType, type_: EntityType,
name: str, name: str,
position: Coordinates, position: Coordinates,
home: typing.Optional[Coordinates] = None): variation_index: int,
home: typing.Optional[Coordinates] = None,):
self.type: EntityType = type_ self.type: EntityType = type_
"""The NPC this object represents.""" """The NPC this object represents."""
@ -25,5 +26,8 @@ class NPC:
self.home: typing.Optional[Coordinates] = home self.home: typing.Optional[Coordinates] = home
"""The coordinates of the home of this NPC, or None if the NPC is homeless.""" """The coordinates of the home of this NPC, or None if the NPC is homeless."""
self.variation_index: int = variation_index
"""Unsure, but seems to be an index to different possible NPC variations."""
def __repr__(self): def __repr__(self):
return f"<NPC {repr(self.type)} at {self.position}>" return f"<NPC {repr(self.type)} at {self.position}>"

View file

@ -4,6 +4,8 @@ import typing
from .fileutils import * from .fileutils import *
from .header import * from .header import *
from .tiles import * from .tiles import *
from .bestiary import *
from .journeypowers import *
from .chests import * from .chests import *
from .signs import * from .signs import *
from .npcs import * from .npcs import *
@ -49,6 +51,8 @@ class World:
clouds: Clouds, clouds: Clouds,
cultist_delay: int, cultist_delay: int,
tiles: TileMatrix, tiles: TileMatrix,
bestiary: Bestiary,
journey_powers: JourneyPowers,
chests: typing.List[Chest], chests: typing.List[Chest],
signs: typing.List[Sign], signs: typing.List[Sign],
npcs: typing.List[NPC], npcs: typing.List[NPC],
@ -69,7 +73,9 @@ class World:
unknown_npcs_data: bytes = b"", unknown_npcs_data: bytes = b"",
unknown_tile_entities_data: bytes = b"", unknown_tile_entities_data: bytes = b"",
unknown_pressure_plates_data: bytes = b"", unknown_pressure_plates_data: bytes = b"",
unknown_town_manager_data: bytes = b""): unknown_town_manager_data: bytes = b"",
unknown_bestiary_data: bytes = b"",
unknown_journey_powers_data: bytes = b""):
self.version: Version = version self.version: Version = version
"""The game version when this savefile was last saved.""" """The game version when this savefile was last saved."""
@ -210,6 +216,12 @@ class World:
self.unknown_pressure_plates_data: bytes = unknown_pressure_plates_data self.unknown_pressure_plates_data: bytes = unknown_pressure_plates_data
self.unknown_town_manager_data: bytes = unknown_town_manager_data self.unknown_town_manager_data: bytes = unknown_town_manager_data
self.bestiary: Bestiary = bestiary
"""Information about the bestiary, including sightings, kills and takling to NPCs."""
self.journey_powers: JourneyPowers= journey_powers
"""Status of powers available in Journey mode."""
def __repr__(self): def __repr__(self):
return f'<World "{self.name}">' return f'<World "{self.name}">'
@ -676,10 +688,14 @@ class World:
if is_homeless: if is_homeless:
npc_home = None npc_home = None
npc_flags = f.bits()
npc_variation_index = 0 if npc_flags[0] else f.int4()
npc = NPC(type_=npc_type, npc = NPC(type_=npc_type,
name=npc_name, name=npc_name,
position=npc_position, position=npc_position,
home=npc_home) home=npc_home,
variation_index=npc_variation_index)
npcs.append(npc) npcs.append(npc)
while f.bool(): while f.bool():
@ -735,7 +751,56 @@ class World:
room = Room(npc=EntityType(f.int4()), position=Coordinates(f.int4(), f.int4())) room = Room(npc=EntityType(f.int4()), position=Coordinates(f.int4(), f.int4()))
rooms.append(room) rooms.append(room)
unknown_town_manager_data = f.read_until(pointers.footer) unknown_town_manager_data = f.read_until(pointers.bestiary)
bestiary_kill_count = f.int4()
bestiary_kill_data = {f.string(): f.int4() for _ in range(bestiary_kill_count)}
bestiary_sighting_count = f.int4()
bestiary_sighting_data = [f.string() for _ in range(bestiary_sighting_count)]
bestiary_chat_count = f.int4()
bestiary_chat_data = [f.string() for _ in range(bestiary_chat_count)]
bestiary = Bestiary(bestiary_chat_count,
bestiary_chat_data,
bestiary_kill_count,
bestiary_kill_data,
bestiary_sighting_count,
bestiary_sighting_data)
unknown_bestiary_data = f.read_until(pointers.journey_powers)
while f.bool():
journey_powers = JourneyPowers()
power_id = f.int2()
if power_id == 0:
freeze_time = f.bool()
journey_powers.freeze_time = freeze_time
elif power_id == 5:
god_mode = f.bool()
journey_powers.god_mode = god_mode
elif power_id == 8:
time_rate = f.single()
journey_powers.time_rate = time_rate
elif power_id == 9:
freeze_rain = f.bool()
journey_powers.freeze_rain = freeze_rain
elif power_id == 10:
freeze_wind = f.bool()
journey_powers.freeze_wind = freeze_wind
elif power_id == 11:
far_placement_range = f.bool()
journey_powers.far_placement_range = far_placement_range
elif power_id == 12:
difficulty = f.single()
journey_powers.difficulty = difficulty
elif power_id == 13:
freeze_biome_spread = f.bool()
journey_powers.freeze_biome_spread = freeze_biome_spread
elif power_id == -1:
spawn_rate: f.single()
journey_powers.spawn_rate = spawn_rate
else:
print(f"Unknown id: {power_id}")
unknown_journey_powers_data = f.read_until(pointers.footer)
# Object creation # Object creation
world = cls(version=version, savefile_type=savefile_type, revision=revision, is_favorite=is_favorite, world = cls(version=version, savefile_type=savefile_type, revision=revision, is_favorite=is_favorite,
@ -751,6 +816,7 @@ class World:
weighed_pressure_plates=weighed_pressure_plates, rooms=rooms, weighed_pressure_plates=weighed_pressure_plates, rooms=rooms,
halloween_today=halloween_today, xmas_today=xmas_today, halloween_today=halloween_today, xmas_today=xmas_today,
treetop_variants=treetop_variants, saved_ore_tiers=saved_ore_tiers, pets=pets, treetop_variants=treetop_variants, saved_ore_tiers=saved_ore_tiers, pets=pets,
bestiary=bestiary, journey_powers=journey_powers,
unknown_file_format_data=unknown_file_format_data, unknown_file_format_data=unknown_file_format_data,
unknown_world_header_data=unknown_world_header_data, unknown_world_header_data=unknown_world_header_data,
unknown_world_tiles_data=unknown_world_tiles_data, unknown_world_tiles_data=unknown_world_tiles_data,
@ -759,7 +825,9 @@ class World:
unknown_npcs_data=unknown_npcs_data, unknown_npcs_data=unknown_npcs_data,
unknown_tile_entities_data=unknown_tile_entities_data, unknown_tile_entities_data=unknown_tile_entities_data,
unknown_pressure_plates_data=unknown_pressure_plates_data, unknown_pressure_plates_data=unknown_pressure_plates_data,
unknown_town_manager_data=unknown_town_manager_data) unknown_town_manager_data=unknown_town_manager_data,
unknown_bestiary_data=unknown_bestiary_data,
unknown_journey_powers_data=unknown_journey_powers_data)
# Footer # Footer
if not f.bool(): if not f.bool():