v2017.11.26

- fixed spider cinematic being played for everyone connected
- fixed rocco sirocco's cinematic not being played
- implemented teleports between NS, starbase and lego club station
This commit is contained in:
lcdr
2017-11-26 16:13:19 +01:00
parent a5227c16e8
commit c863396dab
25 changed files with 225 additions and 85 deletions

View File

@@ -16,17 +16,6 @@ class AddMission(ChatCommand):
else: else:
sender.char.add_mission(int(args.mission)) 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): class CompleteMission(ChatCommand):
def __init__(self): def __init__(self):
super().__init__("completemission") super().__init__("completemission")

View File

@@ -1,6 +1,7 @@
import asyncio import asyncio
import datetime import datetime
import logging import logging
import math
import os import os
import re import re
import secrets import secrets
@@ -8,8 +9,12 @@ import time
from ..auth import Account, GMLevel, PasswordState from ..auth import Account, GMLevel, PasswordState
from ..bitstream import BitStream, c_bool, c_ushort from ..bitstream import BitStream, c_bool, c_ushort
from ..ldf import LDF, LDFDataType
from ..messages import WorldClientMsg from ..messages import WorldClientMsg
from ..world import server 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 from .command import ChatCommand, normal_bool
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -112,6 +117,45 @@ class Mute(ChatCommand):
else: else:
server.chat.sys_msg_sender("Player not connected") 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): class ResetPassword(ChatCommand):
def __init__(self): def __init__(self):
super().__init__("resetpassword") super().__init__("resetpassword")

View File

@@ -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): def bounce_notification(self, object_id_bounced:c_int64=None, object_id_bouncer:c_int64=None, success:bool=None):
pass pass
@single
def display_zone_summary(self, is_property_map:bool=False, is_zone_start:bool=False, sender:GameObject=None):
pass
@broadcast @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): 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() self.object.inventory.push_equipped_items_state()

View File

@@ -11,7 +11,6 @@ from ..mission import check_prereqs, MissionProgress, MissionState, ObtainItemTy
class CharMission: class CharMission:
def __init__(self): def __init__(self):
self.autocomplete_missions = False
self.missions = PersistentMapping() self.missions = PersistentMapping()
# add achievements # add achievements
for mission_id, data in server.db.missions.items(): for mission_id, data in server.db.missions.items():

View File

@@ -5,12 +5,12 @@ from ...messages import single
class CharUI: class CharUI:
@single @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 pass
def disp_message_box(self, text): def disp_message_box(self, text, id="", callback=None):
"""display_message_box with default parameters.""" """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 @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): 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):

View File

@@ -31,6 +31,6 @@ class Comp108Component(Component):
if self.driver_id != 0: if self.driver_id != 0:
server.game_objects[self.driver_id].char.dismount() 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.destructible.deal_damage(10000, self) # die permanently on crash
self.object.call_later(3, self.object.destructible.resurrect) self.object.call_later(3, self.object.destructible.resurrect)

View File

@@ -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.life = max(0, self.object.stats.life - (damage - self.object.stats.armor))
self.object.stats.armor = max(0, self.object.stats.armor - damage) 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.""" """Shorthand for request_die with default values."""
self.request_die(False, death_type, 0, 0, 10, kill_type, killer, loot_owner) 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: if self.object.stats.life == 0:
# already dead # already dead
return return

View File

@@ -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.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] self.is_mission = mission_data[3]
import asyncio
import logging import logging
import random import random
@@ -66,7 +65,6 @@ from ..bitstream import c_int
from ..game_object import GameObject from ..game_object import GameObject
from ..messages import single from ..messages import single
from ..world import server from ..world import server
from ..commands.mission import CompleteMission
from .component import Component from .component import Component
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -149,9 +147,6 @@ class MissionNPCComponent(Component):
if mission_state == MissionState.Available: if mission_state == MissionState.Available:
assert not is_complete assert not is_complete
player.char.add_mission(mission_id) 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: elif mission_state == MissionState.ReadyToComplete:
assert is_complete assert is_complete
player.char.complete_mission(mission_id) player.char.complete_mission(mission_id)

