diff --git a/luserver/commands/mission.py b/luserver/commands/mission.py index a899d6f..4ca7864 100644 --- a/luserver/commands/mission.py +++ b/luserver/commands/mission.py @@ -16,17 +16,6 @@ class AddMission(ChatCommand): else: sender.char.add_mission(int(args.mission)) -class AutocompleteMissions(ChatCommand): - def __init__(self): - super().__init__("autocompletemissions") - self.command.add_argument("--enable", type=toggle_bool) - - def run(self, args, sender): - if args.enable is None: - sender.char.autocomplete_missions = not sender.char.autocomplete_missions - else: - sender.char.autocomplete_missions = args.enable - class CompleteMission(ChatCommand): def __init__(self): super().__init__("completemission") diff --git a/luserver/commands/server.py b/luserver/commands/server.py index c035a69..c3c11b9 100644 --- a/luserver/commands/server.py +++ b/luserver/commands/server.py @@ -1,6 +1,7 @@ import asyncio import datetime import logging +import math import os import re import secrets @@ -8,8 +9,12 @@ import time from ..auth import Account, GMLevel, PasswordState from ..bitstream import BitStream, c_bool, c_ushort +from ..ldf import LDF, LDFDataType from ..messages import WorldClientMsg from ..world import server +from ..components.physics import AABB, CollisionSphere, PrimitiveModelType +from ..math.quaternion import Quaternion +from ..math.vector import Vector3 from .command import ChatCommand, normal_bool log = logging.getLogger(__name__) @@ -112,6 +117,45 @@ class Mute(ChatCommand): else: server.chat.sys_msg_sender("Player not connected") +class PhysicsDebug(ChatCommand): + def __init__(self): + super().__init__("physicsdebug") + self.debug_markers = [] + server.add_handler("proximity_radius", self.on_proximity_radius) + + def run(self, args, sender): + if self.debug_markers: + for marker in self.debug_markers: + server.replica_manager.destruct(marker) + self.debug_markers.clear() + else: + for obj in server.general.tracked_objects.copy(): + self.spawn_debug_marker(obj) + + def on_proximity_radius(self, obj): + if not self.debug_markers: + return + self.spawn_debug_marker(obj) + + def spawn_debug_marker(self, obj): + coll = server.general.tracked_objects[obj] + set_vars = {"parent": obj, "rotation": Quaternion.identity} + if isinstance(coll, AABB): + config = LDF() + config.ldf_set("primitiveModelType", LDFDataType.INT32, PrimitiveModelType.Cuboid) + config.ldf_set("primitiveModelValueX", LDFDataType.FLOAT, coll.max.x-coll.min.x) + config.ldf_set("primitiveModelValueY", LDFDataType.FLOAT, coll.max.y-coll.min.y) + config.ldf_set("primitiveModelValueZ", LDFDataType.FLOAT, coll.max.z-coll.min.z) + + set_vars["position"] = Vector3((coll.min.x+coll.max.x)/2, coll.min.y, (coll.min.z+coll.max.z)/2) + set_vars["config"] = config + marker = server.spawn_object(14510, set_vars) + elif isinstance(coll, CollisionSphere): + set_vars["position"] = coll.position + set_vars["scale"] = math.sqrt(coll.sq_radius)/5 + marker = server.spawn_object(6548, set_vars) + self.debug_markers.append(marker) + class ResetPassword(ChatCommand): def __init__(self): super().__init__("resetpassword") diff --git a/luserver/components/char/__init__.py b/luserver/components/char/__init__.py index 87c85c7..19d2783 100644 --- a/luserver/components/char/__init__.py +++ b/luserver/components/char/__init__.py @@ -561,6 +561,10 @@ class CharacterComponent(Component, CharActivity, CharCamera, CharMission, CharP def bounce_notification(self, object_id_bounced:c_int64=None, object_id_bouncer:c_int64=None, success:bool=None): pass + @single + def display_zone_summary(self, is_property_map:bool=False, is_zone_start:bool=False, sender:GameObject=None): + pass + @broadcast def start_arranging_with_item(self, first_time:bool=True, build_area:GameObject=0, build_start_pos:Vector3=None, source_bag:c_int=None, source_id:c_int64=None, source_lot:c_int=None, source_type:c_int=None, target_id:c_int64=None, target_lot:c_int=None, target_pos:Vector3=None, target_type:c_int=None): self.object.inventory.push_equipped_items_state() diff --git a/luserver/components/char/mission.py b/luserver/components/char/mission.py index 025bf3f..b3b3b3a 100644 --- a/luserver/components/char/mission.py +++ b/luserver/components/char/mission.py @@ -11,7 +11,6 @@ from ..mission import check_prereqs, MissionProgress, MissionState, ObtainItemTy class CharMission: def __init__(self): - self.autocomplete_missions = False self.missions = PersistentMapping() # add achievements for mission_id, data in server.db.missions.items(): diff --git a/luserver/components/char/ui.py b/luserver/components/char/ui.py index 3c34841..32ce000 100644 --- a/luserver/components/char/ui.py +++ b/luserver/components/char/ui.py @@ -5,12 +5,12 @@ from ...messages import single class CharUI: @single - def display_message_box(self, show:bool=None, callback_client:GameObject=None, identifier:str=None, image_id:c_int=None, text:str=None, user_data:str=None): + def display_message_box(self, show:bool=None, callback_client:GameObject=None, id:str=None, image_id:c_int=None, text:str=None, user_data:str=None): pass - def disp_message_box(self, text): + def disp_message_box(self, text, id="", callback=None): """display_message_box with default parameters.""" - self.display_message_box(show=True, callback_client=None, identifier="", image_id=0, text=text, user_data="") + self.display_message_box(show=True, callback_client=callback, id=id, image_id=0, text=text, user_data="") @single def display_tooltip(self, do_or_die:bool=False, no_repeat:bool=False, no_revive:bool=False, is_property_tooltip:bool=False, show:bool=None, translate:bool=False, time:c_int=None, id:str=None, localize_params:LDF=None, image_name:str=None, text:str=None): diff --git a/luserver/components/comp108.py b/luserver/components/comp108.py index 0e4299a..74ebd4e 100644 --- a/luserver/components/comp108.py +++ b/luserver/components/comp108.py @@ -31,6 +31,6 @@ class Comp108Component(Component): if self.driver_id != 0: server.game_objects[self.driver_id].char.dismount() - def request_die(self, unknown_bool:bool=None, death_type:str=None, direction_relative_angle_xz:float=None, direction_relative_angle_y:float=None, direction_relative_force:float=None, kill_type:c_int=0, killer:GameObject=None, loot_owner:GameObject=0): + def request_die(self, unknown_bool:bool=None, death_type:str=None, direction_relative_angle_xz:float=None, direction_relative_angle_y:float=None, direction_relative_force:float=None, kill_type:c_int=0, killer:GameObject=None, loot_owner:GameObject=None): #self.object.destructible.deal_damage(10000, self) # die permanently on crash self.object.call_later(3, self.object.destructible.resurrect) diff --git a/luserver/components/destructible.py b/luserver/components/destructible.py index 3f89bf0..3f9fa3e 100644 --- a/luserver/components/destructible.py +++ b/luserver/components/destructible.py @@ -46,11 +46,11 @@ class DestructibleComponent(Component): self.object.stats.life = max(0, self.object.stats.life - (damage - self.object.stats.armor)) self.object.stats.armor = max(0, self.object.stats.armor - damage) - def simply_die(self, death_type:str="", kill_type:c_int=KillType.Violent, killer:GameObject=None, loot_owner:GameObject=0): + def simply_die(self, death_type:str="", kill_type:c_int=KillType.Violent, killer:GameObject=None, loot_owner:GameObject=None): """Shorthand for request_die with default values.""" self.request_die(False, death_type, 0, 0, 10, kill_type, killer, loot_owner) - def request_die(self, unknown_bool:bool=None, death_type:str=None, direction_relative_angle_xz:float=None, direction_relative_angle_y:float=None, direction_relative_force:float=None, kill_type:c_int=KillType.Violent, killer:GameObject=None, loot_owner:GameObject=0): + def request_die(self, unknown_bool:bool=None, death_type:str=None, direction_relative_angle_xz:float=None, direction_relative_angle_y:float=None, direction_relative_force:float=None, kill_type:c_int=KillType.Violent, killer:GameObject=None, loot_owner:GameObject=None): if self.object.stats.life == 0: # already dead return diff --git a/luserver/components/mission.py b/luserver/components/mission.py index dbfaf94..a68634e 100644 --- a/luserver/components/mission.py +++ b/luserver/components/mission.py @@ -58,7 +58,6 @@ class MissionProgress(Persistent): self.tasks = [MissionTask(task_type, target, target_value, parameter) for task_type, target, target_value, parameter in mission_data[2]] self.is_mission = mission_data[3] -import asyncio import logging import random @@ -66,7 +65,6 @@ from ..bitstream import c_int from ..game_object import GameObject from ..messages import single from ..world import server -from ..commands.mission import CompleteMission from .component import Component log = logging.getLogger(__name__) @@ -149,9 +147,6 @@ class MissionNPCComponent(Component): if mission_state == MissionState.Available: assert not is_complete player.char.add_mission(mission_id) - if player.char.autocomplete_missions: - asyncio.get_event_loop().call_soon(CompleteMission.async_complete_mission, CompleteMission, mission_id, False, player) - elif mission_state == MissionState.ReadyToComplete: assert is_complete player.char.complete_mission(mission_id) diff --git a/luserver/components/physics.py b/luserver/components/physics.py index 7f17c43..20b542e 100644 --- a/luserver/components/physics.py +++ b/luserver/components/physics.py @@ -36,31 +36,11 @@ class PhysicsComponent(Component): for comp in self.object.components: if hasattr(comp, "on_enter") or hasattr(comp, "on_exit"): server.general.tracked_objects[self.object] = CollisionSphere(self.object, radius) - if server.get_objects_in_group("physics_debug_marker"): - self.spawn_debug_marker() + if "proximity_radius" in server._handlers: + for handler in server._handlers["proximity_radius"]: + handler(self) break - def spawn_debug_marker(self): - if self.object not in server.general.tracked_objects: - return - coll = server.general.tracked_objects[self.object] - set_vars = {"groups": ("physics_debug_marker",), "parent": self.object, "rotation": Quaternion.identity} - if isinstance(coll, AABB): - config = LDF() - config.ldf_set("primitiveModelType", LDFDataType.INT32, PrimitiveModelType.Cuboid) - config.ldf_set("primitiveModelValueX", LDFDataType.FLOAT, coll.max.x-coll.min.x) - config.ldf_set("primitiveModelValueY", LDFDataType.FLOAT, coll.max.y-coll.min.y) - config.ldf_set("primitiveModelValueZ", LDFDataType.FLOAT, coll.max.z-coll.min.z) - - set_vars["position"] = Vector3((coll.min.x+coll.max.x)/2, coll.min.y, (coll.min.z+coll.max.z)/2) - set_vars["config"] = config - server.spawn_object(14510, set_vars) - elif isinstance(coll, CollisionSphere): - set_vars["position"] = coll.position - set_vars["scale"] = math.sqrt(coll.sq_radius)/5 - server.spawn_object(6548, set_vars) - - # not really related to physics, but depends on physics and hasn't been conclusively associated with a component def drop_rewards(self, loot_matrix, currency_min, currency_max, owner): @@ -339,8 +319,6 @@ class PhantomPhysicsComponent(PhysicsComponent): continue if hasattr(comp, "on_enter") or hasattr(comp, "on_exit"): server.general.tracked_objects[self.object] = AABB(self.object) - if server.get_objects_in_group("physics_debug_marker"): - self.spawn_debug_marker() break def on_enter(self, player): diff --git a/luserver/components/scripted_activity.py b/luserver/components/scripted_activity.py index 2217be0..ea8d8f1 100644 --- a/luserver/components/scripted_activity.py +++ b/luserver/components/scripted_activity.py @@ -44,8 +44,8 @@ class ScriptedActivityComponent(Component): def activity_start(self): pass - def message_box_respond(self, player, button:c_int=None, identifier:str=None, user_data:str=None): - if identifier == "LobbyReady" and button == 1: + def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None): + if id == "LobbyReady" and button == 1: asyncio.ensure_future(player.char.transfer_to_world((self.transfer_world_id, 0, 0))) @single diff --git a/luserver/game_object.py b/luserver/game_object.py index 89a16db..bd3212c 100644 --- a/luserver/game_object.py +++ b/luserver/game_object.py @@ -17,7 +17,9 @@ class GameObject: self.attr_changed(name) super().__setattr__(name, value) - def __init__(self, lot, object_id, set_vars={}): + def __init__(self, lot, object_id, set_vars=None): + if set_vars is None: + set_vars = {} self._handlers = {} self._flags = {} self._flags["parent_flag"] = "related_objects_flag" @@ -81,11 +83,19 @@ class GameObject: for component_type, component_id in sorted(comp_ids, key=lambda x: component_order.index(x[0]) if x[0] in component_order else 99999): if component_type == 5: if "custom_script" in set_vars and set_vars["custom_script"] is not None: - script = importlib.import_module("luserver.scripts."+set_vars["custom_script"]) - comp = script.ScriptComponent, + try: + script = importlib.import_module("luserver.scripts."+set_vars["custom_script"]) + comp = script.ScriptComponent, + except ModuleNotFoundError as e: + log.warning(str(e)) + comp = ScriptComponent, elif component_id is not None and component_id in server.db.script_component: - script = importlib.import_module("luserver.scripts."+server.db.script_component[component_id]) - comp = script.ScriptComponent, + try: + script = importlib.import_module("luserver.scripts."+server.db.script_component[component_id]) + comp = script.ScriptComponent, + except ModuleNotFoundError as e: + log.warning(str(e)) + comp = ScriptComponent, else: comp = ScriptComponent, elif component_type in component: diff --git a/luserver/messages.py b/luserver/messages.py index d8eda1f..e4cf2a3 100644 --- a/luserver/messages.py +++ b/luserver/messages.py @@ -194,6 +194,7 @@ class GameMessage(Enum): BounceNotification = 932 BBBSaveRequest = 1001 NotifyClientObject = 1042 + DisplayZoneSummary = 1043 StartBuildingWithItem = 1057 StartArrangingWithItem = 1061 FinishArrangingWithItem = 1062 diff --git a/luserver/modules/general.py b/luserver/modules/general.py index 54ab87e..630761b 100644 --- a/luserver/modules/general.py +++ b/luserver/modules/general.py @@ -19,7 +19,7 @@ log = logging.getLogger(__name__) # Constant checksums that the client expects to verify map version # (likely value of the last map revision) -checksum = { +_CHECKSUMS = { World.VentureExplorer: 0x20b8087c, World.ReturnToTheVentureExplorer: 0x26680a3c, World.AvantGardens: 0x49525511, @@ -57,22 +57,11 @@ class GeneralHandling: def __init__(self): server.general = self self.tracked_objects = {} - physics_debug_cmd = server.chat.commands.add_parser("physicsdebug") - physics_debug_cmd.set_defaults(func=self.physics_debug_cmd) server.register_handler(WorldServerMsg.LoadComplete, self.on_client_load_complete) server.register_handler(WorldServerMsg.PositionUpdate, self.on_position_update) server.register_handler(WorldServerMsg.GameMessage, self.on_game_message) - def physics_debug_cmd(self, args, sender): - debug_markers = server.get_objects_in_group("physics_debug_marker") - if not debug_markers: - for obj in self.tracked_objects.copy(): - obj.physics.spawn_debug_marker() - else: - for marker in debug_markers: - server.replica_manager.destruct(marker) - def on_validated(self, address): player = server.accounts[address].characters.selected() if server.world_id[0] != 0: @@ -100,7 +89,7 @@ class GeneralHandling: load_world.write(c_ushort(world_id)) load_world.write(c_ushort(world_instance)) load_world.write(c_uint(world_clone)) - load_world.write(c_uint(checksum.get(World(world_id), 0))) + load_world.write(c_uint(_CHECKSUMS.get(World(world_id), 0))) load_world.write(bytes(2)) load_world.write(c_float(player.physics.position.x)) load_world.write(c_float(player.physics.position.y)) diff --git a/luserver/scripts/avant_gardens/caged_bricks.py b/luserver/scripts/avant_gardens/caged_bricks.py index 28da2e4..8b28b44 100644 --- a/luserver/scripts/avant_gardens/caged_bricks.py +++ b/luserver/scripts/avant_gardens/caged_bricks.py @@ -10,4 +10,4 @@ class ScriptComponent(script.ScriptComponent): assert multi_interact_id is None player.char.set_flag(True, FLAG_ID) player.inventory.remove_item(InventoryType.Items, lot=MAELSTROM_CUBE_LOT) - server.get_objects_in_group("cagedSpider")[0].script.fire_event_client_side(args="toggle", obj=None, sender=player) + server.get_objects_in_group("cagedSpider")[0].script.fire_event_client_side(args="toggle", obj=None, sender=player, player=player) diff --git a/luserver/scripts/avant_gardens/caged_spider.py b/luserver/scripts/avant_gardens/caged_spider.py new file mode 100644 index 0000000..c85a52a --- /dev/null +++ b/luserver/scripts/avant_gardens/caged_spider.py @@ -0,0 +1,14 @@ +import luserver.components.script as script +from luserver.bitstream import c_int, c_int64 +from luserver.game_object import GameObject +from luserver.messages import single + +class ScriptComponent(script.ScriptComponent): + # hacky workaround incoming: + # the clientside implementation is broken and doesn't check the sender param + # so the cinematic would get displayed for everyone since this message is broadcast + # easiest fix is to override it for this script to be single instead + # other option would be to allow broadcast messages to be single per-call, but that seems like even more of a hack + @single + def fire_event_client_side(self, args:str=None, obj:GameObject=None, param1:c_int64=0, param2:c_int=-1, sender:GameObject=None): + pass diff --git a/luserver/scripts/avant_gardens/rocco_sirocco.py b/luserver/scripts/avant_gardens/rocco_sirocco.py index f9d1a09..b1916a4 100644 --- a/luserver/scripts/avant_gardens/rocco_sirocco.py +++ b/luserver/scripts/avant_gardens/rocco_sirocco.py @@ -1,6 +1,7 @@ import luserver.components.script as script from luserver.bitstream import c_int from luserver.game_object import GameObject +from luserver.messages import single from luserver.components.inventory import InventoryType from luserver.components.mission import MissionState @@ -13,3 +14,10 @@ class ScriptComponent(script.ScriptComponent): player.char.complete_mission(1729) elif mission_state == MissionState.ReadyToComplete: player.inventory.remove_item(InventoryType.Items, lot=14397) + self.notify_client_object(name="switch", param1=0, param2=0, param_str=b"", param_obj=None, player=player) + + # manually changed from broadcast to single because the client script abuses this message + # see also caged_spider + @single + def notify_client_object(self, name:str=None, param1:c_int=None, param2:c_int=None, param_obj:GameObject=None, param_str:bytes=None): + pass diff --git a/luserver/scripts/avant_gardens/survival/world_control.py b/luserver/scripts/avant_gardens/survival/world_control.py index 63b1e53..768ca97 100644 --- a/luserver/scripts/avant_gardens/survival/world_control.py +++ b/luserver/scripts/avant_gardens/survival/world_control.py @@ -128,11 +128,11 @@ class ScriptComponent(script.ScriptComponent): self.set_player_spawn_points() - def message_box_respond(self, player, button:c_int=None, identifier:str=None, user_data:str=None): - if identifier == "RePlay": + def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None): + if id == "RePlay": self.start() - elif identifier == "Exit_Question" and button == 1: + elif id == "Exit_Question" and button == 1: self.game_over(player) self.object.scripted_activity.remove_player(player) asyncio.ensure_future(player.char.transfer_to_last_non_instance(Vector3(131.83, 376, -180.31), Quaternion(0, -0.268720, 0, 0.963218))) diff --git a/luserver/scripts/general/console_teleport.py b/luserver/scripts/general/console_teleport.py new file mode 100644 index 0000000..cc943c9 --- /dev/null +++ b/luserver/scripts/general/console_teleport.py @@ -0,0 +1,10 @@ +import asyncio + +import luserver.components.script as script + +# todo: not completely implemented + +class ScriptComponent(script.ScriptComponent): + def transfer(self, player, world, respawn_point_name): + player.render.play_animation("lup-teleport") + asyncio.get_event_loop().call_later(4, asyncio.ensure_future, player.char.transfer_to_world(world, respawn_point_name=respawn_point_name)) diff --git a/luserver/scripts/general/teleport_to_ns_or_nt.py b/luserver/scripts/general/teleport_to_ns_or_nt.py new file mode 100644 index 0000000..010a624 --- /dev/null +++ b/luserver/scripts/general/teleport_to_ns_or_nt.py @@ -0,0 +1,21 @@ +import luserver.scripts.general.console_teleport as script +from luserver.amf3 import AMF3 +from luserver.bitstream import c_int +from luserver.world import server +from luserver.components.char import TerminateType +# todo: implement visited worlds so the NS/NT choice UI can work + +class ScriptComponent(script.ScriptComponent): + def on_use(self, player, multi_interact_id): + assert multi_interact_id is None + # todo: check if player has been to NT, if yes then display choice UI + player.char.disp_message_box(id="TransferBox", text="UI_TRAVEL_TO_LUP_STATION", callback=self.object) + + def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None): + if id == "TransferBox": + if button == 1: + # todo: display zone summary (callback not working right now for some reason) + #player.char.display_zone_summary(sender=self.object) + self.transfer(player, (1200, 0, 0), "NS_LEGO_Club") + else: + player.char.terminate_interaction(terminator=self.object, type=TerminateType.FromInteraction) diff --git a/luserver/scripts/general/transfer_to_last_non_instance.py b/luserver/scripts/general/transfer_to_last_non_instance.py index 9a64ec1..c78dfcf 100644 --- a/luserver/scripts/general/transfer_to_last_non_instance.py +++ b/luserver/scripts/general/transfer_to_last_non_instance.py @@ -6,8 +6,8 @@ from luserver.bitstream import c_int class ScriptComponent(script.ScriptComponent): def on_use(self, player, multi_interact_id): assert multi_interact_id is None - player.char.display_message_box(show=True, callback_client=self.object, identifier="instance_exit", image_id=0, text=self.script_vars.get("transfer_text", "DRAGON_EXIT_QUESTION"), user_data="") + player.char.display_message_box(show=True, callback_client=self.object, id="instance_exit", image_id=0, text=self.script_vars.get("transfer_text", "DRAGON_EXIT_QUESTION"), user_data="") - def message_box_respond(self, player, button:c_int=None, identifier:str=None, user_data:str=None): - if identifier == "instance_exit" and button == 1: + def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None): + if id == "instance_exit" and button == 1: asyncio.ensure_future(player.char.transfer_to_last_non_instance()) diff --git a/luserver/scripts/nimbus_station/lego_club_door.py b/luserver/scripts/nimbus_station/lego_club_door.py new file mode 100644 index 0000000..e4bdc5e --- /dev/null +++ b/luserver/scripts/nimbus_station/lego_club_door.py @@ -0,0 +1,28 @@ +import luserver.scripts.general.teleport_to_ns_or_nt as script +from luserver.amf3 import AMF3 +from luserver.bitstream import c_int +from luserver.world import server +from luserver.components.char import TerminateType + +# todo: not completely implemented +# todo: implement visited worlds so the NS/NT choice UI can work + +class ScriptComponent(script.ScriptComponent): + def on_use(self, player, multi_interact_id): + assert multi_interact_id is None + if server.world_id[0] == 1700: + # todo: check if player has been to NT, if yes then display choice UI + player.char.disp_message_box(id="TransferBox", text="UI_TRAVEL_TO_NS", callback=self.object) + else: + player.char.u_i_message_server_to_single_client(message_name=b"pushGameState", args=AMF3({"state": "Lobby", "context": {"user": str(player.object_id), "callbackObj": str(self.object.object_id), "HelpVisible": "show", "type": "Lego_Club_Valid"}})) + + def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None): + if id == "PlayButton": + self.transfer(player, (1700, 0, 0), "") + elif id == "TransferBox": + if button == 1: + # todo: display zone summary (callback not working right now for some reason) + #player.char.display_zone_summary(sender=self.object) + self.transfer(player, (1200, 0, 0), "NS_LEGO_Club") + else: + player.char.terminate_interaction(terminator=self.object, type=TerminateType.FromInteraction) diff --git a/luserver/scripts/nimbus_station/lup_teleport.py b/luserver/scripts/nimbus_station/lup_teleport.py new file mode 100644 index 0000000..f98c28b --- /dev/null +++ b/luserver/scripts/nimbus_station/lup_teleport.py @@ -0,0 +1,35 @@ +import asyncio + +import luserver.scripts.general.teleport_to_ns_or_nt as script +from luserver.amf3 import AMF3 +from luserver.bitstream import c_int +from luserver.world import server +from luserver.components.char import TerminateType + +# todo: not completely implemented +# todo: implement visited worlds so the NS/NT choice UI can work + +class ScriptComponent(script.ScriptComponent): + def on_use(self, player, multi_interact_id): + assert multi_interact_id is None + # todo: check if player has been to NT, if yes then display choice UI + if server.world_id[0] == 1600: + text = "UI_TRAVEL_TO_NS" + else: + text = "UI_TRAVEL_TO_LUP_STATION" + player.char.disp_message_box(id="TransferBox", text=text, callback=self.object) + + def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None): + if id == "TransferBox": + if button == 1: + # todo: display zone summary (callback not working right now for some reason) + #player.char.display_zone_summary(sender=self.object) + if server.world_id[0] == 1600: + dest = 1200 + spawnpoint = "NS_LW" + else: + dest = 1600 + spawnpoint = "" + self.transfer(player, (dest, 0, 0), spawnpoint) + else: + player.char.terminate_interaction(terminator=self.object, type=TerminateType.FromInteraction) diff --git a/luserver/world.py b/luserver/world.py index e069e0d..8f51e86 100644 --- a/luserver/world.py +++ b/luserver/world.py @@ -116,6 +116,7 @@ class WorldServer(Server): self.not_console_logged_packets.add("GameMessage/ReadyForUpdates") self.not_console_logged_packets.add("GameMessage/ScriptNetworkVarUpdate") self.multi = MultiInstanceAccess() + self._handlers = {} CharHandling() ChatHandling() GeneralHandling() @@ -199,6 +200,19 @@ class WorldServer(Server): lot, position, rotation = spawn_data self.spawn_model(spawner_id, lot, position, rotation) + EVENT_NAMES = "proximity_radius" + def add_handler(self, event_name: str, handler): + if event_name not in WorldServer.EVENT_NAMES: + raise ValueError("Invalid event name %s", event_name) + self._handlers.setdefault(event_name, []).append(handler) + + def remove_handler(self, event_name: str, handler): + if event_name not in WorldServer.EVENT_NAMES: + raise ValueError("Invalid event name %s", event_name) + if event_name not in self._handlers or handler not in self._handlers[event_name]: + raise RuntimeError("handler not found") + self._handlers[event_name].remove(handler) + def spawn_model(self, spawner_id, lot, position, rotation): spawned_vars = {} spawned_vars["position"] = position diff --git a/runtime/db/init.py b/runtime/db/init.py index 3053f41..75ff3b7 100644 --- a/runtime/db/init.py +++ b/runtime/db/init.py @@ -284,13 +284,10 @@ class Init: self.root.components_registry.setdefault(row[0], []).append((row[1], row[2])) if row[1] == 5 and row[2] not in self.root.script_component: - comp_row = self.cdclient.execute("select id, script_name from ScriptComponent where id == %i" % row[2]).fetchone() - if comp_row is None: - continue - id, script_name = comp_row - script_name = scripts.SCRIPTS.get(id) - if script_name is not None: - self.root.script_component[id] = script_name + # we don't even need to query the db since we've got our own scripts table + script_id = row[2] + if script_id in scripts.SCRIPTS: + self.root.script_component[script_id] = scripts.SCRIPTS[script_id] elif row[1] == 7 and row[2] not in self.root.destructible_component: faction, faction_list, level, loot_matrix_index, currency_index, life, armor, imagination, is_smashable = self.cdclient.execute("select faction, factionList, level, LootMatrixIndex, CurrencyIndex, life, armor, imagination, isSmashable from DestructibleComponent where id == %i" % row[2]).fetchone() diff --git a/runtime/db/scripts.py b/runtime/db/scripts.py index dc8ed6b..d475a64 100644 --- a/runtime/db/scripts.py +++ b/runtime/db/scripts.py @@ -16,6 +16,7 @@ SCRIPTS = { 847: "avant_gardens.rusty_steele", 849: "nimbus_station.concert_quickbuild", 867: "avant_gardens.survival.buff_station", + 877: "avant_gardens.caged_spider", 882: "avant_gardens.survival.world_control", 901: "avant_gardens.survival.stromling_mech", 946: "gnarled_forest.torch", @@ -43,10 +44,11 @@ SCRIPTS = { 1216: "items.cauldron_of_life", 1218: "items.anvil_of_armor", 1219: "items.fountain_of_imagination", + 1239: "nimbus_station.lego_club_door", 1270: "avant_gardens.saluting_npcs", 1271: "avant_gardens.saluting_npcs", 1272: "avant_gardens.saluting_npcs", - 1276: "general.transfer_world_on_use", + 1276: "nimbus_station.lup_teleport", 1329: "crux_prime.aura_blossom_flower", 1345: "general.poi_mission", 1349: "crux_prime.scroll_shrine", @@ -54,8 +56,8 @@ SCRIPTS = { 1419: "nexus_tower.water_fountain", 1458: "items.sunflower", 1481: "nexus_tower.vault", - 1484: "general.transfer_world_on_use", - 1485: "general.transfer_world_on_use", + 1484: "nimbus_station.lup_teleport", + 1485: "nimbus_station.lego_club_door", 1486: "general.transfer_world_on_use", 1519: "nexus_tower.venture_cannon", 1527: "general.transfer_world_on_use", @@ -86,6 +88,7 @@ SCRIPTS = { r"02_server\Map\General\L_FRICTION_VOLUME_SERVER.lua": "general.friction_volume", r"02_server\Map\General\L_POI_MISSION.lua": "general.poi_mission", r"02_server\Map\General\L_TOUCH_MISSION_UPDATE_SERVER.lua": "general.touch_complete_mission", + r"02_server\Map\NS\L_NS_LUP_TELEPORT.lua": "nimbus_station.lup_teleport", r"02_server\Map\NS\L_NS_TOKEN_CONSOLE_SERVER.lua": "nimbus_station.token_console", r"02_server\Map\NT\L_NT_ASSEMBLYTUBE_SERVER.lua": "nexus_tower.assembly_tube", r"02_server\Map\NT\L_NT_PARADOXTELE_SERVER.lua": "nexus_tower.paradox_teleporter", @@ -109,3 +112,4 @@ SCRIPTS = { r"ai\NS\L_NS_MODULAR_BUILD.lua": "nimbus_station.rocket_modular_build", r"ai\NS\L_NS_QB_IMAGINATION_STATUE.lua": "nimbus_station.imagination_statue", r"ai\NS\NS_PP_01\L_NS_PP_01_TELEPORT.lua": "property.teleport"} +