/******************************************************************************* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package org.worldgrower.gui; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Component; import java.awt.FlowLayout; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import javax.swing.table.AbstractTableModel; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import org.worldgrower.Constants; import org.worldgrower.World; import org.worldgrower.WorldObject; import org.worldgrower.attribute.IdList; import org.worldgrower.attribute.IdMap; import org.worldgrower.attribute.KnowledgeMap; import org.worldgrower.deity.Deity; import org.worldgrower.deity.DeityAttributes; import org.worldgrower.goal.BountyPropertyUtils; import org.worldgrower.goal.GroupPropertyUtils; import org.worldgrower.gui.cursor.Cursors; import org.worldgrower.gui.music.SoundIdReader; import org.worldgrower.gui.util.DialogUtils; import org.worldgrower.gui.util.IconUtils; import org.worldgrower.gui.util.JButtonFactory; import org.worldgrower.gui.util.JLabelFactory; import org.worldgrower.gui.util.JPanelFactory; import org.worldgrower.gui.util.JProgressBarFactory; import org.worldgrower.gui.util.JScrollPaneFactory; import org.worldgrower.gui.util.JTableFactory; import org.worldgrower.gui.util.JTreeFactory; import org.worldgrower.profession.Profession; public class CommunityDialog extends JDialog { private static final String ORGANIZATIONS_KEY = "organizations"; private static final String RANKS_KEY = "ranks"; private static final String ACQUAINTANCES_KEY = "acquaintances"; private static final String FAMILY_KEY = "family"; private static final String DEITIES_KEY = "deities"; private static final String MATE_TOOLTIP = "indicates the mate of the player character which can have any gender"; private static final String CHILDREN_TOOLTIP = "shows the children of the player character"; private final JPanel contentPanel; private JTable tlbChildren; private JTable tblAcquaintances; private final ImageInfoReader imageInfoReader; public CommunityDialog(WorldObject playerCharacter, ImageInfoReader imageInfoReader, SoundIdReader soundIdReader, World world, JFrame parentFrame) { this.imageInfoReader = imageInfoReader; contentPanel = new TiledImagePanel(imageInfoReader); setModalityType(ModalityType.APPLICATION_MODAL); IconUtils.setIcon(this); setResizable(false); setTitle("Community Overview"); setCursor(Cursors.CURSOR); int width = 622; int height = 685; setSize(width, height); getContentPane().setPreferredSize(getSize()); setLocationRelativeTo(null); getContentPane().setLayout(new BorderLayout()); contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); getContentPane().add(contentPanel, BorderLayout.CENTER); contentPanel.setLayout(null); setUndecorated(true); int infoPanelWidth = 575; int infoPanelHeight = 575; CardLayout cardLayout = new CardLayout(); JPanel infoPanel = createInfoPanel(infoPanelWidth, infoPanelHeight, cardLayout); createToggleButtonPanel(soundIdReader, infoPanelWidth, cardLayout, infoPanel); createFamilyPanel(playerCharacter, imageInfoReader, world, infoPanelWidth, infoPanelHeight, infoPanel); createAcquaintancesPanel(playerCharacter, imageInfoReader, world, infoPanelWidth, infoPanelHeight, infoPanel); createButtonPanel(imageInfoReader, width, height); createRanksPanel(playerCharacter, world, infoPanelWidth, infoPanelHeight, infoPanel); createOrganizationsPanel(playerCharacter, world, infoPanelWidth, infoPanelHeight, infoPanel); createDeitiesPanel(world, infoPanelWidth, infoPanelHeight, infoPanel); SwingUtils.installEscapeCloseOperation(this); DialogUtils.createDialogBackPanel(this, parentFrame.getContentPane()); } private JPanel createInfoPanel(int infoPanelWidth, int infoPanelHeight, CardLayout cardLayout) { JPanel infoPanel = JPanelFactory.createBorderlessPanel(); infoPanel.setBounds(15, 50, infoPanelWidth, infoPanelHeight); infoPanel.setLayout(cardLayout); contentPanel.add(infoPanel); return infoPanel; } private void createToggleButtonPanel(SoundIdReader soundIdReader, int infoPanelWidth, CardLayout cardLayout, JPanel infoPanel) { JPanel toggleButtonPanel = JPanelFactory.createBorderlessPanel(); toggleButtonPanel.setBounds(5, 5, infoPanelWidth, 40); toggleButtonPanel.setLayout(new FlowLayout()); contentPanel.add(toggleButtonPanel); ButtonGroup buttonGroup = new ButtonGroup(); JToggleButton familyButton = createToggleButton("Family", FAMILY_KEY, soundIdReader, cardLayout, infoPanel, buttonGroup, toggleButtonPanel, "Shows family members of the player character"); createToggleButton("Acquaintances", ACQUAINTANCES_KEY, soundIdReader, cardLayout, infoPanel, buttonGroup, toggleButtonPanel, "Shows acquaintances of the player character"); createToggleButton("Player Character Ranks", RANKS_KEY, soundIdReader, cardLayout, infoPanel, buttonGroup, toggleButtonPanel, "Shows group memberships of the player character"); createToggleButton("Organizations", ORGANIZATIONS_KEY, soundIdReader, cardLayout, infoPanel, buttonGroup, toggleButtonPanel, "Shows an overview of all organizations and their members"); createToggleButton("Deities", DEITIES_KEY, soundIdReader, cardLayout, infoPanel, buttonGroup, toggleButtonPanel, "Shows an overview of all deities and their happiness"); buttonGroup.setSelected(familyButton.getModel(), true); } private void createFamilyPanel(WorldObject playerCharacter, ImageInfoReader imageInfoReader, World world, int infoPanelWidth, int infoPanelHeight, JPanel infoPanel) { JPanel familyPanel = JPanelFactory.createJPanel("Family"); familyPanel.setLayout(null); familyPanel.setBounds(12, 13, infoPanelWidth + 5, infoPanelHeight); infoPanel.add(familyPanel, FAMILY_KEY); JLabel lblMate = JLabelFactory.createJLabel("Mate:"); lblMate.setToolTipText(MATE_TOOLTIP); lblMate.setBounds(12, 30, 109, 16); familyPanel.add(lblMate); JLabel lblMateValue = JLabelFactory.createJLabel("<no mate>"); lblMateValue.setToolTipText(MATE_TOOLTIP); Integer mateId = playerCharacter.getProperty(Constants.MATE_ID); if (mateId != null) { WorldObject mate = world.findWorldObjectById(mateId); lblMateValue.setIcon(new ImageIcon(imageInfoReader.getImage(mate.getProperty(Constants.IMAGE_ID), null))); lblMateValue.setText(mate.getProperty(Constants.NAME)); } lblMateValue.setBounds(149, 30, 180, 16); familyPanel.add(lblMateValue); JLabel lblChildren = JLabelFactory.createJLabel("Children:"); lblChildren.setToolTipText(CHILDREN_TOOLTIP); lblChildren.setBounds(12, 60, 109, 16); familyPanel.add(lblChildren); tlbChildren = JTableFactory.createJTable(new ChildrenTableModel(playerCharacter, world)); tlbChildren.setDefaultRenderer(ImageIds.class, new ImageTableRenderer(imageInfoReader)); tlbChildren.setRowHeight(50); tlbChildren.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tlbChildren.setAutoCreateRowSorter(true); tlbChildren.getColumnModel().getColumn(0).setPreferredWidth(50); tlbChildren.getColumnModel().getColumn(1).setPreferredWidth(405); tlbChildren.setToolTipText(CHILDREN_TOOLTIP); JScrollPane scrollPaneChildren = JScrollPaneFactory.createScrollPane(tlbChildren); scrollPaneChildren.setBounds(13, 100, 525, 420); familyPanel.add(scrollPaneChildren); SwingUtils.makeTransparant(tlbChildren, scrollPaneChildren); } private void createAcquaintancesPanel(WorldObject playerCharacter, ImageInfoReader imageInfoReader, World world, int infoPanelWidth, int infoPanelHeight, JPanel infoPanel) { JPanel acquaintancesPanel = JPanelFactory.createJPanel("Acquaintances"); acquaintancesPanel.setLayout(null); acquaintancesPanel.setBounds(12, 363, infoPanelWidth + 5, infoPanelHeight); infoPanel.add(acquaintancesPanel, ACQUAINTANCES_KEY); tblAcquaintances = JTableFactory.createJTable(new AcquaintancesTableModel(playerCharacter, world)); tblAcquaintances.setDefaultRenderer(ImageIds.class, new ImageTableRenderer(imageInfoReader)); tblAcquaintances.setRowHeight(50); tblAcquaintances.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tblAcquaintances.setAutoCreateRowSorter(true); tblAcquaintances.getColumnModel().getColumn(0).setPreferredWidth(50); tblAcquaintances.getColumnModel().getColumn(1).setPreferredWidth(100); tblAcquaintances.getColumnModel().getColumn(2).setPreferredWidth(100); tblAcquaintances.getColumnModel().getColumn(3).setPreferredWidth(100); tblAcquaintances.getColumnModel().getColumn(4).setPreferredWidth(100); JScrollPane scrollPaneAcquaintances = JScrollPaneFactory.createScrollPane(tblAcquaintances); scrollPaneAcquaintances.setBounds(12, 30, 525, 420); acquaintancesPanel.add(scrollPaneAcquaintances); SwingUtils.makeTransparant(tblAcquaintances, scrollPaneAcquaintances); } private void createButtonPanel(ImageInfoReader imageInfoReader, int width, int height) { JPanel buttonPane = JPanelFactory.createBorderlessPanel(); buttonPane.setOpaque(false); buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); contentPanel.add(buttonPane); JButton okButton = JButtonFactory.createButton("OK", imageInfoReader); okButton.setActionCommand("OK"); buttonPane.add(okButton); buttonPane.setBounds(0, height - 55, width - 26, 50); okButton.addActionListener(new CloseDialogAction()); getRootPane().setDefaultButton(okButton); } private void createRanksPanel(WorldObject playerCharacter, World world, int infoPanelWidth, int infoPanelHeight, JPanel infoPanel) { JPanel ranksPanel = JPanelFactory.createJPanel("Player Character Ranks"); ranksPanel.setLayout(null); ranksPanel.setBounds(510, 13, infoPanelWidth + 5, infoPanelHeight); infoPanel.add(ranksPanel, RANKS_KEY); OrganizationsModel worldModel = new OrganizationsModel(playerCharacter, world); JTable organizationsTable = JTableFactory.createJTable(worldModel); organizationsTable.setRowHeight(30); JScrollPane scrollPane = JScrollPaneFactory.createScrollPane(organizationsTable); scrollPane.setBounds(15, 30, 525, 420); ranksPanel.add(scrollPane); SwingUtils.makeTransparant(organizationsTable, scrollPane); } private void createOrganizationsPanel(WorldObject playerCharacter, World world, int infoPanelWidth, int infoPanelHeight, JPanel infoPanel) { JPanel organizationsPanel = JPanelFactory.createJPanel("Organizations"); organizationsPanel.setLayout(null); organizationsPanel.setBounds(510, 363, infoPanelWidth + 5, infoPanelHeight); infoPanel.add(organizationsPanel, ORGANIZATIONS_KEY); JTree tree = JTreeFactory.createJTree(createRootNode(playerCharacter, world)); tree.setRootVisible(false); expandAllNodes(tree, 0, tree.getRowCount()); tree.setCellRenderer(new OrganizationMemberRenderer()); JScrollPane treeView = JScrollPaneFactory.createScrollPane(tree); treeView.setBounds(15, 30, 525, 420); organizationsPanel.add(treeView); } private void createDeitiesPanel(World world, int infoPanelWidth, int infoPanelHeight, JPanel infoPanel) { JPanel deitiesPanel = JPanelFactory.createJPanel("Deities"); deitiesPanel.setLayout(null); deitiesPanel.setBounds(510, 363, infoPanelWidth + 5, infoPanelHeight); infoPanel.add(deitiesPanel, DEITIES_KEY); DeityAttributes deityAttributes = GroupPropertyUtils.getVillagersOrganization(world).getProperty(Constants.DEITY_ATTRIBUTES); String deityTooltip = "deity hapiness indicator: if a deity becomes unhappy, they may lash out against the population"; for(int i=0; i<Deity.ALL_DEITIES.size(); i++) { Deity deity = Deity.ALL_DEITIES.get(i); Image image = imageInfoReader.getImage(deity.getBoonImageId(), null); JLabel nameLabel = JLabelFactory.createJLabel(deity.getName(), image); nameLabel.setBounds(15, 30 + 40 * i, 150, 50); nameLabel.setHorizontalAlignment(SwingConstants.LEFT); nameLabel.setToolTipText(deityTooltip); deitiesPanel.add(nameLabel); JProgressBar relationshipProgresBar = JProgressBarFactory.createHorizontalJProgressBar(deityAttributes.getMinHapinessValue(), deityAttributes.getMaxHapinessValue(), imageInfoReader); relationshipProgresBar.setBounds(175, 40 + 40 * i, 300, 30); relationshipProgresBar.setValue(deityAttributes.getHappiness(deity)); relationshipProgresBar.setToolTipText(deityTooltip); deitiesPanel.add(relationshipProgresBar); } } private JToggleButton createToggleButton(String label, String panelKey, SoundIdReader soundIdReader, CardLayout cardLayout, JPanel infoPanel, ButtonGroup buttonGroup, JPanel toggleButtonPanel, String tooltip) { JToggleButton button = JButtonFactory.createToggleButton(label, soundIdReader); button.addActionListener(e -> cardLayout.show(infoPanel, panelKey)); button.setOpaque(false); button.setToolTipText(tooltip); buttonGroup.add(button); toggleButtonPanel.add(button); return button; } private final class CloseDialogAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { CommunityDialog.this.dispose(); } } private static class ChildrenTableModel extends AbstractTableModel { private final List<WorldObject> children; public ChildrenTableModel(WorldObject playerCharacter, World world) { super(); IdList childrenIds = playerCharacter.getProperty(Constants.CHILDREN); this.children = new ArrayList<>(); for(int childId : childrenIds.getIds()) { children.add(world.findWorldObjectById(childId)); } } @Override public int getRowCount() { return children.size(); } @Override public int getColumnCount() { return 2; } @Override public String getColumnName(int columnIndex) { if (columnIndex == 0) { return "Image"; } else if (columnIndex == 1) { return "Name"; } else { return null; } } @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 0) { return ImageIds.class; } return super.getColumnClass(columnIndex); } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex == 0) { return children.get(rowIndex).getProperty(Constants.IMAGE_ID); } else if (columnIndex == 1) { return children.get(rowIndex).getProperty(Constants.NAME); } return null; } } private static class AcquaintancesTableModel extends AbstractTableModel { private final List<WorldObject> acquaintances = new ArrayList<>(); private final List<Profession> professions = new ArrayList<>(); private final List<Deity> deities = new ArrayList<>(); private final List<String> bounties = new ArrayList<>(); public AcquaintancesTableModel(WorldObject playerCharacter, World world) { super(); IdMap relationshipMap = playerCharacter.getProperty(Constants.RELATIONSHIPS); for(int id : relationshipMap.getIds()) { acquaintances.add(world.findWorldObjectById(id)); } KnowledgeMap knowledgeMap = playerCharacter.getProperty(Constants.KNOWLEDGE_MAP); for(int id : knowledgeMap.getIds()) { WorldObject acquaintance = world.findWorldObjectById(id); if (acquaintance.hasIntelligence() && !acquaintances.contains(acquaintance)) { acquaintances.add(acquaintance); } } for(WorldObject acquaintance : acquaintances) { int id = acquaintance.getProperty(Constants.ID); professions.add(knowledgeMap.getProperty(id, Constants.PROFESSION)); deities.add(knowledgeMap.getProperty(id, Constants.DEITY)); int bounty = BountyPropertyUtils.getBounty(acquaintance, world); final String bountyValue; if (bounty > 0) { bountyValue = Integer.toString(bounty); } else { bountyValue = ""; } bounties.add(bountyValue); } } @Override public int getRowCount() { return acquaintances.size(); } @Override public int getColumnCount() { return 5; } @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 0) { return ImageIds.class; } return super.getColumnClass(columnIndex); } @Override public String getColumnName(int columnIndex) { if (columnIndex == 0) { return "Image"; } else if (columnIndex == 1) { return "Name"; } else if (columnIndex == 2) { return "Profession"; } else if (columnIndex == 3) { return "Deity"; } else if (columnIndex == 4) { return "Bounty"; } else { return null; } } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex == 0) { return acquaintances.get(rowIndex).getProperty(Constants.IMAGE_ID); } else if (columnIndex == 1) { return acquaintances.get(rowIndex).getProperty(Constants.NAME); } else if (columnIndex == 2) { return professions.get(rowIndex) != null ? professions.get(rowIndex).getDescription() : null; } else if (columnIndex == 3) { return deities.get(rowIndex) != null ? deities.get(rowIndex).getName() : null; } else if (columnIndex == 4) { return bounties.get(rowIndex); } return null; } } private void expandAllNodes(JTree tree, int startingIndex, int rowCount){ for(int i=startingIndex;i<rowCount;++i){ tree.expandRow(i); } if(tree.getRowCount()!=rowCount){ expandAllNodes(tree, rowCount, tree.getRowCount()); } } private DefaultMutableTreeNode createRootNode(WorldObject playerCharacter, World world) { DefaultMutableTreeNode top = new DefaultMutableTreeNode("Organizations"); for(WorldObject organization : GroupPropertyUtils.getAllOrganizations(world)) { DefaultMutableTreeNode organizationNode = new DefaultMutableTreeNode(organization); List<WorldObject> members = GroupPropertyUtils.findOrganizationMembers(organization, world); for(WorldObject member : members) { if (isMemberKnownByPlayerCharacter(member, playerCharacter)) { DefaultMutableTreeNode memberNode = new DefaultMutableTreeNode(member); organizationNode.add(memberNode); } } if (organizationNode.getChildCount() > 0) { top.add(organizationNode); } } return top; } private static boolean isMemberKnownByPlayerCharacter(WorldObject member, WorldObject playerCharacter) { return playerCharacter.getProperty(Constants.RELATIONSHIPS).contains(member) || playerCharacter.getProperty(Constants.KNOWLEDGE_MAP).hasKnowledge(member) || playerCharacter.equals(member); } class OrganizationMemberRenderer extends DefaultTreeCellRenderer { public OrganizationMemberRenderer() { this.setOpaque(false); this.setBackgroundNonSelectionColor(ColorPalette.DARK_BACKGROUND_COLOR); this.setBackgroundSelectionColor(ColorPalette.LIGHT_BACKGROUND_COLOR); } public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; WorldObject worldObject = (WorldObject) node.getUserObject(); setIcon(new ImageIcon(imageInfoReader.getImage(worldObject.getProperty(Constants.IMAGE_ID), null))); setText(worldObject.getProperty(Constants.NAME)); setForeground(ColorPalette.FOREGROUND_COLOR); return this; } } private static class OrganizationsModel extends AbstractTableModel { private Map<String, String> organizationDescriptions; private List<String> organizationNames; public OrganizationsModel(WorldObject playerCharacter, World world) { super(); this.organizationDescriptions = new HashMap<>(); this.organizationNames = new ArrayList<>(); List<WorldObject> performerOrganizations = GroupPropertyUtils.getOrganizations(playerCharacter, world); for(WorldObject organization : performerOrganizations) { final String playerCharacterStatus; if (GroupPropertyUtils.performerIsLeaderOfOrganization(playerCharacter, organization, world)) { playerCharacterStatus = "leader"; } else { playerCharacterStatus = "member"; } String organizationName = organization.getProperty(Constants.NAME); organizationDescriptions.put(organizationName, playerCharacterStatus); organizationNames.add(organizationName); } } @Override public int getColumnCount() { return 2; } @Override public int getRowCount() { return organizationDescriptions.size(); } @Override public String getColumnName(int columnIndex) { if (columnIndex == 0) { return "Organization Name"; } else if (columnIndex == 1) { return "Player Character Status"; } else { return null; } } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex == 0) { return organizationNames.get(rowIndex); } else if (columnIndex == 1) { return organizationDescriptions.get(organizationNames.get(rowIndex)); } else { return null; } } } }