Missões personalizadas

De DragonMineZ Wiki
Esta página é uma versão traduzida da página Custom Quests e a tradução está 100% completa.

As missões personalizadas são sagas, missões de saga e missões secundárias que você mesmo escreve apenas com JSON, sem código Java nem um jar de addon. O conteúdo da história são dados de cada mundo (diferente das raças e formas, que ficam em config/dragonminez), então esses arquivos ficam dentro do save do mundo.

Onde ficam os arquivos

<world save>/dragonminez/
  sagas/                     # saga manifests
  quests/<questFolder>/      # quest files for one saga, loaded alphabetically
  sidequests/                # standalone side quests (any subfolder layout, e.g. sidequests/<category>/)

Código-fonte: QuestRegistry.loadAll(server) (src/main/java/com/dragonminez/common/quest/QuestRegistry.java). Veja Encontrar pastas para o mapa completo de pastas.

Opções da história

Configurado em: Configuração do servidorgameplay

Estas opções ficam em general-server.json → gameplay, e todas são true por padrão:

Campo Tipo O que faz
storyModeEnabled Booleano Chave geral do sistema de sagas e missões.
createDefaultSagas Booleano Gera os arquivos padrão de sagas e missões de saga do DragonMineZ (também precisa de storyModeEnabled).
sideQuestsEnabled Booleano Chave geral do sistema de missões secundárias.
createDefaultSideQuests Booleano Gera os arquivos padrão de missões secundárias do DragonMineZ.
autoUpdateQuests Booleano Atualiza no lugar os arquivos padrão do DragonMineZ quando uma nova versão do DragonMineZ os altera (veja Atualização dos arquivos padrão).

Os arquivos padrão são gerados um a um, pelo nome do arquivo, sempre que estão faltando, então um arquivo padrão que você apagar volta na próxima carga enquanto a opção createDefault... correspondente estiver ativada. Seus próprios arquivos (qualquer outro nome de arquivo) nunca são alterados.

Passo 1: crie um manifesto de saga

Crie um arquivo por saga em dragonminez/sagas/, por exemplo my_saga.json:

{
  "id": "my_saga",
  "name": "dmz.saga.my_saga",
  "requirements": {
    "previousSaga": "saiyan_saga"
  },
  "questFolder": "saga_my_saga"
}
Campo Tipo O que faz
id Texto Id único da saga.
name Texto Chave de tradução do nome exibido da saga.
requirements.previousSaga Texto Id da saga que precisa ser concluída antes, ou "" se não houver nenhuma.
questFolder Texto Nome da subpasta dentro de dragonminez/quests/ que guarda os arquivos de missões desta saga.

Os ids de saga padrão são classic_saga, saiyan_saga, frieza_saga, android_saga, future_saga, buu_saga, movies_saga, gt_saga e daima_saga (veja Sagas).

Código-fonte: SagaDefaults.java, Saga.java.

Passo 2: adicione arquivos de missões

Dentro de dragonminez/quests/saga_my_saga/, adicione um arquivo por missão, com nomes que ordenem os arquivos na ordem de jogo (01_...json, 02_...json, ...). A ordem dos nomes de arquivo é a ordem das missões dentro da saga.

{
  "id": 1,
  "title": "dmz.quest.my_saga1.name",
  "description": "dmz.quest.my_saga1.desc",
  "type": "SAGA",
  "category": "saga_my_saga",
  "parallel_objectives": false,
  "party_scaling": true,
  "secret": false,
  "claim_mode": "TREE_OR_NPC",
  "requirements": {
    "operator": "AND",
    "conditions": [
      { "type": "LEVEL", "minLevel": 1 },
      { "type": "DIMENSION", "dimension": "minecraft:overworld" }
    ]
  },
  "objectives": [
    { "type": "KILL", "entity": "dragonminez:saga_raditz", "count": 1, "health": 450.0, "meleeDamage": 22.0, "kiDamage": 39.0 }
  ],
  "rewards": [
    { "type": "ATTRIBUTES", "amount": 10 },
    { "type": "ITEM", "item": "dragonminez:senzu_bean", "count": 1 }
  ]
}

Uma missão de saga é identificada como <sagaId>:<id> (por exemplo, my_saga:1), usando o id numérico, não o nome do arquivo. Dois arquivos com o mesmo id em uma pasta sobrescrevem um ao outro.

