From eddcf012022fd522fcb04fec1c9edccf930b4cf5 Mon Sep 17 00:00:00 2001 From: Treeku Date: Sun, 18 May 2014 19:07:13 +0100 Subject: [PATCH] Implemented gcwPoints and gcwRanks --- src/resources/datatables/GcwType.java | 31 +++ src/services/ai/AIService.java | 58 ++++- src/services/ai/states/DeathState.java | 1 + src/services/gcw/FactionService.java | 76 +++++-- src/services/gcw/GCWService.java | 285 +++++++++++++++++++++++++ 5 files changed, 429 insertions(+), 22 deletions(-) create mode 100644 src/resources/datatables/GcwType.java diff --git a/src/resources/datatables/GcwType.java b/src/resources/datatables/GcwType.java new file mode 100644 index 00000000..30664208 --- /dev/null +++ b/src/resources/datatables/GcwType.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2013 + * + * This File is part of NGECore2. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Using NGEngine to work with NGECore2 is making a combined work based on NGEngine. + * Therefore all terms and conditions of the GNU Lesser General Public License cover the combination. + ******************************************************************************/ +package resources.datatables; + +public class GcwType { + + public static final byte Generic = 0; + public static final byte Enemy = 1; + public static final byte Player = 2; + public static final byte Zone = 3; + +} diff --git a/src/services/ai/AIService.java b/src/services/ai/AIService.java index 5f2f3e63..117d057a 100644 --- a/src/services/ai/AIService.java +++ b/src/services/ai/AIService.java @@ -26,12 +26,14 @@ import java.util.Map.Entry; import java.util.Random; import java.util.Vector; +import resources.datatables.Difficulty; +import resources.datatables.FactionStatus; +import resources.datatables.GcwType; import resources.objects.creature.CreatureObject; import resources.objects.group.GroupObject; import resources.objects.player.PlayerObject; - +import engine.resources.objects.SWGObject; import engine.resources.scene.Point3D; - import main.NGECore; public class AIService { @@ -109,5 +111,55 @@ public class AIService { return baseXP; } - + + public void awardGcw(AIActor actor) { + CreatureObject npc = actor.getCreature(); + + if (core.factionService.isPvpFaction(npc.getFaction())) { + int gcwPoints = 5; + + if (npc.getDifficulty() == Difficulty.ELITE) { + gcwPoints *= 2; + } + + if (npc.getDifficulty() == Difficulty.BOSS) { + gcwPoints *= 5; + } + + //gcwPoints = actor.getMobileTemplate().getGcwPoints(); // We might want to make this get set in mobile templates if it was different for different npcs ie. assault squads. + + for (CreatureObject player : actor.getDamageMap().keySet()) { + if (player.getGroupId() == 0) { + if (player.getFaction().length() > 0 && player.getFactionStatus() > FactionStatus.OnLeave) { + if ((npc.getLevel() / player.getLevel() * 100) < 86) { + continue; + } + + core.gcwService.addGcwPoints(player, gcwPoints, GcwType.Enemy); + } + } else { + for (SWGObject object : ((GroupObject) core.objectService.getObject(player.getGroupId())).getMemberList()) { + CreatureObject member = (CreatureObject) object; + + if (member == null) { + continue; + } + + if (npc.getPlanet().getName().equals(member.getPlanet().getName()) && npc.getPosition().getDistance(member.getPosition()) > 300) { + continue; + } + + if ((npc.getLevel() / member.getLevel() * 100) < 86) { + continue; + } + + if (member.getFaction().length() > 0 && member.getFactionStatus() > FactionStatus.OnLeave) { + core.gcwService.addGcwPoints(member, gcwPoints, GcwType.Enemy); + } + } + } + } + } + } + } diff --git a/src/services/ai/states/DeathState.java b/src/services/ai/states/DeathState.java index 926d60f8..f2e593cb 100644 --- a/src/services/ai/states/DeathState.java +++ b/src/services/ai/states/DeathState.java @@ -31,6 +31,7 @@ public class DeathState extends AIState { @Override public byte onEnter(AIActor actor) { NGECore.getInstance().aiService.awardExperience(actor); + NGECore.getInstance().aiService.awardGcw(actor); actor.getCreature().setAttachment("radial_filename", "npc/corpse"); //NGECore.getInstance().scriptService.callScript("scripts/radial/npc/corpse", "", "createRadial", NGECore.getInstance(), actor.getCreature().getKiller(), actor.getCreature(), new Vector()); NGECore.getInstance().lootService.DropLoot((CreatureObject)(actor.getCreature().getKiller()),(TangibleObject)(actor.getCreature())); diff --git a/src/services/gcw/FactionService.java b/src/services/gcw/FactionService.java index c5bafb16..b42d2b99 100644 --- a/src/services/gcw/FactionService.java +++ b/src/services/gcw/FactionService.java @@ -47,7 +47,9 @@ import resources.objects.creature.CreatureObject; import resources.objects.mission.MissionObject; import resources.objects.player.PlayerObject; import resources.objects.tangible.TangibleObject; +import engine.clientdata.ClientFileManager; import engine.clientdata.StfTable; +import engine.clientdata.visitors.DatatableVisitor; import engine.clients.Client; import engine.resources.common.CRC; import engine.resources.service.INetworkDispatch; @@ -352,28 +354,58 @@ public class FactionService implements INetworkDispatch { return pvpBitmask; } - // temp fix until calculatePvpStatus is fixed - /* - public int calculatePvpStatus(CreatureObject player, TangibleObject target) { - - if(target.getSlottedObject("ghost") != null) { - - if(!player.getFaction().equals(target.getFaction()) && player.getFactionStatus() == FactionStatus.SpecialForces && ((CreatureObject) target).getFactionStatus() == FactionStatus.SpecialForces) - return 55; - else if(core.combatService.areInDuel(player, (CreatureObject) target)) - return 55; - else - return 0x14; - - } else { - if(target.getAttachment("AI") != null) - return PvpStatus.Attackable; - else - return 0; + public boolean isPvpFaction(String faction) { + if (!isFaction(faction)) { + return false; } + try { + DatatableVisitor PvpFactions = ClientFileManager.loadFile("datatables/player/pvp_factions.iff", DatatableVisitor.class); + + for (int i = 0; i < PvpFactions.getRowCount(); i++) { + if (PvpFactions.getObject(i, 0) != null) { + if (((String) PvpFactions.getObject(i, 0)).equals(faction)) { + return true; + } + } + } + + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + + return false; + } + + public void join(CreatureObject actor, String faction) { + if (actor.getPvpBitmask() == PvpStatus.GoingCovert) { + actor.sendSystemMessage("@faction_recruiter:pvp_status_changing", DisplayType.Broadcast); + return; + } + + if (actor.getPvpBitmask() == PvpStatus.GoingOvert) { + actor.sendSystemMessage("@faction_recruiter:pvp_status_changing", DisplayType.Broadcast); + return; + } + + if (actor.getFaction().length() > 0) { + actor.sendSystemMessage("@faction_recruiter:resign_on_leave", DisplayType.Broadcast); + return; + } + + scheduler.schedule(() -> { + actor.setFaction(faction); + actor.setFactionStatus(FactionStatus.Combatant); + actor.setPvpBitmask(0); + PlayerObject player = ((PlayerObject) actor.getSlottedObject("ghost")); + if (player != null) { + player.resetGcwPoints(); + player.resetPvpKills(); + player.setCurrentRank(1); + } + actor.updatePvpStatus(); + }, 1, TimeUnit.SECONDS); } - */ public void changeFactionStatus(CreatureObject actor, int factionStatus) { long time = 1; @@ -464,6 +496,12 @@ public class FactionService implements INetworkDispatch { actor.setPvpBitmask(0); actor.setFactionStatus(FactionStatus.OnLeave); actor.setFaction(""); + PlayerObject player = ((PlayerObject) actor.getSlottedObject("ghost")); + if (player != null) { + player.resetGcwPoints(); + player.resetPvpKills(); + player.setCurrentRank(0); + } actor.updatePvpStatus(); actor.sendSystemMessage("@faction_recruiter:resign_complete", DisplayType.Broadcast); }, time, TimeUnit.SECONDS); diff --git a/src/services/gcw/GCWService.java b/src/services/gcw/GCWService.java index dd7bdc14..4dc8d88e 100644 --- a/src/services/gcw/GCWService.java +++ b/src/services/gcw/GCWService.java @@ -25,6 +25,8 @@ import java.math.BigDecimal; import java.math.MathContext; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -44,15 +46,20 @@ import protocol.swg.GcwGroupsRsp; import protocol.swg.GcwRegionsReq; import protocol.swg.GcwRegionsRsp; import resources.common.Opcodes; +import resources.common.OutOfBand; import resources.common.collidables.CollidableCircle; +import resources.datatables.DisplayType; import resources.datatables.FactionStatus; +import resources.datatables.GcwType; import resources.gcw.CurrentServerGCWZoneHistory; import resources.gcw.CurrentServerGCWZonePercent; import resources.gcw.OtherServerGCWZonePercent; import resources.objects.creature.CreatureObject; import resources.objects.guild.GuildObject; +import resources.objects.player.PlayerObject; import main.NGECore; import engine.clients.Client; +import engine.resources.database.ODBCursor; import engine.resources.objects.SWGObject; import engine.resources.scene.Planet; import engine.resources.scene.Point2D; @@ -63,9 +70,18 @@ import engine.resources.service.INetworkRemoteEvent; public class GCWService implements INetworkDispatch { private NGECore core; + private GuildObject object; + + private static final int[] MOD2 = { 10250, 7250, 7250, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750 }; + private static final int[] DECAY1 = { 0, 0, 0, 0, 0, 0, 2727, 2744, 2769, 2799, 2823, 2857 }; + private static final int[] DECAY2 = { 0, 0, 0, 0, 0, 0, 6000, 6500, 7200, 8200, 9600, 12000 }; + private static final int[] CONST2 = { 0, 0, 0, 0, 0, 0, 12200, 12200, 12900, 14100, 16200, 18700 }; + private static final int[] CONST4 = { 0, 0, 0, 0, 0, 0, 1950, 3550, 5600, 7800, 9350, 11100 }; + private Map> zoneMap() { return object.getZoneMap(); } private Map> pvpZones = new ConcurrentHashMap>(); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); protected final Object objectMutex = new Object(); @@ -287,6 +303,61 @@ public class GCWService implements INetworkDispatch { } } }, 2, 2, TimeUnit.DAYS); + + scheduler.scheduleAtFixedRate(new Runnable() { + @Override public void run() { gcwUpdate(); } + + private void gcwUpdate() { + ODBCursor cursor = core.getSWGObjectODB().getCursor(); + + while (cursor.hasNext()) { + SWGObject object = (SWGObject) cursor.next(); + + if (object == null) { + continue; + } + + if (core.objectService.getObject(object.getObjectID()) == null) { + object = core.objectService.getObject(object.getObjectID()); + } + + if (!(object instanceof CreatureObject)) { + continue; + } + + CreatureObject creature = (CreatureObject) object; + + if (creature.getSlottedObject("ghost") == null) { + continue; + } + + PlayerObject player = (PlayerObject) creature.getSlottedObject("ghost"); + + // TODO update statistics + + if (core.factionService.isPvpFaction(creature.getFaction())) { + updateGcwRank(creature); + player.addLifetimeGcwPoints(player.getGcwPoints()); + player.addLifetimePvpKills(player.getPvpKills()); + player.resetGcwPoints(); + player.resetPvpKills(); + + if (creature.getFaction().equals("rebel") && player.getCurrentRank() > player.getHighestRebelRank()) { + player.setHighestRebelRank(player.getCurrentRank()); + } + + if (creature.getFaction().equals("imperial") && player.getCurrentRank() > player.getHighestImperialRank()) { + player.setHighestImperialRank(player.getCurrentRank()); + } + } + } + + cursor.close(); + + calculateNextUpdateTime(); + } + + }, (calculateNextUpdateTime() - System.currentTimeMillis()), 604800000, TimeUnit.MILLISECONDS); } public void addZone(String planet, String zone, float x, float z, float radius, int weight, int type) { @@ -367,6 +438,118 @@ public class GCWService implements INetworkDispatch { } } + public void addGcwPoints(CreatureObject actor, int gcwPoints, byte gcwType) { + if (actor == null) { + return; + } + + if (!core.factionService.isPvpFaction(actor.getFaction())) { + return; + } + + if (actor.getSlottedObject("ghost") == null) { + return; + } + + PlayerObject player = (PlayerObject) actor.getSlottedObject("ghost"); + + String planet = actor.getPlanet().getName(); + + String prefix = ((planet.startsWith("space")) ? (planet + "_space") : planet); + + switch (gcwType) { + case GcwType.Enemy: + adjustZone(planet, prefix + "_pve", actor.getFaction(), gcwPoints); + actor.sendSystemMessage(OutOfBand.ProsePackage("@gcw:gcw_rank_generic_point_grant", gcwPoints), DisplayType.Broadcast); + break; + case GcwType.Player: + adjustZone(planet, prefix + "_pvp", actor.getFaction(), gcwPoints); + //actor.sendSystemMessage(OutOfBand.ProsePackage("@gcw:gcw_rank_pvp_kill_point_grant", gcwPoints), DisplayType.Broadcast); // Put this msg after this function is used rather than inside function + break; + } + + String zone = getCurrentGcwRegion(actor); + + if (zone != null) { + adjustZone(planet, zone, actor.getFaction(), gcwPoints); + } + + player.setGcwPoints(player.getGcwPoints() + gcwPoints); + } + + /* Ported from javascript. + * Written for us by Hendrik of http://swg.activeframe.de. + */ + private double helper(int points, int rank) { + int faktor = points / (MOD2[rank-1] - 250 * rank); + int rt = points / (1 + faktor) - DECAY1[rank-1]; + + if (rank >= 7) { + if (points > DECAY2[rank-1] && rt > 0) { + rt = rt * (((17 - rank) / (14 - rank)) + (CONST2[rank-1] / (points - CONST4[rank-1]))); + } + } + + return Math.floor(rt); + } + + /* Ported from javascript. + * Written for us by Hendrik of http://swg.activeframe.de. + */ + public void updateGcwRank(CreatureObject actor) { + if (actor == null) { + return; + } + + if (actor.getFaction().length() == 0) { + return; + } + + if (actor.getSlottedObject("ghost") == null) { + return; + } + + PlayerObject player = (PlayerObject) actor.getSlottedObject("ghost"); + + int oldrank = player.getCurrentRank(); + int oldprogress = (int) ((player.getRankProgress() > 100) ? 100 : player.getRankProgress()); + int oldpoints = player.getGcwPoints(); + int newrank = oldrank; + int newprogress = oldprogress; + int oldranktotal = 0; + int oldrankprogress = 0; + int newranktotal = 0; + int newrankprogress = 0; + + oldrankprogress = oldprogress * 50; + oldranktotal = 5000 * (oldrank - 1) + oldrankprogress; + + newprogress = (int) helper(oldpoints, oldrank); + + if (newprogress < -2000) { + newprogress = -2000; + } + + newranktotal = oldranktotal + newprogress; + + if (oldrank == 7 && newprogress < 0 && newranktotal < 30000) { + newranktotal = 29999; + } + + newrank = (int) (Math.floor(newranktotal / 5000) + 1); + + if (newrank > 12) { + newrank = 12; + newranktotal = 12 * 5000 - 1; + } + + newrankprogress = newranktotal - (newrank - 1) * 5000; + newprogress = newrankprogress * 100 / 5000; + + player.setCurrentRank(newrank); + player.setRankProgress((float) Math.floor(newprogress)); + } + public List getSFPlayers() { List flagged = new ArrayList(); @@ -390,6 +573,108 @@ public class GCWService implements INetworkDispatch { return flagged; } + public String getCurrentGcwRegion(CreatureObject actor) { + try { + String planet = actor.getPlanet().getName(); + + if (zoneMap().containsKey(planet)) { + for (String zoneName : zoneMap().get(planet).keySet()) { + CurrentServerGCWZonePercent zone = zoneMap().get(planet).get(zoneName).clone(); + + if (zone.getRadius() > 0) { + List objects = core.simulationService.get(core.terrainService.getPlanetByName(planet), zone.getPosition().x, zone.getPosition().z, (int) zone.getRadius()); + + if (objects.contains(actor)) { + return zoneName; + } + } + } + } + } catch (Exception e) { + return null; + } + + return null; + } + + public long calculateNextUpdateTime() { + Calendar c = Calendar.getInstance(); + Date now = new Date(); + + c.setTime(now); + + int weekday = c.get(Calendar.DAY_OF_WEEK); + + if (weekday != Calendar.THURSDAY) { + int days = ((Calendar.WEDNESDAY - weekday + 2) % 7); + c.add(Calendar.DAY_OF_YEAR, days); + } else if (c.get(Calendar.HOUR_OF_DAY) >= 7) { + c.add(Calendar.DAY_OF_YEAR, 7); + } + + c.set(Calendar.HOUR_OF_DAY, 7); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + + long nextUpdateTime = c.getTimeInMillis(); + + ODBCursor cursor = core.getSWGObjectODB().getCursor(); + + while (cursor.hasNext()) { + SWGObject object = (SWGObject) cursor.next(); + + if (object == null) { + continue; + } + + if (core.objectService.getObject(object.getObjectID()) == null) { + object = core.objectService.getObject(object.getObjectID()); + } + + if (!(object instanceof CreatureObject)) { + continue; + } + + CreatureObject creature = (CreatureObject) object; + + if (creature.getSlottedObject("ghost") == null) { + continue; + } + + PlayerObject player = (PlayerObject) creature.getSlottedObject("ghost"); + + player.setNextUpdateTime((int) nextUpdateTime); + } + + cursor.close(); + + return nextUpdateTime; + } + + public long calculateResetTime() { + Calendar c = Calendar.getInstance(); + Date now = new Date(); + + c.setTime(now); + + int weekday = c.get(Calendar.DAY_OF_WEEK); + + if (weekday != Calendar.THURSDAY) { + int days = ((Calendar.WEDNESDAY - weekday + 2) % 7); + c.add(Calendar.DAY_OF_YEAR, days); + } else if (c.get(Calendar.HOUR_OF_DAY) >= 7) { + c.add(Calendar.DAY_OF_YEAR, 7); + } + + c.set(Calendar.HOUR_OF_DAY, 7); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + + return c.getTimeInMillis(); + } + public void insertOpcodes(Map swgOpcodes, Map objControllerOpcodes) { swgOpcodes.put(Opcodes.GcwRegionsReq, new INetworkRemoteEvent() {