diff --git a/app/Http/Controllers/APIEnemyController.php b/app/Http/Controllers/APIEnemyController.php index ab059c72a..5cd696720 100644 --- a/app/Http/Controllers/APIEnemyController.php +++ b/app/Http/Controllers/APIEnemyController.php @@ -4,9 +4,11 @@ use App\Http\Controllers\Traits\ChecksForDuplicates; use App\Http\Controllers\Traits\PublicKeyDungeonRoute; +use App\Logic\MDT\IO\MDTDungeon; use App\Models\DungeonRouteEnemyRaidMarker; use App\Models\Enemy; use App\Models\EnemyInfestedVote; +use App\Models\Floor; use App\Models\GameServerRegion; use App\Models\Npc; use App\Models\RaidMarker; @@ -25,12 +27,21 @@ function list(Request $request) { $floorId = $request->get('floor_id'); $dungeonRoutePublicKey = $request->get('dungeonroute', null); + $showMdtEnemies = false; - $routeId = -1; + // Only admins are allowed to see this + if (Auth::check()) { + if (Auth::user()->hasRole('admin')) { + // Only fetch it now + $showMdtEnemies = $request->get('show_mdt_enemies', false); + } + } + + $dungeonRoute = null; // If dungeon route was set, fetch the markers as well if ($dungeonRoutePublicKey !== null) { try { - $routeId = $this->_getDungeonRouteFromPublicKey($dungeonRoutePublicKey, false)->id; + $dungeonRoute = $this->_getDungeonRouteFromPublicKey($dungeonRoutePublicKey, false); } catch (\Exception $ex) { return response('Not found', Http::NOT_FOUND); } @@ -57,7 +68,7 @@ function list(Request $request) group by `enemies`.`id`; ', $params = [ 'userId' => Auth::check() ? Auth::user()->id : -1, - 'routeId' => $routeId, + 'routeId' => isset($dungeonRoute) ? $dungeonRoute->id : -1, 'affixGroupId' => $region->getCurrentAffixGroup()->id, 'minTime' => Carbon::now()->subMonth()->format('Y-m-d H:i:s'), 'floorId' => $floorId @@ -74,7 +85,17 @@ function list(Request $request) unset($enemy->npc_id); } - return $result; + $mdtEnemies = []; + + // Only if we should show MDT enemies + if ($showMdtEnemies) { + /** @var Floor $floor */ + $floor = Floor::where('id', $floorId)->first(); + + $mdtEnemies = (new \App\Logic\MDT\Data\MDTDungeon($floor->dungeon->name))->getClonesAsEnemies($floor); + } + + return ['enemies' => $result, 'mdt_enemies' => $mdtEnemies]; } /** diff --git a/app/Http/Controllers/MDTImportController.php b/app/Http/Controllers/MDTImportController.php index ff881edfc..4e4c2b109 100644 --- a/app/Http/Controllers/MDTImportController.php +++ b/app/Http/Controllers/MDTImportController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers; -use App\Logic\MDT\ImportString; +use App\Logic\MDT\IO\ImportString; use Illuminate\Http\Request; class MDTImportController extends Controller diff --git a/app/Logic/MDT/Data/MDTDungeon.php b/app/Logic/MDT/Data/MDTDungeon.php new file mode 100644 index 000000000..8ae4b1b1f --- /dev/null +++ b/app/Logic/MDT/Data/MDTDungeon.php @@ -0,0 +1,169 @@ + 'AtalDazar', + 'Freehold' => 'Freehold', + 'Kings\' Rest' => 'KingsRest', + 'Shrine of the Storm' => 'ShrineoftheStorm', + 'Siege of Boralus' => 'SiegeofBoralus', + 'Temple of Sethraliss' => 'TempleofSethraliss', + 'The MOTHERLODE!!' => 'TheMotherlode', + 'The Underrot' => 'TheUnderrot', + 'Tol Dagor' => 'TolDagor', + 'Waycrest Manor' => 'WaycrestManor' + ]; + + /** @var string The Dungeon's name (Keystone.guru style). Can be converted using self::$dungeonMapping */ + private $_dungeonName; + + + function __construct($dungeonName) + { + assert(array_key_exists($dungeonName, self::$dungeonNameMapping)); + $this->_dungeonName = $dungeonName; + } + + /** + * @return mixed Gets the MDT version of a dungeon name. + */ + private function _getMDTDungeonName() + { + return self::$dungeonNameMapping[$this->_dungeonName]; + } + + /** + * Get a list of NPCs + */ + private function _getMDTNPCs() + { + $lua = new \Lua(); + $lua->eval( + 'local MethodDungeonTools = {} + MethodDungeonTools.dungeonTotalCount = {} + MethodDungeonTools.mapPOIs = {} + MethodDungeonTools.dungeonEnemies = {} + MethodDungeonTools.scaleMultiplier = {} + ' . + file_get_contents( + base_path('vendor/nnogga/MethodDungeonTools/BattleForAzeroth/' . $this->_getMDTDungeonName() . '.lua') + ) . + // Insert dummy function to get what we need + ' + function GetDungeonEnemies() + return MethodDungeonTools.dungeonEnemies[dungeonIndex] + end + '); + return $lua->call('GetDungeonEnemies'); + } + + /** + * Get all clones of a specific NPC. + * @param $npcId int WoW's NPC id. + * @return array The enemy as an array. + */ + private function _getMDTEnemy($npcId) + { + $enemies = $this->_getMDTNPCs(); + + $result = null; + // Find the enemy in a list of enemies + foreach ($enemies as $enemy) { + // Id is classed as a double, some lua -> php conversion issue/choice. + if ((int)$enemy->id === $npcId) { + $result = $enemy; + break; + } + } + + return $result; + } + + + /** + * Get all clones of this dungeon in the format of enemies (Keystone.guru style). + * @param $floor Floor The floor you're looking for. + * @return array + */ + public function getClonesAsEnemies($floor) + { + $mdtNpcs = $this->_getMDTNPCs(); + + // Find the enemy in a list of enemies + $clones = []; + foreach ($mdtNpcs as $mdtNpc) { + foreach ($mdtNpc['clones'] as $mdtId => $clone) { + //Only clones that are on the same floor + if ((int)$clone['sublevel'] === $floor->index) { + // Set some additional props that come in handy when converting to an enemy + $clone['npcId'] = (int)$mdtNpc['id']; + $clone['mdtId'] = (int)$mdtId; + $clones[] = $clone; + } + } + } + + // We now know a list of clones that we want to display, convert those clones to TEMP enemies + $enemies = []; + /** @var Collection $npcs */ + $npcs = Npc::where('dungeon_id', $floor->dungeon->id)->get(); + foreach ($clones as $npcId => $clone) { + $enemy = new Enemy(); + // Dummy so we can ID them later on + $enemy->is_mdt = true; + $enemy->floor_id = $floor->id; + $enemy->enemy_pack_id = -1; + $enemy->npc_id = (int)$clone['npcId']; + $enemy->mdt_id = (int)$clone['mdtId']; + $enemy->is_infested = false; + $enemy->teeming = isset($clone['teeming']) && $clone['teeming'] ? 'visible' : null; + $enemy->faction = isset($clone['faction']) ? ($clone['faction'] === 1 ? 'alliance' : 'horde') : 'any'; + $enemy->enemy_forces_override = -1; + // This seems to match my coordinate system for about 99%. Sometimes it's a bit off but I can work around that. + $enemy->lat = ($clone['y'] / 2.2); + $enemy->lng = ($clone['x'] / 2.2); + $enemy->npc = $npcs->firstWhere('id', $enemy->npc_id); + + // Some properties which are dynamic on a normal enemy but static here + $enemy->raid_marker_name = null; + $enemy->infested_yes_votes = 0; + $enemy->infested_no_votes = 0; + $enemy->infested_user_vote = null; + + $enemies[] = $enemy; + } + + return $enemies; + } + + /** + * Get all enemies of this dungeon (Keystone.guru style). + */ + public function getEnemies() + { + + } +} \ No newline at end of file diff --git a/app/Logic/MDT/Deprecated.php b/app/Logic/MDT/IO/Deprecated.php similarity index 100% rename from app/Logic/MDT/Deprecated.php rename to app/Logic/MDT/IO/Deprecated.php diff --git a/app/Logic/MDT/Huffman.php b/app/Logic/MDT/IO/Huffman.php similarity index 100% rename from app/Logic/MDT/Huffman.php rename to app/Logic/MDT/IO/Huffman.php diff --git a/app/Logic/MDT/ImportString.php b/app/Logic/MDT/IO/ImportString.php similarity index 84% rename from app/Logic/MDT/ImportString.php rename to app/Logic/MDT/IO/ImportString.php index f1ea09594..23b3067a4 100644 --- a/app/Logic/MDT/ImportString.php +++ b/app/Logic/MDT/IO/ImportString.php @@ -6,16 +6,13 @@ * Time: 20:49 */ -namespace App\Logic\MDT; +namespace App\Logic\MDT\IO; -use App\Huffman; use App\Models\DungeonRoute; /** - * Class ImportString. This file was created as a sort of copy of https://github.com/nnogga/MethodDungeonTools/blob/master/Transmission.lua - * All rights belong to their respective owners, I did write this but I did not make this up. I merely translated the LUA - * to PHP to allow for importing of the exported strings. + * This file handles any and all conversion from DungeonRoutes to MDT Export strings and vice versa. * @package App\Logic\MDT * @author Wouter * @since 05/01/2019 @@ -45,7 +42,8 @@ function __construct() private function _getLua() { $lua = new \Lua(); - // $lua->assign("php_var", array(1=>1, 2, 3)); //lua table index begin with 1 + + // Load libraries (yeah can do this with ->library function as well) $lua->eval(file_get_contents(base_path('app/Logic/MDT/Lua/LibStub.lua'))); $lua->eval(file_get_contents(base_path('app/Logic/MDT/Lua/LibCompress.lua'))); $lua->eval(file_get_contents(base_path('app/Logic/MDT/Lua/AceSerializer.lua'))); diff --git a/app/Models/Enemy.php b/app/Models/Enemy.php index 263032b01..cbe292ebc 100644 --- a/app/Models/Enemy.php +++ b/app/Models/Enemy.php @@ -20,7 +20,6 @@ * @property \App\Models\EnemyPack $enemyPack * @property \App\Models\Npc $npc * @property \App\Models\Floor $floor - * @property \Illuminate\Support\Collection $vertices * @property \Illuminate\Support\Collection $thisweeksinfestedvotes */ class Enemy extends Model diff --git a/resources/assets/css/map.css b/resources/assets/css/map.css index 6ebe5b702..54b8c0b4b 100644 --- a/resources/assets/css/map.css +++ b/resources/assets/css/map.css @@ -233,7 +233,8 @@ html, body, #map, #app, .wrapper { .unfriendly_enemy_icon, .friendly_enemy_icon, .unset_enemy_icon, -.flagged_enemy_icon { +.flagged_enemy_icon, +.mdt_enemy_icon { width: 11px; height: 11px; } @@ -350,6 +351,10 @@ html, body, #map, #app, .wrapper { background: url("/images/mapicon/neutral.png") no-repeat center !important; } +.mdt_enemy_icon { + background: url("/images/mapicon/mdt.png") no-repeat center !important; +} + .door_icon { background: url("/images/mapicon/door.png") no-repeat center !important; } diff --git a/resources/assets/images/mapicon/flagged.png b/resources/assets/images/mapicon/flagged.png deleted file mode 100644 index cd49ec0da..000000000 Binary files a/resources/assets/images/mapicon/flagged.png and /dev/null differ diff --git a/resources/assets/images/mapicon/mdt.png b/resources/assets/images/mapicon/mdt.png new file mode 100644 index 000000000..8d2600723 Binary files /dev/null and b/resources/assets/images/mapicon/mdt.png differ diff --git a/resources/assets/js/custom/enemyvisuals/enemyvisualmainaggressiveness.js b/resources/assets/js/custom/enemyvisuals/enemyvisualmainaggressiveness.js index a289f9c44..ddec5718f 100644 --- a/resources/assets/js/custom/enemyvisuals/enemyvisualmainaggressiveness.js +++ b/resources/assets/js/custom/enemyvisuals/enemyvisualmainaggressiveness.js @@ -18,7 +18,8 @@ class EnemyVisualMainAggressiveness extends EnemyVisualMain { 'friendly', 'unset', 'flagged', - 'boss' + 'boss', + 'mdt' ]; } @@ -41,21 +42,25 @@ class EnemyVisualMainAggressiveness extends EnemyVisualMain { * @private */ _updateIcon() { - let npc = this.enemyvisual.enemy.npc; + if( this.enemyvisual.enemy.is_mdt ){ + this.iconName = 'mdt'; + } else { + let npc = this.enemyvisual.enemy.npc; - // May be null if not set at all (yet) - if (npc !== null) { - if (npc.enemy_forces === -1) { - this.iconName = 'flagged'; - } - // @TODO Hard coded 3 = boss - else if (npc.classification_id === 3) { - this.iconName = 'boss'; + // May be null if not set at all (yet) + if (npc !== null) { + if (npc.enemy_forces === -1) { + this.iconName = 'flagged'; + } + // @TODO Hard coded 3 = boss + else if (npc.classification_id === 3) { + this.iconName = 'boss'; + } else { + this.iconName = npc.aggressiveness; + } } else { - this.iconName = npc.aggressiveness; + this.iconName = 'unset'; } - } else { - this.iconName = 'unset'; } } diff --git a/resources/assets/js/custom/mapobjectgroups/enemymapobjectgroup.js b/resources/assets/js/custom/mapobjectgroups/enemymapobjectgroup.js index 1a171f256..698d46b91 100644 --- a/resources/assets/js/custom/mapobjectgroups/enemymapobjectgroup.js +++ b/resources/assets/js/custom/mapobjectgroups/enemymapobjectgroup.js @@ -30,64 +30,76 @@ class EnemyMapObjectGroup extends MapObjectGroup { dataType: 'json', data: { dungeonroute: this.map.getDungeonRoute().publicKey, - floor_id: floor.id + floor_id: floor.id, + // Admin = show mdt enemies, otherwise don't + show_mdt_enemies: this.classname === 'AdminEnemy' }, success: function (json) { - // Now draw the enemies on the map - for (let index in json) { - if (json.hasOwnProperty(index)) { - let remoteEnemy = json[index]; - - let faction = self.map.getDungeonRoute().faction; - - if (remoteEnemy.faction !== 'any' && faction !== 'any' && faction !== remoteEnemy.faction) { - console.log('Skipping enemy that does not belong to the requested faction ', remoteEnemy, faction); - continue; - } - - // If the map isn't teeming, but the enemy is teeming.. - if (!self.map.teeming && remoteEnemy.teeming === 'visible') { - console.log('Skipping teeming enemy ' + remoteEnemy.id); - continue; - } - // If the map is teeming, but the enemy shouldn't be there for teeming maps.. - else if (self.map.teeming && remoteEnemy.teeming === 'invisible') { - console.log('Skipping teeming-filtered enemy ' + remoteEnemy.id); - continue; - } - - let layer = new LeafletEnemyMarker(); - layer.setLatLng(L.latLng(remoteEnemy.lat, remoteEnemy.lng)); - - let enemy = self.createNew(layer); - enemy.id = remoteEnemy.id; - enemy.enemy_pack_id = remoteEnemy.enemy_pack_id; - enemy.floor_id = remoteEnemy.floor_id; - enemy.teeming = remoteEnemy.teeming; - enemy.faction = remoteEnemy.faction; - enemy.enemy_forces_override = remoteEnemy.enemy_forces_override; - enemy.raid_marker_name = remoteEnemy.raid_marker_name; - enemy.infested_yes_votes = remoteEnemy.infested_yes_votes; - enemy.infested_no_votes = remoteEnemy.infested_no_votes; - enemy.infested_user_vote = remoteEnemy.infested_user_vote; - enemy.is_infested = remoteEnemy.is_infested; - - enemy.setNpc(remoteEnemy.npc); - // If actually set.. - if (remoteEnemy.hasOwnProperty('raid_marker_name') && remoteEnemy.raid_marker_name !== null) { - enemy.setRaidMarkerName(remoteEnemy.raid_marker_name); + let enemies = [ + json.enemies, + json.mdt_enemies + ]; + // For each set of enemies.. + for (let i = 0; i < enemies.length; i++) { + let enemySet = enemies[i]; + // Now draw the enemies on the map, if any + for (let index in enemySet) { + // Only if actually set + if (enemySet.hasOwnProperty(index)) { + let remoteEnemy = enemySet[index]; + + let faction = self.map.getDungeonRoute().faction; + + if (remoteEnemy.faction !== 'any' && faction !== 'any' && faction !== remoteEnemy.faction) { + console.log('Skipping enemy that does not belong to the requested faction ', remoteEnemy, faction); + continue; + } + + // If the map isn't teeming, but the enemy is teeming.. + if (!self.map.teeming && remoteEnemy.teeming === 'visible') { + console.log('Skipping teeming enemy ' + remoteEnemy.id); + continue; + } + // If the map is teeming, but the enemy shouldn't be there for teeming maps.. + else if (self.map.teeming && remoteEnemy.teeming === 'invisible') { + console.log('Skipping teeming-filtered enemy ' + remoteEnemy.id); + continue; + } + + let layer = new LeafletEnemyMarker(); + layer.setLatLng(L.latLng(remoteEnemy.lat, remoteEnemy.lng)); + + let enemy = self.createNew(layer); + enemy.id = remoteEnemy.id; + enemy.enemy_pack_id = remoteEnemy.enemy_pack_id; + enemy.floor_id = remoteEnemy.floor_id; + enemy.teeming = remoteEnemy.teeming; + enemy.faction = remoteEnemy.faction; + enemy.enemy_forces_override = remoteEnemy.enemy_forces_override; + enemy.raid_marker_name = remoteEnemy.raid_marker_name; + enemy.infested_yes_votes = remoteEnemy.infested_yes_votes; + enemy.infested_no_votes = remoteEnemy.infested_no_votes; + enemy.infested_user_vote = remoteEnemy.infested_user_vote; + enemy.is_infested = remoteEnemy.is_infested; + // Exception for MDT enemies + enemy.is_mdt = remoteEnemy.hasOwnProperty('is_mdt') ? remoteEnemy.is_mdt : false; + + enemy.setNpc(remoteEnemy.npc); + // If actually set.. + if (remoteEnemy.hasOwnProperty('raid_marker_name') && remoteEnemy.raid_marker_name !== null) { + enemy.setRaidMarkerName(remoteEnemy.raid_marker_name); + } + + // Is probably null if there's no patrol set + if (remoteEnemy.patrol !== null) { + + } + + // We just downloaded the enemy pack, it's synced alright! + enemy.setSynced(true); } - - // Is probably null if there's no patrol set - if (remoteEnemy.patrol !== null) { - - } - - // We just downloaded the enemy pack, it's synced alright! - enemy.setSynced(true); } } - callback(); } });