Para inserir uma missão entre duas existentes, dê a ela um id novo e não usado e um nome de arquivo que fique entre elas na ordenação. As sagas do próprio DragonMineZ fazem isso: a missão 17 da Saga Freeza é 01b_scout_the_ruined_village.json, entre 01_... e 02_.... Renumerar as missões existentes quebraria o progresso salvo e todas as condições SAGA_QUEST ou requisitos de forma que apontam para elas.

Campos de uma missão

As missões de saga e as missões secundárias compartilham exatamente o mesmo esquema, então estes campos valem para as duas.

Campo Tipo O que faz
id Número ou texto Numérico para missões de saga. As missões secundárias usam um id de texto.
title / description Texto Chaves de tradução (texto simples também funciona).
type Texto SAGA, SIDEQUEST, DAILY ou EVENT. Uma missão DAILY pode ser repetida uma vez por dia real (fuso horário do servidor).
category Texto Rótulo de agrupamento livre. Padrão: "general".
parallel_objectives Booleano Se for true, todos os objetivos avançam ao mesmo tempo, em vez de um depois do outro.
party_scaling Booleano Se for true, as quantidades de itens e de abates aumentam para cada membro extra do grupo (defaultQuestPartyMultiplier no general-server.json, padrão 1.45), e os inimigos da missão ganham mais vida e dano para cada membro extra (enemyHealthPerPartyPlayer 1.25, enemyDamagePerPartyPlayer 1.1).
secret Booleano Esconde a missão do registro de missões até que ela seja iniciada ou fique disponível.
claim_mode Texto TREE_OR_NPC (resgatada pela tela de missões ou por um NPC) ou NPC_ONLY (o jogador precisa falar com o NPC de entrega).
quest_giver Texto ou null Id do NPC que oferece a missão (veja NPCs que dão e recebem missões).
turn_in Texto ou null Id do NPC que recebe a missão concluída.
prerequisites Objeto Decide se a missão pode ser desbloqueada. Mesmo esquema de requirements (veja Pré-requisitos e requisitos).
requirements Objeto Decide se a missão pode ser iniciada neste momento (por exemplo, a dimensão, o bioma ou o nível atuais).
repeatable Booleano Se for true, a missão pode ser iniciada de novo depois de concluída. Padrão: false.
repeat_cooldown_seconds Inteiro Segundos reais até que uma missão repetível possa ser iniciada de novo. 0 (o padrão) significa imediatamente.
time_limit_seconds Inteiro Tempo limite depois de aceitar a missão, em segundos do jogo. Quando ele acaba, a missão falha. 0 (o padrão) significa sem limite.
objectives Array Veja Tipos de objetivos.
rewards Array Veja Tipos de recompensas.
defaultsVersion Texto Usado apenas nos arquivos padrão do próprio DragonMineZ (veja Atualização dos arquivos padrão).

Quando uma missão repetível ou diária é iniciada de novo, o progresso dos objetivos e os resgates de recompensas são zerados, então as recompensas podem ser resgatadas outra vez.

Código-fonte: QuestParser.parseQuest, Quest.java.

Tipos de objetivos

Cada entrada em objectives precisa de um type mais os campos desse tipo. Os campos marcados com * são opcionais e têm valores padrão razoáveis.