View File

@@ -36,31 +36,11 @@ class PhysicsComponent(Component):
for comp in self.object.components: for comp in self.object.components:
if hasattr(comp, "on_enter") or hasattr(comp, "on_exit"): if hasattr(comp, "on_enter") or hasattr(comp, "on_exit"):
server.general.tracked_objects[self.object] = CollisionSphere(self.object, radius) server.general.tracked_objects[self.object] = CollisionSphere(self.object, radius)
if server.get_objects_in_group("physics_debug_marker"): if "proximity_radius" in server._handlers:
self.spawn_debug_marker() for handler in server._handlers["proximity_radius"]:
handler(self)
break 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 # 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): def drop_rewards(self, loot_matrix, currency_min, currency_max, owner):
@@ -339,8 +319,6 @@ class PhantomPhysicsComponent(PhysicsComponent):
continue continue
if hasattr(comp, "on_enter") or hasattr(comp, "on_exit"): if hasattr(comp, "on_enter") or hasattr(comp, "on_exit"):
server.general.tracked_objects[self.object] = AABB(self.object) server.general.tracked_objects[self.object] = AABB(self.object)
if server.get_objects_in_group("physics_debug_marker"):
self.spawn_debug_marker()
break break
def on_enter(self, player): def on_enter(self, player):

View File

@@ -44,8 +44,8 @@ class ScriptedActivityComponent(Component):
def activity_start(self): def activity_start(self):
pass pass
def message_box_respond(self, player, button:c_int=None, identifier:str=None, user_data:str=None): def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None):
if identifier == "LobbyReady" and button == 1: if id == "LobbyReady" and button == 1:
asyncio.ensure_future(player.char.transfer_to_world((self.transfer_world_id, 0, 0))) asyncio.ensure_future(player.char.transfer_to_world((self.transfer_world_id, 0, 0)))
@single @single

View File

@@ -17,7 +17,9 @@ class GameObject:
self.attr_changed(name) self.attr_changed(name)
super().__setattr__(name, value) 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._handlers = {}
self._flags = {} self._flags = {}
self._flags["parent_flag"] = "related_objects_flag" 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): 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 component_type == 5:
if "custom_script" in set_vars and set_vars["custom_script"] is not None: if "custom_script" in set_vars and set_vars["custom_script"] is not None:
script = importlib.import_module("luserver.scripts."+set_vars["custom_script"]) try:
comp = script.ScriptComponent, 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: 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]) try:
comp = script.ScriptComponent, 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: else:
comp = ScriptComponent, comp = ScriptComponent,
elif component_type in component: elif component_type in component:

View File

@@ -194,6 +194,7 @@ class GameMessage(Enum):
BounceNotification = 932 BounceNotification = 932
BBBSaveRequest = 1001 BBBSaveRequest = 1001
NotifyClientObject = 1042 NotifyClientObject = 1042
DisplayZoneSummary = 1043
StartBuildingWithItem = 1057 StartBuildingWithItem = 1057
StartArrangingWithItem = 1061 StartArrangingWithItem = 1061
FinishArrangingWithItem = 1062 FinishArrangingWithItem = 1062

View File

