Creando tu Primer Mod Completo¶
Este tutorial te guiará paso a paso en la creación de un mod completo para Hytale, combinando early plugins con contenido personalizado. Crearás bloques, items y entidades custom con sus propias interacciones.
Proyecto: "Mystic Ores"¶
Vamos a crear un mod que agrega:
- Bloque: Mystic Ore (mineral mágico)
- Item: Mystic Gem (gema obtenida del mineral)
- Item: Mystic Staff (bastón con poderes)
- Interacción: Spell Casting (lanzar hechizos)
- Sistema: Mana management
Paso 1: Estructura del Proyecto¶
Crear Directorios¶
mkdir mystic-ores-mod
cd mystic-ores-mod
# Estructura de código
mkdir -p src/main/java/com/ejemplo/mysticores
mkdir -p src/main/java/com/ejemplo/mysticores/interactions
mkdir -p src/main/java/com/ejemplo/mysticores/systems
mkdir -p src/main/resources/META-INF/services
# Estructura de assets
mkdir -p assets/blocks
mkdir -p assets/items
mkdir -p assets/interactions
mkdir -p assets/models
mkdir -p assets/textures
Estructura Final¶
mystic-ores-mod/
├── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── ejemplo/
│ │ └── mysticores/
│ │ ├── MysticOresPlugin.java
│ │ ├── MysticOres.java
│ │ ├── interactions/
│ │ │ ├── MineMysticOreInteraction.java
│ │ │ ├── CastSpellInteraction.java
│ │ │ └── ChargeManaInteraction.java
│ │ └── systems/
│ │ └── ManaSystem.java
│ └── resources/
│ └── META-INF/
│ └── services/
│ └── com.hypixel.hytale.plugin.early.ClassTransformer
├── assets/
│ ├── blocks/
│ │ └── mystic_ore.json
│ ├── items/
│ │ ├── mystic_gem.json
│ │ └── mystic_staff.json
│ ├── interactions/
│ │ ├── mine_mystic_ore.json
│ │ ├── cast_spell.json
│ │ └── charge_mana.json
│ ├── models/
│ │ ├── mystic_ore.vox
│ │ ├── mystic_gem.vox
│ │ └── mystic_staff.vox
│ └── textures/
│ ├── mystic_ore.png
│ ├── mystic_gem.png
│ └── mystic_staff.png
├── build.gradle
├── settings.gradle
├── mod.json
├── README.md
└── LICENSE
Paso 2: Configurar Build System¶
build.gradle¶
plugins {
id 'java'
}
group = 'com.ejemplo.mysticores'
version = '1.0.0'
sourceCompatibility = '17'
targetCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
// Hytale API (descompilada)
compileOnly files('libs/hytale-api.jar')
// ASM para transformación de bytecode
implementation 'org.ow2.asm:asm:9.6'
implementation 'org.ow2.asm:asm-commons:9.6'
implementation 'org.ow2.asm:asm-util:9.6'
// FastUtil (usado por Hytale)
compileOnly 'it.unimi.dsi:fastutil:8.5.12'
// Testing
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3'
}
jar {
archiveFileName = 'mystic-ores-${version}.jar'
manifest {
attributes(
'Implementation-Title': 'Mystic Ores',
'Implementation-Version': version,
'Mod-Id': 'mysticores',
'Mod-Name': 'Mystic Ores',
'Main-Class': 'com.ejemplo.mysticores.MysticOresPlugin'
)
}
// Incluir dependencias
from {
configurations.runtimeClasspath.collect {
it.isDirectory() ? it : zipTree(it)
}
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
tasks.named('test') {
useJUnitPlatform()
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
settings.gradle¶
mod.json¶
{
"id": "mysticores",
"name": "Mystic Ores",
"version": "1.0.0",
"description": "Adds magical ores and mystical items to Hytale",
"authors": ["Tu Nombre"],
"website": "https://ejemplo.com/mystic-ores",
"license": "MIT",
"icon": "assets/textures/icon.png",
"dependencies": {
"hytale": ">=1.0.0"
},
"earlyPlugin": {
"main": "com.ejemplo.mysticores.MysticOresPlugin",
"priority": 100
},
"assets": {
"blocks": "assets/blocks",
"items": "assets/items",
"interactions": "assets/interactions",
"models": "assets/models",
"textures": "assets/textures"
}
}
Paso 3: Early Plugin Base¶
MysticOresPlugin.java¶
package com.ejemplo.mysticores;
import com.hypixel.hytale.plugin.early.ClassTransformer;
import org.objectweb.asm.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Early plugin para Mystic Ores.
*
* Este plugin se carga antes del servidor y:
* - Inyecta inicialización del mod
* - Registra todos los componentes custom
*/
public class MysticOresPlugin implements ClassTransformer {
private static final String TARGET_CLASS =
"com.hypixel.hytale.server.core.HytaleServer";
@Override
public int priority() {
return 100;
}
@Override
@Nullable
public byte[] transform(@Nonnull String className,
@Nonnull String classPath,
@Nonnull byte[] bytecode) {
try {
if (className.equals(TARGET_CLASS)) {
System.out.println("[MysticOres] Initializing mod...");
return injectModInitialization(bytecode);
}
} catch (Exception e) {
System.err.println("[MysticOres] Error transforming " +
className + ": " + e.getMessage());
e.printStackTrace();
}
return null;
}
/**
* Inyecta llamada a MysticOres.initialize() en el constructor
* de HytaleServer.
*/
private byte[] injectModInitialization(byte[] bytecode) {
ClassReader reader = new ClassReader(bytecode);
ClassWriter writer = new ClassWriter(
reader,
ClassWriter.COMPUTE_FRAMES
);
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9, writer) {
@Override
public MethodVisitor visitMethod(int access, String name,
String descriptor, String signature,
String[] exceptions) {
MethodVisitor mv = super.visitMethod(
access, name, descriptor, signature, exceptions
);
// Interceptar constructor
if (name.equals("<init>")) {
return new ConstructorVisitor(Opcodes.ASM9, mv);
}
return mv;
}
};
reader.accept(visitor, 0);
return writer.toByteArray();
}
/**
* MethodVisitor que inyecta la inicialización del mod.
*/
private static class ConstructorVisitor extends MethodVisitor {
public ConstructorVisitor(int api, MethodVisitor mv) {
super(api, mv);
}
@Override
public void visitCode() {
super.visitCode();
// Inyectar: MysticOres.initialize();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"com/ejemplo/mysticores/MysticOres",
"initialize",
"()V",
false
);
}
}
}
Service Loader Registration¶
src/main/resources/META-INF/services/com.hypixel.hytale.plugin.early.ClassTransformer:
Paso 4: Mod Core¶
MysticOres.java¶
package com.ejemplo.mysticores;
import com.ejemplo.mysticores.interactions.*;
import com.ejemplo.mysticores.systems.ManaSystem;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import java.util.List;
/**
* Clase principal del mod Mystic Ores.
*
* Maneja el registro de todo el contenido custom.
*/
public class MysticOres {
public static final String MOD_ID = "mysticores";
public static final String MOD_NAME = "Mystic Ores";
public static final String VERSION = "1.0.0";
private static boolean initialized = false;
/**
* Inicializa el mod.
* Llamado desde MysticOresPlugin durante el inicio del servidor.
*/
public static void initialize() {
if (initialized) {
System.out.println("[MysticOres] Already initialized, skipping");
return;
}
System.out.println("========================================");
System.out.println(" " + MOD_NAME + " v" + VERSION);
System.out.println(" Initializing mod...");
System.out.println("========================================");
try {
// Inicializar sistemas
initializeSystems();
// Registrar interacciones
registerInteractions();
// Los bloques e items se cargan automáticamente desde assets/
initialized = true;
System.out.println("[MysticOres] Initialization complete!");
} catch (Exception e) {
System.err.println("[MysticOres] Error during initialization: " +
e.getMessage());
e.printStackTrace();
}
}
/**
* Inicializa sistemas del mod.
*/
private static void initializeSystems() {
System.out.println("[MysticOres] Initializing systems...");
// Inicializar sistema de mana
ManaSystem.initialize();
System.out.println("[MysticOres] Systems initialized");
}
/**
* Registra todas las interacciones custom.
*/
private static void registerInteractions() {
System.out.println("[MysticOres] Registering interactions...");
var store = Interaction.getAssetStore();
store.loadAssets(MOD_ID + ":" + MOD_ID, List.of(
new MineMysticOreInteraction(),
new CastSpellInteraction(),
new ChargeManaInteraction()
));
System.out.println("[MysticOres] Registered 3 interactions");
}
/**
* Verifica si el mod está inicializado.
*/
public static boolean isInitialized() {
return initialized;
}
}
Paso 5: Sistema de Mana¶
ManaSystem.java¶
package com.ejemplo.mysticores.systems;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.meta.MetaKey;
import com.hypixel.hytale.server.core.meta.MetaRegistry;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Sistema de gestión de mana para jugadores.
*
* Almacena y gestiona el mana de cada jugador usando el Meta System.
*/
public class ManaSystem {
// Registro de metadatos para jugadores
private static final MetaRegistry<Player> PLAYER_META_REGISTRY =
new MetaRegistry<>();
// Key para almacenar datos de mana
private static MetaKey<ManaData> MANA_KEY;
// Cache local para acceso rápido
private static final Map<Player, ManaData> MANA_CACHE =
new ConcurrentHashMap<>();
// Configuración
private static final int DEFAULT_MAX_MANA = 100;
private static final int DEFAULT_REGEN_RATE = 1; // por segundo
/**
* Inicializa el sistema de mana.
*/
public static void initialize() {
System.out.println("[ManaSystem] Initializing...");
// Registrar metadata key para mana
MANA_KEY = PLAYER_META_REGISTRY.registerMetaObject(
player -> new ManaData(DEFAULT_MAX_MANA),
true, // Persistente
"mysticores:mana",
ManaData.CODEC
);
System.out.println("[ManaSystem] Initialized");
}
/**
* Obtiene los datos de mana de un jugador.
*/
public static ManaData getMana(Player player) {
return MANA_CACHE.computeIfAbsent(player, p -> {
// Intentar cargar desde meta store
var metaStore = p.getMetaStore();
if (metaStore != null) {
ManaData data = metaStore.getMetaObject(MANA_KEY);
if (data != null) {
return data;
}
}
// Crear nuevo si no existe
return new ManaData(DEFAULT_MAX_MANA);
});
}
/**
* Consume mana de un jugador.
*
* @return true si había suficiente mana
*/
public static boolean consumeMana(Player player, int amount) {
ManaData mana = getMana(player);
if (mana.current < amount) {
return false;
}
mana.current -= amount;
saveMana(player, mana);
return true;
}
/**
* Restaura mana de un jugador.
*/
public static void restoreMana(Player player, int amount) {
ManaData mana = getMana(player);
mana.current = Math.min(mana.current + amount, mana.max);
saveMana(player, mana);
}
/**
* Establece el mana máximo de un jugador.
*/
public static void setMaxMana(Player player, int maxMana) {
ManaData mana = getMana(player);
mana.max = maxMana;
mana.current = Math.min(mana.current, maxMana);
saveMana(player, mana);
}
/**
* Guarda los datos de mana en el meta store.
*/
private static void saveMana(Player player, ManaData data) {
var metaStore = player.getMetaStore();
if (metaStore != null) {
metaStore.putMetaObject(MANA_KEY, data);
}
MANA_CACHE.put(player, data);
}
/**
* Limpia cache cuando un jugador se desconecta.
*/
public static void cleanupPlayer(Player player) {
MANA_CACHE.remove(player);
}
/**
* Datos de mana de un jugador.
*/
public static class ManaData {
public int current;
public int max;
public ManaData(int max) {
this.current = max;
this.max = max;
}
public ManaData(int current, int max) {
this.current = current;
this.max = max;
}
/**
* Codec para serialización.
*/
public static final Codec<ManaData> CODEC = Codec.INT.flatMap(
current -> Codec.INT.map(
max -> new ManaData(current, max)
)
);
public float getPercentage() {
return max == 0 ? 0 : (float) current / max;
}
@Override
public String toString() {
return current + "/" + max + " (" +
String.format("%.1f%%", getPercentage() * 100) + ")";
}
}
}
Paso 6: Interacciones Custom¶
MineMysticOreInteraction.java¶
package com.ejemplo.mysticores.interactions;
import com.hypixel.hytale.protocol.InteractionState;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
/**
* Interacción para minar Mystic Ore.
*
* Otorga Mystic Gems cuando se mina el bloque.
*/
public class MineMysticOreInteraction extends SimpleInstantInteraction {
public MineMysticOreInteraction() {
this.id = "mysticores:mine_mystic_ore";
this.runTime = 3.0f; // 3 segundos para minar
}
@Override
protected void firstRun(InteractionType type,
InteractionContext context,
CooldownHandler cooldownHandler) {
var entityRef = context.getEntity();
var buffer = context.getCommandBuffer();
var entity = buffer.getEntity(entityRef);
if (!(entity instanceof Player)) {
context.getState().state = InteractionState.Failed;
return;
}
Player player = (Player) entity;
// Calcular gemas obtenidas (1-3 aleatorio)
int gemCount = 1 + (int) (Math.random() * 3);
// Agregar gemas al inventario
// (Simplificado - en producción usar ItemStack y addToInventory)
player.sendMessage("§a¡Obtuviste " + gemCount + " Mystic Gems!");
// Efecto de partículas (ejemplo)
// spawnParticles(player.getPosition(), "mysticores:mining_sparkle");
context.getState().state = InteractionState.Finished;
}
}
CastSpellInteraction.java¶
package com.ejemplo.mysticores.interactions;
import com.ejemplo.mysticores.systems.ManaSystem;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.protocol.InteractionState;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
/**
* Interacción para lanzar hechizos con el Mystic Staff.
*
* Consume mana y lanza un proyectil mágico.
*/
public class CastSpellInteraction extends SimpleInstantInteraction {
private static final int MANA_COST = 20;
private static final float SPELL_DAMAGE = 15.0f;
public CastSpellInteraction() {
this.id = "mysticores:cast_spell";
this.runTime = 0.5f; // Animación de casting
}
@Override
protected void firstRun(InteractionType type,
InteractionContext context,
CooldownHandler cooldownHandler) {
var entityRef = context.getEntity();
var buffer = context.getCommandBuffer();
var entity = buffer.getEntity(entityRef);
if (!(entity instanceof Player)) {
context.getState().state = InteractionState.Failed;
return;
}
Player player = (Player) entity;
// Verificar mana
if (!ManaSystem.consumeMana(player, MANA_COST)) {
player.sendMessage("§c¡No tienes suficiente mana!");
context.getState().state = InteractionState.Failed;
return;
}
// Lanzar proyectil
launchSpellProjectile(player);
// Mostrar mana restante
var mana = ManaSystem.getMana(player);
player.sendMessage("§b✦ Mana: " + mana.toString());
context.getState().state = InteractionState.Finished;
}
/**
* Lanza un proyectil mágico en la dirección del jugador.
*/
private void launchSpellProjectile(Player player) {
Vector3d position = player.getPosition();
Vector3d direction = player.getLookDirection();
// Crear proyectil
// (Simplificado - en producción crear entidad de proyectil)
System.out.println("[CastSpell] Launching spell from " + position +
" in direction " + direction);
// Efectos visuales
// spawnParticles(position, "mysticores:spell_trail");
// playSound(position, "mysticores:spell_cast");
// En implementación real:
// - Crear entidad de proyectil
// - Aplicar física y colisiones
// - Detectar impactos
// - Aplicar daño
}
}
ChargeManaInteraction.java¶
package com.ejemplo.mysticores.interactions;
import com.ejemplo.mysticores.systems.ManaSystem;
import com.hypixel.hytale.protocol.InteractionState;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
/**
* Interacción para recargar mana.
*
* El jugador medita para restaurar mana.
*/
public class ChargeManaInteraction extends SimpleInstantInteraction {
private static final int MANA_RESTORE = 50;
public ChargeManaInteraction() {
this.id = "mysticores:charge_mana";
this.runTime = 2.0f; // 2 segundos de meditación
}
@Override
protected void firstRun(InteractionType type,
InteractionContext context,
CooldownHandler cooldownHandler) {
var entityRef = context.getEntity();
var buffer = context.getCommandBuffer();
var entity = buffer.getEntity(entityRef);
if (!(entity instanceof Player)) {
context.getState().state = InteractionState.Failed;
return;
}
Player player = (Player) entity;
// Restaurar mana
ManaSystem.restoreMana(player, MANA_RESTORE);
// Mostrar mana actualizado
var mana = ManaSystem.getMana(player);
player.sendMessage("§b✦ Mana restaurado: " + mana.toString());
// Efectos visuales
// spawnParticles(player.getPosition(), "mysticores:mana_charge");
// playSound(player.getPosition(), "mysticores:charge_complete");
context.getState().state = InteractionState.Finished;
}
}
Paso 7: Definiciones de Assets¶
assets/blocks/mystic_ore.json¶
{
"id": "mysticores:mystic_ore",
"name": "Mystic Ore",
"type": "Ore",
"hardness": 4.0,
"resistance": 3.0,
"model": "mysticores:models/mystic_ore",
"texture": "mysticores:textures/mystic_ore",
"drops": [
{
"item": "mysticores:mystic_gem",
"count": {
"min": 1,
"max": 3
},
"chance": 1.0
}
],
"interactions": {
"Mine": "mysticores:mine_mystic_ore"
},
"light": {
"emission": 5
},
"sounds": {
"break": "hytale:block.stone.break",
"place": "hytale:block.stone.place",
"step": "hytale:block.stone.step"
},
"metadata": {
"rarity": "Rare",
"biomes": ["Mountains", "Underground"],
"minDepth": 16,
"maxDepth": 64
}
}
assets/items/mystic_gem.json¶
{
"id": "mysticores:mystic_gem",
"name": "Mystic Gem",
"type": "Material",
"maxStackSize": 64,
"model": "mysticores:models/mystic_gem",
"texture": "mysticores:textures/mystic_gem",
"rarity": "Rare",
"metadata": {
"description": "A mysterious gem pulsing with magical energy",
"glowing": true
}
}
assets/items/mystic_staff.json¶
{
"id": "mysticores:mystic_staff",
"name": "Mystic Staff",
"type": "Tool",
"maxStackSize": 1,
"durability": 500,
"model": "mysticores:models/mystic_staff",
"texture": "mysticores:textures/mystic_staff",
"interactions": {
"RightClick": "mysticores:cast_spell",
"Sneak+RightClick": "mysticores:charge_mana"
},
"damage": 5,
"attackSpeed": 1.2,
"rarity": "Epic",
"crafting": {
"recipe": [
["", "", "mysticores:mystic_gem"],
["", "hytale:stick", ""],
["hytale:stick", "", ""]
]
},
"metadata": {
"description": "A staff infused with mystical power",
"manaCost": 20,
"spellDamage": 15
}
}
Paso 8: Compilar y Empaquetar¶
Compilar el Mod¶
# Limpiar builds anteriores
./gradlew clean
# Compilar
./gradlew build
# El JAR estará en: build/libs/mystic-ores-1.0.0.jar
Estructura del JAR¶
mystic-ores-1.0.0.jar
├── com/
│ └── ejemplo/
│ └── mysticores/
│ ├── MysticOresPlugin.class
│ ├── MysticOres.class
│ ├── interactions/
│ └── systems/
├── META-INF/
│ ├── services/
│ │ └── com.hypixel.hytale.plugin.early.ClassTransformer
│ └── MANIFEST.MF
└── (dependencias incluidas)
Empaquetar con Assets¶
# Crear directorio de distribución
mkdir -p dist/mystic-ores
# Copiar JAR del plugin
cp build/libs/mystic-ores-1.0.0.jar dist/mystic-ores/
# Copiar assets
cp -r assets dist/mystic-ores/
# Copiar metadata
cp mod.json dist/mystic-ores/
cp README.md dist/mystic-ores/
cp LICENSE dist/mystic-ores/
# Crear archivo ZIP para distribución
cd dist
zip -r mystic-ores-1.0.0.zip mystic-ores/
Paso 9: Instalación y Pruebas¶
Instalar en Servidor¶
# Estructura del servidor
servidor/
├── earlyplugins/
│ └── mystic-ores-1.0.0.jar
├── mods/
│ └── mystic-ores/
│ ├── assets/
│ └── mod.json
└── hytale-server.jar
# Copiar early plugin
cp mystic-ores-1.0.0.jar /ruta/servidor/earlyplugins/
# Copiar assets y metadata
cp -r assets /ruta/servidor/mods/mystic-ores/
cp mod.json /ruta/servidor/mods/mystic-ores/
Ejecutar Servidor¶
Salida esperada:
[EarlyPlugin] Found: mystic-ores-1.0.0.jar
[EarlyPlugin] Loading transformer: com.ejemplo.mysticores.MysticOresPlugin (priority=100)
[MysticOres] Initializing mod...
========================================
Mystic Ores v1.0.0
Initializing mod...
========================================
[MysticOres] Initializing systems...
[ManaSystem] Initializing...
[ManaSystem] Initialized
[MysticOres] Systems initialized
[MysticOres] Registering interactions...
[MysticOres] Registered 3 interactions
[MysticOres] Initialization complete!
INFO - HytaleServer starting...
Probar en el Juego¶
- Encontrar Mystic Ore
- Generar o colocar bloque de Mystic Ore
-
Minar para obtener Mystic Gems
-
Craftear Mystic Staff
- Usar receta definida en el JSON
-
Obtener Mystic Staff
-
Lanzar Hechizos
- Click derecho con el staff
- Verificar consumo de mana
-
Ver efectos del hechizo
-
Recargar Mana
- Shift + Click derecho
- Verificar que el mana se restaura
Paso 10: Debugging y Optimización¶
Logging Detallado¶
// Agregar modo debug
private static final boolean DEBUG = Boolean.getBoolean("mysticores.debug");
public static void debug(String message) {
if (DEBUG) {
System.out.println("[MysticOres-DEBUG] " + message);
}
}
Pruebas Unitarias¶
package com.ejemplo.mysticores;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
public class ManaSystemTest {
@Test
public void testManaConsumption() {
// Crear jugador mock
Player player = createMockPlayer();
// Inicializar con 100 mana
ManaSystem.setMaxMana(player, 100);
// Consumir 20 mana
boolean success = ManaSystem.consumeMana(player, 20);
assertTrue(success);
assertEquals(80, ManaSystem.getMana(player).current);
}
@Test
public void testInsufficientMana() {
Player player = createMockPlayer();
ManaSystem.setMaxMana(player, 100);
// Intentar consumir más mana del disponible
boolean success = ManaSystem.consumeMana(player, 150);
assertFalse(success);
assertEquals(100, ManaSystem.getMana(player).current);
}
}
Mejoras Futuras¶
Features Adicionales¶
- Múltiples Hechizos
- Fire Spell
- Ice Spell
-
Lightning Spell
-
Regeneración Pasiva de Mana
- Tick system para regen automático
-
Configuración de velocidad de regen
-
UI de Mana
- Barra de mana en HUD
-
Indicadores visuales
-
Sistema de Niveles
- Aumentar mana máxima con niveles
-
Desbloquear hechizos más poderosos
-
Efectos Visuales Avanzados
- Partículas custom
- Shaders
- Animaciones
Distribución del Mod¶
README.md¶
# Mystic Ores
Un mod que agrega minerales mágicos y objetos místicos a Hytale.
## Features
- **Mystic Ore**: Mineral mágico que se encuentra en las profundidades
- **Mystic Gem**: Gema obtenida al minar Mystic Ore
- **Mystic Staff**: Bastón mágico que lanza hechizos
- **Sistema de Mana**: Gestión de energía mágica
## Instalación
1. Descarga `mystic-ores-1.0.0.zip`
2. Extrae el contenido
3. Copia `mystic-ores-1.0.0.jar` a `earlyplugins/`
4. Copia la carpeta `assets/` a `mods/mystic-ores/`
5. Inicia el servidor con `--accept-early-plugins`
## Uso
### Obtener Mystic Gems
Mina bloques de Mystic Ore para obtener 1-3 Mystic Gems.
### Craftear Mystic Staff
G = Mystic Gem S = Stick
### Lanzar Hechizos
- Click derecho: Lanzar hechizo (cuesta 20 mana)
- Shift + Click derecho: Recargar mana (+50)
## Configuración
Edita `mods/mystic-ores/config.properties`:
```properties
mana.default-max=100
mana.regen-rate=1
spell.damage=15
spell.cost=20
Desarrollo¶
Ver CONTRIBUTING.md
Licencia¶
MIT License - Ver LICENSE ```
Próximos Pasos¶
- Interaction System: Profundiza en interacciones avanzadas
- Meta System Usage: Aprende a usar metadata persistente
- Best Practices: Mejores prácticas para mods
¿Problemas? Consulta Troubleshooting