openapi: 3.0.3
info:
  title: Helerion API
  description: |
    HTTP contract for the Helerion game server and Unity client.
  version: 1.0.0
servers:
  - url: https://server.helerion.com/v1
    description: |
      Production server.
  - url: http://localhost:8080/v1
    description: |
      Local server.

security: []

paths:
  /achievements:
    get:
      tags: [Achievements]
      operationId: listAchievements
      summary: List all acheivement definitions
      parameters:
        - $ref: '#/components/parameters/Latitude'
        - $ref: '#/components/parameters/Longitude'
        - $ref: '#/components/parameters/Radius'
        - $ref: '#/components/parameters/Time'
      responses:
        "200":
          description: JSON array of achievements
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Achievement'
  /achievements/{id}:
    get:
      tags: [Achievements]
      operationId: fetchAchievement
      summary: Fetch acheivement by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The achievement that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Achievement'

  /alignments:
    get:
      tags: [Alignments]
      operationId: listAlignments
      summary: List all alignments
      responses:
        "200":
          description: JSON array of alignments
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Alignment'
  /alignments/{id}:
    get:
      tags: [Alignments]
      operationId: fetchAlignment
      summary: Fetch alignment by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The alignment that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Alignment'

  /characters:
    post:
      tags: [Characters]
      operationId: createCharacter
      summary: Creates a character
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Character'
      responses:
        "201":
          description: Character created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Character'

  /characters/{id}:
    get:
      tags: [Characters]
      operationId: getCharacter
      summary: Fetches a character
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: Character fetched
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Character'
    patch:
      tags: [Characters]
      operationId: patchCharacter
      summary: Partially update a character (e.g., modify inventory)
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        description: JSON Patch operations to apply to the character
        required: true
        content:
          application/json-patch+json:
            schema:
              $ref: '#/components/schemas/JsonPatchDocument'
      responses:
        "200":
          description: Character updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Character'

  /characters/{id}/contacts:
    get:
      tags: [Characters]
      operationId: listCharacterContacts
      summary: List persisted contacts for a character
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: Contact list for the character
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterContactList'
        "404":
          description: Character not found

  /characters/{id}/guild:
    get:
      tags: [Characters]
      operationId: getCharacterGuild
      summary: Get guild membership for a character
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: Guild membership for the character
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterGuildMembership'
        "404":
          description: Character not found or not in a guild

  /guilds:
    post:
      tags: [Guilds]
      operationId: createGuild
      summary: Create a guild
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateGuildRequest'
      responses:
        "201":
          description: Guild created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Guild'
        "400":
          description: Invalid request
        "404":
          description: Leader character not found
        "409":
          description: Leader already in a guild or guild name taken

  /guilds/{guildId}/join-requests:
    post:
      tags: [Guilds]
      operationId: createGuildJoinRequest
      summary: Request to join a guild
      parameters:
        - name: guildId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateGuildJoinRequestBody'
      responses:
        "201":
          description: Join request created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GuildJoinRequest'
        "404":
          description: Guild or requester not found
        "409":
          description: Requester already in a guild or duplicate pending request

  /guilds/{guildId}/join-requests/{requestId}/approve:
    post:
      tags: [Guilds]
      operationId: approveGuildJoinRequest
      summary: Approve a pending guild join request
      parameters:
        - name: guildId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
        - name: requestId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveGuildJoinRequestBody'
      responses:
        "200":
          description: Join request approved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GuildJoinRequest'
        "403":
          description: Actor is not the guild leader or an officer
        "404":
          description: Guild or join request not found
        "409":
          description: Requester already in a guild or guild is full

  /guilds/{guildId}/join-requests/{requestId}/deny:
    post:
      tags: [Guilds]
      operationId: denyGuildJoinRequest
      summary: Deny a pending guild join request
      parameters:
        - name: guildId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
        - name: requestId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveGuildJoinRequestBody'
      responses:
        "200":
          description: Join request denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GuildJoinRequest'
        "403":
          description: Actor is not the guild leader or an officer
        "404":
          description: Guild or join request not found

  /guilds/{guildId}/members/{characterId}/role:
    post:
      tags: [Guilds]
      operationId: updateGuildMemberRole
      summary: Promote or demote a guild member (leader only)
      parameters:
        - name: guildId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
        - name: characterId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateGuildMemberRoleBody'
      responses:
        "200":
          description: Updated guild membership
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterGuildMembership'
        "400":
          description: Invalid role or target
        "403":
          description: Actor is not the guild leader
        "404":
          description: Guild or member not found

  /guilds/{guildId}/members/{characterId}:
    delete:
      tags: [Guilds]
      operationId: removeGuildMember
      summary: Leave a guild or kick a member
      parameters:
        - name: guildId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
        - name: characterId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/Id'
        - name: actor_character_id
          in: query
          required: true
          schema:
            $ref: '#/components/schemas/Id'
      responses:
        "204":
          description: Member removed from guild
        "403":
          description: Not authorized to remove this member
        "404":
          description: Guild or member not found

  /classes:
    get:
      tags: [Classes]
      operationId: listClasses
      summary: List all class definitions
      parameters:
        - $ref: '#/components/parameters/Latitude'
        - $ref: '#/components/parameters/Longitude'
        - $ref: '#/components/parameters/Radius'
        - $ref: '#/components/parameters/Time'
        - $ref: '#/components/parameters/PlayerType'
      responses:
        "200":
          description: JSON array of classes
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Class"
  /classes/{id}:
    get:
      tags: [Classes]
      operationId: fetchClass
      summary: Fetch class by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The class that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Class'

  /items:
    get:
      tags: [Items]
      operationId: listItems
      summary: List all item definitions
      parameters:
        - $ref: '#/components/parameters/Latitude'
        - $ref: '#/components/parameters/Longitude'
        - $ref: '#/components/parameters/Radius'
        - $ref: '#/components/parameters/Time'
        - $ref: '#/components/parameters/ItemType'
      responses:
        "200":
          description: JSON array of items
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Item"
  /items/{id}:
    get:
      tags: [Items]
      operationId: fetchItem
      summary: Fetch item by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The item that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Item'

  /quests:
    get:
      tags: [Quests]
      operationId: listQuests
      summary: List all quest definitions
      parameters:
        - $ref: '#/components/parameters/Latitude'
        - $ref: '#/components/parameters/Longitude'
        - $ref: '#/components/parameters/Radius'
        - $ref: '#/components/parameters/Time'
      responses:
        "200":
          description: JSON array of quests
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Quest'
  /quests/{id}:
    get:
      tags: [Quests]
      operationId: fetchQuest
      summary: Fetch quest by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The quest that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quest'

  /races:
    get:
      tags: [Races]
      operationId: listRaces
      summary: List all quest definitions
      parameters:
        - $ref: '#/components/parameters/Latitude'
        - $ref: '#/components/parameters/Longitude'
        - $ref: '#/components/parameters/Radius'
        - $ref: '#/components/parameters/Time'
        - $ref: '#/components/parameters/PlayerType'
      responses:
        "200":
          description: JSON array of quests
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Race'
  /races/{id}:
    get:
      tags: [Races]
      operationId: fetchRace
      summary: Fetch race by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The race that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Race'

  /skills:
    get:
      tags: [Skills]
      operationId: listSkills
      summary: List all skill definitions
      parameters:
        - $ref: '#/components/parameters/Latitude'
        - $ref: '#/components/parameters/Longitude'
        - $ref: '#/components/parameters/Radius'
        - $ref: '#/components/parameters/Time'
      responses:
        "200":
          description: JSON array of skills
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Skill"
  /skills/{id}:
    get:
      tags: [Skills]
      operationId: fetchSkill
      summary: Fetch skill by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The skill that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Skill'

  /slots:
    get:
      tags: [Slots]
      operationId: listSlots
      summary: List all slot definitions
      responses:
        "200":
          description: JSON array of slots
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Slot"
  /slots/{id}:
    get:
      tags: [Slots]
      operationId: fetchSlot
      summary: Fetch slot by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The slot that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Slot'

  /stats:
    get:
      tags: [Stats]
      operationId: listStats
      summary: List all stat definitions
      responses:
        "200":
          description: JSON array of stats
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/BaseStat"
  /stats/{id}:
    get:
      tags: [Stats]
      operationId: fetchStat
      summary: Fetch stat by id
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        "200":
          description: The stat that has the specified id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BaseStat'

