diff --git a/Server/data/botdata/ge_bot_appearances_and_equipment.json b/Server/data/botdata/ge_bot_appearances_and_equipment.json new file mode 100644 index 000000000..a4a1bef50 --- /dev/null +++ b/Server/data/botdata/ge_bot_appearances_and_equipment.json @@ -0,0 +1,68 @@ +{ + "noob": [ + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"3","id":"1265"},{"amount":"1","charge":"1000","slot":"5","id":"1171"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"0"},{"color":"0","look":"11"},{"color":"16","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"34"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1169"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"9183"},{"amount":"1","charge":"1000","slot":"4","id":"1131"},{"amount":"1","charge":"1000","slot":"7","id":"2495"},{"amount":"1","charge":"1000","slot":"9","id":"2491"},{"amount":"1","charge":"1000","slot":"10","id":"88"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"106"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1163"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"5698"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"3105"}]}, + {"appearance":{"appearance_cache":[{"color":"9","look":"266"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"3","id":"1333"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"11019"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"2910"},{"amount":"1","charge":"1000","slot":"1","id":"10499"},{"amount":"1","charge":"1000","slot":"2","id":"552"},{"amount":"1","charge":"1000","slot":"3","id":"861"},{"amount":"1","charge":"1000","slot":"4","id":"544"},{"amount":"1","charge":"1000","slot":"7","id":"2497"},{"amount":"1","charge":"1000","slot":"9","id":"7458"},{"amount":"1","charge":"1000","slot":"10","id":"2904"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"1007"},{"amount":"1","charge":"1000","slot":"3","id":"1381"},{"amount":"1","charge":"1000","slot":"4","id":"426"},{"amount":"1","charge":"1000","slot":"7","id":"428"}]}, +{"appearance":{"appearance_cache":[{"color":"20","look":"267"},{"color":"0","look":"12"},{"color":"0","look":"18"},{"color":"0","look":"31"},{"color":"4","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"3","id":"6908"},{"amount":"1","charge":"1000","slot":"4","id":"1135"},{"amount":"1","charge":"1000","slot":"7","id":"428"}]}, +{"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"6","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1161"},{"amount":"1","charge":"1000","slot":"3","id":"1287"},{"amount":"1","charge":"1000","slot":"4","id":"1123"},{"amount":"1","charge":"1000","slot":"5","id":"1199"},{"amount":"1","charge":"1000","slot":"7","id":"1073"}]}, +{"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1153"},{"amount":"1","charge":"1000","slot":"3","id":"1363"},{"amount":"1","charge":"1000","slot":"4","id":"6184"},{"amount":"1","charge":"1000","slot":"5","id":"1540"},{"amount":"1","charge":"1000","slot":"7","id":"6185"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11021"},{"amount":"1","charge":"1000","slot":"1","id":"4411"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"1333"},{"amount":"1","charge":"1000","slot":"4","id":"11020"},{"amount":"1","charge":"1000","slot":"5","id":"1185"},{"amount":"1","charge":"1000","slot":"7","id":"11022"},{"amount":"1","charge":"1000","slot":"10","id":"11019"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1949"},{"amount":"1","charge":"1000","slot":"1","id":"1007"},{"amount":"1","charge":"1000","slot":"3","id":"1381"},{"amount":"1","charge":"1000","slot":"4","id":"426"},{"amount":"1","charge":"1000","slot":"5","id":"1540"},{"amount":"1","charge":"1000","slot":"7","id":"428"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10774"},{"amount":"1","charge":"1000","slot":"1","id":"10069"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"1333"},{"amount":"1","charge":"1000","slot":"4","id":"1125"},{"amount":"1","charge":"1000","slot":"7","id":"1077"},{"amount":"1","charge":"1000","slot":"9","id":"2997"},{"amount":"1","charge":"1000","slot":"10","id":"7114"}]} + + ], + "intermediate": [ + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"3","id":"1265"},{"amount":"1","charge":"1000","slot":"5","id":"1171"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"1"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"2637"},{"amount":"1","charge":"1000","slot":"1","id":"6568"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1305"},{"amount":"1","charge":"1000","slot":"4","id":"3140"},{"amount":"1","charge":"1000","slot":"5","id":"6524"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"9","id":"3060"},{"amount":"1","charge":"1000","slot":"10","id":"11732"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"1"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11674"},{"amount":"1","charge":"1000","slot":"1","id":"10720"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"1387"},{"amount":"1","charge":"1000","slot":"4","id":"8839"},{"amount":"1","charge":"1000","slot":"7","id":"8840"},{"amount":"1","charge":"1000","slot":"9","id":"8842"},{"amount":"1","charge":"1000","slot":"10","id":"11732"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"261"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"9805"},{"amount":"1","charge":"1000","slot":"4","id":"10348"},{"amount":"1","charge":"1000","slot":"10","id":"1837"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"261"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10392"},{"amount":"1","charge":"1000","slot":"1","id":"1007"},{"amount":"1","charge":"1000","slot":"2","id":"11113"},{"amount":"1","charge":"1000","slot":"3","id":"1387"},{"amount":"1","charge":"1000","slot":"4","id":"1117"},{"amount":"1","charge":"1000","slot":"5","id":"1173"},{"amount":"1","charge":"1000","slot":"7","id":"1075"},{"amount":"1","charge":"1000","slot":"10","id":"4129"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"3755"},{"amount":"1","charge":"1000","slot":"1","id":"2412"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1383"},{"amount":"1","charge":"1000","slot":"4","id":"4091"},{"amount":"1","charge":"1000","slot":"7","id":"4093"},{"amount":"1","charge":"1000","slot":"9","id":"7462"},{"amount":"1","charge":"1000","slot":"10","id":"10696"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9755"},{"amount":"1","charge":"1000","slot":"1","id":"9754"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1275"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"88"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"106"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11665"},{"amount":"1","charge":"1000","slot":"1","id":"9807"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"13290"},{"amount":"1","charge":"1000","slot":"4","id":"8839"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"8840"},{"amount":"1","charge":"1000","slot":"9","id":"8842"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"106"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9672"},{"amount":"1","charge":"1000","slot":"1","id":"9783"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"9674"},{"amount":"1","charge":"1000","slot":"7","id":"2497"},{"amount":"1","charge":"1000","slot":"9","id":"7458"},{"amount":"1","charge":"1000","slot":"10","id":"3105"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"106"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10828"},{"amount":"1","charge":"1000","slot":"1","id":"9763"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"8850"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"3105"}]}, + {"appearance":{"appearance_cache":[{"color":"9","look":"266"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10828"},{"amount":"1","charge":"1000","slot":"1","id":"2414"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"9802"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"6182"},{"amount":"1","charge":"1000","slot":"1","id":"4355"},{"amount":"1","charge":"1000","slot":"2","id":"1478"},{"amount":"1","charge":"1000","slot":"3","id":"9185"},{"amount":"1","charge":"1000","slot":"4","id":"544"},{"amount":"1","charge":"1000","slot":"7","id":"2497"}]} , + {"appearance":{"appearance_cache":[{"color":"1","look":"1"},{"color":"0","look":"10"},{"color":"14","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"20","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"10071"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"8872"},{"amount":"1","charge":"1000","slot":"9","id":"1059"},{"amount":"1","charge":"1000","slot":"10","id":"88"}]}, +{"appearance":{"appearance_cache":[{"color":"20","look":"278"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1149"},{"amount":"1","charge":"1000","slot":"1","id":"6568"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1305"},{"amount":"1","charge":"1000","slot":"4","id":"2623"},{"amount":"1","charge":"1000","slot":"5","id":"6524"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, + {"appearance":{"appearance_cache":[{"color":"20","look":"1"},{"color":"0","look":"14"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"4413"},{"amount":"1","charge":"1000","slot":"3","id":"1271"},{"amount":"1","charge":"1000","slot":"4","id":"1123"},{"amount":"1","charge":"1000","slot":"7","id":"1073"}]}, +{"appearance":{"appearance_cache":[{"color":"20","look":"1"},{"color":"0","look":"14"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"3751"},{"amount":"1","charge":"1000","slot":"1","id":"10499"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"546"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"548"},{"amount":"1","charge":"1000","slot":"10","id":"3105"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"14120"},{"amount":"1","charge":"1000","slot":"2","id":"9470"},{"amount":"1","charge":"1000","slot":"3","id":"1305"},{"amount":"1","charge":"1000","slot":"4","id":"10822"},{"amount":"1","charge":"1000","slot":"5","id":"1540"},{"amount":"1","charge":"1000","slot":"7","id":"10824"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1153"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4153"},{"amount":"1","charge":"1000","slot":"4","id":"1115"},{"amount":"1","charge":"1000","slot":"7","id":"1067"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"3751"},{"amount":"1","charge":"1000","slot":"1","id":"6568"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"5698"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"8847"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"4131"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1163"},{"amount":"1","charge":"1000","slot":"2","id":"3853"},{"amount":"1","charge":"1000","slot":"3","id":"1373"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"1079"}]} , + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11021"},{"amount":"1","charge":"1000","slot":"2","id":"3853"},{"amount":"1","charge":"1000","slot":"3","id":"1373"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"1079"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11021"},{"amount":"1","charge":"1000","slot":"1","id":"14081"},{"amount":"1","charge":"1000","slot":"2","id":"3853"},{"amount":"1","charge":"1000","slot":"3","id":"1373"},{"amount":"1","charge":"1000","slot":"4","id":"6184"},{"amount":"1","charge":"1000","slot":"7","id":"6185"},{"amount":"1","charge":"1000","slot":"10","id":"3107"}]}, +{"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"6","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"6182"},{"amount":"1","charge":"1000","slot":"1","id":"10636"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1371"},{"amount":"1","charge":"1000","slot":"4","id":"10633"},{"amount":"1","charge":"1000","slot":"5","id":"3122"},{"amount":"1","charge":"1000","slot":"7","id":"6181"}]}, +{"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"10071"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1381"},{"amount":"1","charge":"1000","slot":"10","id":"88"}]} + + ], + "veteran": [ + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1038"},{"amount":"1","charge":"1000","slot":"1","id":"6570"},{"amount":"1","charge":"1000","slot":"2","id":"10360"},{"amount":"1","charge":"1000","slot":"3","id":"3053"},{"amount":"1","charge":"1000","slot":"4","id":"3140"},{"amount":"1","charge":"1000","slot":"5","id":"1171"},{"amount":"1","charge":"1000","slot":"7","id":"10394"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"4","look":"48"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1038"},{"amount":"1","charge":"1000","slot":"1","id":"6570"},{"amount":"1","charge":"1000","slot":"2","id":"10360"},{"amount":"1","charge":"1000","slot":"3","id":"3053"},{"amount":"1","charge":"1000","slot":"4","id":"3140"},{"amount":"1","charge":"1000","slot":"5","id":"1171"},{"amount":"1","charge":"1000","slot":"7","id":"10394"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"4","look":"48"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9950"},{"amount":"1","charge":"1000","slot":"1","id":"6570"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"7668"},{"amount":"1","charge":"1000","slot":"4","id":"2503"},{"amount":"1","charge":"1000","slot":"7","id":"2497"},{"amount":"1","charge":"1000","slot":"9","id":"5556"},{"amount":"1","charge":"1000","slot":"10","id":"11732"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"261"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"9805"},{"amount":"1","charge":"1000","slot":"4","id":"10348"},{"amount":"1","charge":"1000","slot":"10","id":"1837"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"9807"},{"amount":"1","charge":"1000","slot":"2","id":"1656"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"9","id":"7462"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9758"},{"amount":"1","charge":"1000","slot":"1","id":"9757"},{"amount":"1","charge":"1000","slot":"2","id":"1656"},{"amount":"1","charge":"1000","slot":"3","id":"1387"},{"amount":"1","charge":"1000","slot":"4","id":"10378"},{"amount":"1","charge":"1000","slot":"5","id":"1183"},{"amount":"1","charge":"1000","slot":"7","id":"10380"},{"amount":"1","charge":"1000","slot":"9","id":"7462"},{"amount":"1","charge":"1000","slot":"10","id":"10696"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10828"},{"amount":"1","charge":"1000","slot":"1","id":"10635"},{"amount":"1","charge":"1000","slot":"2","id":"9470"},{"amount":"1","charge":"1000","slot":"3","id":"9185"},{"amount":"1","charge":"1000","slot":"4","id":"10386"},{"amount":"1","charge":"1000","slot":"5","id":"3842"},{"amount":"1","charge":"1000","slot":"7","id":"10388"},{"amount":"1","charge":"1000","slot":"10","id":"1837"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9755"},{"amount":"1","charge":"1000","slot":"1","id":"9754"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1275"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"88"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9764"},{"amount":"1","charge":"1000","slot":"1","id":"9763"},{"amount":"1","charge":"1000","slot":"2","id":"1704"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"108"},{"color":"3","look":"34"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9814"},{"amount":"1","charge":"1000","slot":"1","id":"10662"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"108"},{"color":"3","look":"34"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9749"},{"amount":"1","charge":"1000","slot":"1","id":"10662"},{"amount":"1","charge":"1000","slot":"2","id":"6585"},{"amount":"1","charge":"1000","slot":"3","id":"5016"},{"amount":"1","charge":"1000","slot":"4","id":"13858"},{"amount":"1","charge":"1000","slot":"5","id":"7053"},{"amount":"1","charge":"1000","slot":"7","id":"13861"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11718"},{"amount":"1","charge":"1000","slot":"1","id":"10499"},{"amount":"1","charge":"1000","slot":"2","id":"87"},{"amount":"1","charge":"1000","slot":"3","id":"9185"},{"amount":"1","charge":"1000","slot":"4","id":"11720"},{"amount":"1","charge":"1000","slot":"5","id":"11283"},{"amount":"1","charge":"1000","slot":"7","id":"11722"},{"amount":"1","charge":"1000","slot":"9","id":"7458"},{"amount":"1","charge":"1000","slot":"10","id":"6920"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"6918"},{"amount":"1","charge":"1000","slot":"1","id":"9771"},{"amount":"1","charge":"1000","slot":"2","id":"87"},{"amount":"1","charge":"1000","slot":"3","id":"1387"},{"amount":"1","charge":"1000","slot":"4","id":"6916"},{"amount":"1","charge":"1000","slot":"5","id":"11283"},{"amount":"1","charge":"1000","slot":"7","id":"6924"},{"amount":"1","charge":"1000","slot":"9","id":"6922"},{"amount":"1","charge":"1000","slot":"10","id":"6920"}]}, +{"appearance":{"appearance_cache":[{"color":"0","look":"45"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"6","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"2637"},{"amount":"1","charge":"1000","slot":"1","id":"9769"},{"amount":"1","charge":"1000","slot":"2","id":"1478"},{"amount":"1","charge":"1000","slot":"3","id":"4151"},{"amount":"1","charge":"1000","slot":"4","id":"10460"},{"amount":"1","charge":"1000","slot":"5","id":"3842"},{"amount":"1","charge":"1000","slot":"7","id":"10394"},{"amount":"1","charge":"1000","slot":"9","id":"2912"},{"amount":"1","charge":"1000","slot":"10","id":"2904"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"45"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"6","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"8903"},{"amount":"1","charge":"1000","slot":"1","id":"9807"},{"amount":"1","charge":"1000","slot":"2","id":"6585"},{"amount":"1","charge":"1000","slot":"3","id":"4151"},{"amount":"1","charge":"1000","slot":"4","id":"14479"},{"amount":"1","charge":"1000","slot":"5","id":"1187"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"9","id":"2912"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, +{"appearance":{"appearance_cache":[{"color":"20","look":"278"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"10635"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4151"},{"amount":"1","charge":"1000","slot":"4","id":"4757"},{"amount":"1","charge":"1000","slot":"5","id":"7053"},{"amount":"1","charge":"1000","slot":"7","id":"4759"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, + {"appearance":{"appearance_cache":[{"color":"20","look":"278"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1163"},{"amount":"1","charge":"1000","slot":"1","id":"9766"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"10551"},{"amount":"1","charge":"1000","slot":"5","id":"8850"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"9","id":"7455"}]}, +{"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"2643"},{"amount":"1","charge":"1000","slot":"1","id":"10446"},{"amount":"1","charge":"1000","slot":"2","id":"6585"},{"amount":"1","charge":"1000","slot":"3","id":"4151"},{"amount":"1","charge":"1000","slot":"4","id":"4712"},{"amount":"1","charge":"1000","slot":"5","id":"8850"},{"amount":"1","charge":"1000","slot":"7","id":"4714"},{"amount":"1","charge":"1000","slot":"9","id":"7461"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, +{"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"6","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10828"},{"amount":"1","charge":"1000","slot":"1","id":"2414"},{"amount":"1","charge":"1000","slot":"2","id":"6585"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"10551"},{"amount":"1","charge":"1000","slot":"5","id":"1187"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"9","id":"7462"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]} + + + ] +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightPitsPlugin.java b/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightPitsPlugin.java index 06f651c79..062b61c59 100644 --- a/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightPitsPlugin.java +++ b/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightPitsPlugin.java @@ -392,7 +392,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { } } } - int skill = lastVictor.getSkills().getHighestCombatSkill(); + int skill = lastVictor.getSkills().getHighestCombatSkillId(); switch (skill) { case Skills.ATTACK: case Skills.STRENGTH: diff --git a/Server/src/main/java/core/game/interaction/player/RequestOption.kt b/Server/src/main/java/core/game/interaction/player/RequestOption.kt new file mode 100644 index 000000000..96ded35ee --- /dev/null +++ b/Server/src/main/java/core/game/interaction/player/RequestOption.kt @@ -0,0 +1,29 @@ +package core.game.interaction.player + +import core.game.interaction.Option._P_ASSIST +import core.game.interaction.Option._P_TRADE +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.request.RequestType +import core.plugin.Initializable +import rs09.game.interaction.IntType +import rs09.game.interaction.InteractionListener + +/** + * Represents the plugin used to handle the player option interacting. + * @author 'Vexia + * @author dginovker + * @version 2.0 + */ +@Initializable +class RequestOption : InteractionListener { + override fun defineListeners() { + on(_P_TRADE.name, IntType.PLAYER) { player, node -> + player.requestManager.request((node as Player), RequestType.TRADE) + return@on true + } + on(_P_ASSIST.name, IntType.PLAYER) { player, node -> + player.requestManager.request((node as Player), RequestType.ASSIST) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/player/RequestOptionPlugin.java b/Server/src/main/java/core/game/interaction/player/RequestOptionPlugin.java deleted file mode 100644 index 6377344b1..000000000 --- a/Server/src/main/java/core/game/interaction/player/RequestOptionPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.interaction.player; - -import core.game.interaction.Option; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.request.RequestType; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle the player option interacting. - * @author 'Vexia - */ -@Initializable -public final class RequestOptionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - Option._P_ASSIST.setHandler(this); - Option._P_TRADE.setHandler(this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getRequestManager().request((Player) node, RequestType.forOption(option)); - return true; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.java b/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.java deleted file mode 100644 index 46cdd15ef..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.java +++ /dev/null @@ -1,132 +0,0 @@ -package core.game.node.entity.player.link.request; - -import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; - -/** - * Represents a managing class for requests of a player. - * @author Vexia - */ -public final class RequestManager { - - /** - * Represents the player instance. - */ - private final Player player; - - /** - * Represents the target being requested. - */ - private Player target; - - /** - * Constructs a new {@code RequestManager} {@code Object}. - * @param player the player. - */ - public RequestManager(final Player player) { - this.player = player; - } - - /** - * Method used to send a request type to a target. - * @param target the target. - * @param type the type of request. - * @return {@code True} if successful in requesting. - */ - public boolean request(final Player target, final RequestType type) { - if (!canRequest(type, target)) { - return false; - } - if (acceptExisting(target, type)) { - return true; - } - player.getPacketDispatch().sendMessage(type.getMessage()); - target.getPacketDispatch().sendMessage(type.getRequestMessage(player)); - player.setAttribute("lastRequest", type); - this.target = target; - return true; - } - - /** - * Method used to check if a player can continue with a request. - * @param type the type. - * @param target the target. - * @return {@code True} if they can request. - */ - private boolean canRequest(RequestType type, Player target) { - if (target == player) { - return false; - } - if (!target.getLocation().withinDistance(player.getLocation(), 15)) { - player.getPacketDispatch().sendMessage("Unable to find " + target.getUsername() + "."); - return false; - } - if (!target.isActive() || target.getInterfaceManager().isOpened()) { - player.getPacketDispatch().sendMessage("Other player is busy at the moment."); - return false; - } - if (target.getAttribute("busy", 0) > GameWorld.getTicks() || player.getAttribute("busy", 0) > GameWorld.getTicks()) { - player.getPacketDispatch().sendMessage("Other player is busy at the moment."); - return false; - } - if (!player.getZoneMonitor().canRequest(type, target)) { - return false; - } - return type.canRequest(player, target); - } - - /** - * Method used to check if we can accept an existing request. - * @param target the target. - * @param type the type. - * @return {@code True} if so. - */ - public boolean acceptExisting(final Player target, final RequestType type) { - final RequestType lastType = target.getAttribute("lastRequest", null); - if (lastType == type && player == target.getRequestManager().getTarget()) { - close(player); - clear(); - target.getRequestManager().clear(); - player.setAttribute("busy", GameWorld.getTicks() + 2); - target.setAttribute("busy", GameWorld.getTicks() + 2); - type.getModule().open(player, target); - return true; - } - close(player); - return false; - } - - /** - * Closes the components for the player. - * @param player the player. - */ - private void close(Player player) { - player.getDialogueInterpreter().close(); - player.getInterfaceManager().close(); - player.getInterfaceManager().closeChatbox(); - } - - /** - * Method used to clear the cached target. - */ - public void clear() { - this.target = null; - } - - /** - * Gets the player. - * @return the player. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the target. - * @return the target. - */ - public Player getTarget() { - return target; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.kt b/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.kt new file mode 100644 index 000000000..8828f228b --- /dev/null +++ b/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.kt @@ -0,0 +1,113 @@ +package core.game.node.entity.player.link.request + +import core.game.node.entity.player.Player +import rs09.game.ai.AIRepository +import rs09.game.ai.general.scriptrepository.DoublingMoney +import rs09.game.world.GameWorld.ticks + +/** + * Represents a managing class for requests of a player. + * @author Vexia + * @author dginovker + * @version 2.0 + */ +class RequestManager(val player: Player) { + /** + * Represents the target being requested. + */ + var target: Player? = null + private set + + /** + * Method used to send a request type to a target. + * @param target the target. + * @param type the type of request. + * @return `True` if successful. + */ + fun request(target: Player, type: RequestType): Boolean { + if (!canRequest(type, target)) { + return false + } + if (acceptExisting(target, type)) { + return true + } + player.packetDispatch.sendMessage(type.message) + target.packetDispatch.sendMessage(type.getRequestMessage(player)) + player.setAttribute("lastRequest", type) + this.target = target + + // If the target is a bot, get their bot manager to handle the request. + if (target.isArtificial) { + val bot = AIRepository.PulseRepository[target.username.lowercase()] + if (bot != null && bot.botScript is DoublingMoney) { + bot.botScript.tradeReceived(player) + } + } + return true + } + + /** + * Method used to check if a player can continue with a request. + * @param type the type. + * @param target the target. + * @return `True` if so. + */ + private fun canRequest(type: RequestType, target: Player): Boolean { + if (target === player) { + return false + } + if (!target.location.withinDistance(player.location, 15)) { + player.packetDispatch.sendMessage("Unable to find " + target.username + ".") + return false + } + if (!target.isActive || target.interfaceManager.isOpened) { + player.packetDispatch.sendMessage("Other player is busy at the moment.") + return false + } + if (target.getAttribute("busy", 0) > ticks || player.getAttribute("busy", 0) > ticks) { + player.packetDispatch.sendMessage("Other player is busy at the moment.") + return false + } + return if (!player.zoneMonitor.canRequest(type, target)) { + false + } else type.canRequest(player, target) + } + + /** + * Method used to check if we can accept an existing request. + * @param target the target. + * @param type the type. + * @return `True` if so. + */ + private fun acceptExisting(target: Player, type: RequestType): Boolean { + val lastType = target.getAttribute("lastRequest", null) + if (lastType === type && player === target.requestManager.target) { + close(player) + clear() + target.requestManager.clear() + player.setAttribute("busy", ticks + 2) + target.setAttribute("busy", ticks + 2) + type.module.open(player, target) + return true + } + close(player) + return false + } + + /** + * Closes the components for the player. + * @param player the player. + */ + private fun close(player: Player) { + player.dialogueInterpreter.close() + player.interfaceManager.close() + player.interfaceManager.closeChatbox() + } + + /** + * Method used to clear the cached target. + */ + fun clear() { + target = null + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.java b/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.java deleted file mode 100644 index 6bf96e385..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.java +++ /dev/null @@ -1,18 +0,0 @@ -package core.game.node.entity.player.link.request; - -import core.game.node.entity.player.Player; - -/** - * Represents the module used for all request types. - * @author 'Vexia - */ -public interface RequestModule { - - /** - * Method invoked when the targeting player accepts a request. - * @param player the player. - * @param target the target. - */ - public void open(final Player player, final Player target); - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.kt b/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.kt new file mode 100644 index 000000000..4e1705157 --- /dev/null +++ b/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.kt @@ -0,0 +1,18 @@ +package core.game.node.entity.player.link.request + +import core.game.node.entity.player.Player + +/** + * Represents the module used for all request types. + * @author 'Vexia + * @author dginovker + * @version 2.0 + */ +interface RequestModule { + /** + * Method invoked when the targeting player accepts a request. + * @param player the player. + * @param target the target. + */ + fun open(player: Player?, target: Player?) +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.java b/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.java deleted file mode 100644 index 24856d956..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.node.entity.player.link.request; - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.request.assist.AssistSession; -import core.game.node.entity.player.link.request.trade.TradeModule; - -/** - * Represents a request type. - * @author 'Vexia - * @author Emperor - */ -public class RequestType { - - /** - * The trade request type. - */ - public static final RequestType TRADE = new RequestType("Sending a trade offer...", ":tradereq:", new TradeModule()); - - /** - * The assist request type. - */ - public static final RequestType ASSIST = new RequestType("Sending assistance request...", ":assistreq:", new AssistSession()); - - /** - * Represents the message to send for the player when requesting. - */ - private final String message; - - /** - * Represents the requesting message type. - */ - private final String requestMessage; - - /** - * Represents the module used for this type of requesting. - */ - private final RequestModule module; - - /** - * Constructs a new {@code RequestManager {@code Object}. - * @param message the message. - * @param requestMessage the requesting message. - */ - public RequestType(String message, String requestMessage, RequestModule module) { - this.message = message; - this.requestMessage = requestMessage; - this.module = module; - } - - /** - * Checks if the request can be made. - * @param player The player. - * @param target The target. - * @return {@code True} if so. - */ - public boolean canRequest(Player player, Player target) { - return true; - } - - /** - * Gets the requesting message formated with the targets name. - * @param target the target. - * @return the message to send. - */ - public String getRequestMessage(Player target) { - return target.getUsername() + getRequestMessage(); - } - - /** - * Method used to get the request type by the option being handled. - * @param option the option being handled. - * @return the request type in correlation. - */ - public static RequestType forOption(String option) { - return option.equals("trade with") ? TRADE : ASSIST; - } - - /** - * Gets the message. - * @return The message. - */ - public String getMessage() { - return message; - } - - /** - * Gets the requestMessage. - * @return The requestMessage. - */ - public String getRequestMessage() { - return requestMessage; - } - - /** - * Gets the request module. - * @return the module. - */ - public RequestModule getModule() { - return module; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.kt b/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.kt new file mode 100644 index 000000000..14f7189e3 --- /dev/null +++ b/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.kt @@ -0,0 +1,63 @@ +package core.game.node.entity.player.link.request + +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.request.assist.AssistSession +import core.game.node.entity.player.link.request.trade.TradeModule + +/** + * Represents a request type. + * @author 'Vexia + * @author Emperor + * @author dginovker + * @version 2.0 + */ +open class RequestType +/** + * Constructs a new `RequestManager { Object}. + * message the message. + * requestMessage the requesting message.` + */( + /** + * Represents the message to send for the player when requesting. + */ + val message: String, + /** + * Represents the requesting message type. + */ + val requestMessage: String, + /** + * Represents the module used for this type of requesting. + */ + val module: RequestModule +) { + /** + * Checks if the request can be made. + * @param player The player. + * @param target The target. + * @return `True` if so. + */ + open fun canRequest(player: Player?, target: Player?): Boolean { + return true + } + + /** + * Gets the requesting message formated with the targets name. + * @param target the target. + * @return the message to send. + */ + fun getRequestMessage(target: Player): String { + return target.username + requestMessage + } + + companion object { + /** + * The trade request type. + */ + val TRADE = RequestType("Sending a trade offer...", ":tradereq:", TradeModule(null, null)) + + /** + * The assist request type. + */ + val ASSIST = RequestType("Sending assistance request...", ":assistreq:", AssistSession()) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.java b/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.java deleted file mode 100644 index 250f5fccd..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.java +++ /dev/null @@ -1,546 +0,0 @@ -package core.game.node.entity.player.link.request.trade; - -import core.game.component.Component; -import core.game.container.Container; -import core.game.container.ContainerType; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.request.RequestModule; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.system.monitor.PlayerMonitor; -import rs09.game.system.SystemLogger; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Represents the module use to handle a request between trading of two players. - * @author Vexia - * - */ -public final class TradeModule implements RequestModule { - - /** - * Represents the overlay interface component. - */ - public static final Component OVERLAY_INTERFACE = new Component(336); - - /** - * Represents the main interface component. - */ - public static final Component MAIN_INTERFACE = new Component(335).setCloseEvent(new TradeCloseEvent()); - - /** - * Represents the accept interface component. - */ - public static final Component ACCEPT_INTERFACE = new Component(334).setCloseEvent(new TradeCloseEvent()); - - /** - * The inventory params for the runscript. - */ - public static final Object[] INVENTORY_PARAMS = new Object[] { "", "", "", "Lend", "Offer-X", "Offer-All", "Offer-10", "Offer-5", "Offer", -1, 0, 7, 4, 93, (336 << 16) }; - - /** - * The trade params for the run script. - */ - public static final Object[] TRADE_PARAMS = new Object[] { "", "", "", "", "Remove-X", "Remove-All", "Remove-10", "Remove-5", "Remove", -1, 0, 7, 4, 90, (335 << 16) | 30 }; - - /** - * The partener params for the run script. - */ - public static final Object[] PARTENER_PARAMS = new Object[] { "", "", "", "", "", "", "", "", "", -1, 0, 7, 4, 91, (335 << 16) | 32 }; - - /** - * Represents the childs that should be hidden. - */ - public static final int[] HIDDEN_CHILDS = new int[] { 42, 43, 44, 42, 44, 40, 41 }; - - /** - * If the container has been retained. - */ - private boolean retained; - - /** - * Represents the player instance. - */ - private Player player; - - /** - * Represents the target instance. - */ - private Player target; - - /** - * Represents the container of this session. - */ - private TradeContainer container; - - /** - * Represents if this session has accepted. - */ - private boolean accepted; - - /** - * Represents if the trade is modified. - */ - private boolean modified = false; - - /** - * Represents the stage of the trade(0=started, 1=second accept) - */ - private int stage; - - /** - * Constructs a new {@code TradeModule} {@code Object}. - * @param player the player. - * @param target the target. - */ - public TradeModule(final Player player, final Player target) { - this.player = player; - this.target = target; - this.container = new TradeContainer(player); - } - - /** - * Constructs a new {@code TradeModule} {@code Object}. - */ - public TradeModule() { - /** - * empty. - */ - } - - @Override - public void open(Player player, Player target) { - extend(player, target); - if (getExtension(target) == null || getExtension(player) == null) { - return; - } - getExtension(player).openInterface(getInterface(stage)).display(stage); - if (getExtension(target) == null || getExtension(player) == null) { - return; - } - getExtension(target).openInterface(getInterface(stage)).display(stage); - player.getDialogueInterpreter().close(); - target.getDialogueInterpreter().close(); - } - - /** - * Method used to update the trade interfaces. - */ - public void update() { - display(stage); - getExtension(target).display(stage); - } - - /** - * Method used to extend the module for each participant. - * @param player the player. - * @param target the target. - */ - public static void extend(final Player player, final Player target) { - player.addExtension(TradeModule.class, new TradeModule(player, target)); - target.addExtension(TradeModule.class, new TradeModule(target, player)); - } - - /** - * Method used to get the extension from the player. - * @param player the player. - * @return the module instance. - */ - public static TradeModule getExtension(final Player player) { - return player.getExtension(TradeModule.class); - } - - /** - * Method used to open an interface at a stage. - * @param component the component. - * @return the module instance for chaining. - */ - private TradeModule openInterface(final Component component) { - return component == MAIN_INTERFACE ? openMain() : openSecond(); - } - - /** - * Gets the accepting message to display. - * @return the message. - */ - private String getAcceptMessage() { - boolean otherAccept = TradeModule.getExtension(target).isAccepted(); - return !otherAccept && !accepted ? "" : otherAccept ? "Other player has accepted." : "Waiting for other player..."; - } - - /** - * Method used to display the interface display depending on stage. - * @param stage the stage. - * @return module the module instance for chaining. - */ - private TradeModule display(int stage) { - switch (stage) { - case 0: - for (int i : HIDDEN_CHILDS) { - player.getPacketDispatch().sendString("", MAIN_INTERFACE.getId(), i); - } - player.getPacketDispatch().sendString("Trading With: " + target.getUsername(), 335, 15); - player.getPacketDispatch().sendString(target.getUsername() + " has " + (target.getInventory().freeSlots() == 0 ? "no" : (target.getInventory().freeSlots())) + " free inventory slots.", 335, 21); - player.getPacketDispatch().sendString(getAcceptMessage(), 335, 36); - break; - case 1: - player.getPacketDispatch().sendString("Trading with:
" + "" + target.getUsername().substring(0, 1).toUpperCase() + target.getUsername().substring(1), 334, 2); - final String acceptMessage = getAcceptMessage(); - if (acceptMessage != "") { - player.getPacketDispatch().sendString(acceptMessage, 334, 33); - } - player.getInterfaceManager().restoreTabs(); - break; - } - displayModified(stage); - container.update(true); - return this; - } - - /** - * Gets the interface for the current stage. - * @param stage the stage. - * @return the component. - */ - private Component getInterface(int stage) { - return stage == 0 ? MAIN_INTERFACE : ACCEPT_INTERFACE; - } - - /** - * Method used to display if the trade is modified. - * @param stage the sytage. - */ - private void displayModified(int stage) { - final boolean otherModified = TradeModule.getExtension(target).isModified(); - if (stage == 1) { - player.getPacketDispatch().sendInterfaceConfig(334, 45, !modified); - player.getPacketDispatch().sendInterfaceConfig(334, 46, !otherModified); - } else { - if (otherModified) { - player.getConfigManager().set(1043, 1); - } - if (modified) { - player.getConfigManager().set(1042, 1); - } - } - } - - /** - * Method used to decline this offer. - */ - public void decline() { - player.getInterfaceManager().close(); - target.getPacketDispatch().sendMessage("Other player has declined trade!"); - } - - /** - * Method used to check if the next stage can proceed. - */ - private void nextStage() { - if (accepted && TradeModule.getExtension(target).isAccepted()) { - if (stage == 0) { - if (!hasSpace()) { - return; - } - incrementStage(); - openInterface(getInterface(stage)); - TradeModule.getExtension(target).incrementStage(); - TradeModule.getExtension(target).openInterface(getInterface(stage)); - TradeModule.getExtension(target).setAccepted(false, false); - setAccepted(false, false); - } else { - giveContainers(this); - incrementStage(); - TradeModule.getExtension(target).incrementStage(); - TradeModule.getExtension(target).setAccepted(false, false); - setAccepted(false, false); - player.getInterfaceManager().close(); - return; - } - } - update(); - } - - /** - * Method used to open the main interface. - * @return the module instance for chaining. - */ - private TradeModule openMain() { - player.getInterfaceManager().closeDefaultTabs(); - player.getInterfaceManager().open(MAIN_INTERFACE); - player.getInterfaceManager().openSingleTab(OVERLAY_INTERFACE); - player.getInventory().refresh(); - player.getPacketDispatch().sendIfaceSettings(1278, 30, 335, 0, 27); - player.getPacketDispatch().sendIfaceSettings(1026, 32, 335, 0, 27); - player.getPacketDispatch().sendIfaceSettings(1278, 0, 336, 0, 27); - player.getPacketDispatch().sendIfaceSettings(2360446, 0, 335, 0, 27); - player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", INVENTORY_PARAMS); - player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", TRADE_PARAMS); - player.getPacketDispatch().sendRunScript(695, "IviiiIsssssssss", PARTENER_PARAMS); - return this; - } - - /** - * Method used to open the second interface. - * @return the module instance for chaining. - */ - private TradeModule openSecond() { - player.getInterfaceManager().open(ACCEPT_INTERFACE); - player.getInterfaceManager().closeSingleTab(); - displayOffer(container, 37); - displayOffer(TradeModule.getExtension(target).getContainer(), 41); - return this; - } - - /** - * Method used to set the value of accepted. - * @param accept the accept. - */ - public void setAccepted(final boolean accept, boolean update) { - this.accepted = accept; - if (update) { - nextStage(); - } - } - - /** - * Method used to alert the player the trade has been modified. - * @param slot the slot. - */ - public void alert(final int slot) { - player.getPacketDispatch().sendRunScript(143, "Iiii", new Object[] { slot, 7, 4, 21954593 }); - target.getPacketDispatch().sendRunScript(143, "Iiii", new Object[] { slot, 7, 4, 21954594 }); - } - - /** - * Method used to display an offer. - * @param container the container. - */ - private void displayOffer(final Container container, int child) { - boolean split = container.itemCount() > 14; - if (split) { - player.getPacketDispatch().sendInterfaceConfig(334, child + 1, false); - player.getPacketDispatch().sendInterfaceConfig(334, child + 2, false); - String[] messages = new String[] { getDisplayMessage(splitList(container.toArray(), 0, 14)), getDisplayMessage(splitList(container.toArray(), 14, container.toArray().length)) }; - player.getPacketDispatch().sendString(messages[0], 334, child + 1); - player.getPacketDispatch().sendString(messages[1], 334, child + 2); - } else { - player.getPacketDispatch().sendInterfaceConfig(334, child, false); - player.getPacketDispatch().sendString(getDisplayMessage(container.toArray()), 334, child); - } - } - - /** - * Method used to get the display message. - * @param items the items. - * @return the message. - */ - private String getDisplayMessage(Item[] items) { - String message = "Absolutely nothing!"; - if (items.length > 0) { - message = ""; - for (int i = 0; i < items.length; i++) { - if (items[i] == null) { - continue; - } - message = message + "" + items[i].getName(); - if (items[i].getAmount() > 1) { - message = message + " x "; - message = message + "" + getFormattedNumber(items[i].getAmount()) + "
"; - } else { - message = message + "
"; - } - } - } - return message; - } - - /** - * Method used to check if the players & targets have enough space. - * @return {@code True} if so. - */ - private boolean hasSpace() { - boolean hasSpace = true; - if (!player.getInventory().hasSpaceFor(TradeModule.getExtension(target).getContainer())) { - target.getPacketDispatch().sendMessage("Other player doesn't have enough space in their inventory for this trade."); - player.getPacketDispatch().sendMessage("You don't have enough inventory space for this."); - hasSpace = false; - } else if (!target.getInventory().hasSpaceFor(container)) { - player.getPacketDispatch().sendMessage("Other player doesn't have enough space in their inventory for this trade."); - target.getPacketDispatch().sendMessage("You don't have enough inventory space for this."); - hasSpace = false; - } - if (!hasSpace) { - setAccepted(false, true); - TradeModule.getExtension(target).setAccepted(false, true); - } - return hasSpace; - } - - /** - * Gets the formatted number. - * @param amount the amount. - * @return the formatted number. - */ - private String getFormattedNumber(int amount) { - return new DecimalFormat("#,###,##0").format(amount).toString(); - } - - /** - * Method used to give the containers to each participants. - * @param module the module. - */ - private void giveContainers(final TradeModule module) { - final Container pContainer = module.getContainer(); - final Container oContainer = TradeModule.getExtension(module.getTarget()).getContainer(); - SystemLogger.logTrade(module.getPlayer().getUsername() + " -> " + module.getTarget().getUsername() + " " + Arrays.toString(pContainer.toArray())); - SystemLogger.logTrade(module.getTarget().getUsername() + " -> " + module.getPlayer().getUsername() + " " + Arrays.toString(oContainer.toArray())); - addContainer(module.getPlayer(), module.getTarget(), oContainer); - addContainer(module.getTarget(), module.getPlayer(), pContainer); - module.getTarget().getPacketDispatch().sendMessage("Accepted trade."); - module.getPlayer().getPacketDispatch().sendMessage("Accepted trade."); - } - - /** - * Method used to add a container for a player. - * @param player the player. - * @param target the target. - * @param container the container. - */ - private void addContainer(final Player player, Player target, final Container container) { - Container c = new Container(container.itemCount(), ContainerType.ALWAYS_STACK); - c.addAll(container); - String log = "traded => " + target.getName() + " received => {"; - for (Item i : container.toArray()) { - if (i == null) { - continue; - } - if (i.getAmount() > player.getInventory().getMaximumAdd(i)) { - i.setAmount(player.getInventory().getMaximumAdd(i)); - } - if (!player.getInventory().add(i)) { - GroundItemManager.create(i, player); - } - } - for (Item i : c.toArray()) { - if (i == null) { - continue; - } - log += i.getName() + " x " + i.getAmount() + ","; - } - if (log.charAt(log.length() - 1) == ',') { - log = log.substring(0, log.length() - 1); - } - log += "}"; - player.getMonitor().log(log, PlayerMonitor.TRADE_LOG); - } - - /** - * Gets the split list as an array. - * @param items the items. - * @param min the min. - * @param max the max. - * @return the split item array. - */ - private Item[] splitList(Item[] items, int min, int max) { - List list = new ArrayList<>(20); - for (int i = min; i < max; i++) { - if (items[i] == null) { - continue; - } - list.add(items[i]); - } - Item[] array = new Item[list.size()]; - for (int i = 0; i < list.size(); i++) { - if (list.get(i) == null) { - continue; - } - array[i] = list.get(i); - } - return array; - } - - /** - * Method used to increment the stage. - */ - public void incrementStage() { - stage++; - } - - /** - * Gets the stage. - * @return the stage. - */ - public int getStage() { - return stage; - } - - /** - * Gets the accepted. - * @return The accepted. - */ - public boolean isAccepted() { - return accepted; - } - - /** - * Gets the trade container. - * @return the container. - */ - public TradeContainer getContainer() { - return container; - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the target. - * @return The target. - */ - public Player getTarget() { - return target; - } - - /** - * Gets the modified. - * @return The modified. - */ - public boolean isModified() { - return modified; - } - - /** - * Sets the modified. - * @param modified The modified to set. - */ - public void setModified(boolean modified) { - this.modified = modified; - } - - /** - * Gets the retained. - * @return The retained. - */ - public boolean isRetained() { - return retained; - } - - /** - * Sets the retained. - * @param retained The retained to set. - */ - public void setRetained(boolean retained) { - this.retained = retained; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.kt b/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.kt new file mode 100644 index 000000000..193dcde50 --- /dev/null +++ b/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.kt @@ -0,0 +1,528 @@ +package core.game.node.entity.player.link.request.trade + +import core.game.component.Component +import core.game.container.Container +import core.game.container.ContainerType +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.request.RequestModule +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.system.monitor.PlayerMonitor +import rs09.game.ai.AIRepository +import rs09.game.ai.general.scriptrepository.DoublingMoney +import rs09.game.system.SystemLogger +import java.text.DecimalFormat +import java.util.* + +/** + * Represents the module use to handle a request between trading of two players. + * @author Vexia + * @author dginovker + * @version 2.0 + */ +class TradeModule +/** + * Constructs a new `TradeModule` `Object`. + * @param player the player. + * @param target the target. + */(player: Player?, target: Player?) : RequestModule { + /** + * If the container has been retained. + */ + var isRetained = false + /** + * Represents the player instance. + */ + var player: Player? = player + private set + /** + * Represents the target instance. + */ + var target: Player? = target + private set + /** + * Represents the container of this session. + */ + var container: TradeContainer? = null + private set + /** + * Represents if this session has accepted. + */ + var isAccepted = false + private set + /** + * Represents if the trade is modified. + */ + var isModified = false + /** + * Represents the stage of the trade(0=started, 1=second accept) + */ + var stage = 0 + private set + + override fun open(player: Player?, target: Player?) { + extend(player, target) + if (getExtension(target) == null || getExtension(player) == null) { + return + } + getExtension(player)!!.openInterface(getInterface(stage)).display(stage) + if (getExtension(target) == null || getExtension(player) == null) { + return + } + getExtension(target)!!.openInterface(getInterface(stage)).display(stage) + player!!.dialogueInterpreter.close() + target!!.dialogueInterpreter.close() + } + + /** + * Method used to update the trade interfaces. + */ + fun update() { + display(stage) + getExtension(target)!!.display(stage) + } + + /** + * Method used to open an interface at a stage. + * @param component the component. + * @return the module instance for chaining. + */ + private fun openInterface(component: Component): TradeModule { + return if (component === MAIN_INTERFACE) openMain() else openSecond() + } + + /** + * Gets the accepting message to display. + * @return the message. + */ + private val acceptMessage: String + get() { + val otherAccept = getExtension(target)!!.isAccepted + return if (!otherAccept && !isAccepted) "" else if (otherAccept) "Other player has accepted." else "Waiting for other player..." + } + + /** + * Method used to display the interface display depending on stage. + * @param stage the stage. + * @return module the module instance for chaining. + */ + private fun display(stage: Int): TradeModule { + when (stage) { + 0 -> { + for (i in HIDDEN_CHILDS) { + player!!.packetDispatch.sendString("", MAIN_INTERFACE.id, i) + } + player!!.packetDispatch.sendString("Trading With: " + target!!.username, 335, 15) + player!!.packetDispatch.sendString( + target!!.username + " has " + (if (target!!.inventory.freeSlots() == 0) "no" else target!!.inventory.freeSlots()) + " free inventory slots.", + 335, + 21 + ) + player!!.packetDispatch.sendString(acceptMessage, 335, 36) + } + + 1 -> { + player!!.packetDispatch.sendString( + "Trading with:
" + "" + target!!.username.substring( + 0, + 1 + ).uppercase(Locale.getDefault()) + target!!.username.substring(1), 334, 2 + ) + val acceptMessage = acceptMessage + if (acceptMessage !== "") { + player!!.packetDispatch.sendString(acceptMessage, 334, 33) + } + player!!.interfaceManager.restoreTabs() + } + } + displayModified(stage) + container!!.update(true) + return this + } + + /** + * Gets the interface for the current stage. + * @param stage the stage, defaults to this TradeModule's stage. + * @return the component. + */ + fun getInterface(stage: Int = this.stage): Component { + return if (stage == 0) MAIN_INTERFACE else ACCEPT_INTERFACE + } + + /** + * Gets the interface for the TradeModule stage + * @return the component + */ + fun getInterface(): Component { + return getInterface(stage) + } + + /** + * Method used to display red text at the top of the trade window when the trade has been modified + * (Red text appears at the bottom in second trade screen) + * @param stage the sytage. + */ + private fun displayModified(stage: Int) { + val otherModified = getExtension(target)!!.isModified + if (stage == 0) { + // Main interface + if (otherModified) { + player!!.configManager[1043] = 1 + } + if (isModified) { + player!!.configManager[1042] = 1 + } + } else { + // Accept interface + player!!.packetDispatch.sendInterfaceConfig(334, 45, !isModified) + player!!.packetDispatch.sendInterfaceConfig(334, 46, !otherModified) + } + } + + /** + * Method used to display flashing alert if the trade has been modified. + * @param slot the slot. + */ + fun alert(slot: Int) { + player!!.packetDispatch.sendRunScript(143, "Iiii", *arrayOf(slot, 7, 4, 21954594)) + target!!.packetDispatch.sendRunScript(143, "Iiii", *arrayOf(slot, 7, 4, 21954593)) + } + + + /** + * Method used to decline this offer. + */ + fun decline() { + player!!.interfaceManager.close() + target!!.packetDispatch.sendMessage("Other player has declined trade!") + } + + /** + * Method used to check if the next stage can proceed. + */ + private fun nextStage() { + if (isAccepted && getExtension(target)!!.isAccepted) { + if (stage == 0) { + if (!hasSpace()) { + return + } + incrementStage() + openInterface(getInterface(stage)) + getExtension(target)!!.incrementStage() + getExtension(target)!!.openInterface(getInterface(stage)) + getExtension(target)!!.setAccepted(false, false) + setAccepted(false, false) + } else { + giveContainers(this) + incrementStage() + getExtension(target)!!.incrementStage() + getExtension(target)!!.setAccepted(false, false) + setAccepted(false, false) + player!!.interfaceManager.close() + return + } + } + update() + } + + /** + * Method used to open the main interface. + * @return the module instance for chaining. + */ + private fun openMain(): TradeModule { + player!!.interfaceManager.closeDefaultTabs() + player!!.interfaceManager.open(MAIN_INTERFACE) + player!!.interfaceManager.openSingleTab(OVERLAY_INTERFACE) + player!!.inventory.refresh() + player!!.packetDispatch.sendIfaceSettings(1278, 30, 335, 0, 27) + player!!.packetDispatch.sendIfaceSettings(1026, 32, 335, 0, 27) + player!!.packetDispatch.sendIfaceSettings(1278, 0, 336, 0, 27) + player!!.packetDispatch.sendIfaceSettings(2360446, 0, 335, 0, 27) + player!!.packetDispatch.sendRunScript(150, "IviiiIsssssssss", *INVENTORY_PARAMS) + player!!.packetDispatch.sendRunScript(150, "IviiiIsssssssss", *TRADE_PARAMS) + player!!.packetDispatch.sendRunScript(695, "IviiiIsssssssss", *PARTENER_PARAMS) + return this + } + + /** + * Method used to open the second interface. + * @return the module instance for chaining. + */ + private fun openSecond(): TradeModule { + player!!.interfaceManager.open(ACCEPT_INTERFACE) + player!!.interfaceManager.closeSingleTab() + displayOffer(container, 37) + displayOffer(getExtension(target)!!.container, 41) + return this + } + + /** + * Method used to set the value of accepted. + * @param accept whether the trade was accepted or declined + */ + fun setAccepted(accept: Boolean, update: Boolean) { + isAccepted = accept + if (update) { + nextStage() + } + } + + /** + * Method used to display an offer. + * @param container the container. + */ + private fun displayOffer(container: Container?, child: Int) { + val split = container!!.itemCount() > 14 + if (split) { + player!!.packetDispatch.sendInterfaceConfig(334, child + 1, false) + player!!.packetDispatch.sendInterfaceConfig(334, child + 2, false) + val messages = arrayOf( + getDisplayMessage(splitList(container.toArray(), 0, 14)), getDisplayMessage( + splitList( + container.toArray(), 14, container.toArray().size + ) + ) + ) + player!!.packetDispatch.sendString(messages[0], 334, child + 1) + player!!.packetDispatch.sendString(messages[1], 334, child + 2) + } else { + player!!.packetDispatch.sendInterfaceConfig(334, child, false) + player!!.packetDispatch.sendString(getDisplayMessage(container.toArray()), 334, child) + } + } + + /** + * Method used to get the display message. + * @param items the items. + * @return the message. + */ + private fun getDisplayMessage(items: Array): String { + var message = "Absolutely nothing!" + if (items.isNotEmpty()) { + message = "" + for (i in items.indices) { + if (items[i] == null) { + continue + } + message = message + "" + items[i]!!.name + if (items[i]!!.amount > 1) { + message = "$message x " + message = message + "" + getFormattedNumber(items[i]!!.amount) + "
" + } else { + message = "$message
" + } + } + } + return message + } + + /** + * Method used to check if the players & targets have enough space. + * @return `True` if so. + */ + private fun hasSpace(): Boolean { + var hasSpace = true + if (!player!!.inventory.hasSpaceFor(getExtension(target)!!.container)) { + target!!.packetDispatch.sendMessage("Other player doesn't have enough space in their inventory for this trade.") + player!!.packetDispatch.sendMessage("You don't have enough inventory space for this.") + hasSpace = false + } else if (!target!!.inventory.hasSpaceFor(container)) { + player!!.packetDispatch.sendMessage("Other player doesn't have enough space in their inventory for this trade.") + target!!.packetDispatch.sendMessage("You don't have enough inventory space for this.") + hasSpace = false + } + if (!hasSpace) { + setAccepted(false, true) + getExtension(target)!!.setAccepted(false, true) + } + return hasSpace + } + + /** + * Gets the formatted number. + * @param amount the amount. + * @return the formatted number. + */ + private fun getFormattedNumber(amount: Int): String { + return DecimalFormat("#,###,##0").format(amount.toLong()).toString() + } + + /** + * Method used to give the containers to each participants. + * @param module the module. + */ + private fun giveContainers(module: TradeModule) { + val pContainer: Container? = module.container + val oContainer: Container? = getExtension(module.target)!!.container + + SystemLogger.logTrade(module.player!!.username + " -> " + module.target!!.username + " " + Arrays.toString(pContainer!!.toArray())) + SystemLogger.logTrade(module.target!!.username + " -> " + module.player!!.username + " " + Arrays.toString(oContainer!!.toArray())) + + (AIRepository.PulseRepository[module.player!!.username.lowercase()]?.botScript as DoublingMoney?)?.itemsReceived(module.target!!, oContainer) + (AIRepository.PulseRepository[module.target!!.username.lowercase()]?.botScript as DoublingMoney?)?.itemsReceived(module.player!!, pContainer) + + addContainer(module.player, module.target, oContainer) + addContainer(module.target, module.player, pContainer) + module.target!!.packetDispatch.sendMessage("Accepted trade.") + module.player!!.packetDispatch.sendMessage("Accepted trade.") + } + + /** + * Method used to add a container for a player. + * @param player the player who loses the items. + * @param target the target who gains the items. + * @param container the container. + */ + private fun addContainer(player: Player?, target: Player?, container: Container?) { + val c = Container(container!!.itemCount(), ContainerType.ALWAYS_STACK) + c.addAll(container) + var log = "traded => " + target!!.name + " received => {" + for (i in container.toArray()) { + if (i == null) { + continue + } + if (i.amount > player!!.inventory.getMaximumAdd(i)) { + i.amount = player.inventory.getMaximumAdd(i) + } + if (!player.inventory.add(i)) { + GroundItemManager.create(i, player) + } + } + for (i in c.toArray()) { + if (i == null) { + continue + } + log += i.name + " x " + i.amount + "," + } + if (log[log.length - 1] == ',') { + log = log.substring(0, log.length - 1) + } + log += "}" + player!!.monitor.log(log, PlayerMonitor.TRADE_LOG) + } + + /** + * Gets the split list as an array. + * @param items the items. + * @param min the min. + * @param max the max. + * @return the split item array. + */ + private fun splitList(items: Array, min: Int, max: Int): Array { + val list: MutableList = ArrayList(20) + for (i in min until max) { + if (items[i] == null) { + continue + } + list.add(items[i]) + } + val array = arrayOfNulls(list.size) + for (i in list.indices) { + if (list[i] == null) { + continue + } + array[i] = list[i] + } + return array + } + + /** + * Method used to increment the stage. + */ + fun incrementStage() { + stage++ + } + + companion object { + /** + * Represents the overlay interface component. + */ + val OVERLAY_INTERFACE = Component(336) + + /** + * Represents the main interface component. + */ + val MAIN_INTERFACE: Component = Component(335).setCloseEvent(TradeCloseEvent()) + + /** + * Represents the "accept" interface component. + */ + val ACCEPT_INTERFACE: Component = Component(334).setCloseEvent(TradeCloseEvent()) + + /** + * The inventory params for the runscript. + */ + val INVENTORY_PARAMS = arrayOf( + "", + "", + "", + "Lend", + "Offer-X", + "Offer-All", + "Offer-10", + "Offer-5", + "Offer", + -1, + 0, + 7, + 4, + 93, + 336 shl 16 + ) + + /** + * The trade params for the run script. + */ + val TRADE_PARAMS = arrayOf( + "", + "", + "", + "", + "Remove-X", + "Remove-All", + "Remove-10", + "Remove-5", + "Remove", + -1, + 0, + 7, + 4, + 90, + 335 shl 16 or 30 + ) + + /** + * The partener params for the run script. + */ + val PARTENER_PARAMS = arrayOf("", "", "", "", "", "", "", "", "", -1, 0, 7, 4, 91, 335 shl 16 or 32) + + /** + * Represents the childs that should be hidden. + */ + val HIDDEN_CHILDS = intArrayOf(42, 43, 44, 42, 44, 40, 41) + + /** + * Method used to extend the module for each participant. + * @param player the player. + * @param target the target. + */ + fun extend(player: Player?, target: Player?) { + player!!.addExtension(TradeModule::class.java, TradeModule(player, target)) + target!!.addExtension(TradeModule::class.java, TradeModule(target, player)) + } + + /** + * Method used to get the extension from the player. + * @param player the player. + * @return the module instance. + */ + @JvmStatic + fun getExtension(player: Player?): TradeModule? { + return player!!.getExtension(TradeModule::class.java) + } + } + + init { + container = TradeContainer(player) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/Skills.java b/Server/src/main/java/core/game/node/entity/skill/Skills.java index c36068f92..5666d5458 100644 --- a/Server/src/main/java/core/game/node/entity/skill/Skills.java +++ b/Server/src/main/java/core/game/node/entity/skill/Skills.java @@ -342,7 +342,7 @@ public final class Skills { * Gets the highest combat skill id. * @return The id of the highest combat skill. */ - public int getHighestCombatSkill() { + public int getHighestCombatSkillId() { int id = 0; int last = 0; for (int i = 0; i < 5; i++) { diff --git a/Server/src/main/java/core/game/system/SystemTermination.java b/Server/src/main/java/core/game/system/SystemTermination.java index 6be390c35..fee8b713c 100644 --- a/Server/src/main/java/core/game/system/SystemTermination.java +++ b/Server/src/main/java/core/game/system/SystemTermination.java @@ -36,10 +36,10 @@ public final class SystemTermination { public void terminate() { SystemLogger.logInfo(this.getClass(), "Initializing termination sequence - do not shutdown!"); try { - SystemLogger.logInfo(this.getClass(), "Stopping all bots..."); - AIRepository.clearAllBots(); SystemLogger.logInfo(this.getClass(), "Shutting down networking..."); Server.setRunning(false); + SystemLogger.logInfo(this.getClass(), "Stopping all bots..."); + AIRepository.clearAllBots(); Server.getReactor().terminate(); SystemLogger.logInfo(this.getClass(), "Stopping all pulses..."); GameWorld.getMajorUpdateWorker().stop(); diff --git a/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.java b/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.java deleted file mode 100644 index 56ed92146..000000000 --- a/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.java +++ /dev/null @@ -1,88 +0,0 @@ -package core.game.world.update.flag.context; - -import core.game.node.entity.player.Player; - -/** - * Represents a chat message. - * @author Emperor - */ -public class ChatMessage { - - /** - * The player reference. - */ - private Player player; - - /** - * The chat text. - */ - private String text; - - /** - * The effects. - */ - private int effects; - - /** - * The numChars. - */ - private int numChars; - - public boolean isQuickChat = false; - - /** - * Constructs a new {@code ChatMessage} {@code Object}. - * @param player The player. - * @param text The chat text. - * @param effects The chat effects. - * @param numChars The num chars. - */ - public ChatMessage(Player player, String text, int effects, int numChars) { - this.player = player; - this.text = text; - this.effects = effects; - this.numChars = numChars; - } - - /** - * Constructs a new {@code ChatMessage.java} {@code Object}. - * @param player the player. - * @param text the text. - */ - public ChatMessage(Player player, String text) { - this.player = player; - this.text = text; - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - - /** - * Get the chat text. - * @return The chat text. - */ - public String getText() { - return text; - } - - /** - * Get the chat effects. - * @return The chat effects. - */ - public int getEffects() { - return effects; - } - - /** - * Get the num chars. - * @return The num chars. - */ - public int getNumChars() { - return numChars; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.kt b/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.kt new file mode 100644 index 000000000..e1f6c7819 --- /dev/null +++ b/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.kt @@ -0,0 +1,40 @@ +package core.game.world.update.flag.context + +import core.game.node.entity.player.Player + +/** + * Represents a chat message. + * @author Emperor + */ +class ChatMessage +/** + * Constructs a new `ChatMessage` `Object`. + * @param player The player. + * @param text The chat text. + * @param effects The chat effects. + * @param numChars The num chars. + */(player: Player, text: String, effects: Int, numChars: Int) { + /** + * The player reference. + */ + var player: Player = player + private set + /** + * The chat text. + */ + var text: String = text + private set + /** + * The effects. + */ + var effects = effects + private set + /** + * The numChars. + */ + var numChars = numChars + private set + + @JvmField + var isQuickChat = false +} \ No newline at end of file diff --git a/Server/src/main/java/rs09/game/ai/general/scriptrepository/Script.java b/Server/src/main/java/rs09/game/ai/general/scriptrepository/Script.java index 7e725eafd..6e7c97f8b 100644 --- a/Server/src/main/java/rs09/game/ai/general/scriptrepository/Script.java +++ b/Server/src/main/java/rs09/game/ai/general/scriptrepository/Script.java @@ -53,5 +53,6 @@ public abstract class Script { bot.getAppearance().sync(); } + // This does not get called and all implementations should be removed public abstract Script newInstance(); } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/AIRepository.kt b/Server/src/main/kotlin/rs09/game/ai/AIRepository.kt index 52f5840d8..e1d087e49 100644 --- a/Server/src/main/kotlin/rs09/game/ai/AIRepository.kt +++ b/Server/src/main/kotlin/rs09/game/ai/AIRepository.kt @@ -15,7 +15,7 @@ class AIRepository { val GEOffers = HashMap() @JvmStatic - val PulseRepository = ArrayList() + val PulseRepository = HashMap() // Lowercase username, pulse @JvmStatic fun addItem(item: GroundItem){ @@ -44,7 +44,11 @@ class AIRepository { } @JvmStatic fun clearAllBots() { - PulseRepository.toTypedArray().forEach { it.stop(); it.botScript.bot.clear(); AIPlayer.deregister((it.botScript.bot as AIPlayer).uid) } + PulseRepository.forEach { (_, it) -> + it.stop(); + it.botScript.bot.clear(); + AIPlayer.deregister((it.botScript.bot as AIPlayer).uid) + } } } -} \ No newline at end of file +} diff --git a/Server/src/main/kotlin/rs09/game/ai/general/GeneralBotCreator.kt b/Server/src/main/kotlin/rs09/game/ai/general/GeneralBotCreator.kt index 78005f894..33f96bfb2 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/GeneralBotCreator.kt +++ b/Server/src/main/kotlin/rs09/game/ai/general/GeneralBotCreator.kt @@ -5,6 +5,7 @@ import core.game.system.task.Pulse import rs09.game.world.GameWorld import core.game.world.map.Location import core.tools.RandomFunction +import rs09.Server import rs09.game.ai.AIPBuilder import rs09.game.ai.AIPlayer import rs09.game.ai.AIRepository @@ -13,14 +14,14 @@ import rs09.game.ai.general.scriptrepository.Script class GeneralBotCreator { //org/crandor/net/packet/in/InteractionPacket.java <<< This is a very useful class for learning to code bots - constructor(loc: Location?, botScript: Script) { + constructor(botScript: Script, loc: Location?) { botScript.bot = AIPBuilder.create(loc) GameWorld.Pulser.submit(BotScriptPulse(botScript)) } constructor(botScript: Script, bot: AIPlayer?) { botScript.bot = bot - GameWorld.Pulser.submit(BotScriptPulse(botScript).also { AIRepository.PulseRepository.add(it) }) + GameWorld.Pulser.submit(BotScriptPulse(botScript).also { AIRepository.PulseRepository[it.botScript.bot.username.lowercase()] = it }) } constructor(botScript: Script, player: Player, isPlayer: Boolean){ @@ -69,13 +70,14 @@ class GeneralBotCreator { ticks = Integer.MAX_VALUE - 20 //Sets the ticks as high as they can go (safely) and then runs pulse again pulse() //to trigger the transition pulse to be submitted. super.stop() - AIRepository.PulseRepository.remove(this) + if (Server.running) AIRepository.PulseRepository.remove(this.botScript.bot.username.lowercase()) } } inner class TransitionPulse(val script: Script) : Pulse(RandomFunction.random(60,200)){ override fun pulse(): Boolean { - GameWorld.Pulser.submit(BotScriptPulse(script.newInstance()).also { AIRepository.PulseRepository.add(it) }) + // This does not get called and should be removed + GameWorld.Pulser.submit(BotScriptPulse(script.newInstance()).also { AIRepository.PulseRepository[it.botScript.bot.username.lowercase()] = it }) return true } } diff --git a/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt b/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt index 92b755ff1..df4517b9d 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt +++ b/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt @@ -30,6 +30,8 @@ import core.game.world.update.flag.player.ChatFlag import core.tools.RandomFunction import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import org.json.simple.JSONArray +import org.json.simple.JSONObject import org.rs09.consts.Items import rs09.ServerConstants.Companion.SERVER_GE_NAME import rs09.game.ai.AIRepository @@ -44,6 +46,8 @@ import rs09.game.world.repository.Repository import rs09.tools.stringtools.colorize import java.util.* import java.util.concurrent.CountDownLatch +import kotlin.math.max +import kotlin.math.min import kotlin.math.pow import kotlin.math.sqrt @@ -734,6 +738,64 @@ class ScriptAPI(private val bot: Player) { bot.inventory.add(item) } + /** + * Method to equip list of items and set stats + * Useful for starting a bot up + * @param items the list of items to equip + * @author dginovker + */ + fun equipAndSetStats(items: List?){ + if (items == null) return + for(item in items){ + equipAndSetStats(item) + } + } + + /** + * Method to equip a single item and set stats + * Useful for starting a bot up + * @param item the item to equip + * @author dginovker + */ + fun equipAndSetStats(item: Item){ + val configs = item.definition.handlers + val slot = configs["equipment_slot"] ?: return + bot.equipment.add(item, slot as Int, + false,false) + val reqs = configs["requirements"] + if(reqs != null) + for(req in configs["requirements"] as HashMap) + bot.skills.setStaticLevel(req.key, req.value) + bot.skills.updateCombatLevel() + } + + /** + * Method to load appearance and equipment from JSON + * Useful for starting a bot up + * @param json the JSON object to load from (dumped via ::dumpappearance) + * @author dginovker + */ + fun loadAppearanceAndEquipment(json: JSONObject?) { + if (json == null) return + bot.equipment.clear() + bot.appearance.parse(json["appearance"] as JSONObject) + val equipment = json["equipment"] as JSONArray + bot.equipment.parse(equipment) + bot.appearance.sync() + for (i in 0 until bot.equipment.capacity()) { + val item = bot.equipment.get(i) + if (item != null) { + equipAndSetStats(item) + } + } + // Set all combat stats to a function of the highest combat stat(otherwise you end up with lopsided stats) + val highestCombatSkill = bot.skills.getStaticLevel(bot.skills.highestCombatSkillId) + for (i in 0 until 7) { + bot.skills.setStaticLevel(i, max((highestCombatSkill * 0.75).toInt(), bot.skills.getStaticLevel(i))) + } + bot.skills.updateCombatLevel() + } + fun getOverlay(): BottingOverlay{ return BottingOverlay(bot) } diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/DoublingMoney.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/DoublingMoney.kt new file mode 100644 index 000000000..a45035c1f --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/DoublingMoney.kt @@ -0,0 +1,376 @@ +package rs09.game.ai.general.scriptrepository + +import core.game.container.Container +import core.game.interaction.Option._P_TRADE +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.request.trade.TradeModule +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.update.flag.context.ChatMessage +import core.game.world.update.flag.player.ChatFlag +import discord.Discord +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import org.rs09.consts.Items +import rs09.ServerConstants +import rs09.game.ai.AIRepository +import rs09.game.ai.general.scriptrepository.Adventurer.Companion.lumbridge +import rs09.game.interaction.IntType +import rs09.game.interaction.InteractionListeners +import rs09.game.world.ImmerseWorld +import java.io.File +import java.io.FileReader +import java.util.Random +import kotlin.math.min + +/** + * A bot that doubles money + * 30% chance to double money, 70% chance to scam + * @author dginovker + */ +class DoublingMoney : Script() { + /** + * If we're in ScamMode, we will never trade away any more coins + */ + private var scamMode = false + + /** + * Whether the bot has run its setup (gotten appearance made/etc.) + */ + private var setup = false + + /** + * Player who most recently traded with this bot + */ + var victim: Player? = null + + /** + * Player who the bot took money from + */ + private var playerOwed: Player? = null + + /** + * Whether we sent a trade request to the player we owe yet + */ + private var sentTradeRequest = false + + /** + * Amount the bot owes the player (2x the amount the player gave the bot) + */ + private var debtOwed: Int = 0 + + /** + * How many ticks the bot has run for + */ + var ticks = 0 + + /** + * Duration for how long the bot will take a nap + */ + private var sleepTime = 0 + + /** + * Debug information about what state the bot is in + */ + private var stateString = "init" + + /** + * How long the bot will stay around for + */ + private val maxTicks = (400..750).random() + + enum class Effort { + LOW, + HIGH, + VERY_HIGH + } + + /** + * Characteristics about how hard the bot will work + * Most likely to be LOW, small chance to be HIGH, very small chance to be VERY_HIGH + */ + private val effort = when (Random().nextInt(100)) { + in 0..90 -> Effort.LOW + in 90..98 -> Effort.HIGH + else -> Effort.VERY_HIGH + } + + // Priority calculation -> Execute method mapping. Priority calculation with the highest return value is executed + var states = mapOf( + fun(): Int { + if (setup) return -1 + return 250 + } to { + stateString = "Putting on my outfit.." + var appearance = if (Math.random() < 0.5) "noob" else "intermediate" + if (effort == Effort.VERY_HIGH && Math.random() < 0.5) appearance = "veteran" + val appearancesArray = botAppearances[appearance] as JSONArray + scriptAPI.loadAppearanceAndEquipment(appearancesArray.random() as JSONObject) + setup = true + }, + fun(): Int { + val botTradeModule = TradeModule.getExtension(bot) + if (botTradeModule != null) return -1 + return 0 + } to { + stateString = "Doing nothing" + }, + fun(): Int { + return 1 + } to { + stateString = "Saying Doubling money" + when (effort) { + Effort.LOW -> { + scriptAPI.sendChat("Doubling money") + sleepTime = 7 + } + Effort.HIGH -> { + val message = arrayOf("Doubling Money", "Doubling Money", "Doubling Money!", "Doubling moneyy").random() + val messageEffect = arrayOf(0, 256).random() + val ctx = ChatMessage(bot, message, messageEffect, message.length) + bot.updateMasks.register(ChatFlag(ctx)) + sleepTime = 8 + } + Effort.VERY_HIGH -> { + val message = arrayOf("Doubling money!", "Doubling money").random() + val messageEffect = arrayOf(771, 2818, 2562, 768, 512, 2304, 2560, 769, 1792).random() + val ctx = ChatMessage(bot, message, messageEffect, message.length) + bot.updateMasks.register(ChatFlag(ctx)) + sleepTime = 9 + } + } + }, + fun(): Int { + if (victim == null) return -1 + return 10 + } to { + stateString = "Accepting trade request" + // Send trade request back to victim + InteractionListeners.run(-1, IntType.PLAYER, "trade with", bot, victim!!) + victim = null + sleepTime = 3 + }, + fun(): Int { + if (debtOwed >= 0) return -1 + val botTradeModule = TradeModule.getExtension(bot) ?: return -1 + val playerTradeModule = TradeModule.getExtension(botTradeModule.target) ?: return -1 + if (!playerTradeModule.container!!.containsAtLeastOneItem(Items.COINS_995)) return 15 + return -1 + } to { + val botTradeModule = TradeModule.getExtension(bot) + stateString = "Waiting for ${botTradeModule!!.target} to post money" + sleepTime = 5 + }, + fun(): Int { + // If we have debt and are in the trade screen with the player who we owe, but have not put up coins, put up coins + if (debtOwed == 0) return -1 + val botTradeModule = TradeModule.getExtension(bot) ?: return -1 + if (botTradeModule.target != playerOwed) return -1 + if (!botTradeModule.container!!.containsAtLeastOneItem(Items.COINS_995)) return 150 + return -1 + } to { + stateString = "Paying off ${playerOwed!!.username} $debtOwed coins" + val botTradeModule = TradeModule.getExtension(bot) + val coinSlot = bot.inventory.getSlot(Item(Items.COINS_995)) + botTradeModule!!.container!!.offer(coinSlot, debtOwed) + debtOwed = 0 + playerOwed = null + }, + fun(): Int { + val botTradeModule = TradeModule.getExtension(bot) ?: return -1 + val playerTradeModule = TradeModule.getExtension(botTradeModule.target) ?: return -1 + if (botTradeModule.container!!.containsAtLeastOneItem(Items.COINS_995)) return 200 + if (playerTradeModule.container!!.containsAtLeastOneItem(Items.COINS_995)) return 200 + return -1 + } to { + stateString = "Accepting any trade screen that has coins put up" + val botTradeModule = TradeModule.getExtension(bot) + val playerTradeModule = TradeModule.getExtension(botTradeModule!!.target) + if (botTradeModule.isAccepted) return@to + + sleepTime = (1..if (botTradeModule.getInterface() == TradeModule.MAIN_INTERFACE) 2 else 5).random() + + val coinsFromBot = botTradeModule.container!!.getAmount(Items.COINS_995) + if (botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE && coinsFromBot > 0 && effort == Effort.VERY_HIGH) { + val message = "Payed ${(if (coinsFromBot < 1000) "${coinsFromBot}gp" else "${coinsFromBot / 1000}k")}" + val ctx = ChatMessage(bot, message, 512, message.length) + bot.updateMasks.register(ChatFlag(ctx)) + + sleepTime = 7 + } + if (botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE && Math.random() < 0.2 && effort == Effort.HIGH && coinsFromBot > 0) { + scriptAPI.sendChat("Enjoy") + sleepTime += 4 + } + + if (botTradeModule.getInterface() == TradeModule.MAIN_INTERFACE && scamMode) { + sleepTime = maxOf(3, sleepTime) + } + + if (botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE) { + val player = botTradeModule.target + var playerNetPoL = player!!.getAttribute("double-money-net-pol", "0").toInt() + playerNetPoL += coinsFromBot + playerNetPoL -= playerTradeModule!!.container!!.getAmount(Items.COINS_995) + player.setAttribute("double-money-net-pol", playerNetPoL.toString()) + // if playerNetPoL > 100k, send a Discord notification + if (playerNetPoL > 100_000) { + Discord.postPlayerAlert(player.username, "Made too much off Doubling Money bots: ${playerNetPoL / 1000}k") + } + } + // If we're in scamMode, we cannot actually setAccepted to true if we're giving away coins! (Gotta fake em out ;) + if (scamMode && botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE && coinsFromBot > 0) { + botTradeModule.decline() + } else { + botTradeModule.setAccepted(true, update = true) + } + sentTradeRequest = false + }, + fun(): Int { + if (sentTradeRequest) return 50 + return -1 + } to { + // Wait for the other player to accept + }, + fun(): Int { + if (TradeModule.getExtension(bot) == null && debtOwed > 0 && playerOwed != null) return 25 + return -1 + } to { + // Scam over 200k payout or 70% chance + if (Math.random() < 0.7 || debtOwed > 200_000) { + stateString = "Scamming" + scamMode = true + if (effort != Effort.VERY_HIGH || Math.random() < 0.5) { + // At high scam effort, there's a chance we fake giving them coins back to keep milking + // with scamMode = true, we will never accept giving coins away at the second trade screen again + terminate() + } + } + stateString = "Sending trade request to ${playerOwed!!.name} to give them $debtOwed coins" + playerOwed!!.interaction.handle(bot, _P_TRADE) + InteractionListeners.run(-1, IntType.PLAYER, "trade with", bot, playerOwed!!) + sentTradeRequest = true + }, + fun(): Int { + // If we're not near a doubling loc, or another doubling bot is on our loc, walk to random one + var minDist = 1000.0 + for (loc in doublingLocs) { + minDist = min(minDist, loc.getDistance(bot.location)) + } + if (minDist > 1) return 2 + RegionManager.forId(bot.location.regionId).planes[bot.location.z].players.forEach { + if (AIRepository.PulseRepository[it?.username?.lowercase()]?.botScript is DoublingMoney + && it != bot + && it.location.getDistance(bot.location) <= 1) { + return 2 + } + } + return -1 + } to { + stateString = "Walking to doubling location" + val loc = doublingLocs.random() + scriptAPI.walkTo(loc) + sleepTime = 5 + }, + fun(): Int { + // If we're past the max ticks, log out + if (ticks > maxTicks) return 30 + return -1 + } to { + stateString = "Logging out" + terminate() + }, + fun(): Int { + // If we're past 1,000 ticks, force terminate + if (ticks > 1000) return 1_000_000 + return -1 + } to { + stateString = "Force terminating" + terminate() + } + ) + + override fun toString(): String { + return "stateString: $stateString, sleepTime: $sleepTime, ticks: $ticks, maxTicks: $maxTicks, debtOwed: $debtOwed, playerOwed: $playerOwed, victim: $victim, effort: $effort, scamMode: $scamMode" + } + + override fun tick() { + if(!bot.isActive){ + running = false + return + } + ticks ++ + if (sleepTime > 0) { + sleepTime -- + return + } + + // Execute the priority calculations, then execute the method of the one which returned highest + states.maxByOrNull { it.key() }?.value?.invoke() + } + + fun tradeReceived(p: Player) { + victim = p + } + + fun itemsReceived(p: Player, container: Container?) { + if (container == null) return + val coins: Item = container.get(Item(Items.COINS_995)) ?: return + playerOwed = p + debtOwed = coins.amount * 2 + if (effort == Effort.VERY_HIGH) { + val message = "Received ${(if (coins.amount < 1000) "${coins.amount}gp" else "${coins.amount / 1000}k")}" + val ctx = ChatMessage(bot, message, 256, message.length) + bot.updateMasks.register(ChatFlag(ctx)) + } + } + + fun terminate() { + scriptAPI.teleport(lumbridge) + debtOwed = 0 + bot.isActive = false + sleepTime = 500 + AIRepository.PulseRepository.remove(bot.username.lowercase()) + + ImmerseWorld.spawnDoubleMoneyBot(true) + } + + override fun newInstance(): Script? { + // this straight doesn't get called.... + return null + } + + init { + skills[Skills.AGILITY] = 99 + inventory.add(Item(Items.COINS_995, 1_000_000)) + } + + companion object { + val startingLocs = arrayOf(Location.create(3184, 3488, 0), + Location.create(3167, 3461, 0), + Location.create(3170, 3444, 0), + Location.create(3165, 3487, 0), + Location.create(3167, 3489, 0), + Location.create(3162, 3491, 0)) + + val doublingLocs = arrayOf(Location.create(3159, 3492, 0), + Location.create(3164, 3484, 0), + Location.create(3169, 3486, 0), + Location.create(3172, 3489, 0), + Location.create(3168, 3483, 0), + Location.create(3166, 3493, 0), + Location.create(3157, 3498, 0), + Location.create(3157, 3485, 0) + ) + + // load from data/botdata/ge_bot_appearances_and_equipment.json + private val jsonFilePath = "${ServerConstants.BOT_DATA_PATH}/ge_bot_appearances_and_equipment.json" + private val jsonFile = File(jsonFilePath) + private val parser = JSONParser() + val botAppearances = parser.parse(FileReader(jsonFile)) as JSONObject + } + +} diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ShootingStarBot.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ShootingStarBot.kt index c5019f70a..d8d7b33fc 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ShootingStarBot.kt +++ b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ShootingStarBot.kt @@ -7,7 +7,6 @@ import core.tools.RandomFunction import org.rs09.consts.Items import rs09.game.ai.general.GeneralBotCreator import rs09.game.content.global.worldevents.shootingstar.ShootingStarPlugin -import rs09.game.interaction.InteractionListener import rs09.game.interaction.IntType import rs09.game.interaction.InteractionListeners @@ -88,7 +87,7 @@ class ShootingStarBot : Script() { val spawnLoc = Location.create(2230, 3339, 0) fun new() : ShootingStarBot { val script = ShootingStarBot() - GeneralBotCreator(spawnLoc, script) + GeneralBotCreator(script, spawnLoc) return script } } diff --git a/Server/src/main/kotlin/rs09/game/interaction/QCRepository.kt b/Server/src/main/kotlin/rs09/game/interaction/QCRepository.kt index efd921457..d35cabdcc 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/QCRepository.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/QCRepository.kt @@ -49,11 +49,11 @@ object QCRepository { builder.rank = player.details.rights.ordinal publish(builder.build()) } else { - val ctx = ChatMessage(player, qcString, 0, qcString.length) + val ctx = ChatMessage(player!!, qcString, 0, qcString.length) ctx.isQuickChat = true Pulser.submit(object : Pulse(0, player) { override fun pulse(): Boolean { - player!!.updateMasks.register(ChatFlag(ctx)) + player.updateMasks.register(ChatFlag(ctx)) return true } }) diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/FunCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/FunCommandSet.kt index aa0f86b90..9a77f1817 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/FunCommandSet.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/sets/FunCommandSet.kt @@ -1,6 +1,7 @@ package rs09.game.system.command.sets import api.* +import com.google.gson.JsonObject import core.game.content.quest.tutorials.tutorialisland.CharacterDesign import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player @@ -9,13 +10,18 @@ import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.update.flag.context.Animation import core.plugin.Initializable +import org.json.simple.JSONObject import rs09.game.content.dialogue.DialogueFile import rs09.game.interaction.InteractionListeners import rs09.game.interaction.SpadeDigListener +import rs09.game.node.entity.player.info.login.PlayerSaver import rs09.game.system.command.Command import rs09.game.system.command.Privilege import rs09.game.world.GameWorld import rs09.tools.END_DIALOGUE +import java.awt.HeadlessException +import java.awt.Toolkit +import java.awt.datatransfer.StringSelection import java.util.* @Initializable @@ -125,6 +131,24 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { CharacterDesign.open(player) } + /** + * Copies your current appearance and equipment as JSON to the clipboard + */ + define("dumpappearance", Privilege.MODERATOR){ player, _ -> + val json = JSONObject() + PlayerSaver(player).saveAppearance(json); + val equipJson = PlayerSaver(player).saveContainer(player.equipment) + json["equipment"] = equipJson + val jsonString = json.toJSONString() + try { + val clpbrd = Toolkit.getDefaultToolkit().systemClipboard + clpbrd.setContents(StringSelection(jsonString), null) + notify(player, "Appearance and equipment copied to clipboard.") + } catch (e: HeadlessException) { + reject(player, "NOTE: Paste will not be available due to remote server.") + } + } + /** * Bury inventory at current location */ diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt index 29dcc37e2..27723868f 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt @@ -200,8 +200,9 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ define("botinfo", Privilege.STANDARD, "::botinfo botname", "Prints debug information about a bot"){ player, args -> val scriptInstances = AIRepository.PulseRepository - // Find the bot with the given name (non-case sensitive) - val bot = scriptInstances.find { it.botScript.bot.username.equals(args[1], true) } + // Find the bot with the given name (non-case sensitive, concat args by space) + val botName = args.slice(1 until args.size).joinToString(" ").lowercase() + val bot = scriptInstances[botName] if (bot == null) { reject(player, "No bot with that name found.") return@define diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt index 0022c7ce1..c7b64d105 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt @@ -309,5 +309,25 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){ } } } + + define("getattribute", Privilege.ADMIN, "::getattribute username attribute", "Gets the value of an attribute for a player.") {player, args -> + val username = args.getOrNull(1) ?: "" + val attribute = args.getOrNull(2) ?: "" + if (username.isEmpty() || attribute.isEmpty()) { + reject(player, "Usage: ::getattribute username attribute") + return@define + } + val p = Repository.getPlayerByName(username) + if (p == null) { + reject(player, "Unable to find player $username.") + return@define + } + val value = p.getAttribute(attribute) + if (value == null) { + reject(player, "Unable to find attribute $attribute for player $username.") + return@define + } + notify(player, "Attribute $attribute for player $username is $value.") + } } } diff --git a/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt b/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt index 7b69f6f68..680c98c17 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt +++ b/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt @@ -75,6 +75,7 @@ object ServerConfigParser { increased_door_time = data.getBoolean("world.increased_door_time"), enabled_botting = data.getBoolean("world.enable_botting"), max_adv_bots = data.getLong("world.max_adv_bots").toInt(), + enable_doubling_money_scammers = data.getBoolean("world.enable_doubling_money_scammers", false), wild_pvp_enabled = data.getBoolean("world.wild_pvp_enabled"), jad_practice_enabled = data.getBoolean("world.jad_practice_enabled"), message_model = data.getString("world.motw_identifier").toInt(), diff --git a/Server/src/main/kotlin/rs09/game/world/GameSettings.kt b/Server/src/main/kotlin/rs09/game/world/GameSettings.kt index 0edf09e6f..85e6bc503 100644 --- a/Server/src/main/kotlin/rs09/game/world/GameSettings.kt +++ b/Server/src/main/kotlin/rs09/game/world/GameSettings.kt @@ -79,6 +79,7 @@ class GameSettings var increased_door_time: Boolean, var enabled_botting : Boolean, var max_adv_bots: Int, + var enable_doubling_money_scammers: Boolean, var wild_pvp_enabled: Boolean, var jad_practice_enabled: Boolean, @@ -132,6 +133,7 @@ class GameSettings val increased_door_time = if(data.containsKey("increased_door_time")) data["increased_door_time"] as Boolean else false val enable_botting = if(data.containsKey("botting_enabled")) data["botting_enabled"] as Boolean else false val max_adv_bots = if(data.containsKey("max_adv_bots")) data["max_adv_bots"].toString().toInt() else 100 + val enable_doubling_money_scammers = if(data.containsKey("enable_doubling_money_scammers")) data["enable_doubling_money_scammers"] as Boolean else false val wild_pvp_enabled = if(data.containsKey("wild_pvp_enabled")) data["wild_pvp_enabled"] as Boolean else true val jad_practice_enabled = if(data.containsKey("jad_practice_enabled")) data["jad_practice_enabled"] as Boolean else true val allow_token_purchase = data["allow_token_purchase"] as Boolean @@ -160,6 +162,7 @@ class GameSettings increased_door_time, enable_botting, max_adv_bots, + enable_doubling_money_scammers, wild_pvp_enabled, jad_practice_enabled, message_of_the_week_identifier, diff --git a/Server/src/main/kotlin/rs09/game/world/ImmerseWorld.kt b/Server/src/main/kotlin/rs09/game/world/ImmerseWorld.kt index 43711d8d6..737fc7e8b 100644 --- a/Server/src/main/kotlin/rs09/game/world/ImmerseWorld.kt +++ b/Server/src/main/kotlin/rs09/game/world/ImmerseWorld.kt @@ -35,6 +35,7 @@ class ImmerseWorld : StartupListener { immerseFishingGuild() immerseAdventurer() // immerseSlayer() + immerseGE() } } } @@ -64,7 +65,7 @@ class ImmerseWorld : StartupListener { fun immerseFishingGuild() { val fishingGuild = Location.create(2604, 3421, 0) for (i in (0..4)) { - GeneralBotCreator(fishingGuild, SharkCatcher()) + GeneralBotCreator(SharkCatcher(), fishingGuild) } } @@ -85,7 +86,7 @@ class ImmerseWorld : StartupListener { GlassBlowingBankstander(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(2807, 3441, 0)) ) - GeneralBotCreator(Location.create(2805, 3435, 0), LobsterCatcher()) + GeneralBotCreator(LobsterCatcher(), Location.create(2805, 3435, 0)) } fun immerseLumbridgeDraynor() { @@ -220,5 +221,20 @@ class ImmerseWorld : StartupListener { ) ) } + + private fun immerseGE() { + spawnDoubleMoneyBot(false) + } + + fun spawnDoubleMoneyBot(delay: Boolean) { + if (GameWorld.settings?.enable_doubling_money_scammers != true) return + val random: Long = (10_000..7_200_000).random().toLong() + Timer().schedule(if (delay) random else 0) { + GeneralBotCreator ( + DoublingMoney(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, DoublingMoney.startingLocs.random()) + ) + } + } } } diff --git a/Server/src/test/resources/test.conf b/Server/src/test/resources/test.conf index 269e2cf95..8d8bcffe9 100644 --- a/Server/src/test/resources/test.conf +++ b/Server/src/test/resources/test.conf @@ -46,6 +46,7 @@ skillcape_perks = false increased_door_time = false enable_botting = false max_adv_bots = 100 +enable_doubling_money_scammers = true wild_pvp_enabled = false jad_practice_enabled = false personalized_shops = false @@ -77,4 +78,4 @@ object_parser_path = "@data/ObjectParser.xml" #path logs are written to logs_path = "@data/logs" bot_data = "@data/botdata" -eco_data = "@data/eco" \ No newline at end of file +eco_data = "@data/eco" diff --git a/Server/worldprops/default.conf b/Server/worldprops/default.conf index dbbd88cf9..b51d65986 100644 --- a/Server/worldprops/default.conf +++ b/Server/worldprops/default.conf @@ -59,6 +59,7 @@ skillcape_perks = true increased_door_time = false enable_botting = false max_adv_bots = 100 +enable_doubling_money_scammers = true wild_pvp_enabled = false jad_practice_enabled = true personalized_shops = true