From dca9ce6cf77ed378394e424ed5c5d4130dc37ed4 Mon Sep 17 00:00:00 2001 From: Ziggy Date: Fri, 8 Jan 2021 22:21:08 +0100 Subject: [PATCH] Newly looted items with a value are now sellable to junk dealers #307 --- .../junk_dealer_generic/accept.json | 4 + .../gameplay/junkdealer/JunkDealerIntents.kt | 39 +++ .../events/ShowSellableItemsEvent.java | 41 +++ .../server_info/loader/StaticItemLoader.kt | 3 +- .../conversation/ConversationLoader.java | 8 +- .../events/ShowSellableItemsEventParser.java | 40 +++ .../support/objects/StaticItemCreator.kt | 12 +- .../support/objects/swg/ServerAttribute.java | 3 +- .../services/gameplay/GameplayManager.java | 2 + .../junkdealer/JunkDealerService.java | 243 ++++++++++++++++++ 10 files changed, 386 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/projectswg/holocore/intents/gameplay/junkdealer/JunkDealerIntents.kt create mode 100644 src/main/java/com/projectswg/holocore/resources/gameplay/conversation/events/ShowSellableItemsEvent.java create mode 100644 src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/events/ShowSellableItemsEventParser.java create mode 100644 src/main/java/com/projectswg/holocore/services/gameplay/junkdealer/JunkDealerService.java diff --git a/serverdata/nge/conversation/junk_dealer_generic/accept.json b/serverdata/nge/conversation/junk_dealer_generic/accept.json index fad8913e1..7a79bb42a 100644 --- a/serverdata/nge/conversation/junk_dealer_generic/accept.json +++ b/serverdata/nge/conversation/junk_dealer_generic/accept.json @@ -12,6 +12,10 @@ "args": { "animation": "thank" } + }, + { + "type": "show_sellable_items", + "args": {} } ] } \ No newline at end of file diff --git a/src/main/java/com/projectswg/holocore/intents/gameplay/junkdealer/JunkDealerIntents.kt b/src/main/java/com/projectswg/holocore/intents/gameplay/junkdealer/JunkDealerIntents.kt new file mode 100644 index 000000000..8b9183254 --- /dev/null +++ b/src/main/java/com/projectswg/holocore/intents/gameplay/junkdealer/JunkDealerIntents.kt @@ -0,0 +1,39 @@ +/*********************************************************************************** + * Copyright (c) 2021 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * --------------------------------------------------------------------------------* + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + ***********************************************************************************/ + +@file:Suppress("NOTHING_TO_INLINE") +package com.projectswg.holocore.intents.gameplay.junkdealer + +import com.projectswg.holocore.resources.support.global.player.Player +import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject +import me.joshlarson.jlcommon.control.Intent + +data class SellItemsIntent(val player: Player, val npc: AIObject): Intent() { + companion object { + @JvmStatic inline fun broadcast(player: Player, npc: AIObject) = SellItemsIntent(player, npc).broadcast() + } +} diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/events/ShowSellableItemsEvent.java b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/events/ShowSellableItemsEvent.java new file mode 100644 index 000000000..8f79657e6 --- /dev/null +++ b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/events/ShowSellableItemsEvent.java @@ -0,0 +1,41 @@ +/*********************************************************************************** + * Copyright (c) 2021 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * --------------------------------------------------------------------------------* + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + ***********************************************************************************/ +package com.projectswg.holocore.resources.gameplay.conversation.events; + +import com.projectswg.holocore.intents.gameplay.junkdealer.SellItemsIntent; +import com.projectswg.holocore.resources.gameplay.conversation.model.Event; +import com.projectswg.holocore.resources.support.global.player.Player; +import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject; + +public class ShowSellableItemsEvent implements Event { + + @Override + public void trigger(Player player, AIObject npc) { + SellItemsIntent.broadcast(player, npc); + } + +} diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/StaticItemLoader.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/StaticItemLoader.kt index a8ff9e0d5..922181352 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/StaticItemLoader.kt +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/StaticItemLoader.kt @@ -168,7 +168,8 @@ class StaticItemLoader internal constructor() : DataLoader() { val noTrade: Boolean = set.getInt("no_trade") != 0L val bioLink: Boolean = set.getInt("bio_link") != 0L val charges: Int = set.getInt("charges").toInt() - + val value: Int = set.getInt("value").toInt() + } class CostumeItemInfo(set: SdbResultSet) { diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/ConversationLoader.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/ConversationLoader.java index ef8251466..192946c73 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/ConversationLoader.java +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/ConversationLoader.java @@ -33,11 +33,8 @@ import com.projectswg.holocore.resources.gameplay.conversation.model.PlayerRespo import com.projectswg.holocore.resources.gameplay.conversation.model.Requirement; import com.projectswg.holocore.resources.support.data.server_info.SdbLoader; import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader; -import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.events.ChangePlayerFactionEventParser; -import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.events.NpcAnimationEventParser; -import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.events.PlayerAnimationEventParser; -import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.requirements.FactionNameRequirementParser; -import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.requirements.FactionStatusRequirementParser; +import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.events.*; +import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.requirements.*; import me.joshlarson.jlcommon.log.Log; import me.joshlarson.json.JSON; import me.joshlarson.json.JSONObject; @@ -198,6 +195,7 @@ public class ConversationLoader extends DataLoader { eventParserMap.put("faction_change", new ChangePlayerFactionEventParser()); eventParserMap.put("player_animation", new PlayerAnimationEventParser()); eventParserMap.put("npc_animation", new NpcAnimationEventParser()); + eventParserMap.put("show_sellable_items", new ShowSellableItemsEventParser()); } private void loadSpawnToConversations() throws IOException { diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/events/ShowSellableItemsEventParser.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/events/ShowSellableItemsEventParser.java new file mode 100644 index 000000000..790f247c8 --- /dev/null +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/events/ShowSellableItemsEventParser.java @@ -0,0 +1,40 @@ +/*********************************************************************************** + * Copyright (c) 2018 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * --------------------------------------------------------------------------------* + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + ***********************************************************************************/ +package com.projectswg.holocore.resources.support.data.server_info.loader.conversation.events; + +import com.projectswg.holocore.resources.gameplay.conversation.events.ShowSellableItemsEvent; +import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.EventParser; + +import java.util.Map; + +public class ShowSellableItemsEventParser implements EventParser { + + @Override + public ShowSellableItemsEvent parse(Map args) { + return new ShowSellableItemsEvent(); + } +} diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/StaticItemCreator.kt b/src/main/java/com/projectswg/holocore/resources/support/objects/StaticItemCreator.kt index 219211141..ba2633293 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/StaticItemCreator.kt +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/StaticItemCreator.kt @@ -181,10 +181,14 @@ object StaticItemCreator { if (info == null) return - if (info.charges != 0) { + if (info.charges > 0) { obj.addAttribute("charges", info.charges.toString()) obj.counter = info.charges } + + if (info.value > 0) { + obj.setServerAttribute(ServerAttribute.ITEM_VALUE, info.value) + } } @Suppress("UNUSED_PARAMETER") @@ -225,11 +229,15 @@ object StaticItemCreator { if (info == null) return - if (info.charges != 0) { + if (info.charges > 0) { obj.addAttribute("charges", info.charges.toString()) obj.counter = info.charges } + if (info.value > 0) { + obj.setServerAttribute(ServerAttribute.ITEM_VALUE, info.value) + } + applyColors(obj, info.color) } diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/ServerAttribute.java b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/ServerAttribute.java index 6a1f3f51a..4bf411b55 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/ServerAttribute.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/ServerAttribute.java @@ -38,7 +38,8 @@ public enum ServerAttribute { GALACTIC_RESOURCE_ID("resources.galactic_resource_id", PredefinedDataType.LONG), SURVEY_TOOL_RANGE ("survey_tool.range", PredefinedDataType.INT), SET_BONUS_ID ("set_bonus.id", PredefinedDataType.INT), - LINK_OBJECT_ID ("link.object.id", PredefinedDataType.LONG); + LINK_OBJECT_ID ("link.object.id", PredefinedDataType.LONG), + ITEM_VALUE ("item.value", PredefinedDataType.INT); private static final EnumLookup KEY_LOOKUP = new EnumLookup<>(ServerAttribute.class, ServerAttribute::getKey); diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/GameplayManager.java b/src/main/java/com/projectswg/holocore/services/gameplay/GameplayManager.java index 28005fadd..f35ae1b5a 100644 --- a/src/main/java/com/projectswg/holocore/services/gameplay/GameplayManager.java +++ b/src/main/java/com/projectswg/holocore/services/gameplay/GameplayManager.java @@ -7,6 +7,7 @@ import com.projectswg.holocore.services.gameplay.entertainment.EntertainmentMana import com.projectswg.holocore.services.gameplay.faction.FactionManager; import com.projectswg.holocore.services.gameplay.gcw.GalacticCivilWarManager; import com.projectswg.holocore.services.gameplay.jedi.JediManager; +import com.projectswg.holocore.services.gameplay.junkdealer.JunkDealerService; import com.projectswg.holocore.services.gameplay.player.PlayerManager; import com.projectswg.holocore.services.gameplay.structures.StructuresManager; import com.projectswg.holocore.services.gameplay.world.WorldManager; @@ -21,6 +22,7 @@ import me.joshlarson.jlcommon.control.ManagerStructure; FactionManager.class, GalacticCivilWarManager.class, JediManager.class, + JunkDealerService.class, PlayerManager.class, StructuresManager.class, WorldManager.class diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/junkdealer/JunkDealerService.java b/src/main/java/com/projectswg/holocore/services/gameplay/junkdealer/JunkDealerService.java new file mode 100644 index 000000000..715d995c8 --- /dev/null +++ b/src/main/java/com/projectswg/holocore/services/gameplay/junkdealer/JunkDealerService.java @@ -0,0 +1,243 @@ +/*********************************************************************************** + * Copyright (c) 2021 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * --------------------------------------------------------------------------------* + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + ***********************************************************************************/ +package com.projectswg.holocore.services.gameplay.junkdealer; + +import com.projectswg.common.data.encodables.oob.ProsePackage; +import com.projectswg.common.data.encodables.oob.StringId; +import com.projectswg.common.data.location.Location; +import com.projectswg.common.data.location.Terrain; +import com.projectswg.common.data.sui.SuiEvent; +import com.projectswg.holocore.intents.gameplay.junkdealer.SellItemsIntent; +import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent; +import com.projectswg.holocore.intents.support.objects.swg.DestroyObjectIntent; +import com.projectswg.holocore.intents.support.objects.swg.MoveObjectIntent; +import com.projectswg.holocore.resources.support.global.player.Player; +import com.projectswg.holocore.resources.support.global.zone.sui.SuiButtons; +import com.projectswg.holocore.resources.support.global.zone.sui.SuiListBox; +import com.projectswg.holocore.resources.support.global.zone.sui.SuiMessageBox; +import com.projectswg.holocore.resources.support.global.zone.sui.SuiWindow; +import com.projectswg.holocore.resources.support.objects.swg.SWGObject; +import com.projectswg.holocore.resources.support.objects.swg.ServerAttribute; +import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject; +import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject; +import com.projectswg.holocore.resources.support.objects.swg.tangible.TangibleObject; +import me.joshlarson.jlcommon.control.IntentHandler; +import me.joshlarson.jlcommon.control.Service; + +import java.util.*; + +public class JunkDealerService extends Service { + + private static final int SUI_ALLOWED_DISTANCE = 7; + + private final Map suiWindowMap; + + public JunkDealerService() { + suiWindowMap = new HashMap<>(); + } + + @IntentHandler + private void handleSellItemsIntent(SellItemsIntent intent) { + Player player = intent.getPlayer(); + AIObject npc = intent.getNpc(); + + displaySuiWindow(player, npc); + } + + @IntentHandler + private void handleMoveObjectIntent(MoveObjectIntent intent) { + SWGObject object = intent.getObject(); + + if (!(object instanceof CreatureObject)) { + return; + } + + CreatureObject creatureObject = (CreatureObject) object; + + if (!suiWindowMap.containsKey(creatureObject)) { + return; + } + + Session session = suiWindowMap.get(creatureObject); + AIObject npc = session.getNpc(); + + + if (isWithinRange(creatureObject, npc)) { + return; + } + + suiWindowMap.remove(creatureObject); + + SuiWindow suiWindow = session.getWindow(); + + suiWindow.close(creatureObject.getOwner()); + } + + private void displaySuiWindow(Player player, AIObject npc) { + List sellableItems = getSellableItems(player); + SuiWindow window; + + if (sellableItems.isEmpty()) { + window = createNoItemsWindow(); + } else { + window = createSellableItemsWindow(player, sellableItems); + } + + window.display(player); + Session session = new Session(npc, window); + suiWindowMap.put(player.getCreatureObject(), session); + } + + private SuiWindow createNoItemsWindow() { + return new SuiMessageBox(SuiButtons.OK, "@loot_dealer:sell_title", "@loot_dealer:no_items"); + } + + private SuiWindow createSellableItemsWindow(Player player, List sellableItems) { + SuiListBox listBox = new SuiListBox(SuiButtons.OK_CANCEL, "@loot_dealer:sell_title", "@loot_dealer:sell_prompt"); + + for (TangibleObject sellableItem : sellableItems) { + String listItem = createListItem(sellableItem); + + listBox.addListItem(listItem, sellableItem); + } + + listBox.setProperty("btnOk", "Text", "@loot_dealer:btn_sell"); + listBox.addCallback(SuiEvent.OK_PRESSED, "handleItemChoice", (event, parameters) -> handleItemChoice(sellableItems, player, parameters)); + + return listBox; + } + + private List getSellableItems(Player player) { + CreatureObject creatureObject = player.getCreatureObject(); + SWGObject inventory = creatureObject.getInventory(); + List sellableItems = new ArrayList<>(); + + Collection childObjects = inventory.getChildObjectsRecursively(); + + for (SWGObject childObject : childObjects) { + if (childObject instanceof TangibleObject) { + TangibleObject item = (TangibleObject) childObject; + Integer itemValue = (Integer) item.getServerAttribute(ServerAttribute.ITEM_VALUE); + + if (itemValue != null) { + if (getPriceForItem(item) > 0) { + if (isContainer(item)) { + if (isEmpty(item)) { + sellableItems.add(item); + } + } else { + sellableItems.add(item); + } + } + } + } + + } + + return sellableItems; + } + + private String createListItem(TangibleObject item) { + String baseFormat = "[%d] %s"; + int price = getPriceForItem(item); + + return String.format(baseFormat, price, item.getObjectName()); + } + + private void handleItemChoice(List sellableItems, Player player, Map parameters) { + int row = SuiListBox.getSelectedRow(parameters); + TangibleObject sellableItem = sellableItems.get(row); + + int price = getPriceForItem(sellableItem); + + CreatureObject creatureObject = player.getCreatureObject(); + creatureObject.addToCash(price); + + Session session = suiWindowMap.remove(creatureObject); + + ProsePackage itemSoldProse = new ProsePackage(new StringId("junk_dealer", "prose_sold_junk"), "TT", sellableItem.getObjectName(), "DI", price); + SystemMessageIntent.broadcastPersonal(player, itemSoldProse); + + DestroyObjectIntent destroyObjectIntent = new DestroyObjectIntent((sellableItem)); + SellItemsIntent sellItemsIntent = new SellItemsIntent(player, session.getNpc()); + sellItemsIntent.broadcastAfterIntent(destroyObjectIntent); + destroyObjectIntent.broadcast(); + } + + private int getPriceForItem(TangibleObject item) { + Integer itemValue = (Integer) item.getServerAttribute(ServerAttribute.ITEM_VALUE); + + if (itemValue == null) { + return 0; + } + + int stackSize = Math.max(1, item.getCounter()); + + return itemValue * stackSize; + } + + private boolean isContainer(TangibleObject item) { + return item.getContainerType() == 2; + } + + private boolean isEmpty(TangibleObject item) { + return item.getChildObjects().isEmpty(); + } + + private boolean isWithinRange(CreatureObject creatureObject, AIObject npc) { + Location playerWorldLocation = creatureObject.getWorldLocation(); + Location npcWorldLocation = npc.getWorldLocation(); + Terrain playerTerrain = playerWorldLocation.getTerrain(); + Terrain npcTerrain = npcWorldLocation.getTerrain(); + + if (playerTerrain != npcTerrain) { + return false; + } + + double distanceTo = playerWorldLocation.distanceTo(npcWorldLocation); + + return distanceTo <= SUI_ALLOWED_DISTANCE; + } + + private static class Session { + private final AIObject npc; + private final SuiWindow window; + + public Session(AIObject npc, SuiWindow window) { + this.npc = npc; + this.window = window; + } + + public AIObject getNpc() { + return npc; + } + + public SuiWindow getWindow() { + return window; + } + } +}