components:
  parameters:
    Id:
      name: id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Id'

    ItemType:
      name: item_type
      in: query
      required: false
      schema:
        $ref: '#/components/schemas/ItemType'

    Latitude:
      name: latitude
      in: query
      required: false
      schema:
        $ref: '#/components/schemas/Latitude'

    Longitude:
      name: longitude
      in: query
      required: false
      schema:
        $ref: '#/components/schemas/Longitude'

    PlayerType:
      name: player_type
      in: query
      required: false
      schema:
        $ref: '#/components/schemas/PlayerType'

    Radius:
      name: radius
      in: query
      required: false
      schema:
        $ref: '#/components/schemas/Radius'

    Time:
      name: time
      in: query
      required: false
      schema:
        $ref: '#/components/schemas/Time'

  schemas:
    Achievement:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        description:
          type: string
        trigger:
          $ref: '#/components/schemas/Trigger'
        drops:
          type: array
          items:
            $ref: '#/components/schemas/ItemComponent'

    BaseStat:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        description:
          type: string
        description_fun:
          type: string
        abbreviation:
          type: string

    Alignment:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        morality:
          $ref: '#/components/schemas/Morality'
        order:
          $ref: '#/components/schemas/Order'
    AnyValue:
      description: "Can be anything — string, number, array, object, etc. — including `null`"
    Class:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        description:
          type: string
        player_type:
          $ref: '#/components/schemas/PlayerType'
        skills:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSkill'

    Character:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        achievements:
          type: array
          items:
            $ref: '#/components/schemas/CharacterAchievement'
        alignment:
          $ref: '#/components/schemas/Id'
        classes:
          type: array
          items:
            $ref: '#/components/schemas/Class'
        items:
          type: array
          items:
            $ref: '#/components/schemas/CharacterItem'
        name:
          type: string
        quests:
          type: array
          items:
            $ref: '#/components/schemas/CharacterQuest'
        races:
          type: array
          items:
            $ref: '#/components/schemas/Race'
        skills:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSkill'
        slots:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSlot'
        stats:
          type: array
          items:
            $ref: '#/components/schemas/CharacterStat'

    CharacterContact:
      type: object
      required:
        - character_id
        - name
      properties:
        character_id:
          $ref: '#/components/schemas/Id'
        name:
          type: string

    CharacterContactList:
      type: object
      required:
        - contacts
      properties:
        contacts:
          type: array
          items:
            $ref: '#/components/schemas/CharacterContact'

    GuildRole:
      type: string
      enum: [LEADER, OFFICER, MEMBER]

    GuildJoinRequestStatus:
      type: string
      enum: [PENDING, APPROVED, DENIED]

    Guild:
      type: object
      required:
        - id
        - name
        - leader_id
        - member_ids
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        leader_id:
          $ref: '#/components/schemas/Id'
        officer_ids:
          type: array
          items:
            $ref: '#/components/schemas/Id'
        member_ids:
          type: array
          items:
            $ref: '#/components/schemas/Id'

    CharacterGuildMembership:
      type: object
      required:
        - guild
        - role
      properties:
        guild:
          $ref: '#/components/schemas/Guild'
        role:
          $ref: '#/components/schemas/GuildRole'

    CreateGuildRequest:
      type: object
      required:
        - name
        - leader_character_id
      properties:
        name:
          type: string
        leader_character_id:
          $ref: '#/components/schemas/Id'

    CreateGuildJoinRequestBody:
      type: object
      required:
        - requester_character_id
      properties:
        requester_character_id:
          $ref: '#/components/schemas/Id'

    GuildJoinRequest:
      type: object
      required:
        - id
        - guild_id
        - requester_id
        - status
      properties:
        id:
          $ref: '#/components/schemas/Id'
        guild_id:
          $ref: '#/components/schemas/Id'
        requester_id:
          $ref: '#/components/schemas/Id'
        status:
          $ref: '#/components/schemas/GuildJoinRequestStatus'

    ResolveGuildJoinRequestBody:
      type: object
      required:
        - actor_character_id
      properties:
        actor_character_id:
          $ref: '#/components/schemas/Id'

    UpdateGuildMemberRoleBody:
      type: object
      required:
        - actor_character_id
        - role
      properties:
        actor_character_id:
          $ref: '#/components/schemas/Id'
        role:
          $ref: '#/components/schemas/GuildRole'

    CharacterAchievement:
      type: object
      required:
        - achievement
        - status
      properties:
        achievement:
          $ref: '#/components/schemas/Id'
        status:
          $ref: '#/components/schemas/Status'

    CharacterItem:
      type: object
      required:
        - item
        - quantity
      properties:
        item:
          $ref: '#/components/schemas/Id'
        quantity:
          $ref: '#/components/schemas/Integer'

    CharacterQuest:
      type: object
      properties:
        quest:
          $ref: '#/components/schemas/Id'
        status:
          $ref: '#/components/schemas/Status'

    CharacterSlot:
      type: object
      properties:
        slot:
          $ref: '#/components/schemas/Id'
        item:
          $ref: '#/components/schemas/Id'

    CharacterSkill:
      type: object
      properties:
        skill:
          $ref: '#/components/schemas/Id'
        experience:
          $ref: '#/components/schemas/Double'
        stat:
          $ref: '#/components/schemas/Id'

    CharacterStat:
      type: object
      properties:
        stat:
          $ref: '#/components/schemas/Id'
        level:
          $ref: '#/components/schemas/Integer'

    Double:
      type: number
      format: double

    EventCondition:
      type: object
      required: [type, count]
      properties:
        type:
          $ref: '#/components/schemas/EventType'
        reference:
          $ref: '#/components/schemas/Id'
        reference_type:
          $ref: '#/components/schemas/ReferenceType'
        count:
          $ref: '#/components/schemas/Integer'
        comparison:
          type: string
          enum: [">=", "=="]
          default: ">="

    EventType:
      type: string
      enum:
        - HARVEST
        - KILL
        - SKILL

    Id:
      $ref: '#/components/schemas/Integer'

    Integer:
      type: integer
      format: int32

    Item:
      type: object
      required:
        - id
        - name
        - description
        - drops
        - type
        - stackable
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        description:
          type: string
        drops:
          type: array
          items:
            $ref: '#/components/schemas/ItemComponent'
        components:
          type: array
          items:
            $ref: '#/components/schemas/ItemComponent'
        prefab:
          type: string
          nullable: true
        weight:
          type: number
          format: double
          nullable: true
        type:
          $ref: '#/components/schemas/ItemType'
        slots:
          type: array
          items:
            $ref: '#/components/schemas/ItemSlot'
        value:
          allOf:
            - $ref: '#/components/schemas/Integer'
          nullable: true
        stackable:
          type: boolean

    ItemComponent:
      type: object
      required:
        - quantity
        - item
      properties:
        chance:
          $ref: '#/components/schemas/Double'
        quantity:
          $ref: '#/components/schemas/Integer'
        item:
          $ref: '#/components/schemas/Id'

    ItemSlot:
      type: object
      properties:
        slot:
          $ref: '#/components/schemas/Id'
        quantity:
          $ref: '#/components/schemas/Integer'

    ItemType:
      type: string
      enum:
        - ARMOR
        - CONSUMABLE
        - COMPONENT
        - HARVESTABLE
        - MELEE
        - TOOL
        - RANGED
        - CRAFTING
    JsonPointer:
      description: A JSON pointer value, as described by RFC 6901 (https://datatracker.ietf.org/doc/html/rfc6901)
      example: /name
      type: string
    JsonPatchOperation:
      description: A JSON Patch operation, as defined by RFC 6902 (https://datatracker.ietf.org/doc/html/rfc6902)
      properties:
        op:
          description: The JSON Patch operation type
          enum:
            - add
            - remove
            - replace
            - test
          example: replace
          type: string
        path:
          $ref: "#/components/schemas/JsonPointer"
        value:
          $ref: "#/components/schemas/AnyValue"
      required:
        - op
        - path
      type: object
    JsonPatchDocument:
      description: A JSON Patch document as defined by RFC 6902
      items:
        $ref: "#/components/schemas/JsonPatchOperation"
      type: array

    Latitude:
      $ref: '#/components/schemas/Double'

    Longitude:
      $ref: '#/components/schemas/Double'

    Morality:
      type: string
      enum:
        - GOOD
        - NEUTRAL
        - EVIL

    Order:
      type: string
      enum:
        - LAWFUL
        - NEUTRAL
        - CHAOTIC

    PlayerType:
      type: string
      enum:
        - PLAYER
        - NPC

    Quest:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        description:
          type: string
        trigger:
          $ref: '#/components/schemas/Trigger'

    Race:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        description:
          type: string
        trigger:
          $ref: '#/components/schemas/Trigger'
        children:
          type: array
          items:
            $ref: '#/components/schemas/Id'
        owner:
          $ref: '#/components/schemas/PlayerType'
        size:
          $ref: '#/components/schemas/Size'
        speed:
          $ref: '#/components/schemas/Integer'
        stat_modifier:
          type: array
          items:
            $ref: '#/components/schemas/StatModifier'

    Radius:
      $ref: '#/components/schemas/Double'

    ReferenceType:
      type: string
      enum:
        - ENEMY
        - ITEM
        - SKILL

    Size:
      type: string
      enum:
        - SMALL
        - MEDIUM
        - LARGE

    Skill:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        description:
          type: string
        trigger:
          $ref: '#/components/schemas/Trigger'
        children:
          type: array
          items:
            $ref: '#/components/schemas/Id'

    Slot:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        slot_type:
          $ref: '#/components/schemas/SlotType'

    SlotType:
      type: string
      enum:
        - ARM
        - BODY
        - FINGER
        - FOOT
        - FOREARM
        - HAND
        - HEAD
        - KNEE
        - LEG
        - SHOULDER
        - TOE

    StatModifier:
      type: object
      properties:
        stat:
          $ref: '#/components/schemas/Id'
        modifier:
          $ref: '#/components/schemas/Integer'
        modifier_type:
          $ref: '#/components/schemas/StatModifierType'

    StatModifierType:
      type: string
      enum:
        - '*'
        - '+'

    Status:
      type: string
      enum:
        - NOT_STARTED
        - IN_PROGRESS
        - COMPLETED
        - FAILED

    Time:
      type: string
      format: date-time

    Trigger:
      type: object
      properties:
        operator:
          type: string
          enum: [AND, OR]
          default: AND
        conditions:
          type: array
          items:
            $ref: '#/components/schemas/EventCondition'
