*/ public const METRICS = ['exercise_count', 'buildings', 'transactions', 'education']; /** * Ensure the character has an active row for every city mission. */ public static function ensureTracking(Character $character): void { $missions = Mission::query() ->where('city_id', $character->city_id) ->where('is_active', true) ->get(); foreach ($missions as $mission) { $exists = DB::table('character_missions') ->where('character_id', $character->id) ->where('mission_id', $mission->id) ->exists(); if (! $exists) { DB::table('character_missions')->insert([ 'character_id' => $character->id, 'mission_id' => $mission->id, 'progress' => 0, 'is_completed' => false, 'created_at' => now(), 'updated_at' => now(), ]); } } } /** * Recompute progress for a metric and complete missions that hit target. * * @return list */ public static function sync(Character $character, string $metric): array { if (! in_array($metric, self::METRICS, true)) { return []; } self::ensureTracking($character); $character->refresh(); $value = (int) match ($metric) { 'exercise_count' => $character->exercise_count, 'education' => $character->education, 'buildings' => $character->buildings()->where('type', 'house')->count(), 'transactions' => DB::table('transactions') ->where('from_character_id', $character->id) ->orWhere('to_character_id', $character->id) ->count(), default => 0, }; $completed = []; $rows = DB::table('character_missions') ->where('character_id', $character->id) ->where('is_completed', false) ->get(); foreach ($rows as $row) { $mission = Mission::query()->find($row->mission_id); if ($mission === null || $mission->metric !== $metric) { continue; } $progress = min($value, (int) $mission->target); DB::table('character_missions')->where('id', $row->id)->update([ 'progress' => $progress, 'is_completed' => $progress >= (int) $mission->target, 'completed_at' => $progress >= (int) $mission->target ? now() : null, 'updated_at' => now(), ]); if ($progress >= (int) $mission->target) { $character->money = round((float) $character->money + (float) $mission->reward, 2); $completed[] = [ 'code' => $mission->code, 'title' => $mission->title, 'reward' => (float) $mission->reward, ]; } } if ($completed !== []) { $character->save(); } return $completed; } }