1
Fork 0
mirror of https://github.com/Steffo99/lihzahrd.git synced 2024-11-21 15:44:24 +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 committed by Stefano Pigozzi
parent dc091f37b8
commit e4d06c2e55
9 changed files with 162 additions and 12 deletions

View file

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

View file

@ -1,5 +1,5 @@
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"]

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,
pressure_plates: int,
town_manager: int,
bestiary: int,
journey_powers: int,
footer: int,
*unknown):
self.file_format: int = 0
@ -22,5 +24,7 @@ class Pointers:
self.tile_entities: int = tile_entities
self.pressure_plates: int = pressure_plates
self.town_manager: int = town_manager
self.bestiary: int = bestiary
self.journey_powers: int = journey_powers
self.footer: int = footer
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:
"""A NPC somewhere in the world."""
__slots__ = "type", "name", "position", "home"
__slots__ = "type", "name", "position", "home", "variation_index"
def __init__(self,
type_: EntityType,
name: str,
position: Coordinates,
home: typing.Optional[Coordinates] = None):
type_: EntityType,
name: str,
position: Coordinates,
variation_index: int,
home: typing.Optional[Coordinates] = None,):
self.type: EntityType = type_
"""The NPC this object represents."""
@ -25,5 +26,8 @@ class NPC:
self.home: typing.Optional[Coordinates] = home
"""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):
return f"<NPC {repr(self.type)} at {self.position}>"

View file

@ -4,6 +4,8 @@ import typing
from .fileutils import *
from .header import *
from .tiles import *
from .bestiary import *
from .journeypowers import *
from .chests import *
from .signs import *
from .npcs import *
@ -49,6 +51,8 @@ class World:
clouds: Clouds,
cultist_delay: int,
tiles: TileMatrix,
bestiary: Bestiary,
journey_powers: JourneyPowers,
chests: typing.List[Chest],
signs: typing.List[Sign],
npcs: typing.List[NPC],
@ -69,7 +73,9 @@ class World:
unknown_npcs_data: bytes = b"",
unknown_tile_entities_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
"""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_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):
return f'<World "{self.name}">'
@ -676,10 +688,14 @@ class World:
if is_homeless:
npc_home = None
npc_flags = f.bits()
npc_variation_index = 0 if npc_flags[0] else f.int4()
npc = NPC(type_=npc_type,
name=npc_name,
position=npc_position,
home=npc_home)
home=npc_home,
variation_index=npc_variation_index)
npcs.append(npc)
while f.bool():
@ -735,7 +751,56 @@ class World:
room = Room(npc=EntityType(f.int4()), position=Coordinates(f.int4(), f.int4()))
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
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,
halloween_today=halloween_today, xmas_today=xmas_today,
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_world_header_data=unknown_world_header_data,
unknown_world_tiles_data=unknown_world_tiles_data,
@ -759,7 +825,9 @@ class World:
unknown_npcs_data=unknown_npcs_data,
unknown_tile_entities_data=unknown_tile_entities_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
if not f.bool():