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();
}