From 21435a7108a0123e26e0da6d60130fd10717d598 Mon Sep 17 00:00:00 2001 From: Harshit Date: Tue, 24 Aug 2021 23:02:28 +0530 Subject: [PATCH] revert changes --- assets/skins/ShopDefault.skin | 29 +++ .../org/terasology/economy/ShopManager.java | 108 ++++++++ .../components/PurchasableComponent.java | 13 + .../economy/components/ValueComponent.java | 18 ++ .../economy/events/GiveItemTypeEvent.java | 36 +++ .../events/WalletTransactionEvent.java | 2 + .../economy/systems/ItemAuthoritySystem.java | 61 +++++ .../economy/systems/WalletClientSystem.java | 7 +- .../org/terasology/economy/ui/ShopScreen.java | 234 ++++++++++++++++++ .../economy/ui/UIInteractionWrapper.java | 101 ++++++++ 10 files changed, 608 insertions(+), 1 deletion(-) create mode 100644 assets/skins/ShopDefault.skin create mode 100644 src/main/java/org/terasology/economy/ShopManager.java create mode 100644 src/main/java/org/terasology/economy/components/PurchasableComponent.java create mode 100644 src/main/java/org/terasology/economy/components/ValueComponent.java create mode 100644 src/main/java/org/terasology/economy/events/GiveItemTypeEvent.java create mode 100644 src/main/java/org/terasology/economy/systems/ItemAuthoritySystem.java create mode 100644 src/main/java/org/terasology/economy/ui/ShopScreen.java create mode 100644 src/main/java/org/terasology/economy/ui/UIInteractionWrapper.java diff --git a/assets/skins/ShopDefault.skin b/assets/skins/ShopDefault.skin new file mode 100644 index 0000000..3e8c742 --- /dev/null +++ b/assets/skins/ShopDefault.skin @@ -0,0 +1,29 @@ +{ + "inherit": "inventoryDefault", + "elements": { + "UIInteractionWrapper": { + "fixed-width": 48, + "fixed-height": 48, + "texture-scale-mode": "scale fit", + "text-align-horizontal": "right", + "text-align-vertical": "bottom", + "margin": { + "top": 4, + "bottom": 4, + "left": 4, + "right": 4 + } + } + }, + "families": { + "redText": { + "text-color": "FF0000FF" + }, + "whiteText": { + "text-color": "FFFFFFFF" + }, + "title":{ + "font":"NotoSans-Regular-Large" + } + } +} diff --git a/src/main/java/org/terasology/economy/ShopManager.java b/src/main/java/org/terasology/economy/ShopManager.java new file mode 100644 index 0000000..291374e --- /dev/null +++ b/src/main/java/org/terasology/economy/ShopManager.java @@ -0,0 +1,108 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 +package org.terasology.economy; + +import com.google.common.base.Preconditions; +import org.terasology.economy.components.PurchasableComponent; +import org.terasology.economy.components.ValueComponent; +import org.terasology.economy.events.GiveItemTypeEvent; +import org.terasology.engine.world.block.Block; +import org.terasology.engine.world.block.BlockExplorer; +import org.terasology.engine.world.block.BlockManager; +import org.terasology.engine.world.block.BlockUri; +import org.terasology.engine.world.block.family.BlockFamily; +import org.terasology.gestalt.assets.management.AssetManager; +import org.terasology.engine.entitySystem.ComponentContainer; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.prefab.Prefab; +import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.logic.inventory.ItemComponent; +import org.terasology.engine.logic.players.LocalPlayer; +import org.terasology.engine.registry.In; +import org.terasology.engine.registry.Share; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Handles the purchasing of blocks + */ +@RegisterSystem +@Share(ShopManager.class) +public class ShopManager extends BaseComponentSystem { + private Set purchasableBlocks = new HashSet<>(); + private Set purchasableItems = new HashSet<>(); + + @In + private AssetManager assetManager; + @In + private BlockManager blockManager; + @In + private LocalPlayer localPlayer; + /** + * Gets how much money a ware will cost. + * Tries to use the cost on the value component. + * + * @param ware The ware to get the price for + * @return The price of the ware. + */ + public static int getWareCost(ComponentContainer ware) { + Preconditions.checkNotNull(ware.getComponent(ValueComponent.class), "Component Container does not contain a Value Component"); + return ware.getComponent(ValueComponent.class).value; + } + + @Override + public void postBegin() { + BlockExplorer blockExplorer = new BlockExplorer(assetManager); + + purchasableItems = assetManager.getLoadedAssets(Prefab.class) + .stream() + .filter(prefab -> prefab.hasComponent(ItemComponent.class) + && prefab.hasComponent(PurchasableComponent.class)) + .collect(Collectors.toSet()); + + Set blocks = new HashSet<>(); + blocks.addAll(blockManager.listRegisteredBlockUris()); + blocks.addAll(blockExplorer.getAvailableBlockFamilies()); + blocks.addAll(blockExplorer.getFreeformBlockFamilies()); + + purchasableBlocks = blocks.stream() + .map(blockManager::getBlockFamily) + .map(BlockFamily::getArchetypeBlock) + .filter(block -> block.getPrefab().isPresent()) + .filter(block -> block.getPrefab().get().hasComponent(PurchasableComponent.class)) + .collect(Collectors.toSet()); + } + + /** + * @return All the blocks for sale + */ + public Set getAllBlocks() { + return purchasableBlocks; + } + + /** + * @return All the items for sale + */ + public Set getAllItems() { + return purchasableItems; + } + + /** + * Attempt to purchase a block. + * + * @param block The block to purchase + */ + public void purchaseBlock(Block block) { + EntityRef character = localPlayer.getCharacterEntity(); + String blockURI = block.toString(); + character.send(new GiveItemTypeEvent(blockURI)); + } + + public void purchaseItem(Prefab prefab) { + EntityRef character = localPlayer.getCharacterEntity(); + character.send(new GiveItemTypeEvent(prefab)); + } +} diff --git a/src/main/java/org/terasology/economy/components/PurchasableComponent.java b/src/main/java/org/terasology/economy/components/PurchasableComponent.java new file mode 100644 index 0000000..66a13a7 --- /dev/null +++ b/src/main/java/org/terasology/economy/components/PurchasableComponent.java @@ -0,0 +1,13 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 +package org.terasology.economy.components; + +import org.terasology.engine.entitySystem.Component; +import org.terasology.engine.world.block.items.AddToBlockBasedItem; + +/** + * Indicates that the item can be bought and thus will be available in the shop + */ +@AddToBlockBasedItem +public class PurchasableComponent implements Component { +} diff --git a/src/main/java/org/terasology/economy/components/ValueComponent.java b/src/main/java/org/terasology/economy/components/ValueComponent.java new file mode 100644 index 0000000..d0fc5f5 --- /dev/null +++ b/src/main/java/org/terasology/economy/components/ValueComponent.java @@ -0,0 +1,18 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 +package org.terasology.economy.components; + +import org.terasology.engine.entitySystem.Component; +import org.terasology.engine.world.block.items.AddToBlockBasedItem; + +/** + * Indicates that the given entity has a value. + */ +@AddToBlockBasedItem +public class ValueComponent implements Component { + + /** + * How much money one instance of this entity is worth + */ + public int value; +} diff --git a/src/main/java/org/terasology/economy/events/GiveItemTypeEvent.java b/src/main/java/org/terasology/economy/events/GiveItemTypeEvent.java new file mode 100644 index 0000000..d96a557 --- /dev/null +++ b/src/main/java/org/terasology/economy/events/GiveItemTypeEvent.java @@ -0,0 +1,36 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 +package org.terasology.economy.events; + +import org.terasology.engine.entitySystem.event.Event; +import org.terasology.engine.entitySystem.prefab.Prefab; +import org.terasology.engine.network.ServerEvent; + +/** + * Trigger event that sends the required item (as a string/prefab) to the server. + * It then creates the item entity and gives the item on the server. + */ +@ServerEvent +public class GiveItemTypeEvent implements Event { + Prefab targetPrefab; + String blockURI; + + private GiveItemTypeEvent() { + } + + public GiveItemTypeEvent(Prefab targetPrefab) { + this.targetPrefab = targetPrefab; + } + + public GiveItemTypeEvent(String blockURI) { + this.blockURI = blockURI; + } + + public Prefab getTargetPrefab() { + return targetPrefab; + } + + public String getBlockURI() { + return blockURI; + } +} diff --git a/src/main/java/org/terasology/economy/events/WalletTransactionEvent.java b/src/main/java/org/terasology/economy/events/WalletTransactionEvent.java index 95bd868..736092d 100644 --- a/src/main/java/org/terasology/economy/events/WalletTransactionEvent.java +++ b/src/main/java/org/terasology/economy/events/WalletTransactionEvent.java @@ -3,10 +3,12 @@ package org.terasology.economy.events; import org.terasology.engine.entitySystem.event.Event; +import org.terasology.engine.network.ServerEvent; /** * Fired when a currency transaction must occur. */ +@ServerEvent public class WalletTransactionEvent implements Event { /** diff --git a/src/main/java/org/terasology/economy/systems/ItemAuthoritySystem.java b/src/main/java/org/terasology/economy/systems/ItemAuthoritySystem.java new file mode 100644 index 0000000..e161939 --- /dev/null +++ b/src/main/java/org/terasology/economy/systems/ItemAuthoritySystem.java @@ -0,0 +1,61 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 +package org.terasology.economy.systems; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.economy.components.ValueComponent; +import org.terasology.economy.events.WalletTransactionEvent; +import org.terasology.engine.entitySystem.entity.EntityManager; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.event.ReceiveEvent; +import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.entitySystem.systems.RegisterMode; +import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.logic.inventory.events.GiveItemEvent; +import org.terasology.engine.registry.In; +import org.terasology.economy.events.GiveItemTypeEvent; +import org.terasology.engine.world.block.BlockManager; +import org.terasology.engine.world.block.family.BlockFamily; +import org.terasology.engine.world.block.items.BlockItemFactory; +import org.terasology.module.inventory.systems.InventoryAuthoritySystem; + +@RegisterSystem(RegisterMode.AUTHORITY) +public class ItemAuthoritySystem extends BaseComponentSystem { + + @In + private WalletAuthoritySystem walletAuthoritySystem; + @In + private EntityManager entityManager; + @In + private BlockManager blockManager; + + private BlockItemFactory blockItemFactory; + + private Logger logger = LoggerFactory.getLogger(InventoryAuthoritySystem.class); + + @Override + public void postBegin() { + blockItemFactory = new BlockItemFactory(entityManager); + } + + @ReceiveEvent + public void onGiveItemToPlayer(GiveItemTypeEvent event, EntityRef entity) { + if (event.getTargetPrefab() != null && event.getTargetPrefab().hasComponent(ValueComponent.class)) { + EntityRef item = entityManager.create(event.getTargetPrefab()); + if (walletAuthoritySystem.isValidTransaction(entity, -item.getComponent(ValueComponent.class).value)) { + entity.send(new WalletTransactionEvent(-item.getComponent(ValueComponent.class).value)); + item.send(new GiveItemEvent(entity)); + } + } else if (event.getBlockURI() != null) { + BlockFamily blockFamily = blockManager.getBlockFamily(event.getBlockURI()); + EntityRef blockItem = blockItemFactory.newInstance(blockFamily); + if (walletAuthoritySystem.isValidTransaction(entity, -blockItem.getComponent(ValueComponent.class).value)) { + entity.send(new WalletTransactionEvent(-blockItem.getComponent(ValueComponent.class).value)); + blockItem.send(new GiveItemEvent(entity)); + } + } else { + logger.warn("Prefab/String is null"); + } + } +} diff --git a/src/main/java/org/terasology/economy/systems/WalletClientSystem.java b/src/main/java/org/terasology/economy/systems/WalletClientSystem.java index 87189f4..0d78b1b 100644 --- a/src/main/java/org/terasology/economy/systems/WalletClientSystem.java +++ b/src/main/java/org/terasology/economy/systems/WalletClientSystem.java @@ -9,6 +9,7 @@ import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterMode; import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.logic.players.LocalPlayer; import org.terasology.engine.registry.In; import org.terasology.engine.rendering.nui.NUIManager; import org.terasology.nui.databinding.Binding; @@ -22,6 +23,8 @@ public class WalletClientSystem extends BaseComponentSystem { @In private NUIManager nuiManager; + @In + private LocalPlayer localPlayer; private Binding walletBalance = new DefaultBinding<>(""); @@ -32,6 +35,8 @@ public void preBegin() { @ReceiveEvent public void onUpdateWallet(WalletUpdatedEvent event, EntityRef character) { - walletBalance.set(String.valueOf(event.amount)); + if (localPlayer.getCharacterEntity().equals(character)) { + walletBalance.set(String.valueOf(event.amount)); + } } } diff --git a/src/main/java/org/terasology/economy/ui/ShopScreen.java b/src/main/java/org/terasology/economy/ui/ShopScreen.java new file mode 100644 index 0000000..7d6ac8c --- /dev/null +++ b/src/main/java/org/terasology/economy/ui/ShopScreen.java @@ -0,0 +1,234 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 +package org.terasology.economy.ui; + +import org.terasology.economy.ShopManager; +import org.terasology.engine.entitySystem.entity.EntityManager; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.prefab.Prefab; +import org.terasology.engine.logic.common.DisplayNameComponent; +import org.terasology.engine.logic.inventory.ItemComponent; +import org.terasology.engine.logic.players.LocalPlayer; +import org.terasology.engine.registry.In; +import org.terasology.engine.rendering.assets.texture.Texture; +import org.terasology.engine.rendering.assets.texture.TextureRegionAsset; +import org.terasology.engine.rendering.nui.CoreScreenLayer; +import org.terasology.engine.world.block.Block; +import org.terasology.gestalt.assets.management.AssetManager; +import org.terasology.module.inventory.ui.InventoryGrid; +import org.terasology.module.inventory.ui.InventoryScreen; +import org.terasology.module.inventory.ui.ItemIcon; +import org.terasology.engine.utilities.Assets; +import org.terasology.nui.WidgetUtil; +import org.terasology.nui.databinding.ReadOnlyBinding; +import org.terasology.nui.layouts.FlowLayout; +import org.terasology.nui.layouts.relative.RelativeLayout; +import org.terasology.nui.widgets.TooltipLine; +import org.terasology.nui.widgets.UILabel; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +/** + * Screen that displays both the shop interface and the player inventory simultaneously. + *

+ * Works by overriding the inventory screen on the asset level. + * This allows it to utilise the inventory opening logic & to be displayed concurrently. + * + * @see InventoryScreen + */ +public class ShopScreen extends CoreScreenLayer { + + private final Texture texture = Assets.getTexture("engine:terrain") + .orElseGet(() -> Assets.getTexture("engine:default").get()); + private FlowLayout wareList; + private UILabel wareName; + private ItemIcon wareDisplay; + private UILabel wareDescription; + private UILabel wareCost; + private Block selectedBlock; + private Prefab selectedPrefab; + @In + private LocalPlayer localPlayer; + @In + private ShopManager shopManager; + @In + private AssetManager assetManager; + + @Override + public void onOpened() { + addItems(shopManager.getAllItems()); + addBlocks(shopManager.getAllBlocks()); + } + + @Override + public void initialise() { + InventoryGrid inventory = find("inventory", InventoryGrid.class); + + wareList = find("wareList", FlowLayout.class); + + wareName = find("wareName", UILabel.class); + wareDisplay = find("wareDisplay", ItemIcon.class); + wareDescription = find("wareDescription", UILabel.class); + wareCost = find("wareCost", UILabel.class); + RelativeLayout wareInfoLayout = find("wareInfoLayout", RelativeLayout.class); + + + WidgetUtil.trySubscribe(this, "buyButton", widget -> attemptItemPurchase()); + + /* No null check is performed, as if a value is null then something has gone wrong and we should crash anyway */ + wareInfoLayout.bindVisible(new ReadOnlyBinding() { + @Override + public Boolean get() { + return selectedPrefab != null || selectedBlock != null; + } + }); + inventory.bindTargetEntity(new ReadOnlyBinding() { + @Override + public EntityRef get() { + return localPlayer.getCharacterEntity(); + } + }); + inventory.setCellOffset(10); + wareDisplay.setMeshTexture(texture); + } + + @Override + public void onClosed() { + wareList.removeAllWidgets(); + wareName.setText(""); + wareDescription.setText(""); + wareCost.setText(""); + + wareDisplay.setMesh(null); + wareDisplay.setIcon(null); + } + + @Override + public boolean isModal() { + return false; + } + + /** + * Adds a number of items to be displayed in the ware list + * + * @param items The items to add + */ + private void addItems(Set items) { + for (Prefab item : items) { + ItemComponent itemComponent = item.getComponent(ItemComponent.class); + ItemIcon itemIcon = new ItemIcon(); + + itemIcon.setIcon(itemComponent.icon); + + UIInteractionWrapper wrapper = new UIInteractionWrapper(); + wrapper.setTooltipLines(Collections.singletonList(new TooltipLine(getPrefabName(item)))); + wrapper.setListener(widget -> handlePrefabSelected(item)); + wrapper.setContent(itemIcon); + wareList.addWidget(wrapper, null); + } + } + + /** + * Adds a number of blocks to be displayed in the ware list. + * + * @param blocks The block to display + */ + private void addBlocks(Set blocks) { + for (Block block : blocks) { + ItemIcon itemIcon = new ItemIcon(); + + itemIcon.setMesh(block.getMeshGenerator().getStandaloneMesh()); + itemIcon.setMeshTexture(texture); + + UIInteractionWrapper wrapper = new UIInteractionWrapper(); + wrapper.setTooltipLines(Collections.singletonList(new TooltipLine(getBlockName(block)))); + wrapper.setListener(widget -> handleBlockSelected(block)); + wrapper.setContent(itemIcon); + wareList.addWidget(wrapper, null); + } + } + + /** + * Calls on the shop manager to attempt to purchase the selected item. + */ + private void attemptItemPurchase() { + if (selectedBlock != null) { + shopManager.purchaseBlock(selectedBlock); + } else if (selectedPrefab != null) { + shopManager.purchaseItem(selectedPrefab); + } + } + + /** + * Handles the prefab being selected by setting all the information labels and displays to the correct data + * + * @param prefab The block selected + */ + private void handlePrefabSelected(Prefab prefab) { + selectedPrefab = prefab; + selectedBlock = null; + if (prefab.hasComponent(DisplayNameComponent.class)) { + DisplayNameComponent component = prefab.getComponent(DisplayNameComponent.class); + wareName.setText(component.name); + wareDescription.setText(component.description); + } else { + wareName.setText(prefab.getUrn().getResourceName().toString()); + } + + if (prefab.hasComponent(ItemComponent.class)) { + ItemComponent itemComponent = prefab.getComponent(ItemComponent.class); + wareDisplay.setIcon(itemComponent.icon); + } + // TODO: Make it translatable + wareCost.setText("Cost: " + ShopManager.getWareCost(prefab)); + } + + /** + * Handles the block being selected by setting all the information labels and displays to the correct data + * + * @param block The block selected + */ + private void handleBlockSelected(Block block) { + if (block.getPrefab().isPresent()) { + handlePrefabSelected(block.getPrefab().get()); + } else { + wareName.setText(getBlockName(block)); + } + + selectedBlock = block; + selectedPrefab = null; + wareDisplay.setMesh(block.getMeshGenerator().getStandaloneMesh()); + } + + /** + * Gets the name of a prefab. + * This is the human readable variant of it. + * + * @param prefab The prefab to get the name of + * @return The string name of the prefab + */ + private String getPrefabName(Prefab prefab) { + return prefab.hasComponent(DisplayNameComponent.class) + ? prefab.getComponent(DisplayNameComponent.class).name + : prefab.getUrn().getResourceName().toString(); + } + + /** + * Gets the name of a block. + * This is the human readable variant of it. + * + * @param block The block to get the name of + * @return The string name of the block + */ + private String getBlockName(Block block) { + String displayName = block.getDisplayName(); + return !displayName.equals("Untitled Block") + ? displayName + : block.getURI() + .getBlockFamilyDefinitionUrn() + .getResourceName() + .toString(); + } +} diff --git a/src/main/java/org/terasology/economy/ui/UIInteractionWrapper.java b/src/main/java/org/terasology/economy/ui/UIInteractionWrapper.java new file mode 100644 index 0000000..bd915c3 --- /dev/null +++ b/src/main/java/org/terasology/economy/ui/UIInteractionWrapper.java @@ -0,0 +1,101 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 +package org.terasology.economy.ui; + +import org.joml.Vector2i; +import org.terasology.engine.utilities.Assets; +import org.terasology.nui.BaseInteractionListener; +import org.terasology.nui.Canvas; +import org.terasology.nui.CoreWidget; +import org.terasology.nui.InteractionListener; +import org.terasology.nui.UIWidget; +import org.terasology.nui.databinding.DefaultBinding; +import org.terasology.nui.events.NUIMouseClickEvent; +import org.terasology.nui.skin.UISkin; +import org.terasology.nui.widgets.ActivateEventListener; +import org.terasology.nui.widgets.TooltipLine; +import org.terasology.nui.widgets.TooltipLineRenderer; +import org.terasology.nui.widgets.UIList; + +import java.util.ArrayList; +import java.util.List; + +/** + * Wrapper widget used in the shop screen. + * Displays a separate widget whilst capturing user interaction with it. + * When the icon corresponding to the item is clicked, a tooltip containing the item's name and additional information is displayed. + *

+ * All size values etc etc are determined by the content widget. + */ +public class UIInteractionWrapper extends CoreWidget { + private UIWidget content; + private ActivateEventListener listener; + private final UIList tooltip; + + private final InteractionListener interactionListener = new BaseInteractionListener() { + @Override + public boolean onMouseClick(NUIMouseClickEvent event) { + if (listener != null) { + listener.onActivated(UIInteractionWrapper.this); + } + return true; + } + }; + + public UIInteractionWrapper() { + tooltip = new UIList<>(); + tooltip.setInteractive(false); + tooltip.setSelectable(false); + final UISkin defaultSkin = Assets.getSkin("Inventory:itemTooltip").get(); + tooltip.setSkin(defaultSkin); + tooltip.setItemRenderer(new TooltipLineRenderer(defaultSkin)); + tooltip.bindList(new DefaultBinding<>(new ArrayList<>())); + } + + @Override + public void onDraw(Canvas canvas) { + canvas.drawWidget(content); + canvas.addInteractionRegion(interactionListener); + } + + @Override + public Vector2i getPreferredContentSize(Canvas canvas, Vector2i sizeHint) { + return canvas.calculatePreferredSize(content); + } + + @Override + public Vector2i getMaxContentSize(Canvas canvas) { + return content.getMaxContentSize(canvas); + } + + @Override + public UIWidget getTooltip() { + if (tooltip.getList().size() > 0) { + return tooltip; + } else { + return null; + } + } + + /** + * @param content The widget to display. + */ + public void setContent(UIWidget content) { + this.content = content; + } + + /** + * @param lines The tooltip lines to display + */ + public void setTooltipLines(List lines) { + tooltip.setList(lines); + } + + + /** + * @param listener The listener to use when this widget is clicked. + */ + public void setListener(ActivateEventListener listener) { + this.listener = listener; + } +}