package services; import java.nio.ByteOrder; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import main.NGECore; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import protocol.swg.ObjControllerMessage; import protocol.swg.OkMessage; import protocol.swg.PlayClientEffectObjectMessage; import protocol.swg.objectControllerObjects.BuffBuilderChangeMessage; import protocol.swg.objectControllerObjects.BuffBuilderEndMessage; import protocol.swg.objectControllerObjects.BuffBuilderStartMessage; import protocol.swg.objectControllerObjects.ImageDesignMessage; import resources.common.BuffBuilder; import resources.common.Console; import resources.common.IDAttribute; import resources.common.MathUtilities; import resources.common.ObjControllerOpcodes; import resources.common.Performance; import resources.common.PerformanceEffect; import resources.common.RGB; import resources.common.StringUtilities; import resources.datatables.Posture; import resources.objects.Buff; import resources.objects.BuffItem; import resources.objects.SWGList; import resources.objects.SkillMod; import resources.objects.creature.CreatureObject; import resources.objects.player.PlayerObject; import resources.objects.tangible.TangibleObject; import resources.visitors.IDManagerVisitor; import services.sui.SUIService.InputBoxType; import services.sui.SUIWindow; import services.sui.SUIWindow.SUICallback; import services.sui.SUIWindow.Trigger; import engine.clientdata.ClientFileManager; import engine.clientdata.visitors.DatatableVisitor; import engine.clients.Client; import engine.resources.objects.SWGObject; import engine.resources.service.INetworkDispatch; import engine.resources.service.INetworkRemoteEvent; public class EntertainmentService implements INetworkDispatch { private NGECore core; private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private Vector buffBuilderSkills = new Vector(); //FIXME: create a wrapper class for double key lookup maps private ConcurrentHashMap performances = new ConcurrentHashMap(); private ConcurrentHashMap performancesByIndex = new ConcurrentHashMap(); private ConcurrentHashMap danceMap = new ConcurrentHashMap(); private ConcurrentHashMap designMap = new ConcurrentHashMap(); private Random ranWorkshop = new Random(); private Map performanceEffects = new ConcurrentHashMap(); public EntertainmentService(NGECore core) { this.core = core; populateSkillCaps(); populatePerformanceTable(); populatePerformanceEffects(); registerCommands(); loadCharacterCustomizationData(); } @Override public void insertOpcodes(Map swgOpcodes, Map objControllerOpcodes) { objControllerOpcodes.put(ObjControllerOpcodes.IMAGE_DESIGN_CHANGE, new INetworkRemoteEvent() { @Override public void handlePacket(IoSession session, IoBuffer data) throws Exception { data.order(ByteOrder.LITTLE_ENDIAN); Client client = core.getClient(session); if(client == null) return; SWGObject sender = client.getParent(); if(sender == null) return; ImageDesignMessage sentPacket = new ImageDesignMessage(); sentPacket.deserialize(data); CreatureObject designTarget = (CreatureObject) core.objectService.getObject(sentPacket.getTargetId()); CreatureObject designer = (CreatureObject) core.objectService.getObject(sentPacket.getDesignerId()); if (designTarget == null || designer == null) return; if (designTarget.getClient() == null || designTarget.getClient().getSession() == null) return; if (designer.getClient() == null || designer.getClient().getSession() == null) return; Vector colorAttributes = sentPacket.getColorAttributes(); Vector bodyAttributes = sentPacket.getBodyAttributes(); // TODO: Attribute check for colors? if (bodyAttributes != null) { for (IDAttribute atr : bodyAttributes) { //System.out.println("ATTRIBUTE: " + atr.getName() + " value of " + atr.getFloatValue()); if (atr.getFloatValue() > 1f || atr.getFloatValue() < 0) { // RIP Height Exploit <3 return; } } } /*if (colorAttributes != null) { for (IDAttribute atr : colorAttributes) { System.out.println("COLOR ATTRIBUTE: " + atr.getName() + " value of " + atr.getValue()); } }*/ if (sentPacket.getTargetId() != sentPacket.getDesignerId()) { if (sentPacket.isCustomerAccepted() && sentPacket.isDesignerCommited()) { System.out.print("Both Accepted!"); sentPacket.setEndMessage(true); int fee = sentPacket.getMoneyDemanded() + sentPacket.getMoneyOffered(); if (fee > 0) { int payersCash = designTarget.getCashCredits(); if (payersCash < fee) { if (designTarget != designer) { ObjControllerMessage msg = new ObjControllerMessage(0x0B, sentPacket); designTarget.getClient().getSession().write(msg.serialize()); // Window is closed already, so this shouldn't be necessary //ObjControllerMessage designMsg = new ObjControllerMessage(0x0B, sentPacket); //designer.getClient().getSession().write(designMsg.serialize()); designTarget.sendSystemMessage("You don't have enough credits to pay the fee.", (byte) 0); designer.sendSystemMessage("Your target did not have the required payment fee", (byte) 0); } }else { // TODO: Apply customization designTarget.setCashCredits(designTarget.getCashCredits() - fee); designer.setCashCredits(designer.getCashCredits() + fee); handleImageDesign(designer, designTarget, colorAttributes, bodyAttributes, sentPacket.getHair(), sentPacket.getHoloEmote()); sentPacket.setEndMessage(true); sentPacket.setObjectId(designTarget.getObjectId()); ObjControllerMessage msg = new ObjControllerMessage(0x0B, sentPacket); designTarget.getClient().getSession().write(msg.serialize()); } } else { handleImageDesign(designer, designTarget, colorAttributes, bodyAttributes, sentPacket.getHair(), sentPacket.getHoloEmote()); sentPacket.setEndMessage(true); sentPacket.setObjectId(designTarget.getObjectId()); ObjControllerMessage msg = new ObjControllerMessage(0x0B, sentPacket); designTarget.getClient().getSession().write(msg.serialize()); } } else if (sentPacket.getObjectId() == designTarget.getObjectId()) { sentPacket.setObjectId(designer.getObjectId()); ObjControllerMessage msg = new ObjControllerMessage(0x0B, sentPacket); designer.getClient().getSession().write(msg.serialize()); } else if (sentPacket.getObjectId() == designer.getObjectId()) { sentPacket.setObjectId(designTarget.getObjectId()); ObjControllerMessage msg = new ObjControllerMessage(0x0B, sentPacket); designTarget.getClient().getSession().write(msg.serialize()); } } else { // No need to send a close packet message because the client automatically closes the ID window for the Desginer when they commit if (sentPacket.isDesignerCommited()) handleImageDesign(designer, designTarget, colorAttributes, bodyAttributes, sentPacket.getHair(), sentPacket.getHoloEmote()); else return; } } }); objControllerOpcodes.put(ObjControllerOpcodes.IMAGE_DESIGN_END, new INetworkRemoteEvent() { @Override public void handlePacket(IoSession session, IoBuffer data) throws Exception { data.order(ByteOrder.LITTLE_ENDIAN); Client client = core.getClient(session); if(client == null) return; SWGObject sender = client.getParent(); if(sender == null) return; ImageDesignMessage sentPacket = new ImageDesignMessage(); sentPacket.deserialize(data); if (sentPacket.getTargetId() == sentPacket.getDesignerId()) return; if (sentPacket.getObjectId() == sentPacket.getDesignerId()) { CreatureObject target = (CreatureObject) core.objectService.getObject(sentPacket.getTargetId()); if (target == null) return; sentPacket.setEndMessage(true); sentPacket.setObjectId(target.getObjectId()); ObjControllerMessage endMessage = new ObjControllerMessage(0x0B, sentPacket); target.getClient().getSession().write(endMessage.serialize()); } else if (sentPacket.getObjectId() == sentPacket.getTargetId()) { CreatureObject designer = (CreatureObject) core.objectService.getObject(sentPacket.getDesignerId()); if (designer == null) return; sentPacket.setEndMessage(true); sentPacket.setObjectId(designer.getObjectId()); ObjControllerMessage endMessage = new ObjControllerMessage(0x0B, sentPacket); designer.getClient().getSession().write(endMessage.serialize()); } } }); objControllerOpcodes.put(ObjControllerOpcodes.BUFF_BUILDER_END, new INetworkRemoteEvent() { @Override public void handlePacket(IoSession session, IoBuffer data) throws Exception { data.order(ByteOrder.LITTLE_ENDIAN); Client client = core.getClient(session); if(client == null) return; SWGObject sender = client.getParent(); if(sender == null) return; BuffBuilderEndMessage sentPacket = new BuffBuilderEndMessage(); sentPacket.deserialize(data); SWGObject buffer = core.objectService.getObject(sentPacket.getBufferId()); SWGObject buffRecipient = core.objectService.getObject(sentPacket.getBuffRecipientId()); if(buffer == null || buffRecipient == null) return; // No need to send end packet when buffing yourself. if (buffRecipient != buffer) { if(buffRecipient.getClient() == null || buffRecipient.getClient().getSession() == null) return; if (sender.getObjectId() == buffer.getObjectId()) { // send close packet to recipient BuffBuilderEndMessage end = new BuffBuilderEndMessage(sentPacket); end.setObjectId(buffRecipient.getObjectId()); end.setBufferId(buffer.getObjectId()); end.setBuffRecipientId(buffRecipient.getObjectId()); ObjControllerMessage closeMessage = new ObjControllerMessage(0x0B, end); buffRecipient.getClient().getSession().write(closeMessage.serialize()); } if (sender.getObjectId() == buffRecipient.getObjectId()) { // send close packet to buffer BuffBuilderEndMessage end = new BuffBuilderEndMessage(sentPacket); end.setObjectId(buffer.getObjectId()); end.setBufferId(buffer.getObjectId()); end.setBuffRecipientId(buffRecipient.getObjectId()); ObjControllerMessage closeMessage = new ObjControllerMessage(0x0B, end); buffer.getClient().getSession().write(closeMessage.serialize()); CreatureObject cre = (CreatureObject) buffer; cre.sendSystemMessage("The buff recipient cancelled the buff builder session.", (byte) 0); } } } }); objControllerOpcodes.put(ObjControllerOpcodes.BUFF_BUILDER_CHANGE, new INetworkRemoteEvent() { @Override public void handlePacket(IoSession session, IoBuffer data) throws Exception { data.order(ByteOrder.LITTLE_ENDIAN); Client client = core.getClient(session); if(client == null) return; SWGObject sender = client.getParent(); if(sender == null) return; BuffBuilderChangeMessage sentPacket = new BuffBuilderChangeMessage(); sentPacket.deserialize(data); Vector statBuffs = sentPacket.getStatBuffs(); CreatureObject buffer = (CreatureObject) core.objectService.getObject(sentPacket.getBufferId()); CreatureObject buffRecipient = (CreatureObject) core.objectService.getObject(sentPacket.getBuffRecipientId()); if (buffer != buffRecipient) { BuffBuilderChangeMessage changeMessage = new BuffBuilderChangeMessage(); changeMessage.setBuffCost(sentPacket.getBuffCost()); changeMessage.setTime(sentPacket.getTime()); changeMessage.setBufferId(buffer.getObjectId()); changeMessage.setBuffRecipientId(buffRecipient.getObjectId()); if (!statBuffs.isEmpty()) changeMessage.setStatBuffs(statBuffs); if (sentPacket.getAccepted() == true && sentPacket.getBuffRecipientAccepted() == 1) { if (sentPacket.getBuffCost() > 0) { if (buffRecipient.getCashCredits() >= sentPacket.getBuffCost()) { buffRecipient.setCashCredits(buffRecipient.getCashCredits() - sentPacket.getBuffCost()); } else { return; } } giveInspirationBuff(buffRecipient, buffer, statBuffs); BuffBuilderEndMessage endBuilder = new BuffBuilderEndMessage(changeMessage); endBuilder.setObjectId(buffer.getObjectId()); BuffBuilderEndMessage endRecipient = new BuffBuilderEndMessage(changeMessage); endRecipient.setObjectId(buffRecipient.getObjectId()); ObjControllerMessage closeBuilder = new ObjControllerMessage(0x0B, endBuilder); buffer.getClient().getSession().write(closeBuilder.serialize()); ObjControllerMessage closeRecipient = new ObjControllerMessage(0x0B, endRecipient); buffRecipient.getClient().getSession().write(closeRecipient.serialize()); } else if (sentPacket.getAccepted() == true && sentPacket.getBuffRecipientAccepted() == 0) { changeMessage.setAccepted(true); changeMessage.setObjectId(buffRecipient.getObjectId()); ObjControllerMessage objMsg = new ObjControllerMessage(0x0B, changeMessage); buffRecipient.getClient().getSession().write(objMsg.serialize()); } else { changeMessage.setAccepted(false); changeMessage.setObjectId(sentPacket.getBuffRecipientId()); ObjControllerMessage objMsg = new ObjControllerMessage(0x0B, changeMessage); buffRecipient.getClient().getSession().write(objMsg.serialize()); } } else { if (sentPacket.getAccepted() == true) { giveInspirationBuff(buffRecipient, buffer, statBuffs); BuffBuilderEndMessage endBuilder = new BuffBuilderEndMessage(sentPacket); endBuilder.setObjectId(buffer.getObjectId()); ObjControllerMessage objMsg = new ObjControllerMessage(0x0B, endBuilder); buffRecipient.getClient().getSession().write(objMsg.serialize()); } } } }); } private void populatePerformanceTable() { try { DatatableVisitor PerformanceVisitor = ClientFileManager.loadFile("datatables/performance/performance.iff", DatatableVisitor.class); //rformanceVisitor. for (int r = 0; r < PerformanceVisitor.getRowCount(); r++) { Performance p = new Performance(); /* for (int j=0; j < PerformanceVisitor.getColumnCount(); j++) { System.out.println(j + ": " + PerformanceVisitor.getObject(r, j)); }*/ p.setPerformanceName( ( (String) PerformanceVisitor.getObject(r, 0) ).toLowerCase()); p.setInstrumentAudioId((int) PerformanceVisitor.getObject(r, 1)); p.setRequiredSong((String) PerformanceVisitor.getObject(r, 2)); p.setRequiredInstrument((String) PerformanceVisitor.getObject(r, 3)); p.setRequiredDance((String) PerformanceVisitor.getObject(r, 4)); p.setDanceVisualId((int) PerformanceVisitor.getObject(r, 5)); p.setActionPointsPerLoop((int) PerformanceVisitor.getObject(r, 6)); p.setLoopDuration((float) PerformanceVisitor.getObject(r, 7)); p.setType((int) PerformanceVisitor.getObject(r, 8)); p.setBaseXp((int) PerformanceVisitor.getObject(r, 9)); p.setFlourishXpMod((int) PerformanceVisitor.getObject(r, 10)); p.setHealMindWound((int) PerformanceVisitor.getObject(r, 11)); p.setHealShockWound((int) PerformanceVisitor.getObject(r, 12)); p.setRequiredSkillMod((String) PerformanceVisitor.getObject(r, 13)); p.setRequiredSkillModValue((int) PerformanceVisitor.getObject(r, 14)); p.setMainloop((String) PerformanceVisitor.getObject(r, 15)); p.setFlourish1((String) PerformanceVisitor.getObject(r, 16)); p.setFlourish2((String) PerformanceVisitor.getObject(r, 17)); p.setFlourish3((String) PerformanceVisitor.getObject(r, 18)); p.setFlourish4((String) PerformanceVisitor.getObject(r, 19)); p.setFlourish5((String) PerformanceVisitor.getObject(r, 20)); p.setFlourish6((String) PerformanceVisitor.getObject(r, 21)); p.setFlourish7((String) PerformanceVisitor.getObject(r, 22)); p.setFlourish8((String) PerformanceVisitor.getObject(r, 23)); p.setIntro((String) PerformanceVisitor.getObject(r, 24)); p.setOutro((String) PerformanceVisitor.getObject(r, 25)); p.setLineNumber(r); if (p.getType() == -1788534963) { danceMap.put(new Integer(p.getDanceVisualId()), p); } performances.put(p.getPerformanceName(), p); performancesByIndex.put(r, p); } } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } private void populateSkillCaps() { try { DatatableVisitor buffBuilder = ClientFileManager.loadFile("datatables/buff/buff_builder.iff", DatatableVisitor.class); for (int r = 0; r < buffBuilder.getRowCount(); r++) { String skillName = ((String) buffBuilder.getObject(r, 0)); String category = ((String) buffBuilder.getObject(r, 1)); String statAffects = ((String) buffBuilder.getObject(r, 2)); int maxTimes = ((int) buffBuilder.getObject(r, 3)); int cost = ((int) buffBuilder.getObject(r, 4)); int affectAmount = ((int) buffBuilder.getObject(r, 5)); String requiredExpertise = ((String) buffBuilder.getObject(r, 6)); BuffBuilder item = new BuffBuilder(); item.setStatName(skillName); item.setCategory(category); item.setStatAffects(statAffects); item.setMaxTimesApplied(maxTimes); item.setCost(cost); item.setAffectAmount(affectAmount); item.setRequiredExpertise(requiredExpertise); buffBuilderSkills.add(item); } } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } private void populatePerformanceEffects() { try { DatatableVisitor effects = ClientFileManager.loadFile("datatables/performance/perform_effect.iff", DatatableVisitor.class); for (int r = 0; r < effects.getRowCount(); r++) { String effectName = ((String) effects.getObject(r, 0)); String performanceType = ((String) effects.getObject(r, 1)); int requiredSkillModValue = ((int) effects.getObject(r, 2)); Boolean requiredPerforming = ((Boolean) effects.getObject(r, 3)); int targetType = ((int) effects.getObject(r, 4)); float effectDuration = ((float) effects.getObject(r, 5)); int effectActionCost = ((int) effects.getObject(r, 6)); PerformanceEffect item = new PerformanceEffect(); item.setEffectActionCost(effectActionCost); item.setEffectDuration(effectDuration); item.setName(effectName); item.setPerformanceType(performanceType); item.setRequiredPerforming(requiredPerforming); item.setRequiredSkillModValue(requiredSkillModValue); item.setTargetType(targetType); performanceEffects.put(effectName.toLowerCase(), item); } } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } private void registerCommands() { core.commandService.registerCommand("bandflourish"); core.commandService.registerCommand("flourish"); core.commandService.registerAlias("flo","flourish"); core.commandService.registerCommand("groupdance"); core.commandService.registerCommand("startdance"); core.commandService.registerCommand("stopdance"); core.commandService.registerCommand("watch"); //core.commandService.registerCommand("stopwatching"); // SWGList error core.commandService.registerCommand("holoEmote"); core.commandService.registerCommand("covercharge"); //core.commandService.registerCommand("en_holographic_recall"); //core.commandService.registerCommand("en_holographic_image"); core.commandService.registerCommand("imagedesign"); // TODO: Add /bandsolo, /bandpause, /changeBandMusic, /changeDance, /changeGroupDance, /changeMusic // Entertainer Effects core.commandService.registerCommand("centerStage"); core.commandService.registerCommand("colorSwirl"); core.commandService.registerCommand("colorlights"); core.commandService.registerCommand("floorLights"); // referred to also as Dance Floor core.commandService.registerCommand("dazzle"); core.commandService.registerCommand("distract"); core.commandService.registerCommand("featuredSolo"); core.commandService.registerCommand("firejet"); core.commandService.registerCommand("firejet2"); core.commandService.registerCommand("laserShow"); core.commandService.registerCommand("smokebomb"); core.commandService.registerCommand("spotlight"); core.commandService.registerCommand("ventriloquism"); } public void giveInspirationBuff(CreatureObject reciever, CreatureObject buffer, Vector buffVector) { Vector availableStats = buffBuilderSkills; Vector stats = new Vector(); for (BuffItem item : buffVector) { for(BuffBuilder builder : availableStats) { if(builder.getStatName().equalsIgnoreCase(item.getSkillName())) { if(builder.getMaxTimesApplied() < item.getInvested()) return; // Ent. Expertise Percent + (invested points * affect amount) = stat int bonusPoints = (int) (builder.getAffectAmount() *((float) item.getEntertainerBonus()/100f)); int affectTotal = bonusPoints + (item.getInvested() * builder.getAffectAmount()); BuffItem stat = new BuffItem(builder.getStatAffects(), item.getInvested(), item.getEntertainerBonus()); stat.setAffectAmount(affectTotal); stats.add(stat); } } } reciever.setAttachment("buffWorkshop", stats); if (buffer == reciever) reciever.setAttachment("inspireDuration", 215); //PlayerObject rPlayer = (PlayerObject) reciever.getSlottedObject("ghost"); //long timeStamp = 0; //if (reciever.getAttachment("buffWorkshopTimestamp") != null) //timeStamp = (long) reciever.getAttachment("buffWorkshopTimestamp"); core.buffService.addBuffToCreature(reciever, "buildabuff_inspiration", buffer); /*if (core.buffService.addBuffToCreature(reciever, "buildabuff_inspiration", buffer) && !rPlayer.getProfession().equals("entertainer_1a")) { if (timeStamp == 0 || (System.currentTimeMillis() - timeStamp > 86400000)) { float random = ranWorkshop.nextFloat(); if (random < 0.75f) { if(rPlayer.getProfession().contains("trader")) { core.collectionService.addCollection(buffer, "prof_trader"); } else { core.collectionService.addCollection(buffer, rPlayer.getProfession()); } } } else { buffer.sendSystemMessage("@collection:buffed_too_soon", (byte) 0); } reciever.setAttachment("buffWorkshopTimestamp", System.currentTimeMillis()); }*/ } public int getDanceVisualId(String danceName) { Performance p = performances.get(danceName); //if 0 , then it's no dance. need to handle that in the script. return ((p == null) ? 0 : p.getDanceVisualId()); } public Map getAvailableDances(CreatureObject actor) { Map dances = new HashMap(); for (int index : danceMap.keySet()) { if (!canDance(actor, danceMap.get(index) )) { continue; } dances.put( new Long( danceMap.get(index).getDanceVisualId() ), danceMap.get(index).getPerformanceName() ); } return dances; } public boolean isDance(int visualId) { return ( danceMap.get(visualId) != null ) ; } public boolean canDance(CreatureObject actor, Performance dance) { if (actor.hasAbility(dance.getRequiredDance())) { return true; } return false; } public boolean canDance(CreatureObject actor, int visualId) { if (!isDance(visualId)) { return false; } return canDance(actor, danceMap.get(visualId)); } public Performance getDance(int visualId) { return danceMap.get(visualId); } public Performance getPerformance(String name) { return performances.get(name); } public Performance getPerformanceByIndex(int index) { return performancesByIndex.get(index); } public Map getPerformanceEffects() { return performanceEffects; } public void startPerformance(CreatureObject actor, int performanceId, int performanceCounter, String skillName, boolean isDance) { actor.setPerformanceId(performanceId, isDance); actor.setPerformanceCounter(performanceCounter); actor.setCurrentAnimation(skillName); actor.setPerformanceType(isDance); actor.startPerformance(); } public void startPerformanceExperience(final CreatureObject entertainer) { final ScheduledFuture experienceTask = scheduler.scheduleAtFixedRate(() -> { Performance p = performancesByIndex.get(entertainer.getPerformanceId()); if (p == null) { entertainer.setFlourishCount(0); return; } int floXP = p.getFlourishXpMod(); int floCount = entertainer.getFlourishCount(); //FIXME: this is not an accurate implementation yet. It needs group bonuses and other things. int XP = (int) Math.round( ((floCount > 2) ? 2 : floCount) * floXP * 3.8 ); entertainer.setFlourishCount(0); core.playerService.giveExperience(entertainer, XP); }, 10000, 10000, TimeUnit.MILLISECONDS); entertainer.setEntertainerExperience(experienceTask); } public void startSpectating(final CreatureObject spectator, final CreatureObject performer, boolean spectateType) { // visual if (spectator.getPerformanceWatchee() == performer && spectateType) spectator.getPerformanceWatchee().removeAudience(spectator); // music else if (spectator.getPerformanceListenee() == performer && !spectateType) spectator.getPerformanceListenee().removeAudience(spectator); spectator.setPerformanceWatchee(performer); performer.addAudience(spectator); spectator.setMoodAnimation("entertained"); final ScheduledFuture spectatorTask = scheduler.scheduleAtFixedRate(() -> { if (spectator.getPosition().getDistance2D(performer.getWorldPosition()) > (float) 70) { if(((performer.getPerformanceType()) ? "dance" : "music").equals("dance")) { spectator.setPerformanceWatchee(null); spectator.sendSystemMessage("You stop watching " + performer.getCustomName() + " because " + performer.getCustomName() + " is out of range.", (byte) 0); } else { spectator.setPerformanceListenee(null); spectator.sendSystemMessage("You stop listening to " + performer.getCustomName() + " because " + performer.getCustomName() + " is out of range.", (byte) 0); } spectator.setMoodAnimation("neutral"); performer.removeAudience(spectator); if (spectator.getInspirationTick().cancel(true)) spectator.getSpectatorTask().cancel(true); } }, 2, 2, TimeUnit.SECONDS); spectator.setSpectatorTask(spectatorTask); if(((PlayerObject)performer.getSlottedObject("ghost")).getProfession().equals("entertainer_1a")) { handleInspirationTicks(spectator, performer); } if(spectateType) spectator.sendSystemMessage("You start watching " + performer.getCustomName() + ".", (byte) 0); else spectator.sendSystemMessage("You start listening to " + performer.getCustomName() + ".", (byte) 0); } public void performFlourish(final CreatureObject performer, int flourish) { if (performer.getFlourishCount() > 0 || performer.isPerformingFlourish()) { performer.sendSystemMessage("@performance:flourish_wait_self", (byte) 0); return; } Performance performance = getPerformanceByIndex(performer.getPerformanceId()); if(performance == null) return; String anmFlo = "skill_action_" + flourish; if (flourish == 9) anmFlo = "mistake"; performer.setFlourishCount(1); performer.setPerformingFlourish(true); performer.sendSystemMessage("@performance:flourish_perform", (byte) 0); performer.doSkillAnimation(anmFlo); scheduler.schedule(() -> { performer.setFlourishCount(0); performer.setPerformingFlourish(true); }, (long) performance.getLoopDuration(), TimeUnit.SECONDS); } public boolean performEffect(final CreatureObject performer, String command, String effect, TangibleObject target) { PerformanceEffect pEffect = performanceEffects.get(command.toLowerCase()); if(pEffect == null) return false; String performance = (performer.getPerformanceType()) ? "dance" : "music"; if(performer.isPerformingEffect()) { performer.sendSystemMessage("@performance:effect_wait_self", (byte) 0); return false; } if (performer.getPosture() != Posture.SkillAnimating) { performer.sendSystemMessage("@performance:effect_not_performing", (byte) 0); return false; } if(performance.equals("dance") && !pEffect.isDance() || performance.equals("music") && !pEffect.isMusic()) { performer.sendSystemMessage("@performance:effect_not_performing_correct", (byte) 0); return false; } if(performer.getAction() < pEffect.getEffectActionCost()) { performer.sendSystemMessage("@performance:effect_too_tired", (byte) 0); return false; } performer.setAction(performer.getAction() - pEffect.getEffectActionCost()); performer.setPerformingEffect(true); if(target != null) target.playEffectObject(effect, ""); else performer.playEffectObject(effect, ""); scheduler.schedule(() -> { performer.setPerformingEffect(false); }, (long) pEffect.getEffectDuration(), TimeUnit.SECONDS); return true; } public void handleInspirationTicks(final CreatureObject spectator, final CreatureObject performer) { // http://youtu.be/WqyAde-oC7o?t=11m14s << Player watching entertainer (Has ring only, no pet, + 15 min ticks) // TODO: Camp/Cantina checks for expertise and duration bonus %. Right now only using basic values. final ScheduledFuture inspirationTick = scheduler.scheduleAtFixedRate(() -> { int time = 0; // current buff duration time (minutes) int buffCap = 215; // 5 hours 35 minutes - 2 hours (buff duration increase bonus) << Taken from video, doesn't account for performance bonuses etc. if (spectator.getAttachment("inspireDuration") != null) time+= (int) spectator.getAttachment("inspireDuration"); if (performer.getSkillMod("expertise_en_inspire_buff_duration_increase") != null) { SkillMod durationMod = performer.getSkillMod("expertise_en_inspire_buff_duration_increase"); buffCap += durationMod.getBase() + durationMod.getModifier(); } if (time >= buffCap) { spectator.setAttachment("inspireDuration", buffCap); // incase someone went over cap spectator.getInspirationTick().cancel(true); } else { int entTick = 10; if (performer.getSkillMod("expertise_en_inspire_pulse_duration_increase") != null) { SkillMod pulseMod = performer.getSkillMod("expertise_en_inspire_pulse_duration_increase"); entTick += pulseMod.getBase() + pulseMod.getModifier(); } int duration = (time + entTick); // minutes int hMinutes = MathUtilities.secondsToHourMinutes(duration * 60); int hours = MathUtilities.secondsToWholeHours(duration * 60); spectator.showFlyText("spam", "buff_duration_tick_observer", String.valueOf(hours) + " hours , " + hMinutes + " minutes ", 0, (float) 0.66, new RGB(255, 182, 193), 3, 78); spectator.setAttachment("inspireDuration", duration); //System.out.println("Inspire Duration: " + spectator.getAttachment("inspireDuration") + " on " + spectator.getCustomName()); } }, 10, 10, TimeUnit.SECONDS); spectator.setInspirationTick(inspirationTick); } public void handleCoverCharge(final CreatureObject actor, final CreatureObject performer) { final int charge = performer.getCoverCharge(); if (charge == 0) return; else { SUIWindow notification = core.suiService.createMessageBox(InputBoxType.INPUT_BOX_OK, "Cover Charge", performer.getCustomName() + " has a cover charge of " + performer.getCoverCharge() + ". Do you wish to pay it?", actor, performer, (float) 30); Vector returnParams = new Vector(); returnParams.add("btnOk:Text"); notification.addHandler(0, "", Trigger.TRIGGER_OK, returnParams, (owner, eventType, returnList) -> { if (eventType == 0) { if (charge > actor.getCashCredits()) { actor.sendSystemMessage("You do not have enough credits to cover the charge.", (byte) 0); // TODO: Find the message in the STF files. return; } else{ actor.setCashCredits(actor.getCashCredits() - charge); actor.sendSystemMessage("You payed the cover charge of " + charge + " to " + performer.getCustomName(), (byte) 0); // TODO: Find the message in the STF files. performer.setCashCredits(performer.getCashCredits() + charge); startSpectating(actor, performer, performer.getPerformanceType()); } } }); core.suiService.openSUIWindow(notification); } } private void handleImageDesign(CreatureObject designer, CreatureObject designTarget, Vector colorAttributes, Vector bodyAttributes, String hairTemplate, String holoEmote) { if (hairTemplate != null && !hairTemplate.equals("")) { if(!hairTemplate.startsWith("object/tangible/hair/")) return; String sharedHairTemplate = hairTemplate.replace("/hair_", "/shared_hair_"); TangibleObject hairObject = (TangibleObject) core.objectService.createObject(sharedHairTemplate, designTarget.getPlanet()); if (hairObject == null) return; TangibleObject oldHair = (TangibleObject) designTarget.getSlottedObject("hair"); if (oldHair == null) return; core.objectService.destroyObject(oldHair); designTarget.add(hairObject); designTarget.addObjectToEquipList(hairObject); } if (bodyAttributes != null) { for (IDAttribute atr : bodyAttributes) { if (atr.getName().equals("height")) designTarget.setHeight(atr.getFloatValue()); // TODO: Body Attributes } } if (colorAttributes != null) { for (IDAttribute atr : colorAttributes) { if (atr.getName().equals("color_hair")){ // TODO: Color Attributes } } } if (holoEmote != null) { PlayerObject player = (PlayerObject) designTarget.getSlottedObject("ghost"); if (player != null) { player.setHoloEmote(holoEmote); player.setHoloEmoteUses(20); designTarget.sendSystemMessage("@image_designer:new_holoemote", (byte) 0); } } } private void loadCharacterCustomizationData() { try { IDManagerVisitor visitor = ClientFileManager.loadFile("customization/customization_id_manager.iff", IDManagerVisitor.class); designMap = visitor.getCustomizationMap(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } public String getCustomizationString(int number){ synchronized (designMap) { for (Entry entry : designMap.entrySet()) { if (entry.getValue().intValue() == number) { return entry.getKey(); } } } return ""; } public int getCustomizationValue(String customizationString) { synchronized(designMap){ for(Entry entry : designMap.entrySet()) { if (entry.getKey().equals(customizationString)) { return entry.getValue().intValue(); } } } return 0; } @Override public void shutdown() { } }