type Campos O que faz
ITEM item, count Ter esta quantidade de um item no inventário. Os itens não são retirados; para isso, use DELIVER.
KILL entity, count, health*, meleeDamage*, kiDamage*, spawn*, count_mode*, AITier*, TextureVariant*, canTransform*, TransformHealth*, TransformMeleeDamage*, TransformKiDamage*, TransformHealthMultiplier*, TransformMeleeDamageMultiplier*, TransformKiMultiplier*, TransformTriggerPercent* entity pode ser um id de registro ou uma tag de entidade (#minecraft:zombies; os spawns da missão escolhem um membro aleatório). health, meleeDamage e kiDamage têm padrão 20, 1 e 1. spawn: QUEST (padrão, gerados quando a missão começa) ou NATURAL. count_mode: QUEST_SPAWNED_ONLY (padrão) ou ANY_MATCHING. Veja Objetivo de abate: nível de IA e transformações.
SPAR Igual a KILL, sem os campos Transform* Uma luta não letal: quando um parceiro gerado pela missão ficaria abaixo de 15% de vida, a luta termina, conta como vitória para o grupo inteiro e o parceiro vai embora.
INTERACT entity*, entityName* Interagir com clique direito com uma entidade correspondente.
TALK_TO npcId O jogador precisa falar com um NPC ou mestre específico.
DELIVER item, count* (padrão 1), npcId (aliases npc_id e npc) Clique com o botão direito no NPC para entregar os itens. Eles são retirados do inventário, e entregas parciais contam.
STRUCTURE structure O jogador precisa entrar em uma estrutura gerada.
BIOME biome O jogador precisa estar neste bioma (ou tag de bioma).
DIMENSION dimension O jogador precisa estar nesta dimensão.
COORDS x, y, z, radius* O jogador precisa estar a até radius blocos (padrão 10) de um ponto.
CHECKPOINT_RACE checkpoints (lista de { "x", "y", "z" }), radius* (padrão 8) Chegar a cada checkpoint em ordem.
SURVIVE_WAVES entity, waves* (padrão 3), mobs_per_wave* (padrão 3), wave_delay_seconds* (padrão 10), health*, meleeDamage*, kiDamage*, AITier*, TextureVariant*, CanTransform* Ondas de inimigos aparecem ao redor do jogador; a próxima chega após uma breve pausa quando uma onda é eliminada. Cada onda eliminada conta como uma etapa de progresso.
ESCORT entity, x, y, z, radius* (padrão 6), health* Um escoltado aparece ao lado do jogador e o segue; leve-o até radius do alvo. Se ele morrer, a missão falha. health substitui a vida máxima dele.
DRAGON_SUMMON dragon*, ball_set* O jogador precisa invocar um dragão e/ou um conjunto de esferas específico (aliases: dragon_id e dragonId; ballSet, ball_set_id, ballSetId e set).
SKILL skill, level* O jogador precisa atingir um nível de habilidade (aliases: skillId e id; minLevel e required; nível padrão 1).

Código-fonte: QuestParser.parseObjective e as classes em common/quest/objectives/.

Enquanto um objetivo COORDS, ESCORT ou CHECKPOINT_RACE está sendo acompanhado, a HUD de missões mostra a distância e a direção até o alvo (ou até o próximo checkpoint).

Em uma missão com parallel_objectives: false, objetivos de abate que vêm um logo após o outro são liberados juntos, então um par "abater A, abater B" pode ser enfrentado em qualquer ordem. Os objetivos dos outros tipos continuam indo um de cada vez.

Objetivo de abate: nível de IA e transformações

  • AITier é opcional: 1 Novato, 2 Inteligente, 3 Elite, 4 Especialista. Se for omitido (ou definido como 0 ou um valor negativo), o nível vem da dificuldade em que a missão foi iniciada: EASY dá 1, NORMAL dá 2, HARD dá 3, e HARD contra o chefe final de uma saga dá 4. Defina AITier explicitamente para forçar um nível específico independentemente da dificuldade.
  • TextureVariant escolhe uma das texturas das entidades que têm várias.
  • canTransform (booleano, padrão true) controla se o inimigo pode se transformar no meio da luta. Defina como false para impedir totalmente que o inimigo se transforme; isso é aplicado no servidor, não é só visual. SURVIVE_WAVES o lê como CanTransform.
  • Quando canTransform é true, os sete campos Transform* ajustam as estatísticas do inimigo transformado sem mexer nos padrões da configuração global. Todos são opcionais (null usa os padrões globais de transformação do entities.json):
    • TransformHealth, TransformMeleeDamage, TransformKiDamage: valores absolutos das estatísticas da forma transformada.
    • TransformHealthMultiplier, TransformMeleeDamageMultiplier, TransformKiMultiplier: multiplicadores aplicados às estatísticas base (antes de se transformar) em vez de valores absolutos.
    • Valores absolutos têm prioridade sobre multiplicadores quando os dois são definidos para a mesma estatística.
    • TransformTriggerPercent: fração da vida máxima (0.0–1.0) em que o inimigo ativa sua transformação.
{ "type": "KILL", "entity": "dragonminez:saga_vegeta", "count": 1, "health": 900.0, "meleeDamage": 40.0, "kiDamage": 60.0,
  "canTransform": true, "TransformTriggerPercent": 0.5, "TransformHealthMultiplier": 1.8, "TransformMeleeDamageMultiplier": 1.6 }

Como uma missão pode falhar

Uma missão que falhou perde o progresso dos objetivos, e os inimigos e escoltados gerados por ela são removidos. Ela pode ser iniciada de novo pelo registro de missões ou pelo NPC dela (os requisitos de início são verificados outra vez).

  • Grupo derrotado: quando o jogador (ou todos os membros do grupo dele) morre, as missões aceitas com um objetivo KILL, SPAR, SURVIVE_WAVES ou ESCORT falham.
  • Escoltado morreu: o alvo do ESCORT foi morto.
  • O tempo acabou: passou time_limit_seconds desde que a missão foi aceita.

Tipos de recompensas

Cada entrada em rewards precisa de um type mais os campos dele. Todo tipo de recompensa também aceita um campo opcional difficulty que restringe as dificuldades em que ela é concedida (veja abaixo).

type Campos O que faz
ITEM item, count* (padrão 1) Concede uma pilha de itens.
GENERIC_ITEM itemReward (um objeto de item) Concede um item com encantamentos, dados de poção ou um acabamento de armadura. Veja Objetos de item.
ATTRIBUTES amount Concede Atributos, a recompensa que todas as missões padrão usam: amount × attributeRewardPointRatio (por padrão a metade, arredondada para baixo) como Pontos de Atributo, e o resto como o TP que esses pontos de estatística custariam ao jogador no momento do resgate. Esse TP não recebe bônus de novo nem é compartilhado com o grupo. A dica de ferramenta mostra a divisão. Veja Estatísticas e atributos.
ATTRIBUTE_POINTS amount Concede apenas Pontos de Atributo (que o jogador distribui no menu de estatísticas).
TPS amount Concede uma quantidade fixa de pontos de treino (passa pelos bônus de TP STORY do servidor).
ALIGNMENT amount Ajusta a índole do jogador (pode ser negativo). Veja Índole.
SKILL skill, level Eleva uma habilidade até pelo menos este nível.
TRANSFORMATION formGroup, formName, mastery* (padrão 100.0), stack* (padrão false) Desbloqueia uma forma da raça do jogador (ou uma forma acumulável se stack for true): evolui a habilidade de forma do grupo até essa forma e concede pelo menos esse domínio. As formas listadas no formRequisite da forma também sobem até o domínio que exigem. Aliases: form_group e group; form_name e form.
KI_TECHNIQUE code Concede uma técnica de ki a partir de um código de técnica exportado (aliases: techniqueCode e technique_code). Veja Técnicas.
COMMAND command, translationKey* Executa um comando no servidor (nível de operador), com %player% substituído pelo nome do jogador. translationKey é o texto mostrado na lista de recompensas no lugar do comando.

As recompensas TRANSFORMATION e SKILL agem sobre os grupos de raça e as habilidades do próprio jogador. Para dar recompensas específicas de raça ou classe, coloque-as em missões bloqueadas por uma condição RACE ou CLASS (veja Pré-requisitos e requisitos).

Código-fonte: QuestParser.parseReward, as classes em common/quest/rewards/, Difficulty.java.

Restringir uma recompensa por dificuldade

O campo opcional difficulty de uma recompensa é um conjunto permitido das dificuldades em que essa recompensa é concedida (aliases: difficulties, difficultyType e minDifficulty). Ele aceita um array JSON (["NORMAL","HARD"]), um texto separado por vírgulas ou espaços ("NORMAL, HARD") ou um único valor ("HARD"). Os valores válidos são EASY, NORMAL e HARD. Quando omitido ou vazio, a recompensa é concedida em todas as dificuldades. É uma verificação de pertencimento ao conjunto, não um limite mínimo: "EASY" sozinho significa apenas Fácil, e ["EASY","HARD"] (pulando Normal) é válido. Valores desconhecidos são descartados.

{ "type": "ITEM", "item": "dragonminez:senzu_bean", "count": 1, "difficulty": ["NORMAL", "HARD"] }
Arquivos antigos Arquivos de missões antigos escreviam a dificuldade como prefixo do tipo ("hard:ITEM", "normal:ITEM"). Esses arquivos ainda carregam: hard: significa apenas Difícil e normal: significa Fácil e Normal, a menos que a recompensa também tenha um campo difficulty. Arquivos novos devem usar o campo difficulty.

Escalonamento de recompensas

As quantidades de ITEM, GENERIC_ITEM, TPS, ATTRIBUTES, ATTRIBUTE_POINTS e ALIGNMENT são escalonadas pelo multiplicador de recompensas de missões da dificuldade em que a missão foi iniciada (easyModeQuestRewardMultiplier e hardModeQuestRewardMultiplier no general-server.json). As recompensas TPS, ATTRIBUTES e ATTRIBUTE_POINTS também são multiplicadas por storyResetTPMultiplier (padrão 0.5) uma vez a cada reinício da história. SKILL, TRANSFORMATION, KI_TECHNIQUE e COMMAND são recompensas de desbloqueio e nunca são escalonadas. As recompensas são por jogador. Cada membro do grupo resgata as próprias recompensas quando a missão é concluída (pelo registro de missões, ou pelo NPC de entrega com claim_mode: NPC_ONLY).

Objetos de item

As recompensas GENERIC_ITEM (e os desejos item_list_wish, veja Desejos personalizados) descrevem itens com um objeto tipado. itemType escolhe o tipo de item; itemId e count estão sempre disponíveis.

itemType Campos extras O que faz
generic_item (padrão) — Qualquer item.
enchanted_item enchantments enchantments é um mapa de id de encantamento para nível, por exemplo { "minecraft:sharpness": 3 }.
enchanted_book enchantments Encantamentos armazenados em um livro encantado (itemId minecraft:enchanted_book).
potion, splash_potion, lingering_potion, tipped_arrow potion*, mobEffects* potion é um id de poção (por exemplo, minecraft:strong_healing). mobEffects é uma lista de { "effect", "duration", "amplifier", "ambient", "visible", "showIcon" } (duração em ticks, pelo menos 1). Defina itemId como o item vanilla correspondente (minecraft:potion, minecraft:splash_potion, minecraft:lingering_potion ou minecraft:tipped_arrow).
trimmed_armor material, pattern, enchantments* Armadura com acabamento, por exemplo "material": "minecraft:gold" e "pattern": "minecraft:coast".

itemId é obrigatório e count precisa ser pelo menos 1. Um objeto de item inválido (sem itemId, um itemType desconhecido, um nível ou duração incorretos) faz o arquivo da missão inteiro falhar ao carregar, e o arquivo é indicado no relatório de carregamento do JSON. Ids de encantamento, poção ou efeito desconhecidos só fazem pular aquela entrada, com um aviso no log.

{ "type": "GENERIC_ITEM", "itemReward": {
    "itemType": "enchanted_item", "itemId": "minecraft:diamond_sword", "count": 1,
    "enchantments": { "minecraft:sharpness": 5, "minecraft:unbreaking": 3 } } }

Código-fonte: src/main/java/com/dragonminez/common/util/types/items/, GenericItemTypeAdapterFactory.java.

Pré-requisitos e requisitos

Tanto prerequisites (se a missão pode ser desbloqueada em algum momento) quanto requirements (se ela pode ser iniciada agora) usam o mesmo esquema de árvore de condições:

{
  "operator": "AND",
  "conditions": [
    { "type": "LEVEL", "minLevel": 30 },
    { "type": "SAGA_QUEST", "sagaId": "saiyan_saga", "questId": 8 },
    { "operator": "OR", "conditions": [ ... ] }
  ]
}

operator é AND ou OR. conditions pode aninhar outro bloco { "operator": ..., "conditions": [...] }.

type Campos
SAGA_QUEST sagaId, questId (int): uma missão de saga específica precisa estar concluída.
QUEST questId (string): uma missão secundária específica precisa estar concluída.
LEVEL minLevel
STAT stat (STR, SKP, RES, VIT, PWR ou ENE), minValue
RACE race (aliases: raceName e race_name): o id da raça do jogador, sem diferenciar maiúsculas e minúsculas. Ids embutidos: human, saiyan, namekian, frostdemon, bioandroid, majin, glind. Atenção: é frostdemon (não frieza nem colddemon) e bioandroid (não android nem cell). Raças personalizadas usam o nome da pasta delas em config/dragonminez/races/.
CLASS class (aliases: className, class_name e characterClass): o id da classe do jogador, sem diferenciar maiúsculas e minúsculas.
SKILL skill, minLevel* (aliases: skillId e id; level e required)
BIOME biome
DIMENSION dimension
STRUCTURE structure, hint* ({ "dimension", "x", "y", "z" }, todos opcionais; ajuda a guiar o jogador até a estrutura)
ALIGNMENT min*, max*
TIME mode (GAME_TIME com ticks, ou REAL_TIME com milliseconds): tempo decorrido desde que a missão ficou disponível.

Código-fonte: QuestParser.parseCondition, QuestPrerequisites.java. Os mesmos blocos de condições controlam as opções em Diálogos de NPCs.

Missões secundárias

As missões secundárias podem ficar em qualquer lugar dentro de dragonminez/sidequests/ (as subpastas também são examinadas) e usam o mesmo esquema de missão das missões de saga, com algumas diferenças práticas:

  • "id" é um texto em vez de um número (por exemplo, "bulma_gero_blueprints").
  • "type" é "SIDEQUEST" (ou "DAILY" ou "EVENT") em vez de "SAGA".
  • Não existe pasta de saga nem manifesto que as agrupe: cada arquivo é independente.
  • quest_giver e turn_in são usados com muito mais frequência, já que as missões secundárias costumam girar em torno de NPCs.
  • prerequisites costuma usar condições SAGA_QUEST ou QUEST para bloquear uma missão secundária até certo progresso da história.

Exemplo (dragonminez/sidequests/collection/bulma_gero_blueprints.json):

{
  "id": "bulma_gero_blueprints",
  "title": "dmz.sidequest.bulma_gero.name",
  "description": "dmz.sidequest.bulma_gero.desc",
  "type": "SIDEQUEST",
  "category": "collection",
  "parallel_objectives": false,
  "party_scaling": true,
  "secret": false,
  "claim_mode": "TREE_OR_NPC",
  "quest_giver": "bulma",
  "turn_in": "bulma",
  "prerequisites": {
    "operator": "AND",
    "conditions": [
      { "type": "SAGA_QUEST", "sagaId": "android_saga", "questId": 6 }
    ]
  },
  "objectives": [
    { "type": "STRUCTURE", "structure": "dragonminez:gero_lab" },
    { "type": "ITEM", "item": "minecraft:redstone", "count": 32 },
    { "type": "ITEM", "item": "dragonminez:kikono_shard", "count": 8 },
    { "type": "TALK_TO", "npcId": "bulma" }
  ],
  "rewards": [
    { "type": "ATTRIBUTES", "amount": 25 }
  ]
}

Uma tarefa repetível, entregue a um NPC:

{
  "id": "my_market_run",
  "title": "dmz.sidequest.my_market_run.name",
  "description": "dmz.sidequest.my_market_run.desc",
  "type": "SIDEQUEST",
  "category": "collection",
  "quest_giver": "bulma",
  "turn_in": "bulma",
  "repeatable": true,
  "repeat_cooldown_seconds": 3600,
  "objectives": [
    { "type": "DELIVER", "item": "minecraft:emerald", "count": 12, "npcId": "bulma" },
    { "type": "TALK_TO", "npcId": "bulma" }
  ],
  "rewards": [
    { "type": "ITEM", "item": "dragonminez:senzu_bean", "count": 2 }
  ]
}

NPCs que dão e recebem missões

quest_giver e turn_in referenciam um NPC pelo id: o mesmo id usado pelas entidades de NPCs de missões e pelos NPCs mestres e treinadores (os objetivos TALK_TO e DELIVER usam os mesmos ids). O DragonMineZ indexa as missões pelos dois campos, então um NPC pode mostrar marcadores de "missão disponível" e aceitar entregas sem nenhuma ligação extra da sua parte. Os dois campos são opcionais: omita-os ou defina como null se a missão não estiver ligada a um NPC (por exemplo, uma missão de saga que só envolve explorar ou abater inimigos).

Falar com quem dá a missão abre o diálogo dele, onde a opção Missões lista as missões que ele oferece, as que estão em andamento e as que estão prontas para entregar, além das missões bloqueadas: passar o cursor sobre uma mostra o que falta (por exemplo, a missão de saga ou a missão secundária que precisa ser concluída antes), e uma missão repeatable concluída mostra uma contagem regressiva até poder ser pega de novo. Missões secret bloqueadas não aparecem. A lista é ordenada por quando cada missão fica disponível na história (ordem de saga, depois ordem de missão, depois requisito de nível), então as primeiras missões de um NPC aparecem primeiro.

Se quem dá a missão é um mestre com requisito de índole, as missões dele só podem ser iniciadas enquanto a índole do jogador se encaixar, em qualquer tela; missões já em andamento ainda podem ser entregues. Goku, Gohan, Rei Kai, Velho Kaioshin, Mestre Kame e Kuririn precisam de uma índole de 61 ou mais, e Cell e Freeza de 40 ou menos. Veja Mestres.

Chaves desconhecidas ou escritas errado

Toda missão, saga, objetivo, recompensa, condição de pré-requisito ou requisito e dica de estrutura é comparada com uma lista explícita de campos permitidos (QuestParser.validate e validateSaga). Qualquer chave não reconhecida (um erro de digitação, maiúsculas erradas, um campo que sobrou de um esquema antigo) não é descartada em silêncio: ela é registrada no log e mostrada aos jogadores no relatório de carregamento do JSON no jogo ao entrar (controlado por developer.reportJsonProblemsInChat, ativado por padrão), além do console do servidor. A entrada ainda carrega com o resto dos campos válidos; só a chave não reconhecida é sinalizada. Use isso para pegar erros de digitação (por exemplo, CanTransfrom em vez de canTransform) em vez de ter um campo que silenciosamente não faz nada. Um arquivo com sintaxe JSON quebrada é ignorado e relatado da mesma forma.

Chaves que começam com _, $ ou // são tratadas como comentários e nunca são sinalizadas, então você pode deixar anotações para si mesmo direto no JSON.

Tipos de objetivos e recompensas personalizados

Addons em Java podem registrar seus próprios types de objetivos e recompensas por meio de QuestObjectiveRegistry e QuestRewardRegistry. Depois de registrados, eles são escritos no JSON das missões igual aos tipos embutidos. Veja Implementação e eventos e Criar um addon de DMZ.

Atualização dos arquivos padrão

Configurado em: Configuração do servidorgameplay.autoUpdateQuests

Todo arquivo de missão, saga e missão secundária gerado recebe um campo defaultsVersion (um texto semver, atualmente "2.2.0"). O QuestUpgrader cuida dele automaticamente e o usa para fazer uma mesclagem de três vias das suas edições com os padrões que vêm no DragonMineZ sempre que o conteúdo embutido muda:

  • Um campo que você nunca alterou recebe o novo padrão.
  • Um campo que você alterou mantém o seu valor. Se o DragonMineZ também o alterou, isso é registrado como conflito, e o seu valor continua valendo.
  • As chaves que você adicionou são mantidas.
  • Quando a mesclagem muda algo, o arquivo anterior é copiado antes para <world>/dragonminez/oldBackup/.

Um resumo é gravado em <world>/dragonminez/quest_update_report.txt e mostrado no relatório de carregamento do JSON. Desative o sistema inteiro com autoUpdateQuests: false. Você não precisa definir defaultsVersion no seu próprio conteúdo personalizado. Veja Recarregar e atualizar mudanças para saber como as atualizações são relatadas.

Chaves de tradução

Siga a convenção de nomes existente para que seu conteúdo combine com o resto da história:

  • Nome da saga: dmz.saga.<saga_id>
  • Título e descrição de uma missão de saga: dmz.quest.<key>.name / dmz.quest.<key>.desc
  • Título e descrição de uma missão secundária: dmz.sidequest.<key>.name / dmz.sidequest.<key>.desc

Adicione essas chaves ao seu en_us.json ou ao seu próprio arquivo de idioma. As traduções próprias se combinam com as do Crowdin da mesma forma que nas raças personalizadas.

Recarregar e validar

  1. Salve o JSON da sua saga, missão ou missão secundária na pasta dragonminez/ do mundo.
  2. Valide a sintaxe JSON (veja Como usar JSON).
  3. Execute /dmzreload story (ou /dmzreload all); veja Recarregar e atualizar mudanças.
  4. Abra o registro de missões no jogo e confira se sua saga, missão ou missão secundária aparece com os requisitos, objetivos e recompensas corretos.