From 94581d182ff07d354b8e309d54303d4131051ffd Mon Sep 17 00:00:00 2001 From: lcdr Date: Thu, 29 Sep 2016 17:57:42 +0200 Subject: [PATCH] v2016.09.29 --- .hgignore | 4 +++ luserver/components/char.py | 23 +++++++++++- luserver/components/destructible.py | 36 ++++--------------- luserver/components/inventory.py | 18 ++-------- luserver/components/mission.py | 4 ++- luserver/components/rebuild.py | 13 +++++-- luserver/components/skill.py | 20 +++++++++-- luserver/components/spawner.py | 3 ++ luserver/components/stats.py | 18 ++++++++++ luserver/ldf.py | 4 +-- luserver/messages.py | 1 + luserver/modules/chat.py | 23 ++++++++++++ luserver/modules/mail.py | 8 +++-- luserver/modules/physics.py | 1 - .../scripts/avant_gardens/caged_bricks.py | 11 ++++++ .../avant_gardens/survival/world_control.py | 8 +++-- .../scripts/gnarled_forest/banana_tree.py | 5 +++ luserver/scripts/gnarled_forest/jail_walls.py | 4 +++ luserver/scripts/gnarled_forest/jailkeep.py | 13 +++++++ luserver/server.py | 2 +- luserver/world.py | 4 +-- runtime/db/init.py | 27 +++++++++++--- runtime/db/luz_importer.py | 2 +- runtime/db/scripts.py | 4 +++ 24 files changed, 186 insertions(+), 70 deletions(-) create mode 100644 .hgignore create mode 100644 luserver/scripts/avant_gardens/caged_bricks.py create mode 100644 luserver/scripts/gnarled_forest/banana_tree.py create mode 100644 luserver/scripts/gnarled_forest/jail_walls.py create mode 100644 luserver/scripts/gnarled_forest/jailkeep.py diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..6b1dd33 --- /dev/null +++ b/.hgignore @@ -0,0 +1,4 @@ +syntax: glob +runtime/db/server_db* +runtime/db/cdclient.sqlite +runtime/packets/* diff --git a/luserver/components/char.py b/luserver/components/char.py index 258b327..e0cb73c 100644 --- a/luserver/components/char.py +++ b/luserver/components/char.py @@ -375,6 +375,16 @@ class CharacterComponent(Component): if task.type == TaskType.Interact and task.target == obj.lot: mission.increment_task(task, self.object) + def client_item_consumed(self, address, item_id:c_int64=None): + for item in self.object.inventory.items: + if item is not None and item.object_id == item_id: + for mission in self.missions: + if mission.state == MissionState.Active: + for task in mission.tasks: + if task.type == TaskType.UseConsumable and item.lot == task.target: + mission.increment_task(task, self.object) + break + def get_flag(self, flag_id): return bool(self.flags & (1 << flag_id)) @@ -407,6 +417,17 @@ class CharacterComponent(Component): def display_tooltip(self, address, do_or_die:c_bit=False, no_repeat:c_bit=False, no_revive:c_bit=False, is_property_tooltip:c_bit=False, show:c_bit=None, translate:c_bit=False, time:c_int=None, id:"wstr"=None, localize_params:"ldf"=None, str_image_name:"wstr"=None, str_text:"wstr"=None): pass + def use_non_equipment_item(self, address, item_to_use:c_int64=None): + for item in self.object.inventory.items: + if item is not None and item.object_id == item_to_use: + for component_type, component_id in self.object._v_server.db.components_registry[item.lot]: + if component_type == 53: # PackageComponent, make an enum for this somewhen + self.object.inventory.remove_item_from_inv(InventoryType.Items, item) + for loot_table in self.object._v_server.db.package_component[component_id]: + for lot, _ in loot_table[0]: + self.object.inventory.add_item_to_inventory(lot) + return + def notify_pet_taming_minigame(self, address, pet_id:c_int64=None, player_taming_id:c_int64=None, force_teleport:c_bit=None, notify_type:c_uint=None, pets_dest_pos:Vector3=None, tele_pos:Vector3=None, tele_rot:Quaternion=Quaternion.identity): pass @@ -430,7 +451,7 @@ class CharacterComponent(Component): pass def update_model_from_client(self, address, model_id:c_int64=None, position:Vector3=None, rotation:Quaternion=Quaternion.identity): - for model in self.models: + for model in self.object.inventory.models: if model is not None and model.object_id == model_id: spawner_id = self.object._v_server.new_object_id() rotation = Quaternion(rotation.y, rotation.z, rotation.w, rotation.x) # don't ask me why this is swapped diff --git a/luserver/components/destructible.py b/luserver/components/destructible.py index 4f13b95..d96a1d4 100644 --- a/luserver/components/destructible.py +++ b/luserver/components/destructible.py @@ -1,5 +1,3 @@ -import random - from ..bitstream import c_bit, c_float, c_int, c_int64, c_uint from .component import Component from .mission import MissionState, TaskType @@ -13,16 +11,14 @@ class DestructibleComponent(Component): def init(self): comp = self.object._v_server.db.destructible_component[self.comp_id] self.object.stats.faction = comp[0] - self.loot_matrix = comp[1] - self.currency_min = comp[2] - self.currency_max = comp[3] - self.object.stats._max_life = comp[4] - self.object.stats._max_armor = comp[5] - self.object.stats._max_imagination = comp[6] + self.death_rewards = comp[1] + self.object.stats._max_life = comp[2] + self.object.stats._max_armor = comp[3] + self.object.stats._max_imagination = comp[4] self.object.stats.life = self.object.stats.max_life self.object.stats.armor = self.object.stats.max_armor self.object.stats.imagination = self.object.stats.max_imagination - self.object.stats.is_smashable = comp[7] + self.object.stats.is_smashable = comp[5] del self.comp_id def serialize(self, out, is_creation): @@ -37,17 +33,6 @@ class DestructibleComponent(Component): if self.object.stats.life <= 0: self.request_die(None, unknown_bool=False, death_type="", direction_relative_angle_xz=0, direction_relative_angle_y=0, direction_relative_force=10, killer_id=dealer.object_id, loot_owner_id=dealer.object_id) - def random_currency_amount(self): - return random.randint(self.currency_min, self.currency_max) - - def random_loot(self, owner): - # ridiculously bad and biased temporary implementation, please fix - loot = [] - for loot_table, percent, min_to_drop, max_to_drop in self.loot_matrix: - lot, _ = random.choice(loot_table) - loot.append(lot) - return loot - def request_die(self, address, unknown_bool:c_bit=None, death_type:"wstr"=None, direction_relative_angle_xz:c_float=None, direction_relative_angle_y:c_float=None, direction_relative_force:c_float=None, kill_type:c_int=0, killer_id:c_int64=None, loot_owner_id:c_int64=None): if self.object.stats.armor != 0: self.object.stats.armor = 0 @@ -67,16 +52,7 @@ class DestructibleComponent(Component): if task.type == TaskType.KillEnemy and self.object.lot in task.target: mission.increment_task(task, killer) - # drops - - if self.currency_min is not None and self.currency_max is not None: - currency = self.random_currency_amount() - self.object._v_server.send_game_message(killer.char.drop_client_loot, currency=currency, item_template=-1, loot_id=0, owner=killer.object_id, source_obj=self.object.object_id, address=killer.char.address) - - if self.loot_matrix is not None: - loot = self.random_loot(killer) - for lot in loot: - self.object.stats.drop_loot(lot, killer) + self.object.stats.drop_rewards(*self.death_rewards, killer) if not hasattr(self.object, "comp_108") and not hasattr(self.object, "char"): self.object._v_server.destruct(self.object) diff --git a/luserver/components/inventory.py b/luserver/components/inventory.py index 23b5700..ab2c1c5 100644 --- a/luserver/components/inventory.py +++ b/luserver/components/inventory.py @@ -1,6 +1,7 @@ class InventoryType: Items = 0 Bricks = 2 + # temp items = 4? Models = 5 TempModels = 6 # Properties = 8 ? @@ -176,8 +177,7 @@ class InventoryComponent(Component): break else: log.error("no space left") - # todo: send items in mail - # todo: there's a game message to warn the player that there's no space left, send it + self.object._v_server.mail.send_mail("%[MAIL_SYSTEM_NOTIFICATION]", "%[MAIL_ACHIEVEMENT_OVERFLOW_HEADER]", "%[MAIL_ACHIEVEMENT_OVERFLOW_BODY]", self.object, stack) return # should probably throw an exception? if module_lots: @@ -207,14 +207,13 @@ class InventoryComponent(Component): if item is not None: object_id = item.object_id - self.object._v_server.send_game_message(self.remove_item_from_inventory, inventory_type=inventory_type, extra_info={}, object_id=object_id, object_template=lot, stack_count=amount, address=self.object.char.address) + self.object._v_server.send_game_message(self.remove_item_from_inventory, inventory_type=inventory_type, extra_info={}, force_deletion=True, object_id=object_id, object_template=lot, stack_count=amount, address=self.object.char.address) def remove_item_from_inventory(self, address, confirmed:c_bit=True, delete_item:c_bit=True, out_success:c_bit=False, inventory_type:c_int=0, loot_type_source:c_int=0, extra_info:"ldf"=None, force_deletion:c_bit=False, loot_type_source_id:c_int64=0, object_id:c_int64=0, object_template:c_int=0, requesting_object_id:c_int64=0, stack_count:c_uint=1, stack_remaining:c_uint=0, subkey:c_int64=0, trade_id:c_int64=0): if confirmed: assert delete_item assert not out_success assert not extra_info - assert not force_deletion assert loot_type_source_id == 0 assert requesting_object_id == 0 assert stack_remaining == 0 @@ -283,17 +282,6 @@ class InventoryComponent(Component): inv = self.inventory_type_to_inventory(inventory_type) inv.extend([None] * (size - len(inv))) - def use_non_equipment_item(self, address, item_to_use:c_int64=None): - for item in self.items: - if item is not None and item.object_id == item_to_use: - for component_type, component_id in self.object._v_server.db.components_registry[item.lot]: - if component_type == 53: # PackageComponent, make an enum for this somewhen - self.remove_item_from_inv(InventoryType.Items, item) - for loot_table in self.object._v_server.db.package_component[component_id]: - for lot, _ in loot_table[0]: - self.add_item_to_inventory(lot) - return - def move_item_between_inventory_types(self, address, inventory_type_a:c_int=None, inventory_type_b:c_int=None, object_id:c_int64=None, show_flying_loot:c_bit=True, stack_count:c_uint=1, template_id:c_int=-1): source = self.inventory_type_to_inventory(inventory_type_a) for item in source: diff --git a/luserver/components/mission.py b/luserver/components/mission.py index 5bb59be..5307d11 100644 --- a/luserver/components/mission.py +++ b/luserver/components/mission.py @@ -16,7 +16,7 @@ class TaskType: Collect = 3 GoToNPC = 4 UseEmote = 5 - # ??? = 9 # use collectible? + UseConsumable = 9 UseSkill = 10 ObtainItem = 11 # Discover = 12 # needs physics @@ -115,6 +115,8 @@ class MissionProgress(Persistent): player.stats.max_life += self.rew_max_life player.stats.max_imagination += self.rew_max_imagination + if self.id == 173: # hardcoded since script wouldn't work due to call order + player.stats.imagination = player.stats.max_imagination if self.rew_max_items: player._v_server.send_game_message(player.inventory.set_inventory_size, inventory_type=InventoryType.Items, size=len(player.inventory.items)+self.rew_max_items, address=player.char.address) diff --git a/luserver/components/rebuild.py b/luserver/components/rebuild.py index 6bc3c28..633b336 100644 --- a/luserver/components/rebuild.py +++ b/luserver/components/rebuild.py @@ -28,6 +28,7 @@ class RebuildComponent(ScriptedActivityComponent): self.smash_time = self.object._v_server.db.rebuild_component[comp_id][1] self.reset_time = self.object._v_server.db.rebuild_component[comp_id][2] self.imagination_cost = self.object._v_server.db.rebuild_component[comp_id][3] + self.completion_rewards = self.object._v_server.db.rebuild_component[comp_id][4] self.callback_handles = [] self.rebuild_start_time = 0 self.last_progress = 0 @@ -70,6 +71,11 @@ class RebuildComponent(ScriptedActivityComponent): out.write(c_bit(True)) self.rebuild_flag = False + def on_destruction(self): + for handle in self.callback_handles: + handle.cancel() + self.callback_handles.clear() + def on_use(self, player, multi_interact_id): assert multi_interact_id is None assert self.rebuild_state in (RebuildState.Open, RebuildState.Incomplete) @@ -107,7 +113,10 @@ class RebuildComponent(ScriptedActivityComponent): assert len(self.object.children) == 1 self.object._v_server.destruct(self.object._v_server.game_objects[self.object.children[0]]) - asyncio.get_event_loop().call_later(self.smash_time, self.smash_rebuild) + self.callback_handles.append(asyncio.get_event_loop().call_later(self.smash_time, self.smash_rebuild)) + + # drop rewards + self.object.stats.drop_rewards(*self.completion_rewards, player) # update missions that have completing this rebuild as requirement for mission in player.char.missions: @@ -143,4 +152,4 @@ class RebuildComponent(ScriptedActivityComponent): pass def rebuild_notify_state(self, address, prev_state:c_int=None, state:c_int=None, player:c_int64=None): - pass \ No newline at end of file + pass diff --git a/luserver/components/skill.py b/luserver/components/skill.py index 994a9a4..d230075 100644 --- a/luserver/components/skill.py +++ b/luserver/components/skill.py @@ -7,7 +7,7 @@ from ..bitstream import BitStream, c_bit, c_float, c_int, c_int64, c_ubyte, c_ui from ..math.quaternion import Quaternion from ..math.vector import Vector3 from .component import Component -from .inventory import ItemType +from .inventory import InventoryType, ItemType from .mission import MissionState, TaskType log = logging.getLogger(__name__) @@ -93,12 +93,17 @@ class SkillSlot: Neck = 2 Hat = 3 +class CastType: + Consumable = 3 + EverlastingConsumable = 4 + class SkillComponent(Component): def __init__(self, obj, set_vars, comp_id): super().__init__(obj, set_vars, comp_id) self.object.skill = self self.delayed_behaviors = {} self.projectile_behaviors = {} + self.everlasting = False def serialize(self, out, is_creation): if is_creation: @@ -131,11 +136,19 @@ class SkillComponent(Component): target = self.object self.picked_target_id = optional_target_id behavior = self.object._v_server.db.skill_behavior[skill_id] + self.original_target_id = target.object_id self.handle_behavior(behavior, bitstream, target) if not bitstream.all_read(): log.warning("not all read, remaining: %s", bitstream[bitstream._read_offset//8:]) + # remove consumable + if not self.everlasting and consumable_item_id != 0 and cast_type == CastType.Consumable: + for item in self.object.inventory.items: + if item is not None and item.object_id == consumable_item_id: + self.object.inventory.remove_item_from_inv(InventoryType.Items, item) + break + def select_skill(self, address, from_skill_set:c_bit=False, skill_id:c_int=None): pass @@ -164,6 +177,7 @@ class SkillComponent(Component): target = self.object._v_server.game_objects[target_id] if behavior is not None: # no, this is not an "else" from above + self.original_target_id = target.object_id self.handle_behavior(behavior, bitstream, target) if not bitstream.all_read(): log.warning("not all read, remaining: %s", bitstream[bitstream._read_offset//8:]) @@ -180,6 +194,7 @@ class SkillComponent(Component): target = self.object._v_server.game_objects[target_id] for behav in self.projectile_behaviors[local_id]: + self.original_target_id = target.object_id self.handle_behavior(behav, bitstream, target) del self.projectile_behaviors[local_id] # todo: do client projectile impact @@ -227,6 +242,7 @@ class SkillComponent(Component): target_id = bitstream.read(c_int64) targets.append(self.object._v_server.game_objects[target_id]) for target in targets: + log.debug("Target %s", target) self.handle_behavior(behavior.action, bitstream, target, level+1) else: @@ -300,7 +316,7 @@ class SkillComponent(Component): self.handle_behavior(casted_behavior, bitstream, target, level+1) elif behavior.template == BehaviorTemplate.Stun: - if target != self.object: + if target.object_id != self.original_target_id: log.debug("Stun reading bit") assert not bitstream.read(c_bit) diff --git a/luserver/components/spawner.py b/luserver/components/spawner.py index ce574e8..6ab8630 100644 --- a/luserver/components/spawner.py +++ b/luserver/components/spawner.py @@ -16,4 +16,7 @@ class SpawnerComponent(Component): self.last_waypoint_index = random.randrange(len(self.waypoints)) position, rotation, spawn_vars, spawned_vars = self.waypoints[self.last_waypoint_index] spawned = self.object._v_server.spawn_object(self.spawntemplate, spawner=self.object, custom_script=spawn_vars.get("custom_script"), position=position, rotation=rotation, set_vars=spawned_vars) + for comp in spawned.components: + if hasattr(comp, "on_startup"): + comp.on_startup() return spawned diff --git a/luserver/components/stats.py b/luserver/components/stats.py index e8b15c6..964877b 100644 --- a/luserver/components/stats.py +++ b/luserver/components/stats.py @@ -119,6 +119,24 @@ class StatsSubcomponent(Component): if self.object.spawner_object is not None: asyncio.get_event_loop().call_later(8, self.object.spawner_object.spawner.spawn) + def random_loot(self, loot_matrix, owner): + # ridiculously bad and biased temporary implementation, please fix + loot = [] + for loot_table, percent, min_to_drop, max_to_drop in loot_matrix: + lot, _ = random.choice(loot_table) + loot.append(lot) + return loot + + def drop_rewards(self, loot_matrix, currency_min, currency_max, owner): + if currency_min is not None and currency_max is not None: + currency = random.randint(currency_min, currency_max) + self.object._v_server.send_game_message(owner.char.drop_client_loot, currency=currency, item_template=-1, loot_id=0, owner=owner.object_id, source_obj=self.object.object_id, address=owner.char.address) + + if loot_matrix is not None: + loot = self.random_loot(loot_matrix, owner) + for lot in loot: + self.drop_loot(lot, owner) + def drop_loot(self, lot, owner): loot_position = Vector3(self.object.physics.position.x+(random.random()-0.5)*20, self.object.physics.position.y, self.object.physics.position.z+(random.random()-0.5)*20) object_id = self.object._v_server.new_spawned_id() diff --git a/luserver/ldf.py b/luserver/ldf.py index c0fa57b..3ab2462 100644 --- a/luserver/ldf.py +++ b/luserver/ldf.py @@ -26,9 +26,7 @@ def _value_to_ldf_text(type_, value): def to_ldf(obj, ldf_type): if ldf_type == "text": if isinstance(obj, dict): - if len(obj) > 1: - raise NotImplementedError - output = "".join("%s=%s" % (key, _value_to_ldf_text(*value)) for key, value in obj.items()) + output = "\n".join("%s=%s" % (key, _value_to_ldf_text(*value)) for key, value in obj.items()) else: output = _value_to_ldf_text(*obj) diff --git a/luserver/messages.py b/luserver/messages.py index dd1222e..75458df 100644 --- a/luserver/messages.py +++ b/luserver/messages.py @@ -127,6 +127,7 @@ class GameMessage(Enum): SellToVendor = 374 SetInventorySize = 389 VendorStatusUpdate = 417 + ClientItemConsumed = 428 SetFlag = 471 HasBeenCollected = 486 PlayerLoaded = 505 diff --git a/luserver/modules/chat.py b/luserver/modules/chat.py index d089346..a10a96c 100644 --- a/luserver/modules/chat.py +++ b/luserver/modules/chat.py @@ -114,6 +114,10 @@ class ChatHandling(ServerModule): dismount_cmd = cmds.add_parser("dismount") dismount_cmd.set_defaults(func=self.dismount_cmd) + everlasting_cmd = cmds.add_parser("everlasting") + everlasting_cmd.add_argument("--enable", type=toggle_bool) + everlasting_cmd.set_defaults(func=self.everlasting_cmd) + extend_inv_cmd = cmds.add_parser("extendinv") extend_inv_cmd.add_argument("amount", type=int) extend_inv_cmd.set_defaults(func=self.extend_inv_cmd) @@ -164,6 +168,10 @@ class ChatHandling(ServerModule): refill_stats_cmd = cmds.add_parser("refillstats") refill_stats_cmd.set_defaults(func=self.refill_stats_cmd) + remove_mission_cmd = cmds.add_parser("removemission") + remove_mission_cmd.add_argument("id", type=int) + remove_mission_cmd.set_defaults(func=self.remove_mission_cmd) + reset_missions_cmd = cmds.add_parser("resetmissions") reset_missions_cmd.set_defaults(func=self.reset_missions_cmd) @@ -310,6 +318,12 @@ class ChatHandling(ServerModule): self.server.game_objects[sender.char.vehicle_id].comp_108.driver_id = 0 sender.char.vehicle_id = 0 + def everlasting_cmd(self, args, sender): + if args.enable is None: + sender.skill.everlasting = not sender.skill.everlasting + else: + sender.skill.everlasting = args.enable + def extend_inv_cmd(self, args, sender): # currently just items, add models functionality when necessary self.server.send_game_message(sender.inventory.set_inventory_size, inventory_type=InventoryType.Items, size=len(sender.inventory.items)+args.amount, address=sender.char.address) @@ -367,6 +381,15 @@ class ChatHandling(ServerModule): sender.stats.armor = sender.stats.max_armor sender.stats.imagination = sender.stats.max_imagination + def remove_mission_cmd(self, args, sender): + for mission in sender.char.missions: + if mission.id == args.id: + sender.char.missions.remove(mission) + print("Mission removed") + break + else: + print("Mission not found") + def reset_missions_cmd(self, args, sender): sender.char.missions.clear() # add achievements diff --git a/luserver/modules/mail.py b/luserver/modules/mail.py index d63387b..ceab6a0 100644 --- a/luserver/modules/mail.py +++ b/luserver/modules/mail.py @@ -46,8 +46,7 @@ class MailHandling(ServerModule): elif mail_id == MailID.MailRead: self.on_mail_read(message, player) elif mail_id == MailID.MailNotificationRequest: - if player.char.mails: - self.send_mail_notification(player) + self.send_mail_notification(player) def on_mail_send(self, data, player): subject = data.read(str, allocated_length=100) @@ -164,12 +163,15 @@ class MailHandling(ServerModule): break def send_mail_notification(self, player): + unread_mails_amount = len([mail for mail in player.char.mails if not mail.is_read]) + if unread_mails_amount == 0: + return notification = BitStream() notification.write_header(WorldClientMsg.Mail) notification.write(c_uint(MailID.MailNotification)) notification.write(bytes(4)) # notification type, seems only 0 is used notification.write(bytes(32)) - notification.write(c_uint(len([mail for mail in player.char.mails if not mail.is_read]))) + notification.write(c_uint(unread_mails_amount)) notification.write(bytes(4)) self.server.send(notification, player.char.address) diff --git a/luserver/modules/physics.py b/luserver/modules/physics.py index b8f596e..2f20e98 100644 --- a/luserver/modules/physics.py +++ b/luserver/modules/physics.py @@ -48,7 +48,6 @@ class PhysicsHandling(ServerModule): for obj in self.server.world_data.objects.values(): if obj.lot in MODEL_DIMENSIONS: if not hasattr(obj, "script") or not hasattr(obj.script, "on_collision"): - log.warn("Object %s doesn't have a collision callback!", obj) continue aabb = AABB(obj) #self.server.spawn_object(2556, position=aabb.min, rotation=Quaternion.identity) diff --git a/luserver/scripts/avant_gardens/caged_bricks.py b/luserver/scripts/avant_gardens/caged_bricks.py new file mode 100644 index 0000000..99ae806 --- /dev/null +++ b/luserver/scripts/avant_gardens/caged_bricks.py @@ -0,0 +1,11 @@ +import luserver.components.script as script +from luserver.components.inventory import InventoryType + +FLAG_ID = 74 +MAELSTROM_CUBE_LOT = 14553 + +class ScriptComponent(script.ScriptComponent): + def on_use(self, player, multi_interact_id): + assert multi_interact_id is None + player.char.set_flag(None, True, FLAG_ID) + player.inventory.remove_item_from_inv(InventoryType.Items, lot=MAELSTROM_CUBE_LOT) diff --git a/luserver/scripts/avant_gardens/survival/world_control.py b/luserver/scripts/avant_gardens/survival/world_control.py index 196640a..7bdc4fa 100644 --- a/luserver/scripts/avant_gardens/survival/world_control.py +++ b/luserver/scripts/avant_gardens/survival/world_control.py @@ -1,8 +1,12 @@ import luserver.components.script as script -from luserver.bitstream import c_bool +from luserver.bitstream import c_bool, c_int class ScriptComponent(script.ScriptComponent): def player_ready(self, address): + player = self.object._v_server.accounts[address].characters.selected() script_vars = {} - script_vars["Show_ScoreBoard"] = c_bool, True + script_vars["NumberOfPlayers"] = c_int, 1 + script_vars["Define_Player_To_UI"] = bytes, player.object_id + script_vars["Show_ScoreBoard"] = c_bool, int(True) + script_vars["Update_ScoreBoard_Players.1"] = bytes, player.object_id self.object._v_server.send_game_message(self.script_network_var_update, script_vars, broadcast=True) diff --git a/luserver/scripts/gnarled_forest/banana_tree.py b/luserver/scripts/gnarled_forest/banana_tree.py new file mode 100644 index 0000000..02c8689 --- /dev/null +++ b/luserver/scripts/gnarled_forest/banana_tree.py @@ -0,0 +1,5 @@ +import luserver.components.script as script + +class ScriptComponent(script.ScriptComponent): + def on_startup(self): + self.object._v_server.spawn_object(6909, position=self.object.physics.position, rotation=self.object.physics.rotation) diff --git a/luserver/scripts/gnarled_forest/jail_walls.py b/luserver/scripts/gnarled_forest/jail_walls.py new file mode 100644 index 0000000..945657c --- /dev/null +++ b/luserver/scripts/gnarled_forest/jail_walls.py @@ -0,0 +1,4 @@ +import luserver.components.script as script + +class ScriptComponent(script.ScriptComponent): + pass # currently not implemented diff --git a/luserver/scripts/gnarled_forest/jailkeep.py b/luserver/scripts/gnarled_forest/jailkeep.py new file mode 100644 index 0000000..80decfc --- /dev/null +++ b/luserver/scripts/gnarled_forest/jailkeep.py @@ -0,0 +1,13 @@ +import luserver.components.script as script +from luserver.bitstream import c_bit, c_int, c_int64 +from luserver.components.mission import MissionState + +FEED_NINJAS_MISSION = 385 +FEED_NINJAS_MISSIONS = [386, 387, 388, 390] + +class ScriptComponent(script.ScriptComponent): + def mission_dialogue_o_k(self, address, is_complete:c_bit=None, mission_state:c_int=None, mission_id:c_int=None, responder:c_int64=None): + if mission_id == FEED_NINJAS_MISSION and mission_state == MissionState.Available: + player = self.object._v_server.game_objects[responder] + for mission in FEED_NINJAS_MISSIONS: + player.char.add_mission(mission) diff --git a/luserver/server.py b/luserver/server.py index ef666ab..48c8b81 100644 --- a/luserver/server.py +++ b/luserver/server.py @@ -110,7 +110,7 @@ class Server(pyraknet.server.Server): # no server found, spawn a new one # todo: os.system probably isn't the best way to do this os.system("start python __main__.py %i %i" % (world_id[0], world_id[2])) - await asyncio.sleep(3) + await asyncio.sleep(4) class DisconnectReason: UnknownServerError = 0 diff --git a/luserver/world.py b/luserver/world.py index 2ac8c6b..e456ac9 100644 --- a/luserver/world.py +++ b/luserver/world.py @@ -176,11 +176,9 @@ class WorldServer(server.Server, pyraknet.replicamanager.ReplicaManager): self.physics.init() def spawn_model(self, spawner_id, lot, position, rotation): - # todo: this is outdated, needs to be updated spawner = GameObject(self, 176, spawner_id) spawner.spawner.spawntemplate = lot - spawner.physics.position.update(position) - spawner.physics.rotation.update(rotation) + spawner.spawner.waypoints = (position, rotation, {}, {}), spawner._v_server = self self.models.append((spawner, spawner.spawner.spawn())) diff --git a/runtime/db/init.py b/runtime/db/init.py index fe0676c..33858ad 100644 --- a/runtime/db/init.py +++ b/runtime/db/init.py @@ -246,7 +246,9 @@ if GENERATE_MISSIONS: reward_items = [] for lot, count in ((reward_item1, reward_item1_count), (reward_item2, reward_item2_count), (reward_item3, reward_item3_count), (reward_item4, reward_item4_count)): - if lot != -1 and count != 0: + if lot != -1: + if count == 0: + count = 1 reward_items.append((lot, count)) if reward_emote == -1: @@ -268,6 +270,21 @@ if GENERATE_COMPS: loot_table_entry = tuple(loot_table_entry) loot_matrix.setdefault(row[0], []).append((loot_table_entry, row[2], row[3], row[3])) + activity_rewards = {} + for object_template, loot_matrix_index, currency_index in cdclient.execute("select objectTemplate, LootMatrixIndex, CurrencyIndex from ActivityRewards"): + # doesn't currently account for activity ratings + if loot_matrix_index is not None: + loot = loot_matrix.get(loot_matrix_index) + else: + loot = None + if currency_index is not None: + _, minvalue, maxvalue = currency_table[currency_index][0] + else: + minvalue = None + maxvalue = None + + activity_rewards[object_template] = loot, minvalue, maxvalue + # actually persistent stuff root.item_component = BTrees.IOBTree.BTree() @@ -331,7 +348,7 @@ if GENERATE_COMPS: if armor is not None: armor = int(armor) - root.destructible_component[row[2]] = faction, loot, minvalue, maxvalue, life, armor, imagination, is_smashable + root.destructible_component[row[2]] = faction, (loot, minvalue, maxvalue), life, armor, imagination, is_smashable elif row[1] == 16 and row[2] not in root.vendor_component: comp_row = cdclient.execute("select LootMatrixIndex from VendorComponent where id == %i" % row[2]).fetchone() @@ -343,12 +360,12 @@ if GENERATE_COMPS: root.inventory_component.setdefault(row[2], []).append((comp_row[0], comp_row[1])) elif row[1] == 48 and row[2] not in root.rebuild_component: - comp_row = cdclient.execute("select complete_time, time_before_smash, reset_time, take_imagination from RebuildComponent where id == %i" % row[2]).fetchone() + comp_row = cdclient.execute("select complete_time, time_before_smash, reset_time, take_imagination, activityID from RebuildComponent where id == %i" % row[2]).fetchone() if comp_row is not None: - complete_time, smash_time, reset_time, take_imagination = comp_row + complete_time, smash_time, reset_time, take_imagination, activity_id = comp_row if complete_time is None: complete_time = 1 - root.rebuild_component[row[2]] = complete_time, smash_time, reset_time, take_imagination + root.rebuild_component[row[2]] = complete_time, smash_time, reset_time, take_imagination, activity_rewards.get(activity_id, (None, None, None)) elif row[1] == 53 and row[2] not in root.package_component: comp_row = cdclient.execute("select LootMatrixIndex from PackageComponent where id == %i" % row[2]).fetchone() diff --git a/runtime/db/luz_importer.py b/runtime/db/luz_importer.py index 3e4945a..75a5b64 100644 --- a/runtime/db/luz_importer.py +++ b/runtime/db/luz_importer.py @@ -53,7 +53,7 @@ class PathType: Race = 6 Rail = 7 -WHITELISTED_SERVERSIDE_LOTS = 176, 3964, 4734, 4764, 4860, 4945, 5633, 5652, 6247, 6396, 6700, 6842, 6958, 6960, 7085, 7608, 7973, 8139, 8419, 9930, 10009, 10042, 10413, 10496, 11165, 11178, 11274, 11279, 11280, 11281, 12232, 12661, 13142, 13834, 13835, 13881, 13882, 14013, 14031, 14086, 14087, 14199, 14214, 14215, 14216, 14217, 14218, 14220, 14226, 14242, 14243, 14244, 14245, 14246, 14248, 14249, 14289, 14290, 14291, 14292, 14293, 14294, 14330, 14331, 14332, 14333, 14347, 14348, 14510, 14530, 16513, 16627 +WHITELISTED_SERVERSIDE_LOTS = 176, 3964, 4734, 4764, 4860, 4945, 5633, 5652, 6247, 6396, 6700, 6842, 6958, 6960, 7085, 7608, 7973, 8139, 8419, 9930, 10009, 10042, 10413, 10496, 11165, 11178, 11274, 11279, 11280, 11281, 12232, 12661, 13142, 13834, 13835, 13881, 13882, 14013, 14031, 14086, 14087, 14199, 14214, 14215, 14216, 14217, 14218, 14220, 14226, 14242, 14243, 14244, 14245, 14246, 14248, 14249, 14289, 14290, 14291, 14292, 14293, 14294, 14330, 14331, 14332, 14333, 14347, 14348, 14510, 14530, 15902, 16513, 16627 def parse_lvl(conn, world_data, lvl_path): with open(lvl_path, "rb") as file: diff --git a/runtime/db/scripts.py b/runtime/db/scripts.py index c128c8a..a553031 100644 --- a/runtime/db/scripts.py +++ b/runtime/db/scripts.py @@ -7,12 +7,14 @@ SCRIPTS[625] = "venture_explorer.bob" SCRIPTS[815] = "avant_gardens.stromling_mech" SCRIPTS[882] = "avant_gardens.survival.world_control" SCRIPTS[946] = "gnarled_forest.torch" +SCRIPTS[947] = "gnarled_forest.banana_tree" SCRIPTS[952] = "general.binoculars" SCRIPTS[975] = "general.binoculars" SCRIPTS[987] = "gnarled_forest.organ" SCRIPTS[992] = "nimbus_station.nexus_jay" SCRIPTS[1002] = "general.binoculars" SCRIPTS[1021] = "general.binoculars" +SCRIPTS[1023] = "gnarled_forest.jailkeep" SCRIPTS[1054] = "general.story_plaque" SCRIPTS[1069] = "pet_cove.coalessa" SCRIPTS[1088] = "general.mailbox" @@ -28,6 +30,7 @@ SCRIPTS[1556] = "nexus_tower.shadow_orb" SCRIPTS[1566] = "avant_gardens.rocco_sirocco" SCRIPTS[1569] = "venture_explorer.broken_console" SCRIPTS[1582] = "items.maelstrom_vacuum" +SCRIPTS[1586] = "avant_gardens.caged_bricks" SCRIPTS[1641] = "ninjago.ninja" SCRIPTS[1647] = "ninjago.ninja" SCRIPTS[1712] = "ninjago.ninja" @@ -43,5 +46,6 @@ SCRIPTS[r"ai\AG\L_AG_QB_Wall.lua"] = "avant_gardens.quickbuild_wall" SCRIPTS[r"ai\AG\L_AG_SHIP_PLAYER_DEATH_TRIGGER.lua"] = "venture_explorer.death_trigger" SCRIPTS[r"ai\AG\L_AG_SHIP_PLAYER_SHOCK_SERVER.lua"] = "venture_explorer.broken_console" SCRIPTS[r"ai\AG\L_AG_SHIP_SHAKE.lua"] = "venture_explorer.ship_shake" +SCRIPTS[r"ai\GF\L_GF_JAIL_WALLS.lua"] = "gnarled_forest.jail_walls" SCRIPTS[r"ai\NS\L_NS_JONNY_FLAG_MISSION_SERVER.lua"] = "nimbus_station.johnny_thunder" SCRIPTS[r"ai\NS\L_NS_QB_IMAGINATION_STATUE.lua"] = "nimbus_station.imagination_statue"