diff --git a/odb/cities/placeholder.txt b/odb/cities/placeholder.txt new file mode 100644 index 00000000..e69de29b diff --git a/scripts/buffs/powerup_weapon.py b/scripts/buffs/powerup_weapon.py new file mode 100644 index 00000000..740fa4e3 --- /dev/null +++ b/scripts/buffs/powerup_weapon.py @@ -0,0 +1,27 @@ +import sys +from java.lang import Float + +def setup(core, actor, buff): + + #powerup = actor.getUseTarget() + powerupID = actor.getAttachment('LastUsedPUP') + powerup = core.objectService.getObject(long(powerupID)) + effectName = powerup.getAttachment("effectName"); + powerValue = powerup.getAttachment("powerValue"); + effectName = effectName.replace('@stat_n:', '') + actor.sendSystemMessage('en %s' % effectName, 0) + actor.sendSystemMessage('pv %s' % powerValue, 0) + buff.setDuration(Float(1800.0)) + if effectName: + buff.setEffect1Name(effectName) + if powerValue: + buff.setEffect1Value(Float(powerValue)) + return + +def add(core, actor, buff): + + return + +def remove(core, actor, buff): + + return \ No newline at end of file diff --git a/scripts/cityRankCaps.py b/scripts/cityRankCaps.py new file mode 100644 index 00000000..02ce24fd --- /dev/null +++ b/scripts/cityRankCaps.py @@ -0,0 +1,13 @@ +import sys +from jarray import array + +def addCityRankCaps(core): + + cityService = core.playerCityService + cityService.addCityRankCap('corellia', array([20, 20, 15, 10, 10], 'i')) + cityService.addCityRankCap('dantooine', array([50, 50, 30, 20, 20], 'i')) + cityService.addCityRankCap('lok', array([50, 50, 30, 20, 20], 'i')) + cityService.addCityRankCap('naboo', array([20, 20, 15, 10, 10], 'i')) + cityService.addCityRankCap('rori', array([50, 50, 30, 20, 20], 'i')) + cityService.addCityRankCap('talus', array([50, 50, 30, 20, 20], 'i')) + cityService.addCityRankCap('tatooine', array([20, 20, 15, 10, 10], 'i')) \ No newline at end of file diff --git a/scripts/commands/applypowerup.py b/scripts/commands/applypowerup.py new file mode 100644 index 00000000..a490c050 --- /dev/null +++ b/scripts/commands/applypowerup.py @@ -0,0 +1,11 @@ +import sys +from java.lang import Long + +def setup(): + return + +def run(core, actor, target, objectid): + object = core.objectService.getObject(long(objectid)) + target.setAttachment('UsedObjectID',Long(objectid)) + core.objectService.useObject(actor, target) + return \ No newline at end of file diff --git a/scripts/commands/placestructure.py b/scripts/commands/placestructure.py index fa7bc65a..35e85d03 100644 --- a/scripts/commands/placestructure.py +++ b/scripts/commands/placestructure.py @@ -19,7 +19,11 @@ def run(core, actor, target, commandString): positionX = float(cmdArgs[1]) positionZ = float(cmdArgs[2]) rotation = float(cmdArgs[3]) - + + if 'cityhall' in deed.getTemplate(): + core.playerCityService.handlePlaceCity(actor, deed, positionX, positionZ, rotation) + return + core.housingService.placeStructure(actor, deed, positionX, positionZ, rotation) return diff --git a/scripts/conversation/missions/deliver.py b/scripts/conversation/missions/deliver.py index 774e3e01..126665a7 100644 --- a/scripts/conversation/missions/deliver.py +++ b/scripts/conversation/missions/deliver.py @@ -43,7 +43,7 @@ def handlePickupStage(core, actor, npc, objective, mission): endConversation(core, actor, npc) return - core.conversationService.sendStopConversation(actor, npc, mission.getMissionDescription(), 'm' + str(mission.getMissionId()) + 'p') + core.conversationService.sendStopConversation(actor, npc, mission.getMissionStf(), 'm' + str(mission.getMissionId()) + 'p') objective.createDeliveryItem(core, actor) objective.update(core, actor) return @@ -54,7 +54,7 @@ def handleDeliveryStage(core, actor, npc, objective, mission): endConversation(core, actor, npc) return - core.conversationService.sendStopConversation(actor, npc, mission.getMissionDescription(), 'm' + str(mission.getMissionId()) + 'r') + core.conversationService.sendStopConversation(actor, npc, mission.getMissionStf(), 'm' + str(mission.getMissionId()) + 'r') core.objectService.destroyObject(objective.getDeliveryObject()) objective.update(core, actor) return @@ -65,6 +65,6 @@ def handleCompletionStage(core, actor, npc, objective, mission): endConversation(core, actor, npc) return - core.conversationService.sendStopConversation(actor, npc, mission.getMissionDescription(), 'm' + str(mission.getMissionId()) + 's') + core.conversationService.sendStopConversation(actor, npc, mission.getMissionStf(), 'm' + str(mission.getMissionId()) + 's') core.missionService.handleMissionComplete(actor, mission) return \ No newline at end of file diff --git a/scripts/houses/player_cityhall_corellia_style_01.py b/scripts/houses/player_cityhall_corellia_style_01.py index ff396cb0..98ce272b 100644 --- a/scripts/houses/player_cityhall_corellia_style_01.py +++ b/scripts/houses/player_cityhall_corellia_style_01.py @@ -9,8 +9,8 @@ def setup(housingTemplates): houseTemplate.addPlaceablePlanet("corellia") houseTemplate.addPlaceablePlanet("talus") houseTemplate.setDefaultItemLimit(400) - houseTemplate.setBaseMaintenanceRate(308) - + houseTemplate.setBaseMaintenanceRate(1000) + houseTemplate.setCivicStructure(True) housingTemplates.put(houseTemplate.getDeedTemplate(), houseTemplate) return \ No newline at end of file diff --git a/scripts/houses/player_cityhall_naboo_style_01.py b/scripts/houses/player_cityhall_naboo_style_01.py index 8bb52c69..5a0be0e3 100644 --- a/scripts/houses/player_cityhall_naboo_style_01.py +++ b/scripts/houses/player_cityhall_naboo_style_01.py @@ -10,7 +10,8 @@ def setup(housingTemplates): houseTemplate.addPlaceablePlanet("rori") houseTemplate.addPlaceablePlanet("dantooine") houseTemplate.setDefaultItemLimit(400) - houseTemplate.setBaseMaintenanceRate(308) - + houseTemplate.setBaseMaintenanceRate(1000) + + houseTemplate.setCivicStructure(True) housingTemplates.put(houseTemplate.getDeedTemplate(), houseTemplate) return \ No newline at end of file diff --git a/scripts/houses/player_cityhall_tatooine_style_01.py b/scripts/houses/player_cityhall_tatooine_style_01.py index 766ed7cf..6f32c157 100644 --- a/scripts/houses/player_cityhall_tatooine_style_01.py +++ b/scripts/houses/player_cityhall_tatooine_style_01.py @@ -10,7 +10,8 @@ def setup(housingTemplates): houseTemplate.addPlaceablePlanet("dantooine") houseTemplate.addPlaceablePlanet("lok") houseTemplate.setDefaultItemLimit(400) - houseTemplate.setBaseMaintenanceRate(308) - + houseTemplate.setBaseMaintenanceRate(1000) + + houseTemplate.setCivicStructure(True) housingTemplates.put(houseTemplate.getDeedTemplate(), houseTemplate) return \ No newline at end of file diff --git a/scripts/houses/player_garden_tatooine_sml_01.py b/scripts/houses/player_garden_tatooine_sml_01.py index ed3b2981..cbbe549d 100644 --- a/scripts/houses/player_garden_tatooine_sml_01.py +++ b/scripts/houses/player_garden_tatooine_sml_01.py @@ -10,6 +10,8 @@ def setup(housingTemplates): houseTemplate.addPlaceablePlanet("dantooine") houseTemplate.addPlaceablePlanet("lok") houseTemplate.setDefaultItemLimit(0) - + + houseTemplate.setCivicStructure(True) + houseTemplate.setBaseMaintenanceRate(2000) housingTemplates.put(houseTemplate.getDeedTemplate(), houseTemplate) return \ No newline at end of file diff --git a/scripts/houses/player_shuttleport_tatooine.py b/scripts/houses/player_shuttleport_tatooine.py index 4d7e480f..7b970481 100644 --- a/scripts/houses/player_shuttleport_tatooine.py +++ b/scripts/houses/player_shuttleport_tatooine.py @@ -8,5 +8,8 @@ def setup(housingTemplates): houseTemplate.addBuildingSign("object/tangible/sign/player/shared_house_address.iff", Point3D(float(3.5),float(-0.3),float(3.5))) houseTemplate.addPlaceablePlanet("tatooine") houseTemplate.setDefaultItemLimit(0) + houseTemplate.setBaseMaintenanceRate(2000) + + houseTemplate.setCivicStructure(True) housingTemplates.put(houseTemplate.getDeedTemplate(), houseTemplate) return \ No newline at end of file diff --git a/scripts/mobiles/spawnareas/naboo_lair_center_2.py b/scripts/mobiles/spawnareas/naboo_lair_center_2.py new file mode 100644 index 00000000..23956c92 --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_center_2.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', 0, 1000, 2000, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_center_3.py b/scripts/mobiles/spawnareas/naboo_lair_center_3.py new file mode 100644 index 00000000..20fdc995 --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_center_3.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', -200, 2200, 800, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_e_1.py b/scripts/mobiles/spawnareas/naboo_lair_e_1.py new file mode 100644 index 00000000..3eae367a --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_e_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', 4900, -1200, 3700, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_n_1.py b/scripts/mobiles/spawnareas/naboo_lair_n_1.py new file mode 100644 index 00000000..2e89cafa --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_n_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', -500, 5200, 2700, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_ne_1.py b/scripts/mobiles/spawnareas/naboo_lair_ne_1.py new file mode 100644 index 00000000..2ea5493c --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_ne_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', 5300, 5000, 3300, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_nw_1.py b/scripts/mobiles/spawnareas/naboo_lair_nw_1.py new file mode 100644 index 00000000..3e4739fd --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_nw_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', -5700, 4700, 3200, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_s_1.py b/scripts/mobiles/spawnareas/naboo_lair_s_1.py new file mode 100644 index 00000000..6e755223 --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_s_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', 2800, -5700, 1900, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_s_3.py b/scripts/mobiles/spawnareas/naboo_lair_s_3.py new file mode 100644 index 00000000..c8c41d37 --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_s_3.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', -2000, -6600, 1500, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_se_1.py b/scripts/mobiles/spawnareas/naboo_lair_se_1.py new file mode 100644 index 00000000..609bfae4 --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_se_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', 6500, -6200, 2300, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/naboo_lair_w_1.py b/scripts/mobiles/spawnareas/naboo_lair_w_1.py new file mode 100644 index 00000000..d4b1c5a0 --- /dev/null +++ b/scripts/mobiles/spawnareas/naboo_lair_w_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('naboo_starter', -6500, 500, 2000, 'naboo') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_center_1.py b/scripts/mobiles/spawnareas/tatooine_lair_center_1.py new file mode 100644 index 00000000..74fa09c8 --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_center_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', -2400, 3600, 1800, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_center_2.py b/scripts/mobiles/spawnareas/tatooine_lair_center_2.py new file mode 100644 index 00000000..e941ed59 --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_center_2.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', -1300, 500, 2000, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_center_3.py b/scripts/mobiles/spawnareas/tatooine_lair_center_3.py new file mode 100644 index 00000000..8f57813a --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_center_3.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', 1700, -2200, 2400, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_e_1.py b/scripts/mobiles/spawnareas/tatooine_lair_e_1.py new file mode 100644 index 00000000..59dd2198 --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_e_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', 6300, -2200, 2600, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_n_1.py b/scripts/mobiles/spawnareas/tatooine_lair_n_1.py new file mode 100644 index 00000000..b6e3b6b9 --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_n_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', -1800, 6500, 1900, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_ne_1.py b/scripts/mobiles/spawnareas/tatooine_lair_ne_1.py new file mode 100644 index 00000000..22eb88af --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_ne_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', 4000, 4200, 4800, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_nw_1.py b/scripts/mobiles/spawnareas/tatooine_lair_nw_1.py new file mode 100644 index 00000000..f51a55fb --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_nw_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', -5900, 6100, 2500, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_s_1.py b/scripts/mobiles/spawnareas/tatooine_lair_s_1.py new file mode 100644 index 00000000..3dae081b --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_s_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', 1200, -6500, 2200, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_s_2.py b/scripts/mobiles/spawnareas/tatooine_lair_s_2.py new file mode 100644 index 00000000..e132c56c --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_s_2.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', 3700, -7000, 1500, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_se_1.py b/scripts/mobiles/spawnareas/tatooine_lair_se_1.py new file mode 100644 index 00000000..ccfc75e9 --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_se_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', 6300, -6400, 2200, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_w_1.py b/scripts/mobiles/spawnareas/tatooine_lair_w_1.py new file mode 100644 index 00000000..754b1ba0 --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_w_1.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', -5900, 1800, 2900, 'tatooine') + return diff --git a/scripts/mobiles/spawnareas/tatooine_lair_w_2.py b/scripts/mobiles/spawnareas/tatooine_lair_w_2.py new file mode 100644 index 00000000..7af63ee7 --- /dev/null +++ b/scripts/mobiles/spawnareas/tatooine_lair_w_2.py @@ -0,0 +1,6 @@ +# Spawn Area file created with PSWG Planetary Spawn Tool +import sys + +def addSpawnArea(core): + core.spawnService.addLairSpawnArea('tatooine_starter', -4500, -4300, 4300, 'tatooine') + return diff --git a/scripts/radial/structure/moveable.py b/scripts/radial/structure/moveable.py index 7977d62f..1419fae6 100644 --- a/scripts/radial/structure/moveable.py +++ b/scripts/radial/structure/moveable.py @@ -2,7 +2,6 @@ from resources.common import RadialOptions import sys def createRadial(core, owner, target, radials): - radials.clear() radials.add(RadialOptions(0, 7, 1, '')) radials.add(RadialOptions(0, 11, 1, '')) diff --git a/scripts/radial/structure/structure_management_terminal.py b/scripts/radial/structure/structure_management_terminal.py index efad7fef..d6756fec 100644 --- a/scripts/radial/structure/structure_management_terminal.py +++ b/scripts/radial/structure/structure_management_terminal.py @@ -7,26 +7,28 @@ import sys def createRadial(core, owner, target, radials): #(byte parentId, short optionId, byte optionType, String description) - radials.clear() - radials.add(RadialOptions(0, 7, 0, 'Examine')) - radials.add(RadialOptions(0, 78, 0, '@player_structure:management')) - radials.add(RadialOptions(0, 117, 0, '@player_structure:permissions')) - radials.add(RadialOptions(2, 128, 0, '@player_structure:permission_destroy')) - radials.add(RadialOptions(2, 124, 0, '@player_structure:management_status')) - radials.add(RadialOptions(2, 129, 0, '@player_structure:management_pay')) - radials.add(RadialOptions(2, 50, 0, '@base_player:set_name')) - radials.add(RadialOptions(2, 127, 0, '@player_structure:management_residence')) - radials.add(RadialOptions(2, 125, 0, '@player_structure:management_privacy : ' + core.housingService.fetchPrivacyString(target))) - radials.add(RadialOptions(2, 171, 0, '@player_structure:find_items_find_all_house_items')) - radials.add(RadialOptions(2, 173, 0, '@player_structure:move_first_item')) - radials.add(RadialOptions(2, 174, 0, '@player_structure:find_items_search_for_house_items')) - radials.add(RadialOptions(2, 175, 0, '@player_structure:delete_all_items_title')) - radials.add(RadialOptions(2, 172, 0, 'Pack Up This Building')) - radials.add(RadialOptions(3, 121, 0, '@player_structure:permission_enter')) - radials.add(RadialOptions(3, 122, 0, '@player_structure:permission_admin')) - radials.add(RadialOptions(3, 123, 0, '@player_structure:permission_banned')) - if owner.getSkillModBase('manage_vendor') >= 1: - radials.add(RadialOptions(2, 130, 0, '@player_structure:create_vendor')) + + if core.housingService.getPermissions(owner, target.getContainer()): + radials.clear() + radials.add(RadialOptions(0, 7, 0, 'Examine')) + radials.add(RadialOptions(0, 78, 0, '@player_structure:management')) + radials.add(RadialOptions(0, 117, 0, '@player_structure:permissions')) + radials.add(RadialOptions(2, 128, 0, '@player_structure:permission_destroy')) + radials.add(RadialOptions(2, 124, 0, '@player_structure:management_status')) + radials.add(RadialOptions(2, 129, 0, '@player_structure:management_pay')) + radials.add(RadialOptions(2, 50, 0, '@base_player:set_name')) + radials.add(RadialOptions(2, 127, 0, '@player_structure:management_residence')) + radials.add(RadialOptions(2, 125, 0, '@player_structure:management_privacy : ' + core.housingService.fetchPrivacyString(target))) + radials.add(RadialOptions(2, 171, 0, '@player_structure:find_items_find_all_house_items')) + radials.add(RadialOptions(2, 173, 0, '@player_structure:move_first_item')) + radials.add(RadialOptions(2, 174, 0, '@player_structure:find_items_search_for_house_items')) + radials.add(RadialOptions(2, 175, 0, '@player_structure:delete_all_items_title')) + radials.add(RadialOptions(2, 172, 0, 'Pack Up This Building')) + radials.add(RadialOptions(3, 121, 0, '@player_structure:permission_enter')) + radials.add(RadialOptions(3, 122, 0, '@player_structure:permission_admin')) + radials.add(RadialOptions(3, 123, 0, '@player_structure:permission_banned')) + if owner.getSkillModBase('manage_vendor') >= 1: + radials.add(RadialOptions(2, 130, 0, '@player_structure:create_vendor')) return @@ -58,7 +60,7 @@ def handleSelection(core, owner, target, option): return if option == 127: if owner is not None: - core.housingService.declareResidency(owner,target) + core.housingService.declareResidency(owner,target.getGrandparent()) return if option == 171: if owner is not None: diff --git a/scripts/radial/terminal/city_maintenance_terminal.py b/scripts/radial/terminal/city_maintenance_terminal.py index e5bab716..4aa8f402 100644 --- a/scripts/radial/terminal/city_maintenance_terminal.py +++ b/scripts/radial/terminal/city_maintenance_terminal.py @@ -1,64 +1,79 @@ from resources.common import RadialOptions +from resources.common import OutOfBand from protocol.swg import ResourceListForSurveyMessage from services.sui.SUIService import MessageBoxType from services.sui.SUIWindow import Trigger from java.util import Vector +from java.lang import System import main.NGECore import sys def createRadial(core, owner, target, radials): #(byte parentId, short optionId, byte optionType, String description) radials.clear() + city = core.playerCityService.getCityObjectIsIn(owner) + if not city: + return radials.add(RadialOptions(0, 7, 0, 'Examine')) - radials.add(RadialOptions(0, 216, 0, '@city/city:city_management')) - radials.add(RadialOptions(0, 226, 0, '@city/city:city_info')) - radials.add(RadialOptions(0, 227, 0, '@city/city:remove_trainers')) - radials.add(RadialOptions(2, 217, 0, '@city/city:city_name_new_t')) - radials.add(RadialOptions(2, 222, 0, '@city/city:city_register')) - radials.add(RadialOptions(2, 224, 0, '@city/city:unzone')) - radials.add(RadialOptions(2, 218, 0, '@city/city:city_militia')) - radials.add(RadialOptions(2, 219, 0, '@city/city:treasury_taxes')) - radials.add(RadialOptions(2, 221, 0, '@city/city:treasury_withdraw')) - radials.add(RadialOptions(2, 225, 0, '@city/city:city_specializations')) - radials.add(RadialOptions(2, 228, 0, '@city/city:align')) - radials.add(RadialOptions(3, 121, 0, '@city/city:non_citizen_city_status')) - radials.add(RadialOptions(3, 122, 0, '@city/city:rank_info_t')) - radials.add(RadialOptions(3, 123, 0, '@city/city:citizen_list_t')) - radials.add(RadialOptions(3, 230, 0, '@city/city:revoke_citizenship')) + radials.add(RadialOptions(0, 211, 0, '@city/city:city_info')) + if city.getMayorID() == owner.getObjectID(): + radials.add(RadialOptions(0, 216, 0, '@city/city:city_management')) - radials.add(RadialOptions(3, 231, 0, 'Add 10 citizens')) - radials.add(RadialOptions(3, 232, 0, 'Deduct 10 citizens')) + radials.add(RadialOptions(2, 212, 0, '@city/city:city_status')) + radials.add(RadialOptions(2, 213, 0, '@city/city:citizen_list_t')) + radials.add(RadialOptions(2, 214, 0, '@city/city:city_structures')) + radials.add(RadialOptions(2, 223, 0, '@city/city:rank_info_t')) + radials.add(RadialOptions(2, 224, 0, '@city/city:city_maint')) + radials.add(RadialOptions(2, 215, 0, '@city/city:treasury_status')) + radials.add(RadialOptions(2, 220, 0, '@city/city:treasury_deposit')) + + if owner.getPlayerObject().getCitizenship() != 3 and core.playerCityService.getPlayerCity(owner) == city: + radials.add(RadialOptions(2, 230, 0, '@city/city:revoke_citizenship')) + + if owner.getClient().isGM(): + radials.add(RadialOptions(2, 231, 0, 'Add 10 citizens')) + radials.add(RadialOptions(2, 232, 0, 'Deduct 10 citizens')) + + if city.getMayorID() != owner.getObjectID(): + print 'here' + return + + radials.add(RadialOptions(0, 216, 0, '@city/city:city_management')) + #radials.add(RadialOptions(0, 227, 0, '@city/city:remove_trainers')) not needed for pswg + radials.add(RadialOptions(3, 217, 0, '@city/city:city_name_new_t')) + if city.isRegistered(): + radials.add(RadialOptions(3, 222, 0, '@city/city:city_unregister')) + else: + radials.add(RadialOptions(3, 222, 0, '@city/city:city_register')) + if city.isZoningEnabled(): + radials.add(RadialOptions(3, 226, 0, '@city/city:unzone')) + else: + radials.add(RadialOptions(3, 226, 0, '@city/city:zone')) + + radials.add(RadialOptions(3, 218, 0, '@city/city:city_militia')) + radials.add(RadialOptions(3, 219, 0, '@city/city:treasury_taxes')) + radials.add(RadialOptions(3, 221, 0, '@city/city:treasury_withdraw')) + radials.add(RadialOptions(3, 225, 0, '@city/city:city_specializations')) + radials.add(RadialOptions(3, 228, 0, '@city/city:align')) return def handleSelection(core, owner, target, option): - playerCity = core.playerCityService.getPlayerCity(owner) - if playerCity==None: + playerCity = core.playerCityService.getCityObjectIsIn(owner) + if playerCity == None: return if option == 217: if owner is not None: handleSetCityName(core, owner, target, option) return - if option == 124: - if owner is not None: - core.housingService.createStatusSUIPage(owner,target) - return - if option == 129: - if owner is not None: - core.housingService.createPayMaintenanceSUIPage(owner,target) - return - if option == 50: - if owner is not None: - core.housingService.createRenameSUIPage(owner,target) - return if option == 222: if owner is not None: - handleRegisterMap(core, owner, target, option) - return - if option == 171: - if owner is not None: - core.housingService.handleListAllItems(owner,target) + if playerCity.getMayorID() == owner.getObjectID(): + if playerCity.isRegistered(): + handleUnregisterMap(core, owner, target, option) + else: + handleRegisterMap(core, owner, target, option) return if option == 219: if owner is not None: @@ -72,45 +87,134 @@ def handleSelection(core, owner, target, option): if owner is not None: handleWithdrawal(core, owner, target, option) return - if option == 121: + if option == 212: + if owner is not None: + handleCityInfo(core, owner, target, option) + return + if option == 223: if owner is not None: handleCityRankInfo(core, owner, target, option) return - if option == 122: - if owner is not None: - handleCityRankInfo(core, owner, target, option) - return - if option == 123: + if option == 213: if owner is not None: handleCitizenList(core, owner, target, option) return - if option == 231: if owner is not None: handle10Add(core, owner, target, option) return - if option == 232: if owner is not None: handle10Deduct(core, owner, target, option) return + if option == 220: + if owner is not None: + handleDeposit(core, owner, target, option) + return + if option == 226: + if owner is not None: + handleToggleZoning(core, owner, target, option) + return + +def handleDeposit(core, owner, target, option): + playerCity = core.playerCityService.getPlayerCity(owner) + suiSvc = core.suiService + suiWindow = suiSvc.createSUIWindow('Script.transfer', owner, target, 10) + suiWindow.setProperty('transaction.txtInputFrom:Text', '@city/city:total_funds') + suiWindow.setProperty('bg.caption.lblTitle:Text', '@city/city:treasury_deposit') + suiWindow.setProperty('Prompt.lblPrompt:Text', '@city/city:treasury_deposit_d') + suiWindow.setProperty('transaction.lblFrom:Text', '@city/city:total_funds') + suiWindow.setProperty('transaction.lblTo:Text', '@city/city:treasury') + suiWindow.setProperty('transaction.lblStartingFrom:Text', str(owner.getCashCredits())) + suiWindow.setProperty('transaction.lblStartingTo:Text', '') + suiWindow.setProperty('transaction.txtInputFrom:Text', str(owner.getCashCredits())) + suiWindow.setProperty('transaction.txtInputTo:Text', '') + suiWindow.setProperty('transaction:ConversionRatioFrom', '1') + suiWindow.setProperty('transaction:ConversionRatioTo', '1') + returnParams = Vector() + returnParams.add('transaction.txtInputFrom:Text') + returnParams.add('transaction.txtInputTo:Text') + suiWindow.addHandler(0, '', Trigger.TRIGGER_OK, returnParams, handleDepositSUI) + + suiSvc.openSUIWindow(suiWindow) + + return + +def handleDepositSUI(owner, window, eventType, returnList): + playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) + if not playerCity: + return + amount = int(returnList.get(0)) + cash = owner.getCashCredits() + deposit = cash - amount + if amount > cash or deposit < 1: + owner.sendSystemMessage('@city/city:positive_deposit', 0) + return + if playerCity.getCityTreasury() + deposit > 100000000: + owner.sendSystemMessage('@city/city:positive_deposit', 0) + return + playerCity.addToTreasury(deposit) + owner.deductCashCredits(deposit) + owner.sendSystemMessage(OutOfBand.ProsePackage('@city/city:deposit_treasury', deposit), 0) + return + +def handleToggleZoning(core, owner, target, option): + playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) + if not playerCity or playerCity.getMayorID() != owner.getObjectID(): + return + + playerCity.setZoningEnabled(!playerCity.isZoningEnabled()) + + if playerCity.isZoningEnabled(): + owner.sendSystemMessage('@city/city:zoning_enabled', 0) + else: + owner.sendSystemMessage('@city/city:zoning_disabled', 0) + + return + + def handle10Add(core, owner, target, option): playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) playerCity.Add10MoreCitizens() return - def handle10Deduct(core, owner, target, option): playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) playerCity.Deduct10Citizens() return -def setnameCallBack(owner, window, eventType, returnList): +def handleSetCityName(core, owner, target, option): + playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) + if not playerCity: + return + if playerCity.getCityNameChangeCooldown() > System.currentTimeMillis(): + owner.sendSystemMessage('You may only change the city name once in 4 weeks.', 0) + return + window = core.suiService.createInputBox(2, '@city/city:city_name_new_t','@city/city:city_name_new_d', owner, None, 0) + window.setProperty('txtInput:MaxLength', '40') + returnList = Vector() + returnList.add('txtInput:LocalText') + window.addHandler(0, '', Trigger.TRIGGER_OK, returnList, setNameCallBack) + + return + + +def setNameCallBack(owner, window, eventType, returnList): if returnList.size()==0: return + name = returnList.get(0) + if not core.characterService.checkName(name, owner.getClient(), True): + owner.sendSystemMessage('@player_structure:not_valid_name', 0) + return + if not core.playerCityService.doesCityNameExist(name): + owner.sendSystemMessage('@player_structure:cityname_not_unique', 0) + return + playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) - playerCity.setCityName(returnList.get(0)) + playerCity.setCityName(name) + playerCity.setCityNameChangeCooldown(System.currentTimeMillis() + str(604800 * 4 * 1000)) + # TODO send mail owner.sendSystemMessage('@city/city:name_changed', 0) return @@ -118,7 +222,7 @@ def setnameCallBack(owner, window, eventType, returnList): def handleAdjustTaxes(core, owner, target, option): playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) - if playerCity.getRank()<2: + if playerCity.getRank() < 2: owner.sendSystemMessage('@city/city:no_rank_taxes', 0) return @@ -284,54 +388,16 @@ def handleSetGarageTaxCallBack(owner, window, eventType, returnList): def handleWithdrawal(core, owner, target, option): playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) - window = core.suiService.createSUIWindow("Script.transfer", owner, target, 10) - - window.setProperty("bg.caption.lblTitle:Text", "@city/city:treasury_withdraw_subject") - window.setProperty("Prompt.lblPrompt:Text", "@city/city:treasury_withdraw_prompt" + "\n \n @city/city:treasury_status : %s" % playerCity.getCityTreasury()) - - window.setProperty("msgPayMaintenance", "transaction.txtInputFrom"); - - window.setProperty("transaction.lblFrom:Text", "@city/city:treasury_balance_t"); - window.setProperty("transaction.lblTo:Text", "@city/city:treasury_withdraw"); - window.setProperty("transaction.lblFrom", "@city/city:treasury_balance_t"); - window.setProperty("transaction.lblTo", "@city/city:treasury_withdraw"); - - window.setProperty("transaction.lblStartingFrom:Text", '%s' % playerCity.getCityTreasury()); - window.setProperty("transaction.lblStartingTo:Text", '0'); - - window.setProperty("transaction:InputFrom", "555555"); - window.setProperty("transaction:InputTo", "666666"); - - window.setProperty("transaction:txtInputFrom", '%s' % playerCity.getCityTreasury()); - window.setProperty("transaction:txtInputTo", "1"); - window.setProperty("transaction.txtInputFrom:Text", '%s' % playerCity.getCityTreasury()); - window.setProperty("transaction.txtInputTo:Text", "" + "0"); - - window.setProperty("transaction.ConversionRatioFrom", "1"); - window.setProperty("transaction.ConversionRatioTo", "0"); - - window.setProperty("btnOk:visible", "True"); - window.setProperty("btnCancel:visible", "True"); - window.setProperty("btnOk:Text", "@ok"); - window.setProperty("btnCancel:Text", "@cancel"); - - returnList = Vector() - returnList.add('transaction.txtInputFrom:Text') - returnList.add('ransaction.txtInputTo:Text') - window.addHandler(0, '', Trigger.TRIGGER_OK, returnList, setWithdrawalCallBack) - window.addHandler(1, '', Trigger.TRIGGER_CANCEL, returnList, setWithdrawalCallBack) - core.suiService.openSUIWindow(window); - return - -def setWithdrawalCallBack(owner, window, eventType, returnList): - if returnList.size()==0: + if not playerCity: return - playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) - playerCity.setCityName(returnList.get(0)) - owner.sendSystemMessage('@city/city:name_changed', 0) - return + if playerCity.getCityTreasuryWithdrawalCooldown() > System.currentTimeMillis(): + owner.sendSystemMessage('@city/city:withdraw_daily', 0) + return + core.playerCityService.handleCityTreasuryWithdrawal(owner) + -def handleCitizenList(core, owner, target, option): +def handleCitizenList(core, owner, target, option): + # wasnt there a new list layout in NGE ? window = core.suiService.createSUIWindow('Script.listBox', owner, target, 0); window.setProperty('bg.caption.lblTitle:Text', '@city/city:citizen_list_t') window.setProperty('Prompt.lblPrompt:Text', '@city/city:citizen_list_prompt') @@ -340,7 +406,12 @@ def handleCitizenList(core, owner, target, option): index = 1; for citizen in playerCity.getCitizens(): citizenObject = core.objectService.getObject(citizen) - window.addListBoxMenuItem(citizenObject.getCustomName(),index) + if not citizenObject: + continue + name = citizenObject.getCustomName() + if citizenObject.getPlayerObject().getCitizenship() == 2: + name += ' (Militia)' + window.addListBoxMenuItem(name,index) index += 1 window.setProperty('btnOk:visible', 'True') @@ -351,35 +422,43 @@ def handleCitizenList(core, owner, target, option): returnList = Vector() returnList.add('txtInput:LocalText') window.addHandler(0, '', Trigger.TRIGGER_OK, returnList, setCitizenListCallBack) - window.addHandler(1, '', Trigger.TRIGGER_CANCEL, returnList, setCitizenListCallBack) core.suiService.openSUIWindow(window); return def setCitizenListCallBack(owner, window, eventType, returnList): - #handle waypoint creation to citizen's house + idx = returnList.get(0) + # todo add waypoint return -def handleCityRankInfo(core, owner, target, option): - window = core.suiService.createSUIWindow('Script.listBox', owner, target, 0); - window.setProperty('bg.caption.lblTitle:Text', '@city/city:rank_info_t') - window.setProperty('Prompt.lblPrompt:Text', '@city/city:rank_info_d') +def handleCityInfo(core, owner, target, option): + window = core.suiService.createSUIWindow('Script.listBox', owner, None, 0); + window.setProperty('bg.caption.lblTitle:Text', '@city/city:city_info_t') + window.setProperty('Prompt.lblPrompt:Text', '@city/city:city_info_d') playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) - - window.addListBoxMenuItem('City : ' + playerCity.getCityName(),0) - window.addListBoxMenuItem('@city/city:radius_prompt : %s' % playerCity.getCityRadius(),1) - window.addListBoxMenuItem('@city/city:city_rank_prompt : @city/city:rank' + str(playerCity.getRank()),2) - window.addListBoxMenuItem('@city/city:reg_citizen_prompt : %s' % len(playerCity.getCitizens()),3) - if playerCity.getRank()>=3 and playerCity.getSpecialization()>-1: + mayor = core.objectService.getObject(playerCity.getMayorID()) + if not mayor: + mayor = core.objectService.getCreatureFromDB(playerCity.getMayorID()) + window.addListBoxMenuItem('@city/city:name_prompt : ' + playerCity.getCityName(),0) + if mayor: + window.addListBoxMenuItem('@city/city:mayor_prompt : ' + mayor.getCustomName(),5) + window.addListBoxMenuItem('@city/city:location_prompt : ' + str(playerCity.getCityCenterPosition().x) + ' ' + str(playerCity.getCityCenterPosition().z),6) + window.addListBoxMenuItem('@city/city:radius_prompt : %s' % playerCity.getCityRadius(),1) + window.addListBoxMenuItem('@city/city:reg_citizen_prompt : %s' % len(playerCity.getCitizens()),7) + window.addListBoxMenuItem('@kb/kb_player_cities_n:civic_structures_n : %s' % playerCity.getCivicStructuresCount(),8) + window.addListBoxMenuItem('@city/city:decorations : %s' % 0,9) # todo when decorations are implemented + if playerCity.getRank() >= 3 and playerCity.getSpecialization() > -1: window.addListBoxMenuItem('@city/city:specialization_prompt : @city/city:' + playerCity.getSpecializationSTFNamesAsList().get(playerCity.getSpecialization()),4) + + window.addListBoxMenuItem('@city/city:city_rank_prompt : @city/city:rank' + str(playerCity.getRank()),2) + # TODO add tax info window.setProperty('btnOk:visible', 'True') window.setProperty('btnCancel:visible', 'True') window.setProperty('btnOk:Text', '@ok') window.setProperty('btnCancel:Text', '@cancel') window.setProperty('btnCancel:Text', '@cancel') - playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) returnList = Vector() returnList.add('txtInput:LocalText') window.addHandler(0, '', Trigger.TRIGGER_OK, returnList, setCityInfoCallBack) @@ -391,6 +470,29 @@ def setCityInfoCallBack(owner, window, eventType, returnList): #handle waypoint creation to citizen's house return +def handleCityRankInfo(core, owner, target, option): + window = core.suiService.createSUIWindow('Script.listBox', owner, None, 0); + window.setProperty('bg.caption.lblTitle:Text', '@city/city:rank_info_t') + window.setProperty('Prompt.lblPrompt:Text', '@city/city:rank_info_d') + + playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) + rank = playerCity.getRank() + nextRank = rank + 1 + if nextRank > 5: + nextRank = 5 + window.addListBoxMenuItem('@city/city:city_rank_prompt : @city/city:rank' + str(playerCity.getRank()),1) + window.addListBoxMenuItem('@city/city:reg_citizen_prompt : %s' % len(playerCity.getCitizens()),2) + window.addListBoxMenuItem('@city/city:pop_req_current_rank : ' + str(playerCity.getReqCitizenCountForRank(rank)),3) + window.addListBoxMenuItem('@city/city:pop_req_next_rank : ' + str(playerCity.getReqCitizenCountForRank(nextRank)),4) + window.setProperty('btnOk:visible', 'True') + window.setProperty('btnCancel:visible', 'True') + window.setProperty('btnOk:Text', '@ok') + window.setProperty('btnCancel:Text', '@cancel') + window.setProperty('btnCancel:Text', '@cancel') + core.suiService.openSUIWindow(window); + + return + def handleRegisterMap(core, owner, target, option): playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) @@ -401,11 +503,31 @@ def handleRegisterMap(core, owner, target, option): if owner.getObjectId()!=playerCity.getMayorID(): owner.sendSystemMessage('@city/city:cant_register', 0) return + + if playerCity.isRegistered(): + return - # now register it somehow - + core.mapService.addLocation(owner.getPlanet(), playerCity.getCityName(), playerCity.getCityCenterPosition().x, playerCity.getCityCenterPosition().z, 17, 0, 0) return +def handleUnregisterMap(core, owner, target, option): + + playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) + if playerCity.getRank()<3: + owner.sendSystemMessage('@city/city:cant_register_rank', 0) + return + + if owner.getObjectId()!=playerCity.getMayorID(): + owner.sendSystemMessage('@city/city:cant_register', 0) + return + + if not playerCity.isRegistered(): + return + + core.mapService.removeLocation(owner.getPlanet(), playerCity.getCityCenterPosition().x, playerCity.getCityCenterPosition().z, 17) + return + + def setRegisterMapCallBack(owner, window, eventType, returnList): if returnList.size()==0: return @@ -417,7 +539,7 @@ def setRegisterMapCallBack(owner, window, eventType, returnList): def handleSpecialization(core, owner, target, option): playerCity = main.NGECore.getInstance().playerCityService.getPlayerCity(owner) - if playerCity.getRank()<3: + if playerCity.getRank() < 3: owner.sendSystemMessage('@city/city:no_rank_spec', 0) return @@ -434,9 +556,6 @@ def handleSpecialization(core, owner, target, option): window.addListBoxMenuItem('@city/city:city_spec_doctor',6) window.addListBoxMenuItem('@city/city:city_spec_research',7) window.addListBoxMenuItem('@city/city:city_spec_sample_rich',8) - window.addListBoxMenuItem('@city/city:city_spec_master_manufacturing',9) - window.addListBoxMenuItem('@city/city:city_spec_master_healing',10) - window.addListBoxMenuItem('@city/city:city_spec_stronghold',11) window.setProperty('btnOk:visible', 'True') window.setProperty('btnCancel:visible', 'True') diff --git a/src/main/NGECore.java b/src/main/NGECore.java index 3f7295f4..757f4d39 100644 --- a/src/main/NGECore.java +++ b/src/main/NGECore.java @@ -99,6 +99,7 @@ import services.object.ObjectId; import services.object.ObjectService; import services.object.UpdateService; import services.pet.MountService; +import services.playercities.PlayerCity; import services.playercities.PlayerCityService; import services.resources.HarvesterService; import services.resources.ResourceService; @@ -228,6 +229,7 @@ public class NGECore { private ObjectDatabase resourceHistoryODB; private ObjectDatabase swgObjectODB; private ObjectDatabase bountiesODB; + private ObjectDatabase cityODB; public static boolean PACKET_DEBUG = false; @@ -306,6 +308,7 @@ public class NGECore { resourceHistoryODB = new ObjectDatabase("resourcehistory", true, true, true, GalacticResource.class); auctionODB = new ObjectDatabase("auction", true, true, true, AuctionItem.class); bountiesODB = new ObjectDatabase("bounties", true, true, true, BountyListItem.class); + cityODB = new ObjectDatabase("cities", true, true, true, PlayerCity.class); // Services loginService = new LoginService(this); @@ -520,7 +523,8 @@ public class NGECore { staticService.spawnStatics(); equipmentService.loadBonusSets(); - + playerCityService.loadCityRankCaps(); + playerCityService.loadCities(); retroService.run(); didServerCrash = false; @@ -676,7 +680,11 @@ public class NGECore { public ObjectDatabase getAuctionODB() { return auctionODB; } - + + public ObjectDatabase getCityODB() { + return cityODB; + } + public int getActiveClients() { int connections = 0; for (Map.Entry c : clients.entrySet()) { @@ -784,6 +792,7 @@ public class NGECore { objectIdODB.close(); duplicateIdODB.close(); auctionODB.close(); + cityODB.close(); } public String getMotd() { diff --git a/src/resources/common/AnimationType.java b/src/resources/common/AnimationType.java index b0bd9a17..d426e009 100644 --- a/src/resources/common/AnimationType.java +++ b/src/resources/common/AnimationType.java @@ -1,3 +1,24 @@ +/******************************************************************************* + * 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.common; public class AnimationType { diff --git a/src/resources/common/ProsePackage.java b/src/resources/common/ProsePackage.java index bdb10e5f..0d7b9dcc 100644 --- a/src/resources/common/ProsePackage.java +++ b/src/resources/common/ProsePackage.java @@ -221,4 +221,11 @@ public class ProsePackage implements Serializable { this.dfFloat = dfFloat; } + public void init() { + stf.init(null); + tuStf.init(null); + ttStf.init(null); + toStf.init(null); + } + } diff --git a/src/resources/datatables/CivicStructures.java b/src/resources/datatables/CivicStructures.java new file mode 100644 index 00000000..8de2f8c9 --- /dev/null +++ b/src/resources/datatables/CivicStructures.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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 CivicStructures { + + public static final int BANK = 1; + public static final int CLONING_FACILITY = 2; + public static final int MEDICAL_CENTER = 3; + public static final int GARAGE = 4; + public static final int CANTINA = 5; + public static final int THEATER = 6; + public static final int SHUTTLEPORT = 7; + public static final int SMALL_GARDEN = 8; + public static final int MEDIUM_GARDEN = 9; + public static final int LARGE_GARDEN = 10; + + // [CiviCode - 1] for rank + public static final int[] rankRequired = new int[] { 2, 3, 3, 2, 2, 4, 4, 1, 2, 3}; + +} diff --git a/src/resources/objects/building/BuildingObject.java b/src/resources/objects/building/BuildingObject.java index ce8bc83e..9ee735d1 100644 --- a/src/resources/objects/building/BuildingObject.java +++ b/src/resources/objects/building/BuildingObject.java @@ -64,7 +64,7 @@ public class BuildingObject extends TangibleObject implements IPersistent, Seria super(objectID, planet, position, orientation, Template); getBaseline(3).set("volume", 255); setOptionsBitmask(Options.INVULNERABLE); - setConditionDamage(100); + setConditionDamage(0); setMaximumCondition(4320); setStaticObject(true); } @@ -73,7 +73,7 @@ public class BuildingObject extends TangibleObject implements IPersistent, Seria super(); getBaseline(3).set("volume", 255); setOptionsBitmask(Options.INVULNERABLE); - setConditionDamage(100); + setConditionDamage(0); setMaximumCondition(4320); setStaticObject(true); } @@ -84,7 +84,7 @@ public class BuildingObject extends TangibleObject implements IPersistent, Seria defendersList = new Vector(); getBaseline(3).set("volume", 255); setOptionsBitmask(Options.INVULNERABLE); - setConditionDamage(100); + setConditionDamage(0); setMaximumCondition(4320); } @@ -92,6 +92,7 @@ public class BuildingObject extends TangibleObject implements IPersistent, Seria public Baseline getOtherVariables() { Baseline baseline = super.getOtherVariables(); baseline.put("maintenanceAmount", (float) 0); + baseline.put("outstandingMaintenance", 0); baseline.put("baseMaintenanceRate", 0); baseline.put("deedTemplate", ""); baseline.put("residency", false); @@ -160,6 +161,7 @@ public class BuildingObject extends TangibleObject implements IPersistent, Seria PortalVisitor portal = ClientFileManager.loadFile(portalLayoutFilename, PortalVisitor.class); for (int i = 0; i <= portal.cellCount; i++) { + System.out.println("Cellname: " + portal.cells.get(i).name); if (cellName.equals(portal.cells.get(i).name)) { return getCellByCellNumber(i + 1); } @@ -192,12 +194,21 @@ public class BuildingObject extends TangibleObject implements IPersistent, Seria otherVariables.set("maintenanceAmount", maintenanceAmount); } + public int getOutstandingMaintenance() { + return (int) otherVariables.get("outstandingMaintenance"); + } + + public void setOutstandingMaintenance(int maintenanceAmount) { + otherVariables.set("outstandingMaintenance", maintenanceAmount); + } + public int getBMR() { return (int) otherVariables.get("baseMaintenanceRate"); } public void setBMR(int BMR) { otherVariables.set("baseMaintenanceRate", BMR); + setMaximumCondition(BMR); } public String getDeedTemplate(){ diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 2c76eff7..e6284a4e 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -1853,4 +1853,14 @@ public class CreatureObject extends TangibleObject implements Serializable { } } } + + public String getFirstName() + { + return getCustomName().split(" ")[0]; + } + + public String getLastName() + { + return getCustomName().split(" ")[1]; + } } diff --git a/src/services/CharacterService.java b/src/services/CharacterService.java index 5e44d250..948a49b8 100644 --- a/src/services/CharacterService.java +++ b/src/services/CharacterService.java @@ -75,6 +75,7 @@ public class CharacterService implements INetworkDispatch { private DatabaseConnection databaseConnection2; private engine.resources.common.NameGen nameGenerator; private static final String allowedCharsRegex = "['-]?[A-Za-z]('[a-zA-Z]|-[a-zA-Z]|[a-zA-Z])*['-]?$"; + private static final String allowedCharsRegexWithSpace = "['-]?[A-Za-z]('[a-zA-Z]|-[a-zA-Z]|[a-zA-Z]| )*['-]?$"; public CharacterService(NGECore core) { @@ -89,9 +90,13 @@ public class CharacterService implements INetworkDispatch { } public boolean checkName(String name, Client client) { + return checkName(name, client, false); + } + + public boolean checkName(String name, Client client, boolean allowSpaces) { // TODO: check for dev names, profane names, iconic names etc try { - if(checkForDuplicateName(name, client.getAccountId()) || !name.matches(allowedCharsRegex)) + if(checkForDuplicateName(name, client.getAccountId()) || (!allowSpaces && !name.matches(allowedCharsRegex)) || (allowSpaces && !name.matches(allowedCharsRegexWithSpace))) return false; } catch (SQLException e) { e.printStackTrace(); diff --git a/src/services/LootService.java b/src/services/LootService.java index 744798d3..7fdf1f08 100644 --- a/src/services/LootService.java +++ b/src/services/LootService.java @@ -44,6 +44,8 @@ import protocol.swg.PlayClientEffectObjectTransformMessage; import protocol.swg.StopClientEffectObjectByLabel; import resources.common.OutOfBand; import resources.datatables.DisplayType; +import resources.datatables.Options; +import resources.datatables.WeaponType; import resources.loot.LootGroup; import resources.loot.LootRollSession; import resources.objects.creature.CreatureObject; @@ -2854,6 +2856,38 @@ public class LootService implements INetworkDispatch { skillEnhancingAttachment2.setAttachment("SEAmodifierValueList", effectValueList); playerInventory.add(skillEnhancingAttachment2); + WeaponObject sword1 = (WeaponObject) core.objectService.createObject("object/weapon/melee/sword/shared_sword_01.iff", player.getPlanet()); + sword1.setIntAttribute("required_combat_level", 90); + sword1.setAttackSpeed(1); + sword1.setMaxRange(5); + sword1.setDamageType("kinetic"); + sword1.setMinDamage(1100); + sword1.setMaxDamage(1200); + sword1.setOptions(Options.SOCKETED, true); + sword1.setOptions(Options.USABLE, true); + sword1.setWeaponType(WeaponType.ONEHANDEDMELEE); + playerInventory.add(sword1); + System.err.println("MIX " + sword1.getObjectID()); + + + String powerUpLabel = "item_reverse_engineering_powerup_weapon_02_01"; + String powerUpDescription = "item_reverse_engineering_powerup_weapon_02_01"; + String powerUpTemplate = "object/tangible/powerup/base/shared_weapon_base.iff"; + + int powerValue = 12; + TangibleObject powerUp = (TangibleObject) core.objectService.createObject(powerUpTemplate, player.getPlanet()); + + powerUp.setStfFilename("static_item_n"); + powerUp.setStfName(powerUpLabel); + powerUp.setDetailFilename("static_item_d"); + powerUp.setDetailName(powerUpDescription); + powerUp.setIntAttribute("@stat_n:droid_experimentation", powerValue); + powerUp.setAttachment("effectName","droid_experimentation"); + powerUp.setAttachment("powerValue",powerValue); + powerUp.setIntAttribute("num_in_stack", 250); + powerUp.setAttachment("radial_filename", "item/item"); + playerInventory.add(powerUp); + } diff --git a/src/services/SimulationService.java b/src/services/SimulationService.java index 85862e17..0f8ac7ba 100644 --- a/src/services/SimulationService.java +++ b/src/services/SimulationService.java @@ -230,7 +230,6 @@ public class SimulationService implements INetworkDispatch { @SuppressWarnings("unchecked") Vector childObjects = (Vector) object.getAttachment("childObjects"); if(childObjects != null) { addChildObjects(object, childObjects); - object.setAttachment("childObjects", null); } if(notifyObservers) { diff --git a/src/services/TerrainService.java b/src/services/TerrainService.java index 7905763e..9cc2924a 100644 --- a/src/services/TerrainService.java +++ b/src/services/TerrainService.java @@ -30,14 +30,13 @@ import java.util.concurrent.ConcurrentHashMap; import resources.common.FileUtilities; import resources.common.collidables.CollidableCircle; - +import services.playercities.ClientRegion; import engine.clientdata.ClientFileManager; import engine.clientdata.visitors.DatatableVisitor; import engine.resources.config.Config; import engine.resources.objects.SWGObject; import engine.resources.scene.Planet; import engine.resources.scene.Point3D; - import main.NGECore; public class TerrainService { @@ -45,6 +44,7 @@ public class TerrainService { private NGECore core; private List planets = Collections.synchronizedList(new ArrayList()); private Map> noBuildAreas = new ConcurrentHashMap>(); + private Map> clientRegions = new ConcurrentHashMap>(); public TerrainService(NGECore core) { this.core = core; @@ -88,13 +88,15 @@ public class TerrainService { for (int i = 0; i < regionTable.getRowCount(); i++) { + String name = (String) regionTable.getObject(i, 0); float x = (Float) regionTable.getObject(i, 1); float z = (Float) regionTable.getObject(i, 2); float radius = (Float) regionTable.getObject(i, 3); CollidableCircle region = new CollidableCircle(new Point3D(x, 0, z), radius, planet); noBuildAreas.get(planet).add(region); - + ClientRegion clientRegion = new ClientRegion(name, new Point3D(x, 0, z), radius, planet); + clientRegions.get(planet).add(clientRegion); } } catch (InstantiationException | IllegalAccessException e) { @@ -174,6 +176,7 @@ public class TerrainService { planets.add(planet); core.mapService.addPlanet(planet); noBuildAreas.put(planet, new ArrayList()); + clientRegions.put(planet, new ArrayList()); loadClientRegions(planet); } @@ -299,6 +302,14 @@ public class TerrainService { return planet.getTerrainVisitor().isWater(worldPosition.x, worldPosition.z); } - + public List getClientRegionsForPlanet(Planet planet) { + return clientRegions.get(planet); + } + + public List getClientRegions() { + List regions = new ArrayList(); + clientRegions.values().forEach(regions::addAll); + return regions; + } } diff --git a/src/services/chat/ChatService.java b/src/services/chat/ChatService.java index 84835f18..95747297 100644 --- a/src/services/chat/ChatService.java +++ b/src/services/chat/ChatService.java @@ -350,7 +350,7 @@ public class ChatService implements INetworkDispatch { return; mail.setStatus(Mail.READ); - + mail.init(); sendPersistentMessage(client, mail); storePersistentMessage(mail); diff --git a/src/services/chat/Mail.java b/src/services/chat/Mail.java index c5f829b4..2cad7452 100644 --- a/src/services/chat/Mail.java +++ b/src/services/chat/Mail.java @@ -27,7 +27,7 @@ import java.util.List; import resources.common.OutOfBand; import resources.common.ProsePackage; - +import engine.resources.common.Stf; import com.sleepycat.persist.model.Entity; import com.sleepycat.persist.model.PrimaryKey; @@ -41,7 +41,7 @@ public class Mail implements Serializable { private String senderName; private long recieverId; private String subject; - private String message; + private String message = ""; private byte status; private int timeStamp; private List attachments = new ArrayList(); @@ -149,5 +149,9 @@ public class Mail implements Serializable { public void addProseAttachment(ProsePackage prose) { proseAttachments.add(prose); } + + public void init() { + proseAttachments.forEach(ProsePackage::init); + } } diff --git a/src/services/housing/HouseTemplate.java b/src/services/housing/HouseTemplate.java index 78f7715f..b13b5a93 100644 --- a/src/services/housing/HouseTemplate.java +++ b/src/services/housing/HouseTemplate.java @@ -15,6 +15,8 @@ public class HouseTemplate { private int baseMaintenanceRate; private Vector placeablePlanets; private Map buildingSigns; + private boolean isCivicStructure; + private int civicStructureType; public HouseTemplate(String deedTemplate, String buildingTemplate, int lotCost) { this.deedTemplate = deedTemplate; @@ -77,5 +79,21 @@ public class HouseTemplate { public void setBaseMaintenanceRate(int baseMaintenanceRate) { this.baseMaintenanceRate = baseMaintenanceRate; } + + public boolean isCivicStructure() { + return isCivicStructure; + } + + public void setCivicStructure(boolean isCivicStructure) { + this.isCivicStructure = isCivicStructure; + } + + public int getCivicStructureType() { + return civicStructureType; + } + + public void setCivicStructureType(int civicStructureType) { + this.civicStructureType = civicStructureType; + } } diff --git a/src/services/housing/HousingService.java b/src/services/housing/HousingService.java index 1cd56f1f..cb7305ad 100644 --- a/src/services/housing/HousingService.java +++ b/src/services/housing/HousingService.java @@ -103,25 +103,25 @@ public class HousingService implements INetworkDispatch { } } - public void placeStructure(final CreatureObject actor, TangibleObject deed, float positionX, float positionZ, float rotation) { + public BuildingObject placeStructure(final CreatureObject actor, TangibleObject deed, float positionX, float positionZ, float rotation) { HouseTemplate houseTemplate = housingTemplates.get(deed.getTemplate()); int structureLotCost = houseTemplate.getLotCost(); String structureTemplate = houseTemplate.getBuildingTemplate(); if (!houseTemplate.canBePlacedOn(actor.getPlanet().getName())) { actor.sendSystemMessage("You may not place this structure on this planet.", (byte) 0); // should probably load this from an stf - return; + return null; } if(!actor.getClient().isGM() && !core.terrainService.canBuildAtPosition(actor, positionX, positionZ)) { actor.sendSystemMessage("You may not place a structure here.", (byte) 0); // should probably load this from an stf - return; + return null; } // Lot stuff if (!actor.getPlayerObject().deductLots(structureLotCost)) { actor.sendSystemMessage("You do not have enough available lots to place this structure.", (byte) 0); // should probably load this from an stf - return; + return null; } // Calculate our orientation and height @@ -146,27 +146,23 @@ public class HousingService implements INetworkDispatch { core.objectService.destroyObject(deed); - building.setAttachment("sign", sign); // meh workaround building.setAttachment("nextMaintenance", System.currentTimeMillis() + 3600000); building.setAttachment("structureOwner", actor.getObjectID()); building.setAttachment("isCondemned", false); + building.setAttachment("isCivicStructure", houseTemplate.isCivicStructure()); + building.setAttachment("civicStructureType", houseTemplate.getCivicStructureType()); building.setAttachment("outstandingMaint", 0); building.addPlayerToAdminList(null, actor.getObjectID(), playerFirstName); building.setDeedTemplate(deed.getTemplate()); building.setMaintenanceAmount(houseTemplate.getBaseMaintenanceRate()); building.setConditionDamage(100); // Ouch - if (structureTemplate.contains("cityhall")){ - PlayerCity newCity = core.playerCityService.addNewPlayerCity(actor); - building.setAttachment("structureCity", newCity.getCityID()); - actor.setAttachment("residentCity", newCity.getCityID()); - } - + /* // Check for city founders joining a new city PlayerCity cityActorIsIn = core.playerCityService.getCityObjectIsIn(actor); - if (cityActorIsIn!=null) { + if (cityActorIsIn != null) { actor.setAttachment("Has24HZoningFor",cityActorIsIn.getCityID()); // for testing int cityActorHasZoning = (int)actor.getAttachment("Has24HZoningFor"); @@ -178,14 +174,17 @@ public class HousingService implements INetworkDispatch { cityActorIsIn.addNewStructure(building.getObjectID()); } } - } + }*/ core.objectService.persistObject(building.getObjectID(), building, core.getSWGObjectODB()); - + return building; } public void startMaintenanceTask(BuildingObject building) { + if(building.getAttachment("isCivicStructure") != null && (boolean) building.getAttachment("isCivicStructure")) + return; + AtomicReference> ref = new AtomicReference>(); ref.set(scheduler.scheduleAtFixedRate(() -> { @@ -213,7 +212,7 @@ public class HousingService implements INetworkDispatch { building.setAttachment("isCondemned", true); building.setBuildingName(building.getBuildingName() + " \\#FF0000(CONDEMNED)\\#FFFFFF"); } - building.setAttachment("outstandingMaint", (int) building.getAttachment("outstandingMaint") + amount); + building.setOutstandingMaintenance(building.getOutstandingMaintenance() + amount); // TODO: lock down building, add option to pay outstanding maintenance and uncondemn building } } @@ -228,7 +227,11 @@ public class HousingService implements INetworkDispatch { core.objectService.getObjectList() .values().stream() .filter(obj -> obj instanceof BuildingObject && obj.getAttachment("structureOwner") != null) - .forEach(building -> core.objectService.persistObject(building.getObjectID(), building, core.getSWGObjectODB())); + .forEach(building -> { + if(building.getAttachment("childObjects") != null) + ((Vector) building.getAttachment("childObjects")).clear(); + core.objectService.persistObject(building.getObjectID(), building, core.getSWGObjectODB()); + }); } @SuppressWarnings("unchecked") @@ -327,10 +330,9 @@ public class HousingService implements INetworkDispatch { public void process(SWGObject owner, int eventType, Vector returnList) { CreatureObject houseOwner = (CreatureObject)owner; core.suiService.closeSUIWindow(owner, 0); - if (returnList.get(0).equals(""+confirmCode)){ + if (returnList.get(0).equals(""+confirmCode)) { // handle creation of correct deed in player inventory PlayerObject player = (PlayerObject) houseOwner.getSlottedObject("ghost"); - PlayerCity city = core.playerCityService.getCityObjectIsIn(building); HouseTemplate houseTemplate = housingTemplates.get(houseToDeed.get(building.getTemplate())); TangibleObject deed = (TangibleObject) core.objectService.createObject(houseTemplate.getDeedTemplate(), owner.getPlanet()); @@ -349,11 +351,8 @@ public class HousingService implements INetworkDispatch { int costs = 800; houseOwner.setBankCredits(houseOwner.getBankCredits()-costs); - if(building.getAttachment("sign") != null) - core.objectService.destroyObject((SWGObject) building.getAttachment("sign")); - - core.objectService.destroyObject(building.getObjectID()); - + destroyStructure(building); + SWGObject ownerInventory = owner.getSlottedObject("inventory"); ownerInventory.add(deed); @@ -361,21 +360,7 @@ public class HousingService implements INetworkDispatch { if (player.getLotsRemaining() + houseTemplate.getLotCost() <= 10) { player.setLotsRemaining(player.getLotsRemaining() + houseTemplate.getLotCost()); } - - if(building.getResidency()) { - owner.setAttachment("residentBuilding", null); - } - - if(city != null) { - city.removeStructure(building.getObjectID()); - if(building.getResidency()) { - city.removeCitizen(owner.getObjectID()); - player.setHome(""); - player.setCitizenship(Citizenship.Homeless); - owner.setAttachment("residentCity", null); - } - } - + houseOwner.sendSystemMessage("@player_structure:processing_destruction",(byte)1); houseOwner.sendSystemMessage("@player_structure:deed_reclaimed",(byte)1); @@ -395,6 +380,38 @@ public class HousingService implements INetworkDispatch { core.suiService.openSUIWindow(window); } + public void destroyStructure(BuildingObject building) { + + PlayerCity city = core.playerCityService.getCityObjectIsIn(building); + CreatureObject owner = null; + if(building.getAttachment("structureOwner") != null) + owner = (CreatureObject) core.objectService.getObject((long) building.getAttachment("structureOwner")); + + if(building.getAttachment("sign") != null) + core.objectService.destroyObject((SWGObject) building.getAttachment("sign")); + + core.objectService.destroyObject(building.getObjectID()); + core.objectService.deletePersistentObject(building.getObjectID(), core.getSWGObjectODB()); + + if(building.getResidency()) + owner.setAttachment("residentBuilding", null); + + if(city != null) { + city.removeStructure(building.getObjectID()); + if(building.getResidency()) { + city.removeCitizen(owner.getObjectID()); + owner.getPlayerObject().setHome(""); + owner.getPlayerObject().setCitizenship(Citizenship.Homeless); + owner.setAttachment("residentCity", null); + } + if(building.getTemplate().contains("cityhall")) { + // destroy city + core.playerCityService.destroyCity(city); + } + } + + } + public void createPayMaintenanceSUIPage(SWGObject owner, TangibleObject target) { CreatureObject creature = (CreatureObject) owner; final BuildingObject building = (BuildingObject) target.getGrandparent(); @@ -472,22 +489,20 @@ public class HousingService implements INetworkDispatch { owner.sendSystemMessage("Structure renamed.", DisplayType.Broadcast); } - public void createStatusSUIPage(SWGObject owner, TangibleObject target) { + public void createStatusSUIPage(CreatureObject owner, TangibleObject target) + { final BuildingObject building = (BuildingObject) target.getGrandparent(); - //final BuildingObject building = (BuildingObject) target.getAttachment("housing_parentstruct"); + String displayname = "@installation_n:"+building.getStfName(); - if (building.getCustomName()!=null) - displayname = building.getCustomName(); + if (building.getCustomName()!=null) displayname = building.getCustomName(); + final SUIWindow window = core.suiService.createSUIWindow("Script.listBox", owner, target, 0); window.setProperty("bg.caption.lblTitle:Text", "@player_structure:structure_status_t"); window.setProperty("Prompt.lblPrompt:Text", "@player_structure:structure_name_prompt" + " " + displayname); - String ownerName = owner.getCustomName(); - if (ownerName.length()>0){ - String[] helper = ownerName.split(" "); - ownerName = helper[0]; - } - + + String ownerName = core.objectService.getObject((long) building.getAttachment("structureOwner")).getFirstName(); String maintenancePool_string = ""+(int)building.getMaintenanceAmount(); + int hourlyMaintenance = building.getBMR(); float totalNumberOfHours = (float)building.getMaintenanceAmount()/hourlyMaintenance; float minuteFraction = ((totalNumberOfHours * 100) % 100) / 100; @@ -498,10 +513,10 @@ public class HousingService implements INetworkDispatch { maintenancePool_string += " (" + nDays + " days, " + nHours + " hours, " + nMinutes + " minutes)"; window.addListBoxMenuItem("@player_structure:owner_prompt" + " " + ownerName, 0); - if (building.getPrivacy()==BuildingObject.PRIVATE) - window.addListBoxMenuItem("@player_structure:structure_private", 1); - else - window.addListBoxMenuItem("@player_structure:structure_public", 1); + + if (building.getPrivacy()==BuildingObject.PRIVATE) window.addListBoxMenuItem("@player_structure:structure_private", 1); + else window.addListBoxMenuItem("@player_structure:structure_public", 1); + window.addListBoxMenuItem("@player_structure:condition_prompt" + " " + target.getConditionDamage()+"%", 2); window.addListBoxMenuItem("@player_structure:maintenance_pool_prompt " + maintenancePool_string, 3); window.addListBoxMenuItem("@player_structure:maintenance_rate_prompt " + building.getBMR() + " cr/h", 4); // @player_structure:credits_per_hour @@ -525,8 +540,8 @@ public class HousingService implements INetworkDispatch { core.suiService.openSUIWindow(window); } - public void declareResidency(SWGObject owner, TangibleObject target) { - final BuildingObject building = (BuildingObject) target.getGrandparent(); + public void declareResidency(SWGObject owner, BuildingObject building) { + PlayerCity cityActorIsIn = core.playerCityService.getCityObjectIsIn(building); if(owner.getObjectID() != (long) building.getAttachment("structureOwner")) { ((CreatureObject) owner).sendSystemMessage("@player_structure:declare_must_be_owner", (byte) 0); return; @@ -534,19 +549,44 @@ public class HousingService implements INetworkDispatch { ((CreatureObject) owner).sendSystemMessage("@player_structure:already_residence", (byte) 0); return; } - if(System.currentTimeMillis() < (long) owner.getAttachment("residencyCooldown")) { + if(((CreatureObject) owner).getPlayerObject().getCitizenship() == Citizenship.Mayor && !building.getTemplate().contains("cityhall")) { // mayors always have the city hall as their residence and cant change it + if(cityActorIsIn == null || cityActorIsIn.getMayorID() != owner.getObjectID()) { + ((CreatureObject) owner).sendSystemMessage("@city/city:mayor_residence_change", (byte) 0); + return; + } + } + if((owner.getAttachment("residencyCooldown") == null || System.currentTimeMillis() < (long) owner.getAttachment("residencyCooldown")) && cityActorIsIn.getMayorID() != owner.getObjectID()) { ((CreatureObject) owner).sendSystemMessage(OutOfBand.ProsePackage("@player_structure:change_residence_time", "DI", (int) ((long) owner.getAttachment("residencyCooldown") - System.currentTimeMillis()) / 3600000), (byte) 0); return; } + BuildingObject oldResidency = null; + if(owner.getAttachment("residentBuilding") != null && core.objectService.getObject((long) owner.getAttachment("residentBuilding")) != null) { + oldResidency = (BuildingObject) core.objectService.getObject((long) owner.getAttachment("residentBuilding")); + oldResidency.setResidency(false); + ((CreatureObject) owner).sendSystemMessage("@player_structure:change_residence", (byte) 0); + PlayerCity oldCity = core.playerCityService.getCityObjectIsIn(oldResidency); + if(oldCity != null) { + oldCity.removeStructure(oldResidency.getObjectID()); + oldCity.removeCitizen(owner.getObjectID()); + ((CreatureObject) owner).getPlayerObject().setHome(""); + ((CreatureObject) owner).getPlayerObject().setCitizenship(Citizenship.Homeless); + owner.setAttachment("residentCity", null); + } + ((CreatureObject) owner).sendSystemMessage("@player_structure:change_residence", (byte) 0); + } else { + ((CreatureObject) owner).sendSystemMessage("@player_structure:declared_residency", (byte) 0); + } building.setResidency(true); - PlayerCity cityActorIsIn = core.playerCityService.getCityObjectIsIn(building); owner.setAttachment("residencyCooldown", System.currentTimeMillis() + 86400000); // 24 hours owner.setAttachment("residentBuilding", building.getObjectID()); - ((CreatureObject) owner).sendSystemMessage("@player_structure:change_residence", (byte) 0); if(cityActorIsIn != null) { + cityActorIsIn.addCitizen(owner.getObjectID()); owner.setAttachment("residentCity", cityActorIsIn.getCityID()); ((CreatureObject) owner).getPlayerObject().setHome(cityActorIsIn.getCityName()); - ((CreatureObject) owner).getPlayerObject().setCitizenship(Citizenship.Citizen); + if(cityActorIsIn.getMayorID() == owner.getObjectID()) + ((CreatureObject) owner).getPlayerObject().setCitizenship(Citizenship.Mayor); + else + ((CreatureObject) owner).getPlayerObject().setCitizenship(Citizenship.Citizen); } } @@ -579,16 +619,11 @@ public class HousingService implements INetworkDispatch { core.suiService.closeSUIWindow(owner, 0); } }); - core.suiService.openSUIWindow(window); - - + core.suiService.openSUIWindow(window); } - public void handleDeleteAllItems(SWGObject owner, TangibleObject target) { - final BuildingObject building = (BuildingObject) target.getGrandparent(); - //final BuildingObject building = (BuildingObject) target.getAttachment("housing_parentstruct"); - //building.getItemsList().clear(); - // confirmation needed + public void handleDeleteAllItems(SWGObject owner, TangibleObject target) + { confirmDeleteAllItems(owner, target); } @@ -634,8 +669,8 @@ public class HousingService implements INetworkDispatch { core.suiService.openSUIWindow(window); } - public void confirmDeleteAllItems2ndStage(final CreatureObject ownerC, BuildingObject building) { - + public void confirmDeleteAllItems2ndStage(final CreatureObject ownerC, BuildingObject building) + { final SUIWindow window = core.suiService.createMessageBox(2,"@player_structure:structure_status","@player_structure:structure_name_prompt", ownerC, building, 0); window.setProperty("bg.caption.lblTitle:Text", "@player_structure:confirm_destruction_t"); window.setProperty("Prompt.lblPrompt:Text", "@player_structure:delete_all_items_second_d"); diff --git a/src/services/map/MapService.java b/src/services/map/MapService.java index 007f4281..edbbc908 100644 --- a/src/services/map/MapService.java +++ b/src/services/map/MapService.java @@ -115,6 +115,15 @@ public class MapService implements INetworkDispatch { } + public void removeLocation(Planet planet, float x, float y, byte category) { + if(locationMap.get(planet) == null) + return; + MapLocation location = locationMap.get(planet).stream().filter(l -> l.getX() == x && l.getY() == y && l.getCategory() == category).findFirst().orElse(null); + if(location == null) + return; + locationMap.get(planet).remove(location); + } + public String getClosestCityName(SWGObject object) { if(object.getPlanet() == null) diff --git a/src/services/mission/MissionService.java b/src/services/mission/MissionService.java index 60af71ec..809bcca8 100644 --- a/src/services/mission/MissionService.java +++ b/src/services/mission/MissionService.java @@ -383,6 +383,11 @@ public class MissionService implements INetworkDispatch { return false; } + if (mission.getPlanetId() != creature.getPlanetId()) { + mission.setPlanet(creature.getPlanet()); + mission.setPlanetId(creature.getPlanetId()); + } + missionBag.transferTo(creature, datapad, mission); createMissionObjective(creature, mission); return true; diff --git a/src/services/object/ObjectService.java b/src/services/object/ObjectService.java index 57c044e6..96070041 100644 --- a/src/services/object/ObjectService.java +++ b/src/services/object/ObjectService.java @@ -165,6 +165,7 @@ public class ObjectService implements INetworkDispatch { core.bazaarService.saveAllItems(); core.housingService.saveBuildings(); core.harvesterService.saveHarvesters(); + core.playerCityService.saveAllCities(); core.closeODBs(); } }); @@ -527,7 +528,14 @@ public class ObjectService implements INetworkDispatch { } else { core.simulationService.remove(object, object.getWorldPosition().x, object.getWorldPosition().z, true); } - + @SuppressWarnings("unchecked") Vector childObjects = (Vector) object.getAttachment("childObjects"); + if(childObjects != null) + { + for(SWGObject child : childObjects) { + if(child != null && child.getParentId() != 0) + destroyObject(child); + } + } } public void destroyObject(long objectID) { @@ -675,6 +683,8 @@ public class ObjectService implements INetworkDispatch { } public void useObject(CreatureObject creature, final SWGObject object) { + System.out.println("creature " + creature); + System.out.println("object " + object); if (creature == null || object == null) { return; } @@ -764,37 +774,25 @@ public class ObjectService implements INetworkDispatch { } if (object.getTemplate().equals(powerUpTemplate3)){ // weapon + SWGObject usedObject = null; + if (object.getAttachment("UsedObjectID")!=null){ + long usedObjectid = (long)object.getAttachment("UsedObjectID"); + usedObject = core.objectService.getObject(usedObjectid); + } - String effectName = (String) object.getAttachment("effectName"); - int powerValue = (int) object.getAttachment("powerValue"); + creature.setAttachment("LastUsedPUP",object.getObjectID()); - Long weaponID = (Long) creature.getAttachment("EquippedWeapon"); - if (weaponID==null) - return; - - WeaponObject weapon = (WeaponObject) core.objectService.getObject(weaponID); - //weapon.setIntAttribute(effectName, powerValue); - weapon.setAttachment("PUPEffectName", effectName); - weapon.setAttachment("PUPEffectValue", powerValue); + if (usedObject==null){ + Long weaponID = (Long) creature.getAttachment("EquippedWeapon"); + System.out.println("weaponID " + weaponID); + if (weaponID==null) + return; + + WeaponObject weapon = (WeaponObject) core.objectService.getObject(weaponID); + usedObject = (SWGObject) weapon; + } - List statList = new ArrayList(); - statList.add("constitution_modified"); - statList.add("agility_modified"); - statList.add("precision_modified"); - statList.add("strength_modified"); - statList.add("stamina_modified"); - statList.add("luck_modified"); - if (statList.contains(effectName)){ - // Probably this has to be done preferably via the buff service or a new power up service - // Problem : Some of the effect names do NOT occur in buff.iff - // So Light or Treeku better look into this - weapon.setIntAttribute("cat_stat_mod_bonus."+effectName, powerValue); - core.skillModService.addSkillMod(creature, "cat_stat_mod_bonus."+effectName, powerValue); - } else { - // assume skillmod then - weapon.setIntAttribute("cat_skill_mod_bonus."+effectName, powerValue); - core.skillModService.addSkillMod(creature, "cat_skill_mod_bonus."+effectName, powerValue); - } + core.buffService.addBuffToCreature(creature, "powerup_weapon", creature); int amount = object.getIntAttribute("num_in_stack"); if (amount==1) diff --git a/src/services/playercities/ClientRegion.java b/src/services/playercities/ClientRegion.java new file mode 100644 index 00000000..d2243d59 --- /dev/null +++ b/src/services/playercities/ClientRegion.java @@ -0,0 +1,53 @@ +package services.playercities; + +import engine.resources.scene.Planet; +import engine.resources.scene.Point3D; + +public class ClientRegion { + + private String name; + private Point3D center; + private float radius; + private Planet planet; + + public ClientRegion(String name, Point3D center, float radius, Planet planet) { + this.name = name; + this.center = center; + this.radius = radius; + this.planet = planet; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public float getRadius() { + return radius; + } + + public void setRadius(float radius) { + this.radius = radius; + } + + public Point3D getCenter() { + return center; + } + + public void setCenter(Point3D center) { + this.center = center; + } + + public Planet getPlanet() { + return planet; + } + + public void setPlanet(Planet planet) { + this.planet = planet; + } + + +} diff --git a/src/services/playercities/PlayerCity.java b/src/services/playercities/PlayerCity.java index 6f609e38..357514e1 100644 --- a/src/services/playercities/PlayerCity.java +++ b/src/services/playercities/PlayerCity.java @@ -26,14 +26,30 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; +import net.engio.mbassy.listener.Handler; +import resources.common.OutOfBand; +import resources.common.ProsePackage; +import resources.common.collidables.AbstractCollidable; +import resources.common.collidables.CollidableCircle; +import resources.common.collidables.AbstractCollidable.EnterEvent; +import resources.common.collidables.AbstractCollidable.ExitEvent; +import resources.datatables.CivicStructures; +import resources.datatables.Options; import resources.objects.building.BuildingObject; import resources.objects.creature.CreatureObject; +import resources.objects.tangible.TangibleObject; import resources.objects.waypoint.WaypointObject; import services.chat.Mail; import services.chat.WaypointAttachment; import main.NGECore; +import engine.clientdata.ClientFileManager; +import engine.clientdata.StfTable; +import engine.clientdata.visitors.DatatableVisitor; +import engine.resources.common.CRC; import engine.resources.objects.SWGObject; import engine.resources.scene.Point3D; @@ -45,8 +61,6 @@ public class PlayerCity implements Serializable { private static final long serialVersionUID = 1L; - public static final int DESERTED = 0; - public static final int NEWCITY = 0; public static final int OUTPOST = 1; public static final int VILLAGE = 2; public static final int TOWNSHIP = 3; @@ -62,9 +76,6 @@ public class PlayerCity implements Serializable { public static final int MEDICAL_CENTER = 7; public static final int RESEARCH_CENTER = 8; public static final int SAMPLE_RICH = 9; - public static final int INDUSTRIAL_SOCIETY = 10; - public static final int SCIENTIFIC_SOCIETY = 11; - public static final int STRONGHOLD = 12; public static final String[] specialisationSTFNames = new String[]{"city_spec_cloning", "city_spec_bm_incubator", @@ -74,25 +85,19 @@ public class PlayerCity implements Serializable { "city_spec_industry", "city_spec_doctor", "city_spec_research", - "city_spec_sample_rich", - "city_spec_master_manufacturing", - "city_spec_master_healing", - "city_spec_stronghold"}; - - public static final int BANK = 1; - public static final int CLONING_FACILITY = 2; - public static final int GARAGE = 3; - public static final int MEDIUM_GARDEN = 4; - public static final int LARGE_GARDEN = 5; - public static final int SHUTTLEPORT = 6; + "city_spec_sample_rich"}; + + public static final int[] citizensPerRank = new int[] { 5, 10, 15, 30, 40 }; private String cityName = ""; - private int cityID = -1; + private int planetId; + private long cityHallId = 0; + private long cityID = -1; private Point3D cityCenterPosition = new Point3D(0,0,0); - private int cityRadius = 0; - private int cityRank = 0; - private int specialization = -1; + private int cityRadius = 150; + private int cityRank = 1; + private int specialization = 0; private long mayorID = 0L; private int cityTreasury = 0; private int maintenanceFee = 0; @@ -109,36 +114,48 @@ public class PlayerCity implements Serializable { private boolean shuttlePort = false; private boolean registered = false; private boolean zoningEnabled = false; - - //private final long cityUpdateSpan = 7*86400*1000; - private final long cityUpdateSpan = 100*1000; - - //private final long legislationPeriod = 21*86400*1000; - private final long legislationPeriod = 100*1000; + private transient CollidableCircle area; + private long cityNameChangeCooldown; + private long cityTreasuryWithdrawalCooldown; + + //public static final long cityUpdateSpan = 7*86400*1000; + public static final long cityUpdateSpan = 100*1000; + //public static final long cityUpdateSpan = 86400*1000; + public static final long newCityGraceSpan = 100*1000; + + //public static final long legislationPeriod = 21*86400*1000; + public static final long legislationPeriod = 100*1000; private Vector placedStructures = new Vector(); private Vector citizens = new Vector(); - private Vector electionCandidates = new Vector(); - private Vector electionVotes = new Vector(); + private Map electionList = new ConcurrentHashMap(); private Vector cityBanList = new Vector(); private Vector militiaList = new Vector(); private Vector foundersList = new Vector(); - public PlayerCity(){} + public PlayerCity() {} - public PlayerCity(CreatureObject founder, int cityID){ - - setCityCenterPosition(founder.getPosition()); - setCityRadius(150); + public PlayerCity(CreatureObject founder, long cityId, BuildingObject cityHall) { + setCityCenterPosition(cityHall.getPosition()); + setCityHallId(cityHall.getObjectID()); setFoundationTime(System.currentTimeMillis()); setMaintenanceFee(1); setNextCityUpdate(foundationTime+cityUpdateSpan); setNextElectionDate(foundationTime+legislationPeriod); - citizens.add(founder.getObjectID()); - this.cityID = cityID; + this.cityID = cityId; setMayorID(founder.getObjectID()); + setPlanetId(cityHall.getPlanetId()); + init(); } + public void init() { + area = new CollidableCircle(getCityCenterPosition(), cityRadius, NGECore.getInstance().terrainService.getPlanetByID(planetId)); + area.getEventBus().subscribe(this); + NGECore.getInstance().simulationService.addCollidable(area, area.getCenter().x, area.getCenter().z); + if(isRegistered()) + NGECore.getInstance().mapService.addLocation(NGECore.getInstance().terrainService.getPlanetByID(planetId), getCityName(), area.getCenter().x, area.getCenter().z, (byte) 17, (byte) 0, (byte) 0); + } + public void handleGrantZoning() { } @@ -151,83 +168,41 @@ public class PlayerCity implements Serializable { } + public int getCivicStructuresCount() { + return (int) placedStructures.stream(). + map(NGECore.getInstance().objectService::getObject). + filter(o -> o.getAttachment("civicStructureType") != null && (int) o.getAttachment("civicStructureType") > 0). + count(); + } + public void processElection() { // ToDo: handle everything long winnerID = mayorID; mayorID = winnerID; - electionVotes.clear(); - electionCandidates.clear(); - electionCandidates.add(mayorID); setNextElectionDate(System.currentTimeMillis()+legislationPeriod); } public void processCityUpdate() { // has something changed? - System.out.println("processCityUpdate"); + System.out.println("processCityUpdate for " + cityName); + + fixupCitizens(); int censusResult = citizens.size(); int currentRank = getRank(); - switch (currentRank) { - case NEWCITY : if (censusResult>=5){ - setRank(++currentRank); // Expand to Outpost - sendCityExpandMailAll(); - } - - if (censusResult<5){ - // kill city - } - break; - - case OUTPOST : if (censusResult>=10){ - setRank(++currentRank); // Expand to Village - sendCityExpandMailAll(); - } - if (censusResult<5){ - setRank(--currentRank); // Contract to Deserted - sendCityContractMailAll(); - } - break; - - case VILLAGE : if (censusResult>=15){ - setRank(++currentRank); // Expand to Township - sendCityExpandMailAll(); - } - if (censusResult<10){ - setRank(--currentRank); // Contract to Outpost - sendCityContractMailAll(); - } - break; - - case TOWNSHIP : if (censusResult>=30){ - setRank(++currentRank); // Expand to City - sendCityExpandMailAll(); - } - if (censusResult<15){ - setRank(--currentRank); // Contract to Village - sendCityContractMailAll(); - } - break; - - case CITY : if (censusResult>=40){ - setRank(++currentRank); // Expand to Metropolis - sendCityExpandMailAll(); - } - if (censusResult<15){ - setRank(--currentRank); // Contract to Township - sendCityContractMailAll(); - } - break; - - case METROPOLIS : if (censusResult<40){ - setRank(--currentRank); // Contract to City - sendCityContractMailAll(); - } - break; - - + int minCitizen = citizensPerRank[currentRank - 1]; + + if(censusResult < minCitizen) { + contractCity(); + } else { + if(getRank() != METROPOLIS && citizensPerRank[currentRank] <= censusResult) + expandCity(); } + NGECore core = NGECore.getInstance(); // collect taxes + if(core.objectService.getObject(cityHallId) == null) + return; for (long citizen : citizens) { CreatureObject citizenObject = core.objectService.getObject(citizen) == null ? core.objectService.getCreatureFromDB(citizen) : (CreatureObject) core.objectService.getObject(citizen); if(citizenObject == null) @@ -245,15 +220,158 @@ public class PlayerCity implements Serializable { } citizenObject.setBankCredits(citizenObject.getBankCredits()-cumulatedPropertyTax); + if(core.objectService.getObject(citizen) == null) + core.objectService.persistObject(citizen, citizenObject, core.getSWGObjectODB()); cityTreasury += cumulatedPropertyTax; } - demolishHighRankStructures(); + calculateAndPayMaintenance(); + core.playerCityService.schedulePlayerCityUpdate(this, cityUpdateSpan); setNextCityUpdate(System.currentTimeMillis()+cityUpdateSpan); } - public void cityFixUp(){ + public void contractCity() { + NGECore core = NGECore.getInstance(); + int newRank = getRank() - 1; + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + + if(newRank < 1) { + core.housingService.destroyStructure((BuildingObject) core.objectService.getObject(cityHallId)); + return; + } + + // TODO unregister city if rank < 3 + + setRank(newRank); + demolishCivicStructuresOutsideRadius(); + demolishHighRankStructures(); + sendCityContractMail(); + fixupCitizens(); + } + + private void demolishCivicStructuresOutsideRadius() { + NGECore core = NGECore.getInstance(); + List remove = new ArrayList(); + placedStructures.stream().map(core.objectService::getObject) + .filter(o -> o.getAttachment("isCivicStructure") != null && (boolean) o.getAttachment("isCivicStructure")) + .filter(o -> !area.doesCollide(o)).forEach(remove::add); + remove.forEach(b -> core.housingService.destroyStructure((BuildingObject) b)); + } + + public void expandCity() { + + NGECore core = NGECore.getInstance(); + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + + if(getRank() == METROPOLIS) + return; + + int newRank = getRank() + 1; + boolean rankCapped = core.playerCityService.isRankCapped(core.terrainService.getPlanetByID(planetId), newRank); + sendCityExpandMail(rankCapped); + + if(!rankCapped) { + setRank(newRank); + addNewStructures(); + fixupCitizens(); + } + + } + + private void addNewStructures() { + NGECore core = NGECore.getInstance(); + List objects = core.simulationService.get(core.terrainService.getPlanetByID(planetId), area.getCenter().x, area.getCenter().z, 500); + objects.stream().filter(o -> o instanceof BuildingObject && o.getAttachment("structureOwner") != null && area.doesCollide(o)).map(SWGObject::getObjectID).forEach(this::addNewStructure); + } + + // cleanup in case something went wrong + public void fixupCitizens() { + List residents = new ArrayList(); + List added = new ArrayList(); + List removed = new ArrayList(); + // TODO: add extra mail for deleted characters + + NGECore core = NGECore.getInstance(); + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + + for (long structureID : new Vector(placedStructures)) { + BuildingObject structure = (BuildingObject) NGECore.getInstance().objectService.getObject(structureID); + if(structure == null) + return; + if(structure.getResidency()) + residents.add((Long) structure.getAttachment("structureOwner")); + } + for(long citizenId : new Vector(citizens)) { + if(!residents.contains(citizenId)) { + removeCitizen(citizenId, false); + CreatureObject citizenObject = core.objectService.getObject(citizenId) == null ? core.objectService.getCreatureFromDB(citizenId) : (CreatureObject) core.objectService.getObject(citizenId); + if(citizenObject != null) + removed.add(citizenObject); + } + } + for(long residentId : residents) { + if(!citizens.contains(residentId)) { + addCitizen(residentId, false); + CreatureObject citizenObject = core.objectService.getObject(residentId) == null ? core.objectService.getCreatureFromDB(residentId) : (CreatureObject) core.objectService.getObject(residentId); + if(citizenObject != null) + added.add(citizenObject); + } + } + + String addTT = ""; + for(CreatureObject addedCitizen : added) { + if(added.indexOf(addedCitizen) == added.size() - 1) + addTT += addedCitizen.getCustomName(); + else + addTT += (addedCitizen.getCustomName() + ", "); + } + + String removedTT = ""; + for(CreatureObject removedCitizen : removed) { + if(removed.indexOf(removedCitizen) == removed.size() - 1) + removedTT += removedCitizen.getCustomName(); + else + removedTT += (removedCitizen.getCustomName() + ", "); + } + + if(added.size() > 0) { + Mail mail = new Mail(); + mail.setMailId(core.chatService.generateMailId()); + mail.setRecieverId(getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSubject("@city/city:city_fixup_add_citizens_subject"); + mail.setSenderName("@city/city:new_city_from"); + mail.addProseAttachment(new ProsePackage("@city/city:city_fixup_add_citizens_body", "TT", addTT)); + + core.chatService.storePersistentMessage(mail); + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); + } + + if(removed.size() > 0) { + Mail mail = new Mail(); + mail.setMailId(core.chatService.generateMailId()); + mail.setRecieverId(getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSubject("@city/city:city_fixup_remove_citizens_subject"); + mail.setSenderName("@city/city:new_city_from"); + mail.addProseAttachment(new ProsePackage("@city/city:city_fixup_remove_citizens_body", "TT", removedTT)); + + core.chatService.storePersistentMessage(mail); + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); + } + + } public void demolishHighRankStructures() { @@ -261,38 +379,18 @@ public class PlayerCity implements Serializable { // that require a higher rank than the current one synchronized(placedStructures){ Vector wreckingBall = new Vector(); - for (long structureID : placedStructures){ + for (long structureID : placedStructures) { SWGObject structure = NGECore.getInstance().objectService.getObject(structureID); - int structureCode = (int) structure.getAttachment("CityStructureCode"); - - if (structureCode==BANK && cityRank<2){ + if(structure.getAttachment("civicStructureType") == null || (int) structure.getAttachment("civicStructureType") == 0) + return; + int structureCode = (int) structure.getAttachment("civicStructureType"); + if(getRank() < CivicStructures.rankRequired[structureCode - 1]) wreckingBall.add(structure); - } - - if (structureCode==CLONING_FACILITY && cityRank<3){ - wreckingBall.add(structure); - } - - if (structureCode==GARAGE && cityRank<2){ - wreckingBall.add(structure); - } - - if (structureCode==MEDIUM_GARDEN && cityRank<2){ - wreckingBall.add(structure); - } - - if (structureCode==LARGE_GARDEN && cityRank<3){ - wreckingBall.add(structure); - } - - if (structureCode==SHUTTLEPORT && cityRank<4){ - wreckingBall.add(structure); - } } for (SWGObject wreck : wreckingBall){ placedStructures.remove(wreck.getObjectID()); - NGECore.getInstance().objectService.destroyObject(wreck.getObjectID()); + NGECore.getInstance().housingService.destroyStructure((BuildingObject) wreck); } } } @@ -312,14 +410,98 @@ public class PlayerCity implements Serializable { } if (founderCitizenCount==founders.size()){ return true; // All founders are citizens now - } return false; } - public void calculateMaintenance() { + public void calculateAndPayMaintenance() { + NGECore core = NGECore.getInstance(); + // 1. Pay City Hall Maintenance + int maintAmount = 0; + BuildingObject cityHall = (BuildingObject) core.objectService.getObject(cityHallId); + int cityHallMaintenance = 1000 + getRank() * 2500; + if(registered) + cityHallMaintenance += 5000; + cityHallMaintenance += getSpecializationMaintenance(); + maintAmount += deductCivicStructureMaintenance(cityHall, cityHallMaintenance); + for(long placedStructures : new Vector(placedStructures)) { + BuildingObject building = (BuildingObject) core.objectService.getObject(placedStructures); + if(building != null) + maintAmount += deductCivicStructureMaintenance(building, building.getBMR()); + } + + sendMaintenanceMail(maintAmount); + } + + private int getSpecializationMaintenance() { + + switch(specialization) { + + case CLONE_LAB: + return 80000; + case DNA_LABORATORY: + return 200000; + case ENCORE_PERFORMANCE: + return 200000; + case ENTERTAINMENT_DISTRICT: + return 80000; + case IMPROVED_JOB_MARKET: + return 80000; + case MANUFACTURING_CENTER: + return 50000; + case MEDICAL_CENTER: + return 80000; + case RESEARCH_CENTER: + return 125000; + case SAMPLE_RICH: + return 70000; + default: + return 0; + } + + } + + private int deductCivicStructureMaintenance(BuildingObject civicStructure, int amount) { + int maintOwed = civicStructure.getOutstandingMaintenance(); + int amountPaid = 0; + if(amount > cityTreasury) { + int decay = civicStructure.getConditionDamage(); + int amountToPay = amount - cityTreasury; + int nextDecay = (int) (amountToPay / amount * civicStructure.getMaximumCondition() * 0.25f); + civicStructure.setOutstandingMaintenance(maintOwed - amountToPay); + civicStructure.setConditionDamage(decay + nextDecay); + amountPaid = cityTreasury; + cityTreasury = 0; + sendStructureDecayMail(civicStructure, amount); + } else { + cityTreasury -= amount; + amountPaid = amount; + if(maintOwed > 0) { + if(maintOwed > cityTreasury) { + amountPaid = cityTreasury; + int decay = civicStructure.getConditionDamage(); + if(decay > 0) { + civicStructure.setConditionDamage(cityTreasury / (maintOwed / decay)); + civicStructure.setOutstandingMaintenance(maintOwed - cityTreasury); + cityTreasury = 0; + } + } else { + amountPaid += maintOwed; + civicStructure.setOutstandingMaintenance(0); + cityTreasury -= maintOwed; + civicStructure.setConditionDamage(0); + } + sendStructureDecayRepairMail(civicStructure); + } + } + if(civicStructure.getConditionDamage() >= civicStructure.getMaximumCondition()) { + placedStructures.remove(civicStructure); + NGECore.getInstance().housingService.destroyStructure(civicStructure); + sendStructureDecayDestroyMail(civicStructure); + } + return amountPaid; } public void handleBanRequest() { @@ -336,6 +518,21 @@ public class PlayerCity implements Serializable { public void setRank(int cityRank) { this.cityRank = cityRank; + switch(cityRank) { + + case OUTPOST: + setCityRadius(150); + case VILLAGE: + setCityRadius(200); + case TOWNSHIP: + setCityRadius(300); + case CITY: + setCityRadius(400); + case METROPOLIS: + setCityRadius(450); + default: + setCityRadius(150); + } } public int getSpecialization() { @@ -355,11 +552,11 @@ public class PlayerCity implements Serializable { this.mayorID = mayorID; } - public int getCityTreasury() { + public synchronized int getCityTreasury() { return cityTreasury; } - public void setCityTreasury(int cityTreasury) { + public synchronized void setCityTreasury(int cityTreasury) { this.cityTreasury = cityTreasury; } @@ -410,19 +607,34 @@ public class PlayerCity implements Serializable { } public void addCitizen(long citizen) { - citizens.add(citizen); - CreatureObject citizenObject = (CreatureObject) NGECore.getInstance().objectService.getObject(citizen); - sendNewCitizenMailAll(citizenObject); - sendNewCitizenMail(citizenObject); + addCitizen(citizen, true); } public void removeCitizen(long citizen) { + removeCitizen(citizen, true); + } + + public void addCitizen(long citizen, boolean sendMail) { + citizens.add(citizen); + NGECore core = NGECore.getInstance(); + CreatureObject citizenObject = core.objectService.getObject(citizen) == null ? core.objectService.getCreatureFromDB(citizen) : (CreatureObject) core.objectService.getObject(citizen); + if(citizenObject == null) + return; + if(sendMail) { + sendNewCitizenMailAll(citizenObject); + sendNewCitizenMail(citizenObject); + } + } + + public void removeCitizen(long citizen, boolean sendMail) { citizens.remove(citizen); NGECore core = NGECore.getInstance(); CreatureObject citizenObject = core.objectService.getObject(citizen) == null ? core.objectService.getCreatureFromDB(citizen) : (CreatureObject) core.objectService.getObject(citizen); if(citizenObject == null) return; - sendCitizenLeftMailAll(citizenObject); + if(sendMail) { + sendCitizenLeftMailAll(citizenObject); + } } public long getNextElectionDate() { @@ -433,14 +645,6 @@ public class PlayerCity implements Serializable { this.nextElectionDate = nextElectionDate; } - public Vector getElectionCandidates() { - return electionCandidates; - } - - public void setElectionCandidates(Vector electionCandidates) { - this.electionCandidates = electionCandidates; - } - public long getNextCityUpdate() { return nextCityUpdate; } @@ -457,27 +661,14 @@ public class PlayerCity implements Serializable { this.cityBanList = cityBanList; } - public Vector getElectionVotes() { - return electionVotes; - } - - public void addToElectionVotes(byte vote) { - electionVotes.add(vote); - } - - public void addToTreasury(int amountToAdd) { + public synchronized void addToTreasury(int amountToAdd) { cityTreasury += amountToAdd; } - public void removeFromTreasury(int amountToDeduct) { + public synchronized void removeFromTreasury(int amountToDeduct) { cityTreasury -= amountToDeduct; } - // Stalinesque method :D - public void setElectionVotes(Vector electionVotes) { - this.electionVotes = electionVotes; - } - public Point3D getCityCenterPosition() { return cityCenterPosition; } @@ -492,6 +683,7 @@ public class PlayerCity implements Serializable { public void setCityRadius(int cityRadius) { this.cityRadius = cityRadius; + area.setRadius(cityRadius); } public Vector getMilitiaList() { @@ -536,11 +728,11 @@ public class PlayerCity implements Serializable { return; } - public int getCityID() { + public long getCityID() { return cityID; } - public void setCityID(int cityID) { + public void setCityID(long cityID) { this.cityID = cityID; } @@ -685,23 +877,9 @@ public class PlayerCity implements Serializable { actorMail.setRecieverId(citizen); actorMail.setStatus(Mail.NEW); actorMail.setTimeStamp((int) (new Date().getTime() / 1000)); - actorMail.setMessage("@city/city:new_city_citizen_body"); actorMail.setSubject("@city/city:new_city_citizen_subject"); actorMail.setSenderName("City " + this.cityName); - - List attachments = new ArrayList(); - WaypointObject constructionWaypoint = (WaypointObject)NGECore.getInstance().objectService.createObject("object/waypoint/shared_world_waypoint_blue.iff", citizenObject.getPlanet(), citizenObject.getPosition().x, 0 ,citizenObject.getPosition().z); - WaypointAttachment attachment = new WaypointAttachment(); - attachment.active = false; - attachment.cellID = constructionWaypoint.getCellId(); - attachment.color = (byte)1; - attachment.name = "City"; - attachment.planetCRC = engine.resources.common.CRC.StringtoCRC(citizenObject.getPlanet().getName()); - attachment.positionX = citizenObject.getPosition().x; - attachment.positionY = 0; - attachment.positionZ = citizenObject.getPosition().z; - attachments.add(attachment); - actorMail.setWaypointAttachments(attachments); + actorMail.addProseAttachment(new ProsePackage("@city/city:new_city_citizen_body", "TO", newCitizen.getCustomName())); NGECore.getInstance().chatService.storePersistentMessage(actorMail); if (newCitizen.getClient()!=null) @@ -710,34 +888,27 @@ public class PlayerCity implements Serializable { } } - public void sendNewCitizenMail(CreatureObject citizen) { + public void sendNewCitizenMail(CreatureObject citizen) { + + NGECore core = NGECore.getInstance(); + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; Mail actorMail = new Mail(); - actorMail.setMailId(NGECore.getInstance().chatService.generateMailId()); + actorMail.setMailId(core.chatService.generateMailId()); actorMail.setRecieverId(citizen.getObjectID()); actorMail.setStatus(Mail.NEW); actorMail.setTimeStamp((int) (new Date().getTime() / 1000)); - actorMail.setMessage("@city/city:new_city_citizen_other_body"); actorMail.setSubject("@city/city:new_city_citizen_other_subject"); actorMail.setSenderName("City " + this.cityName); - - List attachments = new ArrayList(); - WaypointObject constructionWaypoint = (WaypointObject)NGECore.getInstance().objectService.createObject("object/waypoint/shared_world_waypoint_blue.iff", citizen.getPlanet(), citizen.getPosition().x, 0 ,citizen.getPosition().z); - WaypointAttachment attachment = new WaypointAttachment(); - attachment.active = false; - attachment.cellID = constructionWaypoint.getCellId(); - attachment.color = (byte)1; - attachment.name = "City"; - attachment.planetCRC = engine.resources.common.CRC.StringtoCRC(citizen.getPlanet().getName()); - attachment.positionX = citizen.getPosition().x; - attachment.positionY = 0; - attachment.positionZ = citizen.getPosition().z; - attachments.add(attachment); - actorMail.setWaypointAttachments(attachments); + actorMail.addProseAttachment(new ProsePackage("@city/city:new_city_citizen_other_body", "TT", mayor.getCustomName(), "TU", cityName)); + - NGECore.getInstance().chatService.storePersistentMessage(actorMail); - NGECore.getInstance().chatService.sendPersistentMessageHeader(citizen.getClient(), actorMail); + core.chatService.storePersistentMessage(actorMail); + if (citizen.getClient()!=null) + core.chatService.sendPersistentMessageHeader(citizen.getClient(), actorMail); } public void sendCitizenLeftMailAll(CreatureObject newCitizen) { @@ -753,100 +924,60 @@ public class PlayerCity implements Serializable { actorMail.setRecieverId(citizen); actorMail.setStatus(Mail.NEW); actorMail.setTimeStamp((int) (new Date().getTime() / 1000)); - actorMail.setMessage("@city/city:lost_citizen_body"); actorMail.setSubject("@city/city:lost_citizen_subject"); actorMail.setSenderName("City " + this.cityName); - - List attachments = new ArrayList(); - WaypointObject constructionWaypoint = (WaypointObject)NGECore.getInstance().objectService.createObject("object/waypoint/shared_world_waypoint_blue.iff", citizenObject.getPlanet(), citizenObject.getPosition().x, 0 ,citizenObject.getPosition().z); - WaypointAttachment attachment = new WaypointAttachment(); - attachment.active = false; - attachment.cellID = constructionWaypoint.getCellId(); - attachment.color = (byte)1; - attachment.name = "City"; - attachment.planetCRC = engine.resources.common.CRC.StringtoCRC(citizenObject.getPlanet().getName()); - attachment.positionX = citizenObject.getPosition().x; - attachment.positionY = 0; - attachment.positionZ = citizenObject.getPosition().z; - attachments.add(attachment); - actorMail.setWaypointAttachments(attachments); - + actorMail.addProseAttachment(new ProsePackage("@city/city:lost_citizen_body", "TO", newCitizen.getCustomName())); + NGECore.getInstance().chatService.storePersistentMessage(actorMail); - if (newCitizen.getClient()!=null) - NGECore.getInstance().chatService.sendPersistentMessageHeader(newCitizen.getClient(), actorMail); + if (citizenObject.getClient()!=null) + NGECore.getInstance().chatService.sendPersistentMessageHeader(citizenObject.getClient(), actorMail); } } - public void sendCityExpandMailAll() { + public void sendCityExpandMail(boolean rankCapped) { - Vector citizenList = getCitizens(); NGECore core = NGECore.getInstance(); - for (long citizen : citizenList){ - CreatureObject citizenObject = core.objectService.getObject(citizen) == null ? core.objectService.getCreatureFromDB(citizen) : (CreatureObject) core.objectService.getObject(citizen); - if(citizenObject == null) - continue; - Mail actorMail = new Mail(); - actorMail.setMailId(NGECore.getInstance().chatService.generateMailId()); - actorMail.setRecieverId(citizen); - actorMail.setStatus(Mail.NEW); - actorMail.setTimeStamp((int) (new Date().getTime() / 1000)); - actorMail.setMessage("@city/city:city_expand_body"); - actorMail.setSubject("@city/city:city_expand_subject"); - actorMail.setSenderName("City " + this.cityName); - - List attachments = new ArrayList(); - WaypointObject constructionWaypoint = (WaypointObject)NGECore.getInstance().objectService.createObject("object/waypoint/shared_world_waypoint_blue.iff", citizenObject.getPlanet(), citizenObject.getPosition().x, 0 ,citizenObject.getPosition().z); - WaypointAttachment attachment = new WaypointAttachment(); - attachment.active = false; - attachment.cellID = constructionWaypoint.getCellId(); - attachment.color = (byte)1; - attachment.name = "City"; - attachment.planetCRC = engine.resources.common.CRC.StringtoCRC(citizenObject.getPlanet().getName()); - attachment.positionX = citizenObject.getPosition().x; - attachment.positionY = 0; - attachment.positionZ = citizenObject.getPosition().z; - attachments.add(attachment); - actorMail.setWaypointAttachments(attachments); - - NGECore.getInstance().chatService.storePersistentMessage(actorMail); - - } + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + Mail mail = new Mail(); + mail.setMailId(NGECore.getInstance().chatService.generateMailId()); + mail.setRecieverId(getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSenderName("@city/city:new_city_from"); + if(rankCapped) { + mail.setSubject("@city/city:city_expand_cap_subject"); + mail.addProseAttachment(new ProsePackage("@city/city:city_expand_cap_body", "TO", cityName, cityRank + 1)); + } else { + mail.setSubject("@city/city:city_expand_subject"); + mail.addProseAttachment(new ProsePackage("@city/city:city_expand_body", "TO", cityName, cityRank)); + } + core.chatService.storePersistentMessage(mail); + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); } - public void sendCityContractMailAll() { - - Vector citizenList = getCitizens(); + public void sendCityContractMail() { + NGECore core = NGECore.getInstance(); - for (long citizen : citizenList){ - CreatureObject citizenObject = core.objectService.getObject(citizen) == null ? core.objectService.getCreatureFromDB(citizen) : (CreatureObject) core.objectService.getObject(citizen); - if(citizenObject == null) - continue; - Mail actorMail = new Mail(); - actorMail.setMailId(NGECore.getInstance().chatService.generateMailId()); - actorMail.setRecieverId(citizen); - actorMail.setStatus(Mail.NEW); - actorMail.setTimeStamp((int) (new Date().getTime() / 1000)); - actorMail.setMessage("@city/city:city_contract_body"); - actorMail.setSubject("@city/city:city_contract_subject"); - actorMail.setSenderName("City " + this.cityName); - - List attachments = new ArrayList(); - WaypointObject constructionWaypoint = (WaypointObject)NGECore.getInstance().objectService.createObject("object/waypoint/shared_world_waypoint_blue.iff", citizenObject.getPlanet(), citizenObject.getPosition().x, 0 ,citizenObject.getPosition().z); - WaypointAttachment attachment = new WaypointAttachment(); - attachment.active = false; - attachment.cellID = constructionWaypoint.getCellId(); - attachment.color = (byte)1; - attachment.name = "City"; - attachment.planetCRC = engine.resources.common.CRC.StringtoCRC(citizenObject.getPlanet().getName()); - attachment.positionX = citizenObject.getPosition().x; - attachment.positionY = 0; - attachment.positionZ = citizenObject.getPosition().z; - attachments.add(attachment); - actorMail.setWaypointAttachments(attachments); - - NGECore.getInstance().chatService.storePersistentMessage(actorMail); - } + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + Mail mail = new Mail(); + mail.setMailId(NGECore.getInstance().chatService.generateMailId()); + mail.setRecieverId(getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSubject("@city/city:city_contract_subject"); + mail.setSenderName("@city/city:new_city_from"); + mail.addProseAttachment(new ProsePackage("@city/city:city_contract_body", "TO", cityName, cityRank)); + + core.chatService.storePersistentMessage(mail); + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); + } @@ -878,5 +1009,232 @@ public class PlayerCity implements Serializable { public void removeStructure(long objectID) { placedStructures.remove(objectID); } + + public CollidableCircle getArea() { + return area; + } + + public void setArea(CollidableCircle area) { + this.area = area; + } + + public long getCityHallId() { + return cityHallId; + } + + public void setCityHallId(long cityHallId) { + this.cityHallId = cityHallId; + } + + public String getSpecialisationStfValue() { + if(specialization == 0) + return ""; + try { + StfTable stf = new StfTable("clientdata/string/en/city/city.stf"); + + for (int s = 1; s < stf.getRowCount(); s++) { + String stfKey = stf.getStringById(s).getKey(); + + if (stfKey != null && stfKey != "" && stfKey.equals(specialisationSTFNames[specialization - 1])) { + return stf.getStringById(s).getValue(); + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + public String getCityRankStfValue() { + try { + StfTable stf = new StfTable("clientdata/string/en/city/city.stf"); + + for (int s = 1; s < stf.getRowCount(); s++) { + String stfKey = stf.getStringById(s).getKey(); + + if (stfKey != null && stfKey != "" && stfKey.equals("rank" + String.valueOf(cityRank))) { + return stf.getStringById(s).getValue(); + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + + @Handler + public void onEnter(EnterEvent event) { + SWGObject object = event.object; + + if(object == null || !(object instanceof CreatureObject)) + return; + + String rank = getCityRankStfValue(); + String specialisation = getSpecialisationStfValue(); + if(specialisation.length() == 0) + ((CreatureObject) object).sendSystemMessage(OutOfBand.ProsePackage("@city/city:city_enter_city", "TT", getCityName(), "TO", rank), (byte) 0); + else + ((CreatureObject) object).sendSystemMessage(OutOfBand.ProsePackage("@city/city:city_enter_city", "TT", getCityName(), "TO", rank + ", " + specialisation), (byte) 0); + + // TODO: apply specialisation skill mods + } + + @Handler + public void onExit(ExitEvent event) { + SWGObject object = event.object; + + if(object == null || !(object instanceof CreatureObject)) + return; + + ((CreatureObject) object).sendSystemMessage(OutOfBand.ProsePackage("@city/city:city_leave_city", "TO", getCityName()), (byte) 0); + + // TODO: remove specialisation skill mods + + } + private void sendMaintenanceMail(int amount) { + NGECore core = NGECore.getInstance(); + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + Mail mail = new Mail(); + mail.setMailId(NGECore.getInstance().chatService.generateMailId()); + mail.setRecieverId(getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSubject("@city/city:city_maint_subject"); + mail.setSenderName("@city/city:treasury_withdraw_from"); + mail.addProseAttachment(new ProsePackage("@city/city:city_maint_body", "TO", mayor.getObjectID(), amount)); + + core.chatService.storePersistentMessage(mail); + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); + + } + + + private void sendStructureDecayRepairMail(BuildingObject building) { + NGECore core = NGECore.getInstance(); + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + Mail mail = new Mail(); + mail.setMailId(NGECore.getInstance().chatService.generateMailId()); + mail.setRecieverId(getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSubject("@city/city:structure_repaired_subject"); + mail.setSenderName("@city/city:treasury_withdraw_from"); + mail.addProseAttachment(new ProsePackage("@city/city:structure_repaired_body", "TO", mayor.getObjectID(), "TT", building.getLookAtText())); + + core.chatService.storePersistentMessage(mail); + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); + + } + + private void sendStructureDecayMail(BuildingObject building, int amount) { + NGECore core = NGECore.getInstance(); + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + Mail mail = new Mail(); + mail.setMailId(NGECore.getInstance().chatService.generateMailId()); + mail.setRecieverId(getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSubject("@city/city:structure_damaged_subject"); + mail.setSenderName("@city/city:treasury_withdraw_from"); + mail.addProseAttachment(new ProsePackage("@city/city:structure_damaged_body", "TO", mayor.getObjectID(), "TT", building.getObjectName().getString(), amount)); + + core.chatService.storePersistentMessage(mail); + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); + + } + + + private void sendStructureDecayDestroyMail(BuildingObject building) { + NGECore core = NGECore.getInstance(); + CreatureObject mayor = core.objectService.getObject(getMayorID()) == null ? core.objectService.getCreatureFromDB(getMayorID()) : (CreatureObject) core.objectService.getObject(getMayorID()); + if(mayor == null) + return; + Mail mail = new Mail(); + mail.setMailId(NGECore.getInstance().chatService.generateMailId()); + mail.setRecieverId(getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSubject("@city/city:structure_destroyed_maint_subject"); + mail.setSenderName("@city/city:treasury_withdraw_from"); + mail.addProseAttachment(new ProsePackage("@city/city:structure_destroyed_maint_body", "TO", mayor.getObjectID(), "TT", building.getObjectName().getString())); + + core.chatService.storePersistentMessage(mail); + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); + + } + + public int getPlanetId() { + return planetId; + } + + public void setPlanetId(int planetId) { + this.planetId = planetId; + } + + public Map getElectionList() { + return electionList; + } + + public void setElectionList(Map electionList) { + this.electionList = electionList; + } + + public int getReqCitizenCountForRank(int rank) { + return citizensPerRank[rank - 1]; + } + + public long getCityNameChangeCooldown() { + return cityNameChangeCooldown; + } + + public void setCityNameChangeCooldown(long cityNameChangeCooldown) { + this.cityNameChangeCooldown = cityNameChangeCooldown; + } + + public long getCityTreasuryWithdrawalCooldown() { + return cityTreasuryWithdrawalCooldown; + } + + public void setCityTreasuryWithdrawalCooldown(long cityTreasuryWithdrawalCooldown) { + this.cityTreasuryWithdrawalCooldown = cityTreasuryWithdrawalCooldown; + } + + public void sendTreasuryWithdrawalMail(CreatureObject mayor, int amount, String reason) { + Vector citizenList = getCitizens(); + NGECore core = NGECore.getInstance(); + for (long citizen : citizenList){ + CreatureObject citizenObject = core.objectService.getObject(citizen) == null ? core.objectService.getCreatureFromDB(citizen) : (CreatureObject) core.objectService.getObject(citizen); + if(citizenObject == null) + continue; + Mail actorMail = new Mail(); + actorMail.setMailId(NGECore.getInstance().chatService.generateMailId()); + actorMail.setRecieverId(citizen); + actorMail.setStatus(Mail.NEW); + actorMail.setTimeStamp((int) (new Date().getTime() / 1000)); + actorMail.setSubject("@city/city:treasury_withdraw_subject"); + actorMail.setSenderName("@city/city:treasury_withdraw_from"); + actorMail.addProseAttachment(new ProsePackage("@city/city:treasury_withdraw_body", "TO", mayor.getCustomName(), "TT", reason, amount)); + + NGECore.getInstance().chatService.storePersistentMessage(actorMail); + if (citizenObject.getClient() != null) + NGECore.getInstance().chatService.sendPersistentMessageHeader(citizenObject.getClient(), actorMail); + + } + + } + } diff --git a/src/services/playercities/PlayerCityService.java b/src/services/playercities/PlayerCityService.java index c3ea7fc2..cc99e1da 100644 --- a/src/services/playercities/PlayerCityService.java +++ b/src/services/playercities/PlayerCityService.java @@ -22,19 +22,29 @@ package services.playercities; import java.util.Collections; +import java.util.Date; import java.util.Map; +import java.util.Random; import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import resources.common.OutOfBand; +import resources.common.ProsePackage; +import resources.datatables.Citizenship; import resources.objects.building.BuildingObject; import resources.objects.creature.CreatureObject; import resources.objects.tangible.TangibleObject; +import services.chat.Mail; +import services.sui.SUIService.InputBoxType; import services.sui.SUIWindow; import services.sui.SUIWindow.SUICallback; import services.sui.SUIWindow.Trigger; +import engine.resources.database.ODBCursor; import engine.resources.objects.SWGObject; +import engine.resources.scene.Planet; import engine.resources.scene.Point3D; import engine.resources.service.INetworkDispatch; import engine.resources.service.INetworkRemoteEvent; @@ -48,52 +58,132 @@ public class PlayerCityService implements INetworkDispatch { private NGECore core; private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - + private Random random = new Random(); private Vector playerCities = new Vector(); private int cityID = 0; // This must be persisted or handled via objectservice somehow - + private Map cityRankCaps = new ConcurrentHashMap(); // static helper variable to be deleted later public static boolean sandboxCityBuilt = false; public PlayerCityService(NGECore core) { this.core = core; - - scheduler.scheduleAtFixedRate(() -> { - administratePlayerCities(); - }, 0, 1, TimeUnit.MINUTES); - } - - public void administratePlayerCities() { - synchronized(playerCities){ - Vector unfoundedCities = new Vector(); - for (PlayerCity city : playerCities){ - - if (city.getNextCityUpdate()<=System.currentTimeMillis()) - city.processCityUpdate(); - - if (city.getNextElectionDate()<=System.currentTimeMillis()) - city.processElection(); - - if (! city.isFounded() && city.getFoundationTime()<=System.currentTimeMillis()-(60*60*24*1000)){ - if (! city.checkFoundationSuccess()) - unfoundedCities.add(city); - } - } - - if (unfoundedCities.size()>0){ - playerCities.removeAll(unfoundedCities); - } - playerCities.removeAll(Collections.singleton(null)); - } } - public PlayerCity addNewPlayerCity(CreatureObject founder) { + public void loadCityRankCaps() { + core.scriptService.callScript("scripts/", "cityRankCaps", "addCityRankCaps", core); + } + + public void addCityRankCap(String planetName, int[] caps) { + cityRankCaps.put(planetName, caps); + } + + public void saveAllCities() { + playerCities.forEach(c -> core.objectService.persistObject(c.getCityID(), c, core.getCityODB())); + } + + public void loadCities() { + ODBCursor cursor = core.getCityODB().getCursor(); + while(cursor.hasNext()) { + PlayerCity city = (PlayerCity) cursor.next(); + if(city == null) + continue; + city.init(); + playerCities.add(city); + if(System.currentTimeMillis() < city.getNextCityUpdate()) + city.processCityUpdate(); + else + schedulePlayerCityUpdate(city, city.getNextCityUpdate() - System.currentTimeMillis()); + } + cursor.close(); + } + + public boolean isRankCapped(Planet planet, int rank) { + if(cityRankCaps.get(planet.getName()) == null) + return true; + int cap = cityRankCaps.get(planet.getName())[rank - 1]; + return playerCities.stream().filter(c -> c.getPlanetId() == planet.getID()).count() >= cap; + } + + private long generateCityId() { + boolean found = false; + long id = 0; + while(!found) { + id = random.nextLong(); + final long cityId = id; + if(!playerCities.stream().filter(c -> c.getCityID() == cityId).findFirst().isPresent()) + found = true; + } + return id; + } + + private boolean doesCityNameExist(String name) { + return playerCities.stream().map(PlayerCity::getCityName).filter(n -> n.equalsIgnoreCase(name)).findFirst().isPresent() || + core.terrainService.getClientRegions().stream().map(ClientRegion::getName).filter(n -> n.equalsIgnoreCase(name)).findFirst().isPresent(); + } + + public void handlePlaceCity(final CreatureObject actor, TangibleObject deed, float positionX, float positionZ, float rotation) { + + if(!canPlacePlayerCityAtPosition(actor, actor.getPlanet(), new Point3D(positionX, 0, positionZ))) + return; + + if(isRankCapped(actor.getPlanet(), PlayerCity.OUTPOST)) { + actor.sendSystemMessage("This Planet can not support any more cities.", (byte) 0); + return; + } + + if(actor.getPlayerObject().getCitizenship() == Citizenship.Mayor) { + actor.sendSystemMessage("@city/city:already_mayor", (byte) 0); + return; + } + + final SUIWindow window = core.suiService.createInputBox(2, "@city/city:city_name_t","@city/city:city_name_d", actor, null, 0); + window.setProperty("txtInput:MaxLength", "40"); + Vector returnList = new Vector(); + returnList.add("txtInput:LocalText"); + window.addHandler(0, "", Trigger.TRIGGER_OK, returnList, new SUICallback() { + @Override + public void process(SWGObject owner, int eventType, Vector returnList) { + if (returnList.size() == 0 || returnList.get(0).length() == 0) + return; + String name = returnList.get(0); + if(!core.characterService.checkName(name, owner.getClient(), true)) { + actor.sendSystemMessage("@player_structure:not_valid_name", (byte) 0); + return; + } + if(doesCityNameExist(name)) { + actor.sendSystemMessage("@player_structure:cityname_not_unique", (byte) 0); + return; + } + + BuildingObject cityHall = core.housingService.placeStructure(actor, deed, positionX, positionZ, rotation); + + if(cityHall == null) { + actor.sendSystemMessage("Error: NULL city hall", (byte) 0); + return; + } + + PlayerCity playerCity = addNewPlayerCity(actor, cityHall); + playerCity.setCityName(name); + playerCity.addNewStructure(cityHall.getObjectID()); + PlayerCityService.this.schedulePlayerCityUpdate(playerCity, PlayerCity.newCityGraceSpan); + cityHall.setAttachment("structureCity", playerCity.getCityID()); + actor.setAttachment("residentCity", playerCity.getCityID()); + core.housingService.declareResidency(actor, cityHall); + core.objectService.persistObject(playerCity.getCityID(), playerCity, core.getCityODB()); + newCitySUI2((CreatureObject) owner, playerCity); + + } + }); + core.suiService.openSUIWindow(window); + + } + + public PlayerCity addNewPlayerCity(CreatureObject founder, BuildingObject cityHall) { PlayerCity newCity = null; synchronized(playerCities){ - newCity = new PlayerCity(founder,cityID++); + newCity = new PlayerCity(founder, generateCityId(), cityHall); playerCities.add(newCity); } - newCitySUI1(founder,newCity); return newCity; } @@ -115,7 +205,7 @@ public class PlayerCityService implements INetworkDispatch { PlayerCity foundCity = null; synchronized(playerCities){ for (PlayerCity city : playerCities){ - int id = (int)citizen.getAttachment("residentCity"); + long id = (long)citizen.getAttachment("residentCity"); if (city.getCityID()==id){ foundCity = city; } @@ -124,7 +214,20 @@ public class PlayerCityService implements INetworkDispatch { return foundCity; } - + public boolean canPlacePlayerCityAtPosition(CreatureObject actor, Planet planet, Point3D position) { + + PlayerCity closest = playerCities.stream().min((c1, c2) -> (int) (c1.getCityCenterPosition().getDistance2D(position) - c2.getCityCenterPosition().getDistance2D(position))).orElse(null); + if(closest != null && closest.getCityCenterPosition().getDistance2D(position) < 1024) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@player_structure:city_too_close", "TO", closest.getCityName()), (byte) 0); + return false; + } + ClientRegion closest2 = core.terrainService.getClientRegionsForPlanet(planet).stream().min((c1, c2) -> (int) (c1.getCenter().getDistance2D(position) - c2.getCenter().getDistance2D(position))).orElse(null); + if(closest2 != null && closest2.getCenter().getDistance2D(position) < 1024) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@player_structure:city_too_close", "TO", closest2.getName()), (byte) 0); + return false; + } + return true; + } public void newCitySUI1(CreatureObject citizen, final PlayerCity newCity) { final SUIWindow window = core.suiService.createInputBox(2,"@city/city:city_name_t","@city/city:city_name_d", citizen, citizen, 0); @@ -185,7 +288,7 @@ public class PlayerCityService implements INetworkDispatch { PlayerCity sandboxCity = null; synchronized(playerCities){ - sandboxCity = new PlayerCity(founder,cityID++); + sandboxCity = new PlayerCity(founder,cityID++, cityHall); sandboxCity.setCityName("Sandbox City"); playerCities.add(sandboxCity); } @@ -268,5 +371,132 @@ public class PlayerCityService implements INetworkDispatch { public void shutdown() { // TODO Auto-generated method stub - } + } + + public void destroyCity(PlayerCity city) { + + CreatureObject mayor = core.objectService.getObject(city.getMayorID()) == null ? core.objectService.getCreatureFromDB(city.getMayorID()) : (CreatureObject) core.objectService.getObject(city.getMayorID()); + if(mayor == null) + return; + + Mail mail = new Mail(); + mail.setMailId(NGECore.getInstance().chatService.generateMailId()); + mail.setRecieverId(city.getMayorID()); + mail.setStatus(Mail.NEW); + mail.setTimeStamp((int) (new Date().getTime() / 1000)); + mail.setSubject("@city/city:new_city_fail_subject"); + mail.setSenderName("@city/city:new_city_from"); + mail.setMessage("@city/city:new_city_fail_body"); + + if(mayor.getClient() != null) + core.chatService.sendPersistentMessageHeader(mayor.getClient(), mail); + core.chatService.storePersistentMessage(mail); + for(long civicStructureId : new Vector(city.getPlacedStructures())) { + BuildingObject civicStructure = (BuildingObject) core.objectService.getObject(civicStructureId); + if(civicStructure == null) + continue; + core.housingService.destroyStructure(civicStructure); + } + core.simulationService.removeCollidable(city.getArea(), city.getArea().getCenter().x, city.getArea().getCenter().z); + playerCities.remove(city); + if(city.isRegistered()) + core.mapService.removeLocation(core.terrainService.getPlanetByID(city.getPlanetId()), city.getCityCenterPosition().x, city.getCityCenterPosition().z, (byte) 17); + core.objectService.deletePersistentObject(city.getCityID(), core.getCityODB()); + + } + + public void schedulePlayerCityUpdate(PlayerCity playerCity, long time) { + scheduler.schedule(() -> { + try { + playerCity.processCityUpdate(); + } catch(Exception e) { + e.printStackTrace(); + } + }, time, TimeUnit.MILLISECONDS); + } + + public void handleCityTreasuryWithdrawal(CreatureObject owner) { + SUIWindow window = core.suiService.createInputBox(InputBoxType.INPUT_BOX_OK_CANCEL, "@city/city:withdraw_reason_t", "@city/city:withdraw_reason_d", owner, null, 0, (actor, eventType, returnList) -> { + String reason = returnList.get(0); + if(reason.length() <= 0) + return; + promptCityTreasuryWithdrawal(owner, reason); + }); + window.setProperty("txtInput:MaxLength", "255"); + core.suiService.openSUIWindow(window); + } + + public void promptCityTreasuryWithdrawal(CreatureObject owner, String reason) { + PlayerCity playerCity = core.playerCityService.getPlayerCity(owner); + SUIWindow window = core.suiService.createSUIWindow("Script.transfer", owner, null, 0); + + window.setProperty("bg.caption.lblTitle:Text", "@city/city:treasury_withdraw_subject"); + window.setProperty("Prompt.lblPrompt:Text", "@city/city:treasury_withdraw_prompt"); + + window.setProperty("msgPayMaintenance", "transaction.txtInputFrom"); + + window.setProperty("transaction.lblFrom:Text", "@city/city:treasury_balance_t"); + window.setProperty("transaction.lblTo:Text", "@city/city:treasury_withdraw"); + window.setProperty("transaction.lblFrom", "@city/city:treasury_balance_t"); + window.setProperty("transaction.lblTo", "@city/city:treasury_withdraw"); + + window.setProperty("transaction.lblStartingFrom:Text", String.valueOf(playerCity.getCityTreasury())); + window.setProperty("transaction.lblStartingTo:Text", "0"); + + window.setProperty("transaction:InputFrom", "555555"); + window.setProperty("transaction:InputTo", "666666"); + + window.setProperty("transaction:txtInputFrom", String.valueOf(playerCity.getCityTreasury())); + window.setProperty("transaction:txtInputTo", "1"); + window.setProperty("transaction.txtInputFrom:Text", String.valueOf(playerCity.getCityTreasury())); + window.setProperty("transaction.txtInputTo:Text", "0"); + + window.setProperty("transaction.ConversionRatioFrom", "1"); + window.setProperty("transaction.ConversionRatioTo", "0"); + + window.setProperty("btnOk:visible", "True"); + window.setProperty("btnCancel:visible", "True"); + window.setProperty("btnOk:Text", "@ok"); + window.setProperty("btnCancel:Text", "@cancel"); + + VectorreturnList = new Vector(); + returnList.add("transaction.txtInputFrom:Text"); + returnList.add("transaction.txtInputTo:Text"); + window.addHandler(0, "", Trigger.TRIGGER_OK, returnList, (actor, eventType, inputList) -> { + + if(inputList.size() < 2) + return; + + if(playerCity.getCityTreasury() <= 0) { + owner.sendSystemMessage("@city/city:no_money", (byte) 0); + return; + } + + if(playerCity.getCityTreasuryWithdrawalCooldown() > System.currentTimeMillis()) { + owner.sendSystemMessage("@city/city:withdraw_daily", (byte) 0); + return; + } + + int amount = Integer.parseInt(inputList.get(1)); + + if(amount < 5000 || amount > 150000) { + owner.sendSystemMessage("@city/city:withdraw_limits", (byte) 0); + return; + } + + if(amount > playerCity.getCityTreasury()) { + owner.sendSystemMessage("@city/city:withdraw_treasury_error", (byte) 0); + return; + } + + owner.addBankCredits(amount); + playerCity.removeFromTreasury(amount); + playerCity.setCityTreasuryWithdrawalCooldown(System.currentTimeMillis() + 86400000); // 24 hours + owner.sendSystemMessage(OutOfBand.ProsePackage("@city/city:you_withdraw_from_treasury", amount), (byte) 0); + playerCity.sendTreasuryWithdrawalMail(owner, amount, reason); + + }); + core.suiService.openSUIWindow(window); + + } } diff --git a/src/services/reverseengineering/ReverseEngineeringService.java b/src/services/reverseengineering/ReverseEngineeringService.java index 941f69fb..5094a5fc 100644 --- a/src/services/reverseengineering/ReverseEngineeringService.java +++ b/src/services/reverseengineering/ReverseEngineeringService.java @@ -31,6 +31,7 @@ import java.util.Vector; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import resources.datatables.Options; import resources.objects.creature.CreatureObject; import resources.objects.player.PlayerObject; import resources.objects.tangible.TangibleObject; @@ -3313,6 +3314,8 @@ public class ReverseEngineeringService implements INetworkDispatch { skillEnhancingAttachment.setAttachment("SEAeffectNameList", effectNameList); skillEnhancingAttachment.setAttachment("SEAmodifierValueList", effectValueList); skillEnhancingAttachment.setAttachment("radial_filename", "item/item"); + //skillEnhancingAttachment.setOptions(Options.SOCKETED, true); + skillEnhancingAttachment.setOptions(Options.USABLE, true); engineerInventory.add(skillEnhancingAttachment); } } @@ -3454,11 +3457,18 @@ public class ReverseEngineeringService implements INetworkDispatch { skillEnhancingAttachment.setAttachment("SEAeffectNameList", effectNameList); skillEnhancingAttachment.setAttachment("SEAmodifierValueList", effectValueList); skillEnhancingAttachment.setAttachment("radial_filename", "item/item"); + //skillEnhancingAttachment.setOptions(Options.SOCKETED, true); + skillEnhancingAttachment.setOptions(Options.USABLE, true); engineerInventory.add(skillEnhancingAttachment); } } } + public void applyPowerUp(TangibleObject powerUpObject, long objectID){ + TangibleObject object = (TangibleObject)core.objectService.getObject(objectID); + + } + public int getHighestStat(TangibleObject piece){ int highestStat = 0; Map attributesMap = piece.getAttributes();