package me.km.snuviscript; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Collectors; import me.hammerle.code.Code; import me.hammerle.code.Script; import me.hammerle.code.ScriptUtils; import me.hammerle.code.SnuviParser; import me.hammerle.math.Fraction; import me.km.KajetansMod; import me.km.api.Location; import me.km.api.ModuleListener; import me.km.api.Module; import me.km.api.Utils; import me.km.dimensions.ModDimensions; import me.km.effects.PlayerUsesEffectEvent; import me.km.entities.EntityHuman; import me.km.entities.EntityItemProjectile; import me.km.events.PlayerHurtEvent; import me.km.events.PlayerJoinMessageEvent; import me.km.events.PlayerLeaveMessageEvent; import me.km.events.PlayerMoveEvent; import me.km.events.PlayerRespawnAtEvent; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.passive.EntitySheep; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Items; import net.minecraft.inventory.ClickType; import net.minecraft.item.ItemStack; import net.minecraft.server.management.PlayerList; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.world.WorldServer; import net.minecraftforge.event.CommandEvent; import net.minecraftforge.event.entity.EntityMountEvent; import net.minecraftforge.event.entity.ThrowableImpactEvent; import net.minecraftforge.event.entity.item.ItemTossEvent; import net.minecraftforge.event.entity.living.*; import net.minecraftforge.event.entity.player.EntityItemPickupEvent; import net.minecraftforge.event.entity.player.FillBucketEvent; import net.minecraftforge.event.entity.player.ItemFishedEvent; import net.minecraftforge.event.entity.player.PlayerDropsEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent; public class ScriptEvents extends ModuleListener { private final HashMap<UUID, UUID> questJoin; private final SnuviParser parser; public ScriptEvents(Module m) { super(m); questJoin = new HashMap<>(); parser = KajetansMod.scripts.getSnuviParser(); } // ------------------------------------------------------------------------- // Basics // ------------------------------------------------------------------------- private void handleEvent(EntityPlayer p, String event, Consumer<Script> before, Consumer<Script> after) { parser.callEvent(event, (sc) -> { if(p != null) { ScriptVars.setPlayerVars(sc, p); } before.accept(sc); }, after); if(p != null) { Script data = KajetansMod.scripts.getScript(p); if(data != null) { parser.callEvent(event, data, (sc) -> { ScriptVars.setPlayerVars(sc, p); before.accept(sc); }, after); } } } private void handleEvent(EntityPlayer p, String event, Consumer<Script> before) { handleEvent(p, event, before, null); } // ------------------------------------------------------------------------- // Questevents // ------------------------------------------------------------------------- @SubscribeEvent public void onOtherScriptJoin(PlayerInteractEvent.EntityInteract e) { if(e.getHand() == EnumHand.OFF_HAND || !(e.getTarget() instanceof EntityPlayer)) { return; } EntityPlayer p = e.getEntityPlayer(); EntityPlayer affectedPlayer = (EntityPlayer) e.getTarget(); if(KajetansMod.scripts.hasScript(p) && !KajetansMod.scripts.hasScript(affectedPlayer)) { if(questJoin.get(affectedPlayer.getUniqueID()) != null) { if(questJoin.get(affectedPlayer.getUniqueID()).equals(p.getUniqueID())) { return; } } questJoin.put(affectedPlayer.getUniqueID(), p.getUniqueID()); this.getModule().send(affectedPlayer, p.getName() + " fragt dich, ob du seiner Quest beitreten willst."); this.getModule().send(p, affectedPlayer.getName() + " wurde eine Anfrage gesendet."); return; } if(!KajetansMod.scripts.hasScript(p) && questJoin.get(p.getUniqueID()) != null && KajetansMod.scripts.hasScript(affectedPlayer)) { if(questJoin.get(p.getUniqueID()).equals(affectedPlayer.getUniqueID())) { questJoin.remove(p.getUniqueID()); KajetansMod.scripts.addPlayerToPlayer(p, affectedPlayer); } } } @SubscribeEvent public void onPlayerMove(PlayerMoveEvent e) { EntityPlayer p = e.getEntityPlayer(); Location l = new Location(p); l.round(); parser.callEvent("player_move", (sc) -> ScriptVars.setPlayerVars(sc, p), null, sc -> ((MinecraftScript) sc).removeLocation(l)); PlayerScript data = KajetansMod.scripts.getScript(p); if(data != null) { parser.callEvent("player_move", data, (sc) -> { ScriptVars.setPlayerVars(sc, p); }, null, data.removeLocation(l)); } } public boolean onInventoryClick(Script qd, SnuviInventory inv, int slot, ClickType click, EntityPlayer p) { parser.callEvent("inv_click", qd, sc -> { ScriptVars.setPlayerVars(qd, p); qd.setEventVar("inv_id", new Fraction(inv.getId())); qd.setEventVar("inv_name", inv.getName()); qd.setEventVar("inv_slot", new Fraction(slot)); ScriptVars.setItemVars(qd, inv.getStackInSlot(slot)); qd.setVar("cancel", false); }, null); return qd.getBooleanVar("cancel"); } public void onInventoryClose(Script qd, SnuviInventory inv, EntityPlayer p) { parser.callEvent("inv_close", qd, sc -> { ScriptVars.setPlayerVars(qd, p); qd.setEventVar("inv_id", new Fraction(inv.getId())); qd.setEventVar("inv_name", inv.getName()); }, null); } public void onHumanHurt(EntityPlayer p, EntityHuman h) { handleEvent(p, "human_hurt", sc -> ScriptVars.setEntityVars(sc, h)); } @SubscribeEvent public void onPlayerRespawn(PlayerRespawnAtEvent e) { handleEvent(e.getEntityPlayer(), "player_respawn", (qd) -> { qd.setEventVar("keep_inventory", e.isInventoryKeepingForced()); qd.setEventVar("respawn_loc", new Location(e.getWorld(), e.getRespawnLoc())); }, (qd) -> { try { e.setForcedInventoryKeeping(qd.getBooleanVar("keep_inventory")); Location l = (Location) qd.getVar("respawn_loc"); e.setRespawnLoc(l.getPos()); e.setWorld((WorldServer) l.getWorld()); } catch(ClassCastException | NullPointerException ex) { KajetansMod.scripts.logger.printException(ex, "player_respawn", qd, qd.getActiveRealCodeLine()); } }); } @SubscribeEvent public void onPlayerDamage(PlayerHurtEvent e) { EntityPlayer p = e.getEntityPlayer(); handleEvent(p, "player_hurt", (qd) -> { if(p.getHealth() <= e.getAmount()) { qd.setEventVar("player_killed", true); } else { qd.setEventVar("player_killed", false); } qd.setEventVar("player_damage", Fraction.fromDouble(e.getAmount())); qd.setEventVar("player_damage_cause", e.getSource().getDamageType()); EntityPlayer ent = Utils.getDamager(e.getSource()); if(ent != null) { qd.setEventVar("player_involved", true); ScriptVars.setSecPlayer(qd, ent); } else { qd.setEventVar("player_involved", false); } qd.setVar("cancel", e.isCanceled()); }, (qd) -> { try { e.setAmount(ScriptUtils.getFloat(qd.getVar("player_damage"))); } catch(Exception ex) { KajetansMod.scripts.logger.printException(ex, "player_hurt", qd, qd.getActiveRealCodeLine()); } e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onPlayerDamage(LivingHealEvent e) { if(e.getEntityLiving() instanceof EntityPlayer) { handleEvent((EntityPlayer) e.getEntityLiving(), "player_heal", (qd) -> { qd.setEventVar("heal", Fraction.fromDouble(e.getAmount())); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { try { e.setAmount(ScriptUtils.getFloat(qd.getVar("heal"))); } catch(Exception ex) { KajetansMod.scripts.logger.printException(ex, "player_heal", qd, qd.getActiveRealCodeLine()); } e.setCanceled(qd.getBooleanVar("cancel")); }); } } @SubscribeEvent public void onLivingDeath(LivingDeathEvent e) { if(!(e.getEntityLiving() instanceof EntityPlayer)) { handleEvent(null, "entity_death", (qd) -> { EntityLivingBase ent = e.getEntityLiving(); ScriptVars.setEntityVars(qd, ent); EntityPlayer p = Utils.getDamager(e.getSource()); if(p != null) { qd.setEventVar("player_involved", true); ScriptVars.setPlayerVars(qd, p); } else { qd.setEventVar("player_involved", false); } qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); return; } EntityPlayer p = (EntityPlayer) e.getEntity(); handleEvent(p, "player_death", (qd) -> { qd.setEventVar("clear", false); EntityPlayer ent = Utils.getDamager(e.getSource()); if(ent != null) { ScriptVars.setSecPlayer(qd, ent); } qd.setVar("cancel", e.isCanceled()); }, (qd) -> { if(qd.getBooleanVar("clear")) { p.inventory.clear(); } e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onEntityDamage(LivingHurtEvent e) { EntityPlayer p = Utils.getDamager(e.getSource()); if(p == null) { return; } handleEvent(p, "entity_hurt", (qd) -> { qd.setEventVar("entity_killed", e.getEntityLiving().getHealth() <= e.getAmount()); ScriptVars.setEntityVars(qd, e.getEntity()); qd.setEventVar("entity_damage", Fraction.fromDouble(e.getAmount())); qd.setEventVar("entity_damage_cause", e.getSource().getDamageType()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { try { e.setAmount(ScriptUtils.getFloat(qd.getVar("entity_damage"))); } catch(Exception ex) { KajetansMod.scripts.logger.printException(ex, "entity_hurt", qd, qd.getActiveRealCodeLine()); } e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onEntityDrop(LivingDropsEvent e) { try { handleEvent(null, "entity_drop", (qd) -> { qd.setEventVar("drops", e.getDrops()); ScriptVars.setEntityVars(qd, e.getEntityLiving()); EntityPlayer p = Utils.getDamager(e.getSource()); if(p != null) { qd.setEventVar("player_involved", true); ScriptVars.setPlayerVars(qd, p); } else { qd.setEventVar("player_involved", false); } qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } catch(NullPointerException ex) { KajetansMod.scripts.sendWarningToConsole(ex.toString() + " " + ex.getMessage()); } } @SubscribeEvent public void onPlayerDrop(PlayerDropsEvent e) { handleEvent(e.getEntityPlayer(), "player_drop", (qd) -> { qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onProjectileHit(ThrowableImpactEvent e) { EntityPlayer p = Utils.getPlayerFromProjectile(e.getEntityThrowable()); if(p != null) { handleEvent(p, "throw_hit", (qd) -> { if(e.getRayTraceResult().entityHit != null) { qd.setEventVar("is_entity_hit", true); qd.setEventVar("entity_hit", e.getRayTraceResult().entityHit); } else { qd.setEventVar("is_entity_hit", false); } ScriptVars.setEntityVars(qd, e.getEntityThrowable()); }); } } public void onEntityItemProjectileHit(EntityItemProjectile ent, EntityPlayer p, ItemStack stack, List<Entity> ents) { handleEvent(p, "item_hit", (qd) -> { ScriptVars.setEntityVars(qd, ent); ScriptVars.setItemVars(qd, stack); qd.setEventVar("entities", ents); }); } @SubscribeEvent public void onEntityShear(PlayerInteractEvent.EntityInteract e) { if(e.getHand() == EnumHand.OFF_HAND || !(e.getTarget() instanceof EntitySheep)) { return; } EntityPlayer p = e.getEntityPlayer(); if(p.getHeldItemMainhand().getItem() != Items.SHEARS) { return; } EntitySheep sheep = (EntitySheep) e.getTarget(); handleEvent(p, "entity_shear", (qd) -> { ScriptVars.setEntityVars(qd, sheep); qd.setEventVar("entity_sheared", sheep.getSheared()); qd.setEventVar("entity_color", sheep.getFleeceColor().toString()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); sheep.setSheared(qd.getBooleanVar("entity_sheared")); }); } @SubscribeEvent public void onBlockBreak(BlockEvent.BreakEvent e) { handleEvent(e.getPlayer(), "block_break", (qd) -> { ScriptVars.setBlockVars(qd, e.getWorld(), e.getPos()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent e) { EntityPlayer p = e.player; if(p == null) { return; } handleEvent(p, "player_login", (qd) -> { qd.setEventVar("first_join", Utils.hasPlayedBefore(p)); PlayerList list = KajetansMod.server.getPlayerList(); qd.setEventVar("is_banned", list.getBannedPlayers().isBanned(p.getGameProfile())); qd.setEventVar("is_whitelisted", list.getWhitelistedPlayers().isWhitelisted(p.getGameProfile())); }); } @SubscribeEvent public void onPlayerJoin(PlayerJoinMessageEvent e) { EntityPlayer p = e.getEntityPlayer(); handleEvent(p, "player_join_server", (qd) -> { qd.setEventVar("message", e.getMessage()); qd.setEventVar("changed_name", e.hasNameChanged()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setMessage(String.valueOf(qd.getVar("message"))); e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onPlayerLeave(PlayerLeaveMessageEvent e) { EntityPlayer p = e.getEntityPlayer(); handleEvent(p, "player_leave", (qd) -> { qd.setEventVar("message", e.getMessage()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setMessage(String.valueOf(qd.getVar("message"))); e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onBucketFill(FillBucketEvent e) { handleEvent(e.getEntityPlayer(), "bucket_fill", (qd) -> { RayTraceResult ray = e.getTarget(); if(ray != null) { BlockPos pos = ray.getBlockPos(); if(pos != null) { qd.setEventVar("has_block", true); ScriptVars.setBlockVars(qd, e.getWorld(), pos); } else { qd.setEventVar("has_block", false); } } else { qd.setEventVar("has_block", false); } qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onBlockPlace(BlockEvent.PlaceEvent e) { handleEvent(e.getPlayer(), "block_place", (qd) -> { qd.setEventVar("block_type_after", e.getPlacedBlock().getBlock().getRegistryName()); ScriptVars.setBlockVars(qd, e.getWorld(), e.getPos()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onRightClickBlock(PlayerInteractEvent.RightClickBlock e) { if(e.getHand() == EnumHand.OFF_HAND) { return; } handleEvent(e.getEntityPlayer(), "block_click", (qd) -> { qd.setEventVar("action", "right"); ScriptVars.setBlockVars(qd, e.getWorld(), e.getPos()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onLeftClickBlock(PlayerInteractEvent.LeftClickBlock e) { if(e.getHand() == EnumHand.OFF_HAND) { return; } handleEvent(e.getEntityPlayer(), "block_click", (qd) -> { qd.setEventVar("action", "left"); ScriptVars.setBlockVars(qd, e.getWorld(), e.getPos()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent(receiveCanceled = true) public void onEntityClick(PlayerInteractEvent.EntityInteract e) { if(e.getHand() != EnumHand.OFF_HAND) { handleEvent(e.getEntityPlayer(), "entity_click", (qd) -> { ScriptVars.setEntityVars(qd, e.getTarget()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } } @SubscribeEvent public void onFishing(ItemFishedEvent e) { handleEvent(e.getEntityPlayer(), "fishing", (qd) -> { qd.setEventVar("drops", e.getDrops()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onItemClick(PlayerInteractEvent.RightClickItem e) { handleEvent(e.getEntityPlayer(), "item_air_click", (qd) -> { ScriptVars.setItemVars(qd, e.getItemStack()); qd.setEventVar("hand", e.getHand().toString()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } public void onArmSwing(EntityPlayer p, EnumHand hand) { handleEvent(p, "arm_swing", (qd) -> { qd.setEventVar("hand", hand); }); } @SubscribeEvent public void onItemUseStart(LivingEntityUseItemEvent.Start e) { if(!(e.getEntityLiving() instanceof EntityPlayer)) { return; } handleEvent((EntityPlayer) e.getEntityLiving(), "item_use_start", (qd) -> { qd.setEventVar("duration", new Fraction(e.getDuration())); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); try { e.setDuration(ScriptUtils.getInt(qd.getVar("duration"))); } catch(Exception ex) { KajetansMod.scripts.logger.printException(ex, "item_use_start", qd, qd.getActiveRealCodeLine()); } }); } @SubscribeEvent public void onConsuming(LivingEntityUseItemEvent.Finish e) { if(!(e.getEntityLiving() instanceof EntityPlayer)) { return; } handleEvent((EntityPlayer) e.getEntityLiving(), "item_use_finish", (qd) -> { qd.setEventVar("result_stack", e.getResultStack()); }); } @SubscribeEvent public void onCrafting(PlayerEvent.ItemCraftedEvent e) { handleEvent(e.player, "craft", (qd) -> { ScriptVars.setItemVars(qd, e.crafting); }); } @SubscribeEvent public void onItemDrop(ItemTossEvent e) { handleEvent(e.getPlayer(), "player_toss", (qd) -> { ScriptVars.setItemVars(qd, e.getEntityItem().getItem()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onItemPickup(EntityItemPickupEvent e) { handleEvent(e.getEntityPlayer(), "player_pickup", (qd) -> { ScriptVars.setEntityVars(qd, e.getItem()); ScriptVars.setItemVars(qd, e.getItem().getItem()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onVehicleEnter(EntityMountEvent e) { if(!(e.getEntityMounting() instanceof EntityPlayer)) { return; } EntityPlayer p = (EntityPlayer) e.getEntityMounting(); handleEvent(p, "entity_mount", (qd) -> { qd.setEventVar("mounting", e.isMounting()); ScriptVars.setEntityVars(qd, e.getEntityBeingMounted()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } @SubscribeEvent public void onPlayerUsePortal(PlayerEvent.PlayerChangedDimensionEvent e) { handleEvent(e.player, "portal", (qd) -> { qd.setEventVar("from", ModDimensions.getWorldName(e.fromDim)); qd.setEventVar("to", ModDimensions.getWorldName(e.toDim)); }); } @SubscribeEvent public void onCommand(CommandEvent e) { if(e.getSender() instanceof EntityPlayer) { handleEvent((EntityPlayer) e.getSender(), "command", (qd) -> { qd.setEventVar("command", e.getCommand().getName()); qd.setEventVar("args", Arrays.stream(e.getParameters()).map(s -> Code.convertInput(null, s, false)).collect(Collectors.toList())); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { e.setCanceled(qd.getBooleanVar("cancel")); }); } } public void onCustomCommand(EntityPlayer p, String command, String args) { handleEvent(p, "custom_command", (qd) -> { qd.setEventVar("command", command); if(args.length() == 0) { qd.setEventVar("args", new ArrayList<>()); return; } qd.setEventVar("args", Arrays.stream(args.trim().split(" ")).map(s -> Code.convertInput(null, s, false)).collect(Collectors.toList())); }); } public void onEffectUse(PlayerUsesEffectEvent e) { handleEvent(e.getPlayer(), "player_use_effect", (qd) -> { qd.setEventVar("power", new Fraction(e.getPower())); qd.setEventVar("mana_cost", new Fraction(e.getMana())); qd.setEventVar("cause", e.getCause().toString()); qd.setEventVar("effect", e.getEffect()); qd.setVar("cancel", e.isCanceled()); }, (qd) -> { try { int power = ScriptUtils.getInt(qd.getVar("power")); if(power < 1 || power > 20) { throw new IllegalArgumentException(); } e.setPower(power); } catch(Exception ex) { KajetansMod.scripts.logger.printException(ex, "player_use_effect", qd, qd.getActiveRealCodeLine()); } try { int mana = ScriptUtils.getInt(qd.getVar("mana_cost")); if(mana < 0) { throw new IllegalArgumentException(); } e.setMana(mana); } catch(Exception ex) { KajetansMod.scripts.logger.printException(ex, "player_use_effect", qd, qd.getActiveRealCodeLine()); } e.setCanceled(qd.getBooleanVar("cancel")); }); } public void onFunctionKey(EntityPlayerMP p, int key) { handleEvent(p, "function_key", (qd) -> { qd.setEventVar("key", new Fraction(key)); }); } /*@SubscribeEvent public void QuestVillagerPickUpItem(ItemTossEvent e) { EntityPlayer p = e.getPlayer(); PlayerScript qd = KajetansMod.scripts.getScript(p); if(qd == null || !qd.isLoadedEvent("villager_give")) { return; } final EntityItem itemEnt = e.getEntityItem(); KajetansMod.scheduler.scheduleTask(() -> { if(itemEnt.isDead) { return; } EntityVillager v = Utils.getNearestEntity(p.world, itemEnt.getPositionVector(), 2, EntityVillager.class); if(v == null) { return; } Script data = KajetansMod.scripts.getScript(p); if(data == null || !data.isLoadedEvent("villager_give")) { return; } data.setVar("event", "villager_give"); ScriptVars.setPlayerVars(data, p); ScriptVars.setItemVars(data, itemEnt.getItem()); data.setEventVar("villager_loc", new Location(v)); data.setEventVar("villager_prof", v.getProfessionForge().getRegistryName()); data.setVar("cancel", e.isCanceled()); data.runCode(); if(data.getBooleanVar("cancel")) { return; } itemEnt.setDead(); }, 40); }*/ }