@@ -19,7 +19,7 @@ log = logging.getLogger(__name__)
# Constant checksums that the client expects to verify map version # Constant checksums that the client expects to verify map version
# (likely value of the last map revision) # (likely value of the last map revision)
checksum = { _CHECKSUMS = {
World.VentureExplorer: 0x20b8087c, World.VentureExplorer: 0x20b8087c,
World.ReturnToTheVentureExplorer: 0x26680a3c, World.ReturnToTheVentureExplorer: 0x26680a3c,
World.AvantGardens: 0x49525511, World.AvantGardens: 0x49525511,
@@ -57,22 +57,11 @@ class GeneralHandling:
def __init__(self): def __init__(self):
server.general = self server.general = self
self.tracked_objects = {} 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.LoadComplete, self.on_client_load_complete)
server.register_handler(WorldServerMsg.PositionUpdate, self.on_position_update) server.register_handler(WorldServerMsg.PositionUpdate, self.on_position_update)
server.register_handler(WorldServerMsg.GameMessage, self.on_game_message) 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): def on_validated(self, address):
player = server.accounts[address].characters.selected() player = server.accounts[address].characters.selected()
if server.world_id[0] != 0: 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_id))
load_world.write(c_ushort(world_instance)) load_world.write(c_ushort(world_instance))
load_world.write(c_uint(world_clone)) 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(bytes(2))
load_world.write(c_float(player.physics.position.x)) load_world.write(c_float(player.physics.position.x))
load_world.write(c_float(player.physics.position.y)) load_world.write(c_float(player.physics.position.y))

View File

@@ -10,4 +10,4 @@ class ScriptComponent(script.ScriptComponent):
assert multi_interact_id is None assert multi_interact_id is None
player.char.set_flag(True, FLAG_ID) player.char.set_flag(True, FLAG_ID)
player.inventory.remove_item(InventoryType.Items, lot=MAELSTROM_CUBE_LOT) 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)

View File

@@ -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

View File

@@ -1,6 +1,7 @@
import luserver.components.script as script import luserver.components.script as script
from luserver.bitstream import c_int from luserver.bitstream import c_int
from luserver.game_object import GameObject from luserver.game_object import GameObject
from luserver.messages import single
from luserver.components.inventory import InventoryType from luserver.components.inventory import InventoryType
from luserver.components.mission import MissionState from luserver.components.mission import MissionState
@@ -13,3 +14,10 @@ class ScriptComponent(script.ScriptComponent):
player.char.complete_mission(1729) player.char.complete_mission(1729)
elif mission_state == MissionState.ReadyToComplete: elif mission_state == MissionState.ReadyToComplete:
player.inventory.remove_item(InventoryType.Items, lot=14397) 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

View File

@@ -128,11 +128,11 @@ class ScriptComponent(script.ScriptComponent):
self.set_player_spawn_points() self.set_player_spawn_points()
def message_box_respond(self, player, button:c_int=None, identifier:str=None, user_data:str=None): def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None):
if identifier == "RePlay": if id == "RePlay":
self.start() self.start()
elif identifier == "Exit_Question" and button == 1: elif id == "Exit_Question" and button == 1:
self.game_over(player) self.game_over(player)
self.object.scripted_activity.remove_player(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))) asyncio.ensure_future(player.char.transfer_to_last_non_instance(Vector3(131.83, 376, -180.31), Quaternion(0, -0.268720, 0, 0.963218)))

View File

@@ -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))

View File

@@ -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)

View File

@@ -6,8 +6,8 @@ from luserver.bitstream import c_int
class ScriptComponent(script.ScriptComponent): class ScriptComponent(script.ScriptComponent):
def on_use(self, player, multi_interact_id): def on_use(self, player, multi_interact_id):
assert multi_interact_id is None 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): def message_box_respond(self, player, button:c_int=None, id:str=None, user_data:str=None):
if identifier == "instance_exit" and button == 1: if id == "instance_exit" and button == 1:
asyncio.ensure_future(player.char.transfer_to_last_non_instance()) asyncio.ensure_future(player.char.transfer_to_last_non_instance())

View File

@@ -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)

View File

@@ -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)

View File

