From aaf630a3a237b6548b79729fc89fe29932e04fbb Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 30 Apr 2016 08:52:28 +0200 Subject: [PATCH 01/18] Lots of work on leveling, XP and skills --- src/intents/experience/ExperienceIntent.java | 74 ++++++ .../experience/LevelChangedIntent.java | 64 ++++++ .../experience/SkillBoxGrantedIntent.java | 58 +++++ .../ChangeRoleIconChoice.java | 70 ++++++ .../object_controller/ObjectController.java | 1 + .../objects/creature/CreatureObject.java | 22 +- .../objects/player/PlayerObject.java | 55 +++-- .../experience/ExperienceManager.java | 143 ++++++++++++ src/services/experience/SkillService.java | 217 ++++++++++++++++++ .../experience/SkillTemplateService.java | 100 ++++++++ src/services/galaxy/GameManager.java | 4 + .../player/CharacterCreationService.java | 2 + 12 files changed, 776 insertions(+), 34 deletions(-) create mode 100644 src/intents/experience/ExperienceIntent.java create mode 100644 src/intents/experience/LevelChangedIntent.java create mode 100644 src/intents/experience/SkillBoxGrantedIntent.java create mode 100644 src/network/packets/swg/zone/object_controller/ChangeRoleIconChoice.java create mode 100644 src/services/experience/ExperienceManager.java create mode 100644 src/services/experience/SkillService.java create mode 100644 src/services/experience/SkillTemplateService.java diff --git a/src/intents/experience/ExperienceIntent.java b/src/intents/experience/ExperienceIntent.java new file mode 100644 index 000000000..2103d46ec --- /dev/null +++ b/src/intents/experience/ExperienceIntent.java @@ -0,0 +1,74 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// 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 intents.experience; + +import resources.control.Intent; +import resources.objects.creature.CreatureObject; + +/** + * + * @author Mads + */ +public final class ExperienceIntent extends Intent { + + public static final String TYPE = "ExperienceIntent"; + + private final CreatureObject creatureObject; + private final String xpType; + private final int experienceGained; + private final EventType eventType; + + public ExperienceIntent(EventType eventType, CreatureObject creatureObject, String xpType, int experienceGained) { + super(TYPE); + this.eventType = eventType; + this.creatureObject = creatureObject; + this.xpType = xpType; + this.experienceGained = experienceGained; + } + + public EventType getEventType() { + return eventType; + } + + public CreatureObject getCreatureObject() { + return creatureObject; + } + + public String getXpType() { + return xpType; + } + + public int getExperienceGained() { + return experienceGained; + } + + public enum EventType { + GRANT, GIVEN + } + +} diff --git a/src/intents/experience/LevelChangedIntent.java b/src/intents/experience/LevelChangedIntent.java new file mode 100644 index 000000000..2176816af --- /dev/null +++ b/src/intents/experience/LevelChangedIntent.java @@ -0,0 +1,64 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// 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 intents.experience; + +import resources.control.Intent; +import resources.objects.creature.CreatureObject; + +/** + * + * @author Mads + */ +public final class LevelChangedIntent extends Intent { + + public static final String TYPE = "LevelChangedIntent"; + + private final CreatureObject creatureObject; + private final short previousLevel; + private final short newLevel; + + public LevelChangedIntent(CreatureObject creatureObject, short previousLevel, short newLevel) { + super(TYPE); + this.creatureObject = creatureObject; + this.previousLevel = previousLevel; + this.newLevel = newLevel; + } + + public CreatureObject getCreatureObject() { + return creatureObject; + } + + public short getPreviousLevel() { + return previousLevel; + } + + public short getNewLevel() { + return newLevel; + } + +} diff --git a/src/intents/experience/SkillBoxGrantedIntent.java b/src/intents/experience/SkillBoxGrantedIntent.java new file mode 100644 index 000000000..55e2365e8 --- /dev/null +++ b/src/intents/experience/SkillBoxGrantedIntent.java @@ -0,0 +1,58 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// 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 intents.experience; + +import resources.control.Intent; +import resources.objects.creature.CreatureObject; + +/** + * + * @author Mads + */ +public final class SkillBoxGrantedIntent extends Intent { + + public static final String TYPE = "SkillBoxGrantedIntent"; + + private String skillName; + private CreatureObject target; + + public SkillBoxGrantedIntent(String skillName, CreatureObject target) { + super(TYPE); + this.skillName = skillName; + this.target = target; + } + + public String getSkillName() { + return skillName; + } + + public CreatureObject getTarget() { + return target; + } + +} diff --git a/src/network/packets/swg/zone/object_controller/ChangeRoleIconChoice.java b/src/network/packets/swg/zone/object_controller/ChangeRoleIconChoice.java new file mode 100644 index 000000000..b7d7c7b54 --- /dev/null +++ b/src/network/packets/swg/zone/object_controller/ChangeRoleIconChoice.java @@ -0,0 +1,70 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// 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 network.packets.swg.zone.object_controller; + +import java.nio.ByteBuffer; + +public class ChangeRoleIconChoice extends ObjectController { + + public static final int CRC = 1101; + + private int iconChoice; + + public ChangeRoleIconChoice(long objectId) { + super(objectId, CRC); + } + + public ChangeRoleIconChoice(ByteBuffer data) { + super(CRC); + decode(data); + } + + public ChangeRoleIconChoice(long objectId, int iconChoice) { + super(objectId, CRC); + this.iconChoice = iconChoice; + } + + @Override + public void decode(ByteBuffer data) { + decodeHeader(data); + iconChoice = getInt(data); + } + + @Override + public ByteBuffer encode() { + ByteBuffer data = ByteBuffer.allocate(HEADER_LENGTH + Integer.BYTES); + encodeHeader(data); + addInt(data, iconChoice); + return data; + } + + public int getIconChoice() { + return iconChoice; + } + +} diff --git a/src/network/packets/swg/zone/object_controller/ObjectController.java b/src/network/packets/swg/zone/object_controller/ObjectController.java index ba6650474..b8e93e723 100644 --- a/src/network/packets/swg/zone/object_controller/ObjectController.java +++ b/src/network/packets/swg/zone/object_controller/ObjectController.java @@ -98,6 +98,7 @@ public abstract class ObjectController extends SWGPacket { case 0x0131: return new PostureUpdate(data); case 0x0146: return new ObjectMenuRequest(data); case 0x04C5: return new IntendedTarget(data); + case ChangeRoleIconChoice.CRC: return new ChangeRoleIconChoice(data); } Log.w("ObjectController", "Unknown object controller: %08X", crc); return new GenericObjectController(crc, data); diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 13930b4c5..9e8cb7d02 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -112,7 +112,7 @@ public class CreatureObject extends TangibleObject { private SWGSet missionCriticalObjs = new SWGSet<>(4, 13); private SWGList baseAttributes = new SWGList(1, 2); - private SWGList skills = new SWGList(1, 3, StringType.ASCII); // SWGSet + private SWGSet skills = new SWGSet(1, 3, StringType.ASCII); private SWGList hamEncumbList = new SWGList(4, 2); private SWGList attributes = new SWGList(6, 21); private SWGList maxAttributes = new SWGList(6, 22); @@ -187,7 +187,16 @@ public class CreatureObject extends TangibleObject { return appearanceList; } - public SWGList getSkills() { + public void addSkill(String skillName) { + skills.add(skillName); + skills.sendDeltaMessage(this); + } + + public boolean hasSkill(String skillName) { + return skills.contains(skillName); + } + + public SWGSet getSkills() { return skills; } @@ -263,7 +272,7 @@ public class CreatureObject extends TangibleObject { return guildId; } - public int getLevel() { + public short getLevel() { return level; } @@ -713,7 +722,10 @@ public class CreatureObject extends TangibleObject { return attributes.get(2); } - public void addAbility(String abilityName){ abilities.put(abilityName, 1); }//TODO: Figure out what the integer value should be for each ability + public void addAbility(String abilityName){ + abilities.put(abilityName, 1); //TODO: Figure out what the integer value should be for each ability + abilities.sendDeltaMessage(this); + } public void removeAbility(String abilityName) { abilities.remove(abilityName); } @@ -935,7 +947,7 @@ public class CreatureObject extends TangibleObject { bankBalance = buffer.getInt(); cashBalance = buffer.getInt(); baseAttributes = buffer.getSwgList(1, 2, Integer.class); - skills = buffer.getSwgList(1, 2, StringType.ASCII); + skills = buffer.getSwgSet(1, 2, StringType.ASCII); } protected void parseBaseline3(NetBuffer buffer) { diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index 616766b47..3c3209447 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -63,6 +63,7 @@ public class PlayerObject extends IntangibleObject { private String title = ""; private int bornDate = 0; private int playTime = 0; + private int professionIcon = 0; private String profession = ""; private int gcwPoints = 0; private int pvpKills = 0; @@ -81,7 +82,7 @@ public class PlayerObject extends IntangibleObject { private int gcwNextUpdate = 0; private String home = ""; // PLAY 08 - private SWGMap experience = new SWGMap<>(8, 0); + private SWGMap experience = new SWGMap<>(8, 0, StringType.ASCII); private SWGMap waypoints = new SWGMap<>(8, 1); private boolean citizen = false; private int guildRankTitle = 0; @@ -92,7 +93,7 @@ public class PlayerObject extends IntangibleObject { private int experimentFlag = 0; private int craftingStage = 0; private long nearbyCraftStation = 0; - private SWGList draftSchemList = new SWGList<>(9, 3); + private SWGList draftSchemList = new SWGList<>(9, 3, StringType.ASCII); private int experimentPoints = 0; private SWGList friendsList = new SWGList<>(9, 7, StringType.ASCII); private SWGList ignoreList = new SWGList<>(9, 8, StringType.ASCII); @@ -403,7 +404,7 @@ public class PlayerObject extends IntangibleObject { public void setProfWheelPosition(String profWheelPosition) { this.profWheelPosition = profWheelPosition; - sendDelta(8, 8, profWheelPosition); + sendDelta(8, 8, profWheelPosition, StringType.ASCII); } public void setFlagBitmask(PlayerFlags ... flags) { @@ -446,31 +447,27 @@ public class PlayerObject extends IntangibleObject { } } - private int getProfessionIcon() { - switch (profession) { - case "entertainer_1a": - return 5; - case "medic_1a": - return 10; - case "officer_1a": - return 15; - case "bounty_hunter_1a": - return 20; - case "smuggler_1a": - return 25; - case "commando_1a": - return 30; - case "spy_1a": - return 35; - case "force_sensitive_1a": - return 40; - case "trader_0a": - case "trader_0b": - case "trader_0c": - case "trader_0d": - default: - return 0; - } + public void setProfessionIcon(int professionIcon) { + this.professionIcon = professionIcon; + sendDelta(3, 10, professionIcon); + } + + public int getProfessionIcon() { + return professionIcon; + } + + public void addDraftSchematic(String schematic) { + draftSchemList.add(schematic); + draftSchemList.sendDeltaMessage(this); + } + + public Integer getExperiencePoints(String xpType) { + return experience.get(xpType); + } + + public void setExperiencePoints(String xpType, int experiencePoints) { + experience.put(xpType, experiencePoints); + experience.sendDeltaMessage(this); } @Override @@ -497,7 +494,7 @@ public class PlayerObject extends IntangibleObject { bb.addAscii(title); // 7 bb.addInt(bornDate); // Born Date -- 4001 = 12/15/2011 || Number of days after 12/31/2000 -- 8 bb.addInt(playTime); // 9 - bb.addInt(getProfessionIcon()); // 10 + bb.addInt(professionIcon); // 10 bb.addAscii(profession); // 11 bb.addInt(gcwPoints); // 12 bb.addInt(pvpKills); // 13 diff --git a/src/services/experience/ExperienceManager.java b/src/services/experience/ExperienceManager.java new file mode 100644 index 000000000..326a6dcd1 --- /dev/null +++ b/src/services/experience/ExperienceManager.java @@ -0,0 +1,143 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// 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 services.experience; + +import intents.experience.ExperienceIntent; +import intents.experience.LevelChangedIntent; +import java.util.HashMap; +import java.util.Map; +import resources.client_info.ClientFactory; +import resources.client_info.visitors.DatatableData; +import resources.control.Intent; +import resources.control.Manager; +import resources.objects.creature.CreatureObject; +import resources.objects.player.PlayerObject; +import resources.server_info.Log; + +/** + * The {@code ExperienceManager} listens for {@link ExperienceIntent} and + * grants XP based on it. + * @author Mads + */ +public final class ExperienceManager extends Manager { + + private SkillService skillService; + private SkillTemplateService skillTemplateService; + private final Map levelXpMap; + + public ExperienceManager() { + skillService = new SkillService(); + skillTemplateService = new SkillTemplateService(); + levelXpMap = new HashMap<>(); + + registerForIntent(ExperienceIntent.TYPE); + + addChildService(skillService); + addChildService(skillTemplateService); + } + + @Override + public boolean initialize() { + DatatableData skillTemplateTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/player/player_level.iff", true); + + for (int row = 0; row < skillTemplateTable.getRowCount(); row++) { + int level = (int) skillTemplateTable.getCell(row, 0); + int xpRequired = (int) skillTemplateTable.getCell(row, 1); + + levelXpMap.put((short) level, xpRequired); + } + + return super.initialize(); + } + + @Override + public void onIntentReceived(Intent i) { + switch(i.getType()) { + case ExperienceIntent.TYPE: handleExperienceGainedIntent((ExperienceIntent) i); break; + } + } + + private void handleExperienceGainedIntent(ExperienceIntent i) { + if (i.getEventType() == ExperienceIntent.EventType.GRANT) { + CreatureObject creatureObject = i.getCreatureObject(); + PlayerObject playerObject = creatureObject.getPlayerObject(); + String xpType = i.getXpType(); + int xpGained = i.getExperienceGained(); + + if (playerObject != null) { + Integer currentXp = playerObject.getExperiencePoints(xpType); + int newXpTotal; + + if (currentXp == null) { // They don't have this type of XP already + newXpTotal = xpGained; + } else { // They already have this kind of XP - add gained to current + newXpTotal = currentXp + xpGained; + } + + playerObject.setExperiencePoints(xpType, newXpTotal); + creatureObject.setTotalLevelXp(newXpTotal); + new ExperienceIntent(ExperienceIntent.EventType.GIVEN, creatureObject, xpType, xpGained).broadcast(); + Log.d(this, "%s gained %d %s XP", creatureObject, xpGained, xpType); + // TODO show +XP flytext + + // At this point, we check if their level should be adjusted. + short currentLevel = creatureObject.getLevel(); + + if (currentLevel == getMaxLevel()) { + // This player has already reached max level + Log.d(this, "%s is already max level (%d) - skipping remaining checks", creatureObject, currentLevel); + } else { + short nextLevel = (short) (currentLevel + 1); + Integer xpNextLevel = levelXpMap.get(nextLevel); + + if (xpNextLevel != null) { + if (newXpTotal >= xpNextLevel) { + new LevelChangedIntent(creatureObject, currentLevel, nextLevel).broadcast(); + creatureObject.setLevel(nextLevel); + // TODO increase health of creatureObject + // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); + // TODO client effect clienteffect/skill_granted.cef + // TODO audio sound/music_acq_bountyhunter.snd + Log.i(this, "%s leveled up to %d from %d", creatureObject, currentLevel, nextLevel); + } else { + Log.d(this, "%s didn't gain enough %s XP to level up from %d to %d", creatureObject, xpType, currentLevel, nextLevel); + } + } else { + Log.e(this, "%s can't become level %d because it was not found in the level-to-XP Map", creatureObject, nextLevel); + } + } + } else { + Log.e(this, "%d %s XP to %s failed because XP can't be given to NPCs", xpGained, xpType, creatureObject); + } + } + } + + private int getMaxLevel() { + return levelXpMap.size(); + } +} diff --git a/src/services/experience/SkillService.java b/src/services/experience/SkillService.java new file mode 100644 index 000000000..b4b12569f --- /dev/null +++ b/src/services/experience/SkillService.java @@ -0,0 +1,217 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// 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 services.experience; + +import intents.experience.LevelChangedIntent; +import intents.experience.SkillBoxGrantedIntent; +import intents.network.GalacticPacketIntent; +import java.util.HashMap; +import java.util.Map; +import network.packets.Packet; +import network.packets.swg.zone.object_controller.ChangeRoleIconChoice; +import resources.client_info.ClientFactory; +import resources.client_info.visitors.DatatableData; +import resources.control.Intent; +import resources.control.Service; +import resources.objects.SWGObject; +import resources.objects.creature.CreatureObject; +import resources.objects.player.PlayerObject; +import resources.server_info.Log; + +/** + * + * @author Mads + */ +public final class SkillService extends Service { + + // Maps icon index to qualifying skill. + private Map roleIconMap; + private Map skillDataMap; + + public SkillService() { + roleIconMap = new HashMap<>(); + skillDataMap = new HashMap<>(); + registerForIntent(SkillBoxGrantedIntent.TYPE); + registerForIntent(GalacticPacketIntent.TYPE); + } + + @Override + public void onIntentReceived(Intent i) { + switch(i.getType()) { + case SkillBoxGrantedIntent.TYPE: handleSkillAddIntent((SkillBoxGrantedIntent) i); break; + case GalacticPacketIntent.TYPE: handleGalacticPacket((GalacticPacketIntent) i); + } + } + + @Override + public boolean initialize() { + DatatableData roleIconTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/role/role.iff"); + + for (int i = 0; i < roleIconTable.getRowCount(); i++) { + int iconIndex = (int) roleIconTable.getCell(i, 0); + String qualifyingSkill = (String) roleIconTable.getCell(i, 2); + + roleIconMap.put(iconIndex, qualifyingSkill); + } + + DatatableData skillsTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/skill/skills.iff", true); + + for (int i = 0; i < skillsTable.getRowCount(); i++) { + String skillName = (String) skillsTable.getCell(i, 0); + String requiredSkillString = (String) skillsTable.getCell(i, 11); + String xpType = (String) skillsTable.getCell(i, 12); + int xpCost = (int) skillsTable.getCell(i, 13); + String commandsString = (String) skillsTable.getCell(i, 21); + String schematicsString = (String) skillsTable.getCell(i, 23); + + String skillModsString = (String) skillsTable.getCell(i, 22); + Map skillMods = new HashMap<>(); + if(!skillModsString.isEmpty()) { + String[] skillModsStrings = skillModsString.split(","); + + for(String skillModString : skillModsStrings) { + String[] values = skillModString.split("="); + skillMods.put(values[0], Integer.parseInt(values[1])); + } + } + + String[] requiredSkills = requiredSkillString.split(","); + if(requiredSkills.length == 1 && requiredSkills[0].isEmpty()) { + requiredSkills = new String[0]; + } + + String[] commands = commandsString.split(","); + if(commands.length == 1 && commands[0].isEmpty()) { + commands = new String[0]; + } + + String[] schematics = schematicsString.split(","); + if(schematics.length == 1 && schematics[0].isEmpty()) { + schematics = new String[0]; + } + + SkillData skillData = new SkillData( + requiredSkills, + xpType, + xpCost, + commands, + skillMods, + schematics + ); + + skillDataMap.put(skillName, skillData); + } + + return super.initialize(); + } + + private void handleSkillAddIntent(SkillBoxGrantedIntent intent) { + String skillName = intent.getSkillName(); + CreatureObject target = intent.getTarget(); + SkillData skillData = skillDataMap.get(skillName); + String[] requiredSkills = skillData.requiredSkills; + PlayerObject playerObject = target.getPlayerObject(); + + for(String requiredSkill : requiredSkills) { + if(!target.hasSkill(requiredSkill)) { + Log.w(this, "%s lacks required skill %s before being granted skill %s", target, requiredSkill, skillName); + return; + } + } + + target.addSkill(skillName); + + for(String commandName : skillData.commands) { + target.addAbility(commandName); + } + + skillData.skillMods.forEach((skillModName, skillModValue) -> target.adjustSkillmod(skillModName, 0, skillModValue)); + + for(String schematic : skillData.schematics) { + playerObject.addDraftSchematic(schematic); + } + + Log.d(this, "%s was given skill %s", target, skillName); + } + + private void handleGalacticPacket(GalacticPacketIntent gpi) { + Packet packet = gpi.getPacket(); + if (packet instanceof ChangeRoleIconChoice) { + ChangeRoleIconChoice iconChoice = (ChangeRoleIconChoice) packet; + + int chosenIcon = iconChoice.getIconChoice(); + SWGObject object = gpi.getObjectManager().getObjectById(iconChoice.getObjectId()); + + if(object instanceof CreatureObject) { + changeRoleIcon((CreatureObject) object, chosenIcon); + } else { + Log.e(this, "Could not alter role icon for object %s because it's not a CreatureObject", object); + } + } + } + + private void changeRoleIcon(CreatureObject creature, int chosenIcon) { + String qualifyingSkill = roleIconMap.get(chosenIcon); + + if (qualifyingSkill != null) { + if (creature.hasSkill(qualifyingSkill)) { + PlayerObject playerObject = creature.getPlayerObject(); + + if(playerObject != null) { + playerObject.setProfessionIcon(chosenIcon); + } else { + Log.e(this, "Could not alter role icon for PlayerObject of %s because it has none attached" , creature); + } + } else { + Log.w(this, "%s cannot use role icon %d because they lack the qualifying skill %s", creature, chosenIcon, qualifyingSkill); + } + } else { + Log.w(this, "%s tried to use undefined role icon %d", creature, chosenIcon); + } + } + + private static class SkillData { + private String[] requiredSkills; + private String xpType; + private int xpCost; + private String[] commands; + private Map skillMods; + private String[] schematics; + + public SkillData(String[] requiredSkills, String xpType, int xpCost, String[] commands, Map skillMods, String[] schematics) { + this.requiredSkills = requiredSkills; + this.xpType = xpType; + this.xpCost = xpCost; + this.commands = commands; + this.skillMods = skillMods; + this.schematics = schematics; + } + + } + +} diff --git a/src/services/experience/SkillTemplateService.java b/src/services/experience/SkillTemplateService.java new file mode 100644 index 000000000..71c096c72 --- /dev/null +++ b/src/services/experience/SkillTemplateService.java @@ -0,0 +1,100 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// 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 services.experience; + +import intents.experience.LevelChangedIntent; +import intents.experience.SkillBoxGrantedIntent; +import java.util.HashMap; +import java.util.Map; +import resources.client_info.ClientFactory; +import resources.client_info.visitors.DatatableData; +import resources.control.Intent; +import resources.control.Service; +import resources.objects.creature.CreatureObject; +import resources.objects.player.PlayerObject; +import resources.server_info.Log; + +/** + * + * @author Mads + */ +public final class SkillTemplateService extends Service { + + private final Map skillTemplates; + + SkillTemplateService() { + skillTemplates = new HashMap<>(); + registerForIntent(LevelChangedIntent.TYPE); + } + + @Override + public boolean initialize() { + DatatableData skillTemplateTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/skill_template/skill_template.iff"); + + for (int row = 0; row < skillTemplateTable.getRowCount(); row++) { + String profession = (String) skillTemplateTable.getCell(row, 0); + String[] templates = ((String) skillTemplateTable.getCell(row, 4)).split(","); + + skillTemplates.put(profession, templates); + } + return super.initialize(); + } + + @Override + public void onIntentReceived(Intent i) { + switch(i.getType()) { + case LevelChangedIntent.TYPE: handleLevelChangedIntent((LevelChangedIntent) i); break; + } + } + + private void handleLevelChangedIntent(LevelChangedIntent i) { + short newLevel = i.getNewLevel(); + CreatureObject creatureObject = i.getCreatureObject(); + + // Skills are only awarded every third or fourth level + if ((newLevel == 4 || newLevel == 7 || newLevel == 10) || ((newLevel > 10) && (((newLevel - 10) % 4) == 0))) { + PlayerObject playerObject = creatureObject.getPlayerObject(); + String profession = playerObject.getProfession(); + String[] templates = skillTemplates.get(profession); + + if(templates == null) { + Log.w(this, "%s tried to level up to %d with invalid profession %s", creatureObject, newLevel, profession); + } else { + int skillIndex = ((newLevel <= 10) ? ((newLevel - 1) / 3) : ((((newLevel - 10) / 4)) + 3)); + + String skillName = templates[skillIndex]; + new SkillBoxGrantedIntent(skillName, creatureObject).broadcast(); + playerObject.setProfWheelPosition(skillName); + // TODO roadmap reward items + } + } else { + Log.d(this, "Level %d has no skillbox - %s is rewarded nothing", newLevel, creatureObject); + } + } + +} diff --git a/src/services/galaxy/GameManager.java b/src/services/galaxy/GameManager.java index 792ecb043..a353da990 100644 --- a/src/services/galaxy/GameManager.java +++ b/src/services/galaxy/GameManager.java @@ -31,6 +31,7 @@ import resources.control.Manager; import services.collections.CollectionBadgeManager; import services.commands.CommandService; import services.commands.EntertainmentService; +import services.experience.ExperienceManager; import services.faction.FactionService; import services.galaxy.terminals.TerminalService; import services.sui.SuiService; @@ -47,6 +48,7 @@ public class GameManager extends Manager { //private final GroupService groupService; private final SkillModService skillModService; private final EntertainmentService entertainmentService; + private final ExperienceManager experienceManager; public GameManager() { commandService = new CommandService(); @@ -59,6 +61,7 @@ public class GameManager extends Manager { // groupService = new GroupService(); skillModService = new SkillModService(); entertainmentService = new EntertainmentService(); + experienceManager = new ExperienceManager(); addChildService(commandService); addChildService(connectionService); @@ -70,5 +73,6 @@ public class GameManager extends Manager { // addChildService(groupService); addChildService(skillModService); addChildService(entertainmentService); + addChildService(experienceManager); } } diff --git a/src/services/player/CharacterCreationService.java b/src/services/player/CharacterCreationService.java index ddaa83ad7..9bc65e1b5 100644 --- a/src/services/player/CharacterCreationService.java +++ b/src/services/player/CharacterCreationService.java @@ -29,6 +29,7 @@ package services.player; import intents.GalacticIntent; import intents.PlayerEventIntent; +import intents.experience.SkillBoxGrantedIntent; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -317,6 +318,7 @@ public class CharacterCreationService extends Service { creatureObj.addObject(playerObj); // ghost slot playerObj.setAdminTag(player.getAccessLevel()); player.setCreatureObject(creatureObj); + new SkillBoxGrantedIntent(create.getStartingPhase(), creatureObj).broadcast(); return creatureObj.getObjectId(); } From afaaea71001087b852e4483af879b362059a5377 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 30 Apr 2016 09:08:56 +0200 Subject: [PATCH 02/18] Removed obsolete intents.experience.ExperienceIntent.EventType enum --- src/intents/experience/ExperienceIntent.java | 8 +- .../experience/ExperienceManager.java | 95 +++++++++---------- 2 files changed, 47 insertions(+), 56 deletions(-) diff --git a/src/intents/experience/ExperienceIntent.java b/src/intents/experience/ExperienceIntent.java index 2103d46ec..549c3161b 100644 --- a/src/intents/experience/ExperienceIntent.java +++ b/src/intents/experience/ExperienceIntent.java @@ -41,20 +41,14 @@ public final class ExperienceIntent extends Intent { private final CreatureObject creatureObject; private final String xpType; private final int experienceGained; - private final EventType eventType; - public ExperienceIntent(EventType eventType, CreatureObject creatureObject, String xpType, int experienceGained) { + public ExperienceIntent(CreatureObject creatureObject, String xpType, int experienceGained) { super(TYPE); - this.eventType = eventType; this.creatureObject = creatureObject; this.xpType = xpType; this.experienceGained = experienceGained; } - public EventType getEventType() { - return eventType; - } - public CreatureObject getCreatureObject() { return creatureObject; } diff --git a/src/services/experience/ExperienceManager.java b/src/services/experience/ExperienceManager.java index 326a6dcd1..9db1c3280 100644 --- a/src/services/experience/ExperienceManager.java +++ b/src/services/experience/ExperienceManager.java @@ -63,7 +63,7 @@ public final class ExperienceManager extends Manager { @Override public boolean initialize() { - DatatableData skillTemplateTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/player/player_level.iff", true); + DatatableData skillTemplateTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/player/player_level.iff"); for (int row = 0; row < skillTemplateTable.getRowCount(); row++) { int level = (int) skillTemplateTable.getCell(row, 0); @@ -83,57 +83,54 @@ public final class ExperienceManager extends Manager { } private void handleExperienceGainedIntent(ExperienceIntent i) { - if (i.getEventType() == ExperienceIntent.EventType.GRANT) { - CreatureObject creatureObject = i.getCreatureObject(); - PlayerObject playerObject = creatureObject.getPlayerObject(); - String xpType = i.getXpType(); - int xpGained = i.getExperienceGained(); + CreatureObject creatureObject = i.getCreatureObject(); + PlayerObject playerObject = creatureObject.getPlayerObject(); + String xpType = i.getXpType(); + int xpGained = i.getExperienceGained(); - if (playerObject != null) { - Integer currentXp = playerObject.getExperiencePoints(xpType); - int newXpTotal; + if (playerObject != null) { + Integer currentXp = playerObject.getExperiencePoints(xpType); + int newXpTotal; - if (currentXp == null) { // They don't have this type of XP already - newXpTotal = xpGained; - } else { // They already have this kind of XP - add gained to current - newXpTotal = currentXp + xpGained; - } - - playerObject.setExperiencePoints(xpType, newXpTotal); - creatureObject.setTotalLevelXp(newXpTotal); - new ExperienceIntent(ExperienceIntent.EventType.GIVEN, creatureObject, xpType, xpGained).broadcast(); - Log.d(this, "%s gained %d %s XP", creatureObject, xpGained, xpType); - // TODO show +XP flytext - - // At this point, we check if their level should be adjusted. - short currentLevel = creatureObject.getLevel(); - - if (currentLevel == getMaxLevel()) { - // This player has already reached max level - Log.d(this, "%s is already max level (%d) - skipping remaining checks", creatureObject, currentLevel); - } else { - short nextLevel = (short) (currentLevel + 1); - Integer xpNextLevel = levelXpMap.get(nextLevel); - - if (xpNextLevel != null) { - if (newXpTotal >= xpNextLevel) { - new LevelChangedIntent(creatureObject, currentLevel, nextLevel).broadcast(); - creatureObject.setLevel(nextLevel); - // TODO increase health of creatureObject - // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); - // TODO client effect clienteffect/skill_granted.cef - // TODO audio sound/music_acq_bountyhunter.snd - Log.i(this, "%s leveled up to %d from %d", creatureObject, currentLevel, nextLevel); - } else { - Log.d(this, "%s didn't gain enough %s XP to level up from %d to %d", creatureObject, xpType, currentLevel, nextLevel); - } - } else { - Log.e(this, "%s can't become level %d because it was not found in the level-to-XP Map", creatureObject, nextLevel); - } - } - } else { - Log.e(this, "%d %s XP to %s failed because XP can't be given to NPCs", xpGained, xpType, creatureObject); + if (currentXp == null) { // They don't have this type of XP already + newXpTotal = xpGained; + } else { // They already have this kind of XP - add gained to current + newXpTotal = currentXp + xpGained; } + + playerObject.setExperiencePoints(xpType, newXpTotal); + creatureObject.setTotalLevelXp(newXpTotal); + Log.d(this, "%s gained %d %s XP", creatureObject, xpGained, xpType); + // TODO show +XP flytext + + // At this point, we check if their level should be adjusted. + short currentLevel = creatureObject.getLevel(); + + if (currentLevel == getMaxLevel()) { + // This player has already reached max level + Log.d(this, "%s is already max level (%d) - skipping remaining checks", creatureObject, currentLevel); + } else { + short nextLevel = (short) (currentLevel + 1); + Integer xpNextLevel = levelXpMap.get(nextLevel); + + if (xpNextLevel != null) { + if (newXpTotal >= xpNextLevel) { + new LevelChangedIntent(creatureObject, currentLevel, nextLevel).broadcast(); + creatureObject.setLevel(nextLevel); + // TODO increase health of creatureObject + // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); + // TODO client effect clienteffect/skill_granted.cef + // TODO audio sound/music_acq_bountyhunter.snd + Log.i(this, "%s leveled up to %d from %d", creatureObject, currentLevel, nextLevel); + } else { + Log.d(this, "%s didn't gain enough %s XP to level up from %d to %d", creatureObject, xpType, currentLevel, nextLevel); + } + } else { + Log.e(this, "%s can't become level %d because it was not found in the level-to-XP Map", creatureObject, nextLevel); + } + } + } else { + Log.e(this, "%d %s XP to %s failed because XP can't be given to NPCs", xpGained, xpType, creatureObject); } } From 61e48a96b8cdf1d8a00481b48b15b9b5fad3a4db Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 30 Apr 2016 09:18:27 +0200 Subject: [PATCH 03/18] Implemented recursive level-ups for amounts of XP that'll advance the creature multiple levels --- .../experience/ExperienceManager.java | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/services/experience/ExperienceManager.java b/src/services/experience/ExperienceManager.java index 9db1c3280..10fc487c8 100644 --- a/src/services/experience/ExperienceManager.java +++ b/src/services/experience/ExperienceManager.java @@ -104,36 +104,43 @@ public final class ExperienceManager extends Manager { // TODO show +XP flytext // At this point, we check if their level should be adjusted. - short currentLevel = creatureObject.getLevel(); - - if (currentLevel == getMaxLevel()) { - // This player has already reached max level - Log.d(this, "%s is already max level (%d) - skipping remaining checks", creatureObject, currentLevel); - } else { - short nextLevel = (short) (currentLevel + 1); - Integer xpNextLevel = levelXpMap.get(nextLevel); - - if (xpNextLevel != null) { - if (newXpTotal >= xpNextLevel) { - new LevelChangedIntent(creatureObject, currentLevel, nextLevel).broadcast(); - creatureObject.setLevel(nextLevel); - // TODO increase health of creatureObject - // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); - // TODO client effect clienteffect/skill_granted.cef - // TODO audio sound/music_acq_bountyhunter.snd - Log.i(this, "%s leveled up to %d from %d", creatureObject, currentLevel, nextLevel); - } else { - Log.d(this, "%s didn't gain enough %s XP to level up from %d to %d", creatureObject, xpType, currentLevel, nextLevel); - } - } else { - Log.e(this, "%s can't become level %d because it was not found in the level-to-XP Map", creatureObject, nextLevel); - } - } + attemptLevelUp(creatureObject, xpType, newXpTotal); } else { Log.e(this, "%d %s XP to %s failed because XP can't be given to NPCs", xpGained, xpType, creatureObject); } } + private void attemptLevelUp(CreatureObject creatureObject, String xpType, int newXpTotal) { + short currentLevel = creatureObject.getLevel(); + + if (currentLevel == getMaxLevel()) { + // This player has already reached max level + Log.d(this, "%s is already max level (%d) - skipping remaining checks", creatureObject, currentLevel); + } else { + short nextLevel = (short) (currentLevel + 1); + Integer xpNextLevel = levelXpMap.get(nextLevel); + + if (xpNextLevel != null) { + if (newXpTotal >= xpNextLevel) { + new LevelChangedIntent(creatureObject, currentLevel, nextLevel).broadcast(); + creatureObject.setLevel(nextLevel); + // TODO increase health of creatureObject + // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); + // TODO client effect clienteffect/skill_granted.cef + // TODO audio sound/music_acq_bountyhunter.snd + + // Recursively attempt to level up again, in case we've gained enough XP to level up multiple times. + attemptLevelUp(creatureObject, xpType, newXpTotal); + Log.i(this, "%s leveled up to %d from %d", creatureObject, currentLevel, nextLevel); + } else { + Log.d(this, "%s didn't gain enough %s XP to level up from %d to %d", creatureObject, xpType, currentLevel, nextLevel); + } + } else { + Log.e(this, "%s can't become level %d because it was not found in the level-to-XP Map", creatureObject, nextLevel); + } + } + } + private int getMaxLevel() { return levelXpMap.size(); } From 0c383ac185f78c0520a753b0e88c1b079ffdcd51 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 30 Apr 2016 09:26:18 +0200 Subject: [PATCH 04/18] SkillService now uses the SkillModIntent instead of adjusting directly --- src/services/experience/SkillService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/experience/SkillService.java b/src/services/experience/SkillService.java index b4b12569f..23360b433 100644 --- a/src/services/experience/SkillService.java +++ b/src/services/experience/SkillService.java @@ -27,6 +27,7 @@ ***********************************************************************************/ package services.experience; +import intents.SkillModIntent; import intents.experience.LevelChangedIntent; import intents.experience.SkillBoxGrantedIntent; import intents.network.GalacticPacketIntent; @@ -150,7 +151,7 @@ public final class SkillService extends Service { target.addAbility(commandName); } - skillData.skillMods.forEach((skillModName, skillModValue) -> target.adjustSkillmod(skillModName, 0, skillModValue)); + skillData.skillMods.forEach((skillModName, skillModValue) -> new SkillModIntent(skillModName, 0, skillModValue, target).broadcast()); for(String schematic : skillData.schematics) { playerObject.addDraftSchematic(schematic); From 74bb64d3b904c8d2ccea6fedae182f5230e36c4b Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 30 Apr 2016 09:49:01 +0200 Subject: [PATCH 05/18] Changed a log message in SkillService to be INFO instead of DEBUG --- src/services/experience/SkillService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/experience/SkillService.java b/src/services/experience/SkillService.java index 23360b433..eca555cbf 100644 --- a/src/services/experience/SkillService.java +++ b/src/services/experience/SkillService.java @@ -157,7 +157,7 @@ public final class SkillService extends Service { playerObject.addDraftSchematic(schematic); } - Log.d(this, "%s was given skill %s", target, skillName); + Log.i(this, "%s was given skill %s", target, skillName); } private void handleGalacticPacket(GalacticPacketIntent gpi) { From 99fad6861284764641e09348bacc7a15627ffd4f Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sun, 1 May 2016 20:53:29 +0200 Subject: [PATCH 06/18] Fixed an issue with level up effect and music spamming --- .../experience/ExperienceManager.java | 15 +++++--- .../experience/SkillTemplateService.java | 36 ++++++++++--------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/services/experience/ExperienceManager.java b/src/services/experience/ExperienceManager.java index 10fc487c8..63ebb6bad 100644 --- a/src/services/experience/ExperienceManager.java +++ b/src/services/experience/ExperienceManager.java @@ -104,7 +104,17 @@ public final class ExperienceManager extends Manager { // TODO show +XP flytext // At this point, we check if their level should be adjusted. + short oldLevel = creatureObject.getLevel(); attemptLevelUp(creatureObject, xpType, newXpTotal); + short newLevel = creatureObject.getLevel(); + + if (oldLevel > newLevel) { // If we've leveled up at least once + new LevelChangedIntent(creatureObject, oldLevel, newLevel).broadcast(); + // TODO increase health of creatureObject + // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); + // TODO client effect clienteffect/skill_granted.cef + // TODO audio sound/music_acq_bountyhunter.snd + } } else { Log.e(this, "%d %s XP to %s failed because XP can't be given to NPCs", xpGained, xpType, creatureObject); } @@ -122,12 +132,7 @@ public final class ExperienceManager extends Manager { if (xpNextLevel != null) { if (newXpTotal >= xpNextLevel) { - new LevelChangedIntent(creatureObject, currentLevel, nextLevel).broadcast(); creatureObject.setLevel(nextLevel); - // TODO increase health of creatureObject - // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); - // TODO client effect clienteffect/skill_granted.cef - // TODO audio sound/music_acq_bountyhunter.snd // Recursively attempt to level up again, in case we've gained enough XP to level up multiple times. attemptLevelUp(creatureObject, xpType, newXpTotal); diff --git a/src/services/experience/SkillTemplateService.java b/src/services/experience/SkillTemplateService.java index 71c096c72..0c5106b0a 100644 --- a/src/services/experience/SkillTemplateService.java +++ b/src/services/experience/SkillTemplateService.java @@ -73,28 +73,30 @@ public final class SkillTemplateService extends Service { } private void handleLevelChangedIntent(LevelChangedIntent i) { + short oldLevel = i.getPreviousLevel(); short newLevel = i.getNewLevel(); CreatureObject creatureObject = i.getCreatureObject(); - // Skills are only awarded every third or fourth level - if ((newLevel == 4 || newLevel == 7 || newLevel == 10) || ((newLevel > 10) && (((newLevel - 10) % 4) == 0))) { - PlayerObject playerObject = creatureObject.getPlayerObject(); - String profession = playerObject.getProfession(); - String[] templates = skillTemplates.get(profession); - - if(templates == null) { - Log.w(this, "%s tried to level up to %d with invalid profession %s", creatureObject, newLevel, profession); + for (int level = oldLevel; level < newLevel; level++) { + // Skills are only awarded every third or fourth level + if ((level == 4 || level == 7 || level == 10) || ((level > 10) && (((level - 10) % 4) == 0))) { + PlayerObject playerObject = creatureObject.getPlayerObject(); + String profession = playerObject.getProfession(); + String[] templates = skillTemplates.get(profession); + + if (templates == null) { + Log.w(this, "%s tried to level up to %d with invalid profession %s", creatureObject, level, profession); + } else { + int skillIndex = ((level <= 10) ? ((level - 1) / 3) : ((((level - 10) / 4)) + 3)); + + String skillName = templates[skillIndex]; + new SkillBoxGrantedIntent(skillName, creatureObject).broadcast(); + playerObject.setProfWheelPosition(skillName); + // TODO roadmap reward items + } } else { - int skillIndex = ((newLevel <= 10) ? ((newLevel - 1) / 3) : ((((newLevel - 10) / 4)) + 3)); - - String skillName = templates[skillIndex]; - new SkillBoxGrantedIntent(skillName, creatureObject).broadcast(); - playerObject.setProfWheelPosition(skillName); - // TODO roadmap reward items + Log.d(this, "Level %d has no skillbox - %s is rewarded nothing", level, creatureObject); } - } else { - Log.d(this, "Level %d has no skillbox - %s is rewarded nothing", newLevel, creatureObject); } } - } From 584ec4464bd1d6736a0dec001a83f353165dc7b2 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 3 May 2016 01:27:46 +0200 Subject: [PATCH 07/18] Added short javadoc for SkillTemplateService --- src/services/experience/SkillTemplateService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/experience/SkillTemplateService.java b/src/services/experience/SkillTemplateService.java index 0c5106b0a..a78f52260 100644 --- a/src/services/experience/SkillTemplateService.java +++ b/src/services/experience/SkillTemplateService.java @@ -40,7 +40,8 @@ import resources.objects.player.PlayerObject; import resources.server_info.Log; /** - * + * This is a service that listens for {@link LevelCHangedIntent} and grants + * everything linked to a skillbox. * @author Mads */ public final class SkillTemplateService extends Service { From fb4b28c9f3e4d222525a95da071afabe45d384a0 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 5 May 2016 04:40:06 +0200 Subject: [PATCH 08/18] Removed unused import in SkillService --- src/services/experience/SkillService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/experience/SkillService.java b/src/services/experience/SkillService.java index eca555cbf..58bcac81c 100644 --- a/src/services/experience/SkillService.java +++ b/src/services/experience/SkillService.java @@ -28,7 +28,6 @@ package services.experience; import intents.SkillModIntent; -import intents.experience.LevelChangedIntent; import intents.experience.SkillBoxGrantedIntent; import intents.network.GalacticPacketIntent; import java.util.HashMap; From 77b09c5efd258b1e6ad1a8071cbcecc47321fa74 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 5 May 2016 04:40:49 +0200 Subject: [PATCH 09/18] Made roleIconMap and skillDataMap fields in SkillService final --- src/services/experience/SkillService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/experience/SkillService.java b/src/services/experience/SkillService.java index 58bcac81c..6365df173 100644 --- a/src/services/experience/SkillService.java +++ b/src/services/experience/SkillService.java @@ -50,8 +50,8 @@ import resources.server_info.Log; public final class SkillService extends Service { // Maps icon index to qualifying skill. - private Map roleIconMap; - private Map skillDataMap; + private final Map roleIconMap; + private final Map skillDataMap; public SkillService() { roleIconMap = new HashMap<>(); From acab9dd4d1d0ad0db5c5e6425eec3da2764ae0e7 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 5 May 2016 07:57:11 +0200 Subject: [PATCH 10/18] Added a few TODOs in SkillTemplateService --- src/services/experience/SkillTemplateService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/experience/SkillTemplateService.java b/src/services/experience/SkillTemplateService.java index a78f52260..bc5f9429c 100644 --- a/src/services/experience/SkillTemplateService.java +++ b/src/services/experience/SkillTemplateService.java @@ -94,6 +94,9 @@ public final class SkillTemplateService extends Service { new SkillBoxGrantedIntent(skillName, creatureObject).broadcast(); playerObject.setProfWheelPosition(skillName); // TODO roadmap reward items + // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); + // TODO client effect clienteffect/skill_granted.cef + // TODO audio sound/music_acq_bountyhunter.snd } } else { Log.d(this, "Level %d has no skillbox - %s is rewarded nothing", level, creatureObject); From 3b0be70ac9c1e0c501f56aea848ae5ea2bd60669 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Fri, 6 May 2016 05:26:44 +0200 Subject: [PATCH 11/18] Fixed log spamming when leveling up multiple times with a single XP burst in ExperienceManager --- .../experience/ExperienceManager.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/services/experience/ExperienceManager.java b/src/services/experience/ExperienceManager.java index 63ebb6bad..bf01b58ad 100644 --- a/src/services/experience/ExperienceManager.java +++ b/src/services/experience/ExperienceManager.java @@ -108,12 +108,11 @@ public final class ExperienceManager extends Manager { attemptLevelUp(creatureObject, xpType, newXpTotal); short newLevel = creatureObject.getLevel(); - if (oldLevel > newLevel) { // If we've leveled up at least once + if (oldLevel < newLevel) { // If we've leveled up at least once new LevelChangedIntent(creatureObject, oldLevel, newLevel).broadcast(); - // TODO increase health of creatureObject - // TODO flytext object.showFlyText(OutOfBand.ProsePackage("@cbt_spam:skill_up"), 2.5f, new RGB(154, 205, 50), 0, true); - // TODO client effect clienteffect/skill_granted.cef - // TODO audio sound/music_acq_bountyhunter.snd + adjustHealth(creatureObject, newLevel); + adjustAction(creatureObject, newLevel); + Log.i(this, "%s leveled from %d to %d", creatureObject, oldLevel, newLevel); } } else { Log.e(this, "%d %s XP to %s failed because XP can't be given to NPCs", xpGained, xpType, creatureObject); @@ -129,23 +128,51 @@ public final class ExperienceManager extends Manager { } else { short nextLevel = (short) (currentLevel + 1); Integer xpNextLevel = levelXpMap.get(nextLevel); - + if (xpNextLevel != null) { if (newXpTotal >= xpNextLevel) { creatureObject.setLevel(nextLevel); // Recursively attempt to level up again, in case we've gained enough XP to level up multiple times. attemptLevelUp(creatureObject, xpType, newXpTotal); - Log.i(this, "%s leveled up to %d from %d", creatureObject, currentLevel, nextLevel); } else { Log.d(this, "%s didn't gain enough %s XP to level up from %d to %d", creatureObject, xpType, currentLevel, nextLevel); } } else { - Log.e(this, "%s can't become level %d because it was not found in the level-to-XP Map", creatureObject, nextLevel); + Log.d(this, "%s can't become level %d because it was not found in the level-to-XP Map", creatureObject, nextLevel); } } } + private void adjustHealth(CreatureObject creatureObject, short newLevel) { + int currentLevelHealthGranted = creatureObject.getLevelHealthGranted(); // The existing levelHealthGranted + int newLevelHealthGranted = 100 * newLevel; // new levelHealthGranted + int difference = newLevelHealthGranted - currentLevelHealthGranted; + + // Set new levelHealthGranted + creatureObject.setLevelHealthGranted(newLevelHealthGranted); + + // Add the difference to their max health + int newMaxHealth = creatureObject.getMaxHealth() + difference; + creatureObject.setMaxHealth(newMaxHealth); + + // Give them full health + creatureObject.setHealth(newMaxHealth); + } + + private void adjustAction(CreatureObject creatureObject, short newLevel) { + int currentMaxAction = creatureObject.getMaxAction(); + int newLevelActionGranted = 75 * newLevel; + int difference = newLevelActionGranted - currentMaxAction; + + // Add the difference to their max action + int newMaxAction = currentMaxAction + difference; + creatureObject.setMaxAction(newMaxAction); + + // Give them full action + creatureObject.setAction(newMaxAction); + } + private int getMaxLevel() { return levelXpMap.size(); } From f7d3bcb57ea404a61ceb901fb60709f4959c8da1 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Fri, 6 May 2016 05:37:52 +0200 Subject: [PATCH 12/18] Fixed an issue where you wouldn't always get the rewards from a skillbox --- src/services/experience/SkillTemplateService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/experience/SkillTemplateService.java b/src/services/experience/SkillTemplateService.java index bc5f9429c..49e8d1b58 100644 --- a/src/services/experience/SkillTemplateService.java +++ b/src/services/experience/SkillTemplateService.java @@ -78,7 +78,7 @@ public final class SkillTemplateService extends Service { short newLevel = i.getNewLevel(); CreatureObject creatureObject = i.getCreatureObject(); - for (int level = oldLevel; level < newLevel; level++) { + for (int level = oldLevel; level <= newLevel; level++) { // Skills are only awarded every third or fourth level if ((level == 4 || level == 7 || level == 10) || ((level > 10) && (((level - 10) % 4) == 0))) { PlayerObject playerObject = creatureObject.getPlayerObject(); From c51a35d28098f2e5e46928cae9561d7a624e130e Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Fri, 6 May 2016 05:58:12 +0200 Subject: [PATCH 13/18] Fixed possible concurrency issues with skillmods and abilities in CreatureObject --- .../objects/creature/CreatureObject.java | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 9e8cb7d02..980b71630 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -645,25 +645,29 @@ public class CreatureObject extends TangibleObject { } public void adjustSkillmod(String skillModName, int base, int modifier) { - SkillMod skillMod = skillMods.get(skillModName); - - if(skillMod == null) { - // They didn't have this SkillMod already. - // Therefore, we send a full delta. - skillMods.put(skillModName, new SkillMod(base, modifier)); - skillMods.sendDeltaMessage(this); - } else { - // They already had this skillmod. - // All we need to do is adjust the base and the modifier and send an update from the SWGMap - skillMod.adjustBase(base); - skillMod.adjustModifier(modifier); - skillMods.update(skillModName, this); + synchronized(skillMods) { + SkillMod skillMod = skillMods.get(skillModName); + + if(skillMod == null) { + // They didn't have this SkillMod already. + // Therefore, we send a full delta. + skillMods.put(skillModName, new SkillMod(base, modifier)); + skillMods.sendDeltaMessage(this); + } else { + // They already had this skillmod. + // All we need to do is adjust the base and the modifier and send an update from the SWGMap + skillMod.adjustBase(base); + skillMod.adjustModifier(modifier); + skillMods.update(skillModName, this); + } } } public int getSkillModValue(String skillModName) { - SkillMod skillMod = skillMods.get(skillModName); - return skillMod != null ? skillMod.getValue() : 0; + synchronized(skillMods) { + SkillMod skillMod = skillMods.get(skillModName); + return skillMod != null ? skillMod.getValue() : 0; + } } public boolean isVisible() { @@ -723,8 +727,10 @@ public class CreatureObject extends TangibleObject { } public void addAbility(String abilityName){ - abilities.put(abilityName, 1); //TODO: Figure out what the integer value should be for each ability - abilities.sendDeltaMessage(this); + synchronized(abilities) { + abilities.put(abilityName, 1); //TODO: Figure out what the integer value should be for each ability + abilities.sendDeltaMessage(this); + } } public void removeAbility(String abilityName) { abilities.remove(abilityName); } From 237bac4f465387169ba4a52b468c9257c0870a76 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Fri, 6 May 2016 12:04:34 +0200 Subject: [PATCH 14/18] Deleted unused inner enum EventType from ExperienceIntent --- src/intents/experience/ExperienceIntent.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/intents/experience/ExperienceIntent.java b/src/intents/experience/ExperienceIntent.java index 549c3161b..084064381 100644 --- a/src/intents/experience/ExperienceIntent.java +++ b/src/intents/experience/ExperienceIntent.java @@ -61,8 +61,4 @@ public final class ExperienceIntent extends Intent { return experienceGained; } - public enum EventType { - GRANT, GIVEN - } - } From 2669568eaf31d3880d448c82b6a707d9ff53081c Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 9 May 2016 18:06:33 +0200 Subject: [PATCH 15/18] Base action and health for leveling CreatureObjects is now set in ExperienceManager --- src/services/experience/ExperienceManager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/experience/ExperienceManager.java b/src/services/experience/ExperienceManager.java index bf01b58ad..eaa300172 100644 --- a/src/services/experience/ExperienceManager.java +++ b/src/services/experience/ExperienceManager.java @@ -112,6 +112,7 @@ public final class ExperienceManager extends Manager { new LevelChangedIntent(creatureObject, oldLevel, newLevel).broadcast(); adjustHealth(creatureObject, newLevel); adjustAction(creatureObject, newLevel); + // TODO NGE: system message health and action differences. @spam:level_up_stat_gain_# Log.i(this, "%s leveled from %d to %d", creatureObject, oldLevel, newLevel); } } else { @@ -157,6 +158,7 @@ public final class ExperienceManager extends Manager { creatureObject.setMaxHealth(newMaxHealth); // Give them full health + creatureObject.setBaseHealth(newMaxHealth); creatureObject.setHealth(newMaxHealth); } @@ -170,6 +172,7 @@ public final class ExperienceManager extends Manager { creatureObject.setMaxAction(newMaxAction); // Give them full action + creatureObject.setBaseAction(newMaxAction); creatureObject.setAction(newMaxAction); } From dad8760e53b70e2384e7d830e9fcf11d549de748 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 9 May 2016 18:07:29 +0200 Subject: [PATCH 16/18] Fixed incorrect getters for base attributes in CreatureObject and added some setters --- .../objects/creature/CreatureObject.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 980b71630..2f645f8eb 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -719,11 +719,11 @@ public class CreatureObject extends TangibleObject { } public int getMaxAction() { - return attributes.get(2); + return maxAttributes.get(2); } public int getBaseAction() { - return attributes.get(2); + return baseAttributes.get(2); } public void addAbility(String abilityName){ @@ -739,6 +739,13 @@ public class CreatureObject extends TangibleObject { public Set getAbilityNames() { return abilities.keySet(); }; + public void setBaseHealth(int baseHealth) { + synchronized(baseAttributes) { + baseAttributes.set(0, baseHealth); + baseAttributes.sendDeltaMessage(this); + } + } + public void setHealth(int health) { synchronized(attributes) { attributes.set(0, health); @@ -753,6 +760,13 @@ public class CreatureObject extends TangibleObject { } } + public void setBaseAction(int baseAction) { + synchronized(baseAttributes) { + baseAttributes.set(2, baseAction); + baseAttributes.sendDeltaMessage(this); + } + } + public void setAction(int action) { synchronized(attributes) { attributes.set(2, action); From c901a34402ab896f50398395e2602de78356f2b9 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 9 May 2016 18:08:56 +0200 Subject: [PATCH 17/18] SkillService no longer grants schematics associated with skills due to a missing structure in PLAY, causing client crashes --- src/services/experience/SkillService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/services/experience/SkillService.java b/src/services/experience/SkillService.java index 6365df173..cabea1976 100644 --- a/src/services/experience/SkillService.java +++ b/src/services/experience/SkillService.java @@ -150,11 +150,12 @@ public final class SkillService extends Service { target.addAbility(commandName); } - skillData.skillMods.forEach((skillModName, skillModValue) -> new SkillModIntent(skillModName, 0, skillModValue, target).broadcast()); +// skillData.skillMods.forEach((skillModName, skillModValue) -> new SkillModIntent(skillModName, 0, skillModValue, target).broadcast()); - for(String schematic : skillData.schematics) { - playerObject.addDraftSchematic(schematic); - } + // Ziggy: These are disabled for now, since we don't have the structs in place for it. +// for(String schematic : skillData.schematics) { + // Add schematic to PlayerObject +// } Log.i(this, "%s was given skill %s", target, skillName); } From 87773cb34311d7f33f54b7b201c5ca90e0dbe161 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 9 May 2016 18:09:52 +0200 Subject: [PATCH 18/18] Fixed incorrect an incorrect class name in the code comment for SkillTemplateService --- src/services/experience/SkillTemplateService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/experience/SkillTemplateService.java b/src/services/experience/SkillTemplateService.java index 49e8d1b58..ccd4bf789 100644 --- a/src/services/experience/SkillTemplateService.java +++ b/src/services/experience/SkillTemplateService.java @@ -40,7 +40,7 @@ import resources.objects.player.PlayerObject; import resources.server_info.Log; /** - * This is a service that listens for {@link LevelCHangedIntent} and grants + * This is a service that listens for {@link LevelChangedIntent} and grants * everything linked to a skillbox. * @author Mads */