--- /dev/null
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\Technique;
+use App\Models\TechniqueMap;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Http;
+
+class SyncMfnsMap extends Command {
+
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'sync:mfns-map';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Synchronize CMS data from muffins glitch map';
+
+ /**
+ * Execute the console command.
+ */
+ public function handle(): int {
+ $rsp = Http::get('https://glitchmaps.mfns.dev/cms/api/non-door-glitches?pagination[limit]=1000&populate=tile')->throw()->json();
+ foreach ($rsp['data'] as $entry) {
+ $this->line($entry['Map'].' '.$entry['Type'].' - '.$entry['Title']);
+ $this->syncEntry($entry);
+ }
+ return 0;
+ }
+
+ protected function syncEntry($entry): void {
+ $ext_id = 'mfns:'.$entry['documentId'];
+ $tech = Technique::query()->where('ext_id', '=', $ext_id)->first();
+ if (!$tech) {
+ $tech = new Technique();
+ $tech->ext_id = $ext_id;
+ $tech->attribution = 'Imported from Muffins Glitch Maps';
+ $tech->halt_sync = false;
+ }
+ if ($tech->halt_sync) {
+ return;
+ }
+
+ $this->syncTech($entry, $tech);
+ $tech->save();
+
+ $map = TechniqueMap::query()->where('ext_id', '=', $ext_id)->first();
+ if (!$map) {
+ $map = new TechniqueMap();
+ $map->ext_id = $ext_id;
+ }
+ $this->syncMap($entry, $map);
+ $map->technique()->associate($tech);
+ $map->save();
+ }
+
+ protected function syncTech($entry, Technique $tech): void {
+ $tech->title = $entry['Title'];
+ $tech->short = $entry['Description'];
+ if (!is_null($entry['Guide'])) {
+ $tech->description = $this->convertGuide($entry['Guide']);
+ } else {
+ $tech->description = $entry['Description'];
+ }
+ }
+
+ protected function convertGuide($guide): string {
+ $html = '';
+ foreach ($guide as $part) {
+ $html .= '<p>'.$part['type'].'</p>'."\n";
+ }
+ return $html;
+ }
+
+ protected function syncMap($entry, TechniqueMap $map): void {
+ $tile_x = 0;
+ $tile_y = 0;
+ if (isset($entry['tile'])) {
+ $tile_x = doubleval(hexdec(substr($entry['tile']['TileID'], -1, 1))) / 16;
+ $tile_y = doubleval(hexdec(substr($entry['tile']['TileID'], -2, 1))) / 16;
+ }
+ switch ($entry['Map']) {
+ case 'DW':
+ case 'LW':
+ $map->map = strtolower($entry['Map']);
+ $map->x = doubleval($entry['x']) / 8192;
+ $map->y = doubleval($entry['y']) / 8192;
+ break;
+ case 'EG1':
+ $map->map = 'uw';
+ $map->x = $tile_x + doubleval($entry['x']) / 8192;
+ $map->y = $tile_y + doubleval($entry['y']) / 8192;
+ break;
+ case 'EG2':
+ $map->map = 'uw2';
+ $map->x = $tile_x + doubleval($entry['x']) / 8192;
+ $map->y = $tile_y + doubleval($entry['y']) / 8192;
+ break;
+ }
+ switch ($entry['Type']) {
+ case 'Bomb Clip':
+ $map->marker = 'bomb';
+ break;
+ case 'Up Teleport':
+ case 'Down Teleport':
+ case 'Spinspeed Clip/Clip Through':
+ $map->marker = 'boots';
+ break;
+ case 'Overworld YBA':
+ $map->marker = 'red-potion';
+ break;
+ case 'Swim Clip':
+ $map->marker = 'flippers';
+ break;
+ case 'Mirror Wrap':
+ case 'Mirror Clip/Portal Offset':
+ $map->marker = 'mirror';
+ break;
+ case 'Citrus Clip':
+ $map->marker = 'no-boots';
+ break;
+ case 'Mirrorless Wrap':
+ $map->marker = 'no-mirror';
+ break;
+ default:
+ $this->line('unknown type: '.$entry['Type']);
+ break;
+ }
+ }
+
+}
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::table('techniques', function (Blueprint $table) {
+ $table->string('ext_id')->nullable()->default(null);
+ $table->boolean('halt_sync')->default(false);
+ });
+ Schema::table('technique_maps', function (Blueprint $table) {
+ $table->string('ext_id')->nullable()->default(null);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('techniques', function (Blueprint $table) {
+ $table->dropColumn('ext_id');
+ $table->dropColumn('halt_sync');
+ });
+ Schema::table('technique_maps', function (Blueprint $table) {
+ $table->dropColumn('ext_id');
+ });
+ }
+};