@@ -116,6 +116,7 @@ class WorldServer(Server):
self.not_console_logged_packets.add("GameMessage/ReadyForUpdates") self.not_console_logged_packets.add("GameMessage/ReadyForUpdates")
self.not_console_logged_packets.add("GameMessage/ScriptNetworkVarUpdate") self.not_console_logged_packets.add("GameMessage/ScriptNetworkVarUpdate")
self.multi = MultiInstanceAccess() self.multi = MultiInstanceAccess()
self._handlers = {}
CharHandling() CharHandling()
ChatHandling() ChatHandling()
GeneralHandling() GeneralHandling()
@@ -199,6 +200,19 @@ class WorldServer(Server):
lot, position, rotation = spawn_data lot, position, rotation = spawn_data
self.spawn_model(spawner_id, lot, position, rotation) 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): def spawn_model(self, spawner_id, lot, position, rotation):
spawned_vars = {} spawned_vars = {}
spawned_vars["position"] = position spawned_vars["position"] = position

View File

@@ -284,13 +284,10 @@ class Init:
self.root.components_registry.setdefault(row[0], []).append((row[1], row[2])) 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: 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() # we don't even need to query the db since we've got our own scripts table
if comp_row is None: script_id = row[2]
continue if script_id in scripts.SCRIPTS:
id, script_name = comp_row self.root.script_component[script_id] = scripts.SCRIPTS[script_id]
script_name = scripts.SCRIPTS.get(id)
if script_name is not None:
self.root.script_component[id] = script_name
elif row[1] == 7 and row[2] not in self.root.destructible_component: 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() 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()

View File

@@ -16,6 +16,7 @@ SCRIPTS = {
847: "avant_gardens.rusty_steele", 847: "avant_gardens.rusty_steele",
849: "nimbus_station.concert_quickbuild", 849: "nimbus_station.concert_quickbuild",
867: "avant_gardens.survival.buff_station", 867: "avant_gardens.survival.buff_station",
877: "avant_gardens.caged_spider",
882: "avant_gardens.survival.world_control", 882: "avant_gardens.survival.world_control",
901: "avant_gardens.survival.stromling_mech", 901: "avant_gardens.survival.stromling_mech",
946: "gnarled_forest.torch", 946: "gnarled_forest.torch",
@@ -43,10 +44,11 @@ SCRIPTS = {
1216: "items.cauldron_of_life", 1216: "items.cauldron_of_life",
1218: "items.anvil_of_armor", 1218: "items.anvil_of_armor",
1219: "items.fountain_of_imagination", 1219: "items.fountain_of_imagination",
1239: "nimbus_station.lego_club_door",
1270: "avant_gardens.saluting_npcs", 1270: "avant_gardens.saluting_npcs",
1271: "avant_gardens.saluting_npcs", 1271: "avant_gardens.saluting_npcs",
1272: "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", 1329: "crux_prime.aura_blossom_flower",
1345: "general.poi_mission", 1345: "general.poi_mission",
1349: "crux_prime.scroll_shrine", 1349: "crux_prime.scroll_shrine",
@@ -54,8 +56,8 @@ SCRIPTS = {
1419: "nexus_tower.water_fountain", 1419: "nexus_tower.water_fountain",
1458: "items.sunflower", 1458: "items.sunflower",
1481: "nexus_tower.vault", 1481: "nexus_tower.vault",
1484: "general.transfer_world_on_use", 1484: "nimbus_station.lup_teleport",
1485: "general.transfer_world_on_use", 1485: "nimbus_station.lego_club_door",
1486: "general.transfer_world_on_use", 1486: "general.transfer_world_on_use",
1519: "nexus_tower.venture_cannon", 1519: "nexus_tower.venture_cannon",
1527: "general.transfer_world_on_use", 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_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_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\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\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_ASSEMBLYTUBE_SERVER.lua": "nexus_tower.assembly_tube",
r"02_server\Map\NT\L_NT_PARADOXTELE_SERVER.lua": "nexus_tower.paradox_teleporter", 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_MODULAR_BUILD.lua": "nimbus_station.rocket_modular_build",
r"ai\NS\L_NS_QB_IMAGINATION_STATUE.lua": "nimbus_station.imagination_statue", 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"} r"ai\NS\NS_PP_01\L_NS_PP_01_TELEPORT.lua": "property.teleport"}