WhatsApp Discord
Rune effect Main 5.2 - Source Mu - Mu Server Files
 

Noticias:

SMF - Just Installed!

Menú principal

Rune effect Main 5.2

Publicado por Dakosmu, Mar 04, 2026, 07:27 AM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Dakosmu

Rune effect Main 5.2

Regístrate para ver el enlace

Rune Effect correctamente integrado Esta versión aplica el efecto clásico con los destellos de luz y la aurora a todos los jugadores por igual.


void RenderCharactersClient()
{
    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        // --- Bloque de Seguridad y GM Invisible ---
        if (c != Hero && battleCastle::IsBattleCastleStart() == true && g_isCharacterBuff(o, eBuff_Cloaking))
        {
            // ... (omitido por espacio, mantén tu código de marcas de Battle Castle aquí)
        }
        if (c == Hero && (0x04 & Hero->CtlCode) && SceneFlag == MAIN_SCENE)
        {
            o->OBB.StartPos[0] = 1000.0000;
            o->OBB.XAxis[0] = 1.00000;
            o->OBB.XAxis[1] = 1.00000;
            o->OBB.XAxis[2] = 1.00000;
            continue;
        }

        // --- RUNE EFFECT FIX (ESTANDAR PARA TODOS) ---
        if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            if (!g_isCharacterBuff(o, eBuff_Cloaking))
            {
                vec3_t vLight, vPos;
                VectorCopy(o->Position, vPos);
                vPos[2] += 100.f;

                Vector(0.4f, 0.6f, 0.8f, vLight); // Color Celeste Base
                CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);

                float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
                Vector(fLumi * 0.3f, fLumi * 0.5f, fLumi * 0.8f, vLight);
                CreateSprite(BITMAP_LIGHT, vPos, 2.0f, vLight, o);

                Vector(0.3f, 0.2f, 1.f, vLight);
                RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.5f, 2.5f, vLight);

                Vector(1.0f, 1.0f, 1.f, vLight);
                fLumi = sinf(WorldTime * 0.0015f) * 0.3f + 0.5f;
                EnableAlphaBlend();
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vLight);
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 2.0f, 2.0f, vLight, WorldTime * 0.01f);
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 1.f, 1.f, vLight, -WorldTime * 0.01f);
                RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.5f, 1.5f, vLight);
                DisableAlphaBlend();
            }
        }

        if (o->Live)
        {
            if (o->Visible)
            {
                RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
                if (o->Type == MODEL_PLAYER)
                    battleCastle::CreateBattleCastleCharacter_Visual(c, o);
            }
        }
    }
    if (gMapManager->InBattleCastle() || World == WD_31HUNTING_GROUND)
        battleCastle::InitEtcSetting();
}




Ajuste de velocidad y tamaño
Si sientes que la runa gira muy rápido o es muy grande, ajusta estos valores en la línea de RenderTerrainAlphaBitmap:

Tamaño: Cambia 2.2f, 2.2f por números más pequeños (ej. 1.8f) para achicarla.

Velocidad de giro: Cambia WorldTime * 0.005f. Si pones 0.01f girará más rápido, si pones 0.002f será más lento.
Bon Dia

Dakosmu

runa cambie de color según la raza

void RenderCharactersClient()
{
for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
{
CHARACTER* c = gmCharacters->GetCharacter(i);
OBJECT* o = &c->Object;

// ... (Mantén aquí todo tu código anterior de Battle Castle y GM Invisible) ...
if (c == Hero && (0x04 & Hero->CtlCode) && SceneFlag == MAIN_SCENE)
{
o->OBB.StartPos[0] = 1000.0000;
o->OBB.XAxis[0] = 1.00000;
o->OBB.XAxis[1] = 1.00000;
o->OBB.XAxis[2] = 1.00000;
continue;
}

// --- INICIO RUNE EFFECT POR CLASES ---
if (o->Kind == 1 && SceneFlag == MAIN_SCENE)
{
if (!g_isCharacterBuff(o, eBuff_Cloaking))
{
vec3_t vLight, vPos;
VectorCopy(o->Position, vPos);

// Definir color según la clase (R, G, B)
switch (c->Class)
{
case CLASS_WIZARD:
Vector(0.2f, 0.2f, 1.0f, vLight); // Azul - Mago
break;
case CLASS_KNIGHT:
Vector(1.0f, 0.2f, 0.2f, vLight); // Rojo - Guerrero
break;
case CLASS_ELF:
Vector(0.2f, 1.0f, 0.2f, vLight); // Verde - Elfa
break;
case CLASS_MAGUMSA:
Vector(0.8f, 0.2f, 1.0f, vLight); // Morado - MG
break;
case CLASS_DARKLORD:
Vector(1.0f, 0.8f, 0.2f, vLight); // Dorado/Naranja - DL
break;
case CLASS_SUMMONER:
Vector(1.0f, 0.2f, 0.6f, vLight); // Rosado - Summoner
break;
default:
Vector(1.0f, 1.0f, 1.0f, vLight); // Blanco - Otros
break;
}

// Efecto de luz central (Sprite)
vPos[2] += 50.f;
CreateSprite(BITMAP_LIGHT, vPos, 3.0f, vLight, o);

// Renderizado de la Runa en el suelo con el color de la clase
float fLumi = sinf(WorldTime * 0.0015f) * 0.3f + 0.6f;
vec3_t vFinalColor;
Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalColor);

EnableAlphaBlend();
RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 2.2f, 2.2f, vFinalColor, WorldTime * 0.005f);
DisableAlphaBlend();
}
}
// --- FIN RUNE EFFECT POR CLASES ---

if (o->Live)
{
if (o->Visible)
{
RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
if (o->Type == MODEL_PLAYER)
battleCastle::CreateBattleCastleCharacter_Visual(c, o);
}
}
}

if (gMapManager->InBattleCastle() || World == WD_31HUNTING_GROUND)
{
battleCastle::InitEtcSetting();
}
}


Detalles técnicos de los colores:
Mago: Azul intenso.

Guerrero: Rojo fuego.

Elfa: Verde bosque.

MG: Púrpura/Morado.

DL: Dorado/Amarillo brillante.

Summoner: Rosa/Fucsia.

¿Qué pasa si no reconoce CLASS_WIZARD? Si el compilador te dice que CLASS_WIZARD no está definido, puedes usar los números directamente en el switch:

case 0: (Wizard)

case 1: (Knight)

case 2: (Elf)

case 3: (Magic Gladiator)

case 4: (Dark Lord)

case 5: (Summoner)
Bon Dia

Dakosmu

Versión 2: Runa por Niveles (Solo nivel 300+)
Ideal si quieres que el efecto sea un "premio" por subir de nivel

void RenderCharactersClient()
{
for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
{
CHARACTER* c = gmCharacters->GetCharacter(i);
OBJECT* o = &c->Object;

// ... (Código de GM Invisible igual al anterior) ...

// --- RUNE EFFECT FIX (SOLO NIVEL 300+) ---
if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live && c->Level > 300)
{
if (!g_isCharacterBuff(o, eBuff_Cloaking))
{
// ... (Mismo contenido visual que la Versión 1) ...
vec3_t vLight, vPos;
VectorCopy(o->Position, vPos);
vPos[2] += 100.f;
Vector(0.4f, 0.6f, 0.8f, vLight);
CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);
float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
Vector(fLumi * 0.3f, fLumi * 0.5f, fLumi * 0.8f, vLight);
CreateSprite(BITMAP_LIGHT, vPos, 2.0f, vLight, o);
Vector(0.3f, 0.2f, 1.f, vLight);
RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.5f, 2.5f, vLight);
Vector(1.0f, 1.0f, 1.f, vLight);
fLumi = sinf(WorldTime * 0.0015f) * 0.3f + 0.5f;
EnableAlphaBlend();
Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vLight);
RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 2.0f, 2.0f, vLight, WorldTime * 0.01f);
RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 1.f, 1.f, vLight, -WorldTime * 0.01f);
RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.5f, 1.5f, vLight);
DisableAlphaBlend();
}
}

if (o->Live) { /* ... resto del código igual ... */ }
}
}
Bon Dia

Dakosmu

#3
Versión 3: Runa Evolucionada (Colores por Clase)
Esta es la más recomendada. Usa el FIX y además cambia el color según si es BK, SM, ELF, etc.

Regístrate para ver el enlace
Regístrate para ver el enlace
Regístrate para ver el enlace


void RenderCharactersClient()
{
    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        // ... (Omitido código de Battle Castle/GM para claridad) ...

        // --- RUNE EFFECT FIX + COLORES POR CLASE ---
        if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            if (!g_isCharacterBuff(o, eBuff_Cloaking))
            {
                vec3_t vLight, vPos, vFinalLumi;
                VectorCopy(o->Position, vPos);

                // Selector de colores por Clase
                switch (c->Class) {
                    case 0: Vector(0.2f, 0.4f, 1.0f, vLight); break; // Wizard - Azul
                    case 1: Vector(1.0f, 0.2f, 0.2f, vLight); break; // Knight - Rojo
                    case 2: Vector(0.2f, 1.0f, 0.4f, vLight); break; // Elf - Verde
                    case 3: Vector(0.7f, 0.2f, 1.0f, vLight); break; // MG - Morado
                    case 4: Vector(1.0f, 0.7f, 0.1f, vLight); break; // DL - Dorado
                    case 5: Vector(1.0f, 0.2f, 0.6f, vLight); break; // SU - Rosa
                    default: Vector(1.0f, 1.0f, 1.0f, vLight); break;
                }

                vPos[2] += 100.f;
                CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);

                float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                CreateSprite(BITMAP_LIGHT, vPos, 2.0f, vFinalLumi, o);

                RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.5f, 2.5f, vFinalLumi);

                fLumi = sinf(WorldTime * 0.0015f) * 0.3f + 0.5f;
                EnableAlphaBlend();
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 2.0f, 2.0f, vFinalLumi, WorldTime * 0.01f);
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 1.f, 1.f, vFinalLumi, -WorldTime * 0.01f);
                RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.5f, 1.5f, vFinalLumi);
                DisableAlphaBlend();
            }
        }

        if (o->Live)
        {
            if (o->Visible)
            {
                RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
                if (o->Type == MODEL_PLAYER)
                    battleCastle::CreateBattleCastleCharacter_Visual(c, o);
            }
        }
    }
}
Bon Dia

Dakosmu

Efecto de Partículas Ascendentes.

Regístrate para ver el enlace

¿Qué añadimos?
Partículas de Clase: He añadido CreateParticle, que genera chispas que suben desde el suelo.

Diferenciación de Partículas: El Mago suelta burbujas/energía, el Guerrero chispas de fuego, la Elfa destellos verdes, etc.

void RenderCharactersClient()
{
    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        // ... (Omitido código de Battle Castle/GM) ...

        // --- RUNE EFFECT DINÁMICO + PARTÍCULAS ---
        if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            if (!g_isCharacterBuff(o, eBuff_Cloaking))
            {
                vec3_t vLight, vPos, vFinalLumi;
                VectorCopy(o->Position, vPos);

                int ParticleType = BITMAP_TRUE_FIRE; // Tipo de partícula por defecto

                switch (c->Class) {
                    case 0: // Wizard
                        Vector(0.2f, 0.4f, 1.0f, vLight);
                        ParticleType = BITMAP_SMOKE; // Energía azulada
                        break;
                    case 1: // Knight
                        Vector(1.0f, 0.2f, 0.2f, vLight);
                        ParticleType = BITMAP_TRUE_FIRE; // Fuego rojo
                        break;
                    case 2: // Elf
                        Vector(0.2f, 1.0f, 0.4f, vLight);
                        ParticleType = BITMAP_SPARK + 1; // Destellos verdes
                        break;
                    case 3: // MG
                        Vector(0.7f, 0.2f, 1.0f, vLight);
                        ParticleType = BITMAP_LIGHT; // Brillo púrpura
                        break;
                    case 4: // DL
                        Vector(1.0f, 0.7f, 0.1f, vLight);
                        ParticleType = BITMAP_FIRE; // Fuego dorado
                        break;
                    case 5: // SU
                        Vector(1.0f, 0.2f, 0.6f, vLight);
                        ParticleType = BITMAP_SHINY + 1; // Brillos rosas
                        break;
                    default:
                        Vector(1.0f, 1.0f, 1.0f, vLight);
                        break;
                }

                // --- GENERACIÓN DE PARTÍCULAS (DINÁMICA) ---
                // Crea una pequeña chispa que sube cada ciertos frames
                if (rand() % 10 == 0) {
                    vec3_t vPartPos;
                    VectorCopy(o->Position, vPartPos);
                    vPartPos[0] += (rand() % 40 - 20); // Dispersión X
                    vPartPos[1] += (rand() % 40 - 20); // Dispersión Y
                    CreateParticle(ParticleType, vPartPos, o->Angle, vLight, 1, 0.5f, o);
                }

                // --- RENDERIZADO VISUAL (Igual a tu versión 3) ---
                vPos[2] += 100.f;
                CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);

                float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                CreateSprite(BITMAP_LIGHT, vPos, 2.0f, vFinalLumi, o);

                RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.5f, 2.5f, vFinalLumi);

                fLumi = sinf(WorldTime * 0.0015f) * 0.3f + 0.5f;
                EnableAlphaBlend();
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 2.0f, 2.0f, vFinalLumi, WorldTime * 0.01f);
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 1.0f, 1.0f, vFinalLumi, -WorldTime * 0.01f);
                RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.5f, 1.5f, vFinalLumi);
                DisableAlphaBlend();
            }
        }

        if (o->Live)
        {
            if (o->Visible)
            {
                RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
                if (o->Type == MODEL_PLAYER)
                    battleCastle::CreateBattleCastleCharacter_Visual(c, o);
            }
        }
    }

    if (gMapManager->InBattleCastle() || World == WD_31HUNTING_GROUND)
        battleCastle::InitEtcSetting();
}



Movimiento Vertical: La runa en el suelo es estática, pero las partículas suben, lo que da una sensación de poder real.

Variedad: El MG no suelta lo mismo que el Knight; cada uno tiene su propio estilo de "aura".

Rendimiento: El código if (rand() % 10 == 0) hace que las partículas no se saturen, manteniendo los FPS estables.

Bon Dia

Dakosmu

#5
Guía completa para implementar el sistema Rune.txt



1. Crear el archivo Data\Rune.txt Crea un archivo de texto en tu cliente con esta estructura:

Citar// Clase | Red | Green | Blue | Tamaño | Velocidad | Particula
// -----------------------------------------------------------
0  0.2  0.4  1.0  2.2  0.01  0  // Wizard
1  1.0  0.2  0.2  2.2  0.01  0  // Knight
2  0.2  1.0  0.4  2.2  0.01  0  // Elf
3  0.7  0.2  1.0  2.2  0.01  0  // MG
4  1.0  0.7  0.1  2.2  0.01  0  // DL
5  1.0  0.2  0.6  2.2  0.01  0  // SU

Definir la estructura en el Código
Primero, en tu archivo de cabecera (puede ser Interface.h o arriba de tu función), define dónde se guardarán estos datos:

struct RUNE_DATA { float Red, Green, Blue; float Size; float Speed; int Particle; };

RUNE_DATA g_RuneConfig[6]; // Un slot para cada clase

Crear el Lector del Archivo
Debes crear una función para leer ese archivo al iniciar el juego. Pon esto en tu archivo de utilidades o antes del RenderCharactersClient:

void LoadRuneSettings() { FILE* fp = fopen("Data\Rune.txt", "r"); if (!fp) return;

char line[256];
while (fgets(line, sizeof(line), fp)) {
    if (line[0] == '/' || line[0] == '\n') continue; // Saltar comentarios

    int Class, Part;
    float R, G, B, Sz, Sp;
    if (sscanf(line, "%d %f %f %f %f %f %d", &Class, &R, &G, &B, &Sz, &Sp, &Part) == 7) {
        if (Class >= 0 && Class < 6) {
            g_RuneConfig[Class].Red = R;
            g_RuneConfig[Class].Green = G;
            g_RuneConfig[Class].Blue = B;
            g_RuneConfig[Class].Size = Sz;
            g_RuneConfig[Class].Speed = Sp;
            g_RuneConfig[Class].Particle = Part;
        }
    }
}
fclose(fp);
}

Nota: Llama a LoadRuneSettings() dentro de la función Init() de tu Main para que cargue al abrir el juego.

Modificar RenderCharactersClient (Versión Dinámica)
Ahora, en lugar de un switch gigante, el código simplemente leerá los datos que cargamos del archivo:

void RenderCharactersClient() { for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i) { CHARACTER* c = gmCharacters->GetCharacter(i); OBJECT* o = &c->Object;

    // --- RUNE EFFECT DINÁMICO DESDE TXT ---
    if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
    {
        if (!g_isCharacterBuff(o, eBuff_Cloaking))
        {
            int cls = c->Class;
            if (cls < 0 || cls > 5) cls = 0; // Seguridad

            vec3_t vLight, vPos, vFinalLumi;
            VectorCopy(o->Position, vPos);

            // Usamos los valores del archivo Rune.txt
            Vector(g_RuneConfig[cls].Red, g_RuneConfig[cls].Green, g_RuneConfig[cls].Blue, vLight);

            // Partículas dinámicas según el archivo
            if (rand() % 10 == 0) {
                vec3_t vPartPos; VectorCopy(o->Position, vPartPos);
                vPartPos[0] += (rand() % 40 - 20);
                vPartPos[1] += (rand() % 40 - 20);
                CreateParticle(g_RuneConfig[cls].Particle, vPartPos, o->Angle, vLight, 1, 0.5f, o);
            }

            // Renderizado usando Size y Speed del archivo
            vPos[2] += 100.f;
            CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);

            float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
            Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
           
            EnableAlphaBlend();
            RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1],
                g_RuneConfig[cls].Size, g_RuneConfig[cls].Size,
                vFinalLumi, WorldTime * g_RuneConfig[cls].Speed);
           
            RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1],
                1.0f, 1.0f, vFinalLumi, -WorldTime * g_RuneConfig[cls].Speed);
           
            DisableAlphaBlend();
        }
    }
    // ... (Resto del código original)
}
}


Buscar void CreateWebzenScene()

void CreateWebzenScene()
{
    CUIMng& rUIMng = CUIMng::Instance();

    OpenFont();
    LoadRuneSettings(); // <-- Dakosmu
    ClearInput();


¿Qué beneficios tiene este cambio?

  • Flexibilidad Total: Si quieres que el BK tenga una runa gigante y lenta, solo cambias el 2.2 por 4.0 y el 0.01 por 0.001 en el archivo de texto.
  • Sin Compilar: Puedes enviarle a tus jugadores una actualización del Rune.txt sin necesidad de mandarles un nuevo ejecutable.
  • Orden: Tu código queda mucho más limpio y fácil de mantener.
Bon Dia

Dakosmu

#6
Guía: Sistema Rune Dinámico Expandido (Textura y Partícula)

Regístrate para ver el enlace
Regístrate para ver el enlace
Regístrate para ver el enlace

Para que el sistema sea realmente dinámico, modificamos la estructura para que lea el ID de la textura y el ID de la partícula directamente desde el archivo. Así podrás asignar formas únicas a cada clase.

Paso 1: Actualizar la estructura en el archivo Cabecera (.h)
Necesitamos agregar nuevas variables para guardar la textura y la partícula. Reemplaza tu bloque de estructura anterior por este:

// --- SISTEMA DE RUNE DINAMICO EXPANDIDO --- struct RUNE_DATA { float Red, Green, Blue; float Size; float Speed; int TextureID; // ID de la imagen (forma) int ParticleID; // ID del efecto de particula float Rotation; // Direccion de giro (1.0 horario, -1.0 antihorario) };

extern RUNE_DATA g_RuneConfig[6];

void LoadRuneSettings();

Paso 2: Actualizar la función de carga (.cpp)
Ahora el sscanf leerá más columnas de datos. Reemplaza tu función LoadRuneSettings por esta:

RUNE_DATA g_RuneConfig[6];

void LoadRuneSettings() { // Valores por defecto seguros for (int i = 0; i < 6; i++) { g_RuneConfig[i].Red = 1.0f; g_RuneConfig[i].Green = 1.0f; g_RuneConfig[i].Blue = 1.0f; g_RuneConfig[i].Size = 2.0f; g_RuneConfig[i].Speed = 0.01f; g_RuneConfig[i].TextureID = 32002; // ID por defecto (BITMAP_GM_AURORA) g_RuneConfig[i].ParticleID = 0; g_RuneConfig[i].Rotation = 1.0f; }

FILE* fp = fopen("Data\\Rune.txt", "r");
if (!fp) return;

char line[256];
while (fgets(line, sizeof(line), fp)) {
    if (line[0] == '/' || line[0] == '\n' || line[0] == '\r') continue;

    int Class, Tex, Part;
    float R, G, B, Sz, Sp, Rot;
    // Formato: Clase R G B Size Speed Texture Particle Rotation
    if (sscanf(line, "%d %f %f %f %f %f %d %d %f", &Class, &R, &G, &B, &Sz, &Sp, &Tex, &Part, &Rot) == 9) {
        if (Class >= 0 && Class < 6) {
            g_RuneConfig[Class].Red = R;
            g_RuneConfig[Class].Green = G;
            g_RuneConfig[Class].Blue = B;
            g_RuneConfig[Class].Size = Sz;
            g_RuneConfig[Class].Speed = Sp;
            g_RuneConfig[Class].TextureID = Tex;
            g_RuneConfig[Class].ParticleID = Part;
            g_RuneConfig[Class].Rotation = Rot;
        }
    }
}
fclose(fp);
}

Paso 3: Actualizar la función principal (.cpp)
Modificamos el renderizado para que use la textura y la partícula dinámicas. Reemplaza RenderCharactersClient completo:

void RenderCharactersClient() { for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i) { CHARACTER* c = gmCharacters->GetCharacter(i); OBJECT* o = &c->Object;

    if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
    {
        if (!g_isCharacterBuff(o, eBuff_Cloaking))
        {
            int cls = (c->Class >= 0 && c->Class < 6) ? c->Class : 0;
            RUNE_DATA* config = &g_RuneConfig[cls]; // Acceso rapido a la config

            vec3_t vLight, vPos, vFinalLumi;
            VectorCopy(o->Position, vPos);
            Vector(config->Red, config->Green, config->Blue, vLight);

            // --- Particulas dinámicas desde TXT ---
            if (config->ParticleID > 0 && rand() % 15 == 0) {
                vec3_t vPartPos; VectorCopy(o->Position, vPartPos);
                vPartPos[0] += (rand() % 40 - 20);
                vPartPos[1] += (rand() % 40 - 20);
                CreateParticle(config->ParticleID, vPartPos, o->Angle, vLight, 1, 0.5f, o);
            }

            vPos[2] += 100.f;
            CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);

            float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
            Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);

            EnableAlphaBlend();

            // Runa principal usando TextureID y Rotation del TXT
            RenderTerrainAlphaBitmap(config->TextureID, o->Position[0], o->Position[1],
                   config->Size, config->Size,
                   vFinalLumi, (WorldTime * config->Speed) * config->Rotation);

            // Renderizamos una segunda capa opcional (efecto brillo)
            RenderAurora(config->TextureID, RENDER_BRIGHT, o->Position[0], o->Position[1],
                   config->Size * 0.7f, config->Size * 0.7f, vFinalLumi);

            DisableAlphaBlend();
        }
    }

    if (o->Live && o->Visible) {
        RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
    }
}
}

Paso 4: Configurar el nuevo Rune.txt
Ahora tienes muchas más opciones. Aquí la descripción de las columnas:

  • Clase: 0:DW, 1:DK, 2:ELF, 3:MG, 4:DL, 5:SU
  • R G B: Colores (0.0 a 1.0)
  • Size: Tamaño de la runa
  • Speed: Velocidad de giro
  • Texture: ID de la textura (Ej: 32002)
  • Particle: ID de la partícula (Ej: 3229 chispas, 3201 fuego)
  • Rotation: 1.0 derecha, -1.0 izquierda

Ejemplo de Data\Rune.txt:
// Clase | R | G | B | Size | Speed | Texture | Particle | Rotation // ------------------------------------------------------------------- 0 0.2 0.4 1.0 2.2 0.01 32002 3229 1.0 // Wizard (Azul, Chispas) 1 1.0 0.2 0.2 2.5 0.01 32002 3201 -1.0 // Knight (Rojo, Fuego, Giro inverso) 2 0.2 1.0 0.4 2.0 0.01 32002 3229 1.0 // Elf 3 0.7 0.2 1.0 2.8 0.02 32002 3235 1.0 // MG (Runa grande y rapida) 4 1.0 0.8 0.1 2.4 0.01 32002 3201 1.0 // DL 5 1.0 0.2 0.6 2.2 0.01 32002 3229 1.0 // SU
(Nota: Sustituye 32002 por el número real que tenga tu BITMAP_GM_AURORA en los #define de tu proyecto)

Ideas para futuras expansiones

  • Altura: Columna para decidir si la runa está en el suelo o sobre la cabeza.
  • Opacidad: Para que algunas runas sean más transparentes.
  • Efecto Pulse: Para que la runa se agrande y achique sola como un latido.
Bon Dia

Dakosmu

Tutorial: Sistema Rune Dinámico PRO (Smoke y Toros)

Regístrate para ver el enlace
Regístrate para ver el enlace
Regístrate para ver el enlace

En esta versión Pro, añadiremos las funciones EnableSmoke y EnableBull directamente en la estructura para que puedas activar el efecto de humo o la pisada de toros desde el archivo .txt.

Paso 1: Actualizar la estructura (.h)
Añadimos las variables de control a la estructura. Reemplaza el bloque en tu archivo de cabecera:

// --- SISTEMA DE RUNE DINAMICO PRO --- struct RUNE_DATA { float Red, Green, Blue; float Size; float Speed; int TextureID; int ParticleID; float Rotation; int EnableSmoke; // 0: No, 1: Sí int EnableBull; // 0: No, 1: Sí (Efecto Toros/Pisada) };

extern RUNE_DATA g_RuneConfig[6];

void LoadRuneSettings();

Paso 2: Actualizar la carga de datos (.cpp)
Ahora el lector debe reconocer las dos nuevas columnas al final. Reemplaza la función LoadRuneSettings:

RUNE_DATA g_RuneConfig[6];

void LoadRuneSettings() { for (int i = 0; i < 6; i++) { g_RuneConfig[i].Red = 1.0f; g_RuneConfig[i].Green = 1.0f; g_RuneConfig[i].Blue = 1.0f; g_RuneConfig[i].Size = 2.0f; g_RuneConfig[i].Speed = 0.01f; g_RuneConfig[i].TextureID = 32002; // Cambia por tu ID de imagen g_RuneConfig[i].ParticleID = 0; g_RuneConfig[i].Rotation = 1.0f; g_RuneConfig[i].EnableSmoke = 0; g_RuneConfig[i].EnableBull = 0; }

  FILE* fp = fopen("Data\\Rune.txt", "r");
  if (!fp) return;

  char line[256];
  while (fgets(line, sizeof(line), fp)) {
      if (line[0] == '/' || line[0] == '\n' || line[0] == '\r') continue;

      int Class, Tex, Part, Smoke, Bull;
      float R, G, B, Sz, Sp, Rot;
      // Formato: Clase R G B Size Speed Texture Particle Rotation Smoke Bull
      if (sscanf(line, "%d %f %f %f %f %f %d %d %f %d %d",
          &Class, &R, &G, &B, &Sz, &Sp, &Tex, &Part, &Rot, &Smoke, &Bull) == 11) {
          if (Class >= 0 && Class < 6) {
              g_RuneConfig[Class].Red = R;
              g_RuneConfig[Class].Green = G;
              g_RuneConfig[Class].Blue = B;
              g_RuneConfig[Class].Size = Sz;
              g_RuneConfig[Class].Speed = Sp;
              g_RuneConfig[Class].TextureID = Tex;
              g_RuneConfig[Class].ParticleID = Part;
              g_RuneConfig[Class].Rotation = Rot;
              g_RuneConfig[Class].EnableSmoke = Smoke;
              g_RuneConfig[Class].EnableBull = Bull;
          }
      }
  }
  fclose(fp);
}

Paso 3: Actualizar la función principal (.cpp)
Aquí es donde ocurre la magia. Si EnableSmoke es 1, crearemos humo; si EnableBull es 1, crearemos el efecto de pisada/toros. Reemplaza RenderCharactersClient completo:

void RenderCharactersClient() { for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i) { CHARACTER* c = gmCharacters->GetCharacter(i); OBJECT* o = &c->Object;

      if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
      {
          if (!g_isCharacterBuff(o, eBuff_Cloaking))
          {
              int cls = (c->Class >= 0 && c->Class < 6) ? c->Class : 0;
              RUNE_DATA* config = &g_RuneConfig[cls];

              vec3_t vLight, vPos, vFinalLumi;
              VectorCopy(o->Position, vPos);
              Vector(config->Red, config->Green, config->Blue, vLight);

              // --- EFECTO SMOKE (HUMO) ---
              if (config->EnableSmoke == 1 && rand() % 5 == 0) {
                  CreateParticle(BITMAP_SMOKE, o->Position, o->Angle, vLight, 2, 1.0f);
              }

              // --- EFECTO TOROS (PISADA FORTE) ---
              if (config->EnableBull == 1 && rand() % 20 == 0) {
                  // El efecto 124 suele ser el de pisada de Dark Lord/Toros en muchos clientes
                  CreateEffect(124, o->Position, o->Angle, vLight, 0, o);
              }

              // --- PARTICULAS ---
              if (config->ParticleID > 0 && rand() % 15 == 0) {
                  vec3_t vPartPos; VectorCopy(o->Position, vPartPos);
                  vPartPos[0] += (rand() % 40 - 20);
                  vPartPos[1] += (rand() % 40 - 20);
                  CreateParticle(config->ParticleID, vPartPos, o->Angle, vLight, 1, 0.5f, o);
              }

              // --- RENDER VISUAL DE RUNA ---
              vPos[2] += 100.f;
              CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);
              float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
              Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);

              EnableAlphaBlend();
              RenderTerrainAlphaBitmap(config->TextureID, o->Position[0], o->Position[1],
                  config->Size, config->Size, vFinalLumi, (WorldTime * config->Speed) * config->Rotation);
              DisableAlphaBlend();
          }
      }

      if (o->Live && o->Visible) {
          RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
      }
  }
}

Paso 4: Configurar el archivo Rune.txt con Smoke y Bull
Ahora tu archivo tiene 11 columnas. Aquí tienes un ejemplo de configuración:

Citar// Clase | R | G | B | Size | Speed | TexID | PartID | Rot | Smoke | Bull
// -----------------------------------------------------------------------
0  0.2  0.4  1.0  2.2  1.01  32002  3229   1.0   1   0   // Wizard con Humo
1  1.0  0.2  0.2  2.5  0.01  32002  3201  -1.0   0   1   // Knight con Toros
2  0.2  1.0  0.4  2.0  0.01  32002  3229   1.0   1   0   // Elf con Humo
3  0.7  0.2  1.0  2.5  0.01  32002  3235   1.0   1   1   // MG con AMBOS
4  1.0  0.8  0.1  2.5  0.01  32002  3201   1.0   0   1   // DL con Toros
5  1.0  0.2  0.6  2.2  0.01  32002  3229   1.0   1   0   // SU con Humo

Notas finales:

  • Humo (Smoke): Es un efecto suave que rodea al personaje constantemente.
  • Toros (Bull): Es el efecto de impacto en el suelo. Si el ID 124 no muestra nada, prueba con otros IDs de efectos entre el 100 y el 150.
  • Seguridad: Al usar extern en el .h y definirlo en el .cpp, evitas errores de compilación por duplicados.
Bon Dia

Dakosmu

#8
Cómo saber el número (ID) para el Rune.txt

Los enum o listas de BITMAP_ funcionan por orden correlativo. Si conoces un ID base, los siguientes incrementan de uno en uno. Por ejemplo, si BITMAP_GM_AURORA es 32002, el que le sigue en la lista (BITMAP_FREETICKET_R) será el 32003.

Aquí tienes una selección de texturas que se ven excelentes como Runas en el suelo:

  • BITMAP_MAGIC_ZIN: Un círculo mágico con glifos antiguos. Es la opción más profesional.
  • BITMAP_SHOCK_WAVE: Ideal para guerreros (Knight/MG), simula una onda de choque expansiva.
  • BITMAP_ENERGY_RING: Un anillo de energía pura, perfecto para clases mágicas.
  • BITMAP_SNOW_EFFECT_1: Crea una runa con una estética de hielo o cristal.
  • BITMAP_FIRE_RED: Ideal para simular un aura de fuego o un portal infernal.
  • BITMAP_JOINT_SPIRIT2: Proporciona efectos espirituales con transparencias muy suaves.

¿Cómo probar IDs rápidamente?

Para no tener que adivinar los números, puedes usar un pequeño truco de depuración. Agrega estas líneas temporalmente dentro de tu función
LoadRuneSettings para ver los IDs reales en la consola al abrir el juego:

// Agrega esto temporalmente en tu LoadRuneSettings para saber los IDs
printf("ID Aurora: %d\n", BITMAP_GM_AURORA);
printf("ID Magic Zin: %d\n", BITMAP_MAGIC_ZIN);
printf("ID Shock Wave: %d\n", BITMAP_SHOCK_WAVE);

Consejos de Diseño Visual

Es importante evitar texturas que no fueron diseñadas para efectos de suelo, como marcos de interfaz o partes de personajes (ej: BITMAP_INGAMESHOP_FRAME), ya que aparecerán como cuadrados sólidos y se verán mal.


Textura IDID EstimadoDescripción Visual
BITMAP_GM_AURORA32002Círculo sólido con brillo (Glow).
BITMAP_ORORA32050Aurora difuminada, efecto suave.
BITMAP_ENERGY_RING32043Un aro de luz fino, vacío por dentro.
BITMAP_MAGIC_ZIN32048Círculo con símbolos rúnicos antiguos.
BITMAP_RING_OF_GRADATION32128Anillo con degradado suave hacia afuera.
BITMAP_PIN_LIGHT32049Destello puntual (parece una estrella pequeña).

Las mejores texturas siempre son las que terminan en:
  • _AURORA
  • _RING
  • _ZIN
  • _WAVE
  • _EFFECT


// Esto imprimirá los números reales en la consola de Visual Studio (Debug)
// o en una consola si el Main tiene una activa.
printf("--- IDENTIFICADORES DE TEXTURAS ---\n");
printf("BITMAP_GM_AURORA: %d\n", BITMAP_GM_AURORA);
printf("BITMAP_ORORA: %d\n", BITMAP_ORORA);
printf("BITMAP_ENERGY_RING: %d\n", BITMAP_ENERGY_RING);
printf("BITMAP_MAGIC_ZIN: %d\n", BITMAP_MAGIC_ZIN);
printf("BITMAP_PIN_LIGHT: %d\n", BITMAP_PIN_LIGHT);
printf("-----------------------------------\n");
Bon Dia

Dakosmu

este efecto solo se active a partir de cierto nivel (por ejemplo, solo para personajes nivel 400)?


void RenderCharactersClient()
{
    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        // --- RUNE EFFECT + COLORES POR CLASE + RESTRICCIÓN NIVEL 400 ---
        // 1. Verificamos que sea un jugador, esté vivo y en la escena principal
        if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            // 2. NUEVA RESTRICCIÓN: Solo si el personaje es Nivel 400 o superior
            // 3. También verificamos que no esté invisible (Cloaking)
            if (c->Level >= 400 && !g_isCharacterBuff(o, eBuff_Cloaking))
            {
                vec3_t vLight, vPos, vFinalLumi;
                VectorCopy(o->Position, vPos);

                // Selector de colores por Clase
                switch (c->Class) {
                    case 0: Vector(0.2f, 0.4f, 1.0f, vLight); break; // Wizard - Azul
                    case 1: Vector(1.0f, 0.2f, 0.2f, vLight); break; // Knight - Rojo
                    case 2: Vector(0.2f, 1.0f, 0.4f, vLight); break; // Elf - Verde
                    case 3: Vector(0.7f, 0.2f, 1.0f, vLight); break; // MG - Morado
                    case 4: Vector(1.0f, 0.7f, 0.1f, vLight); break; // DL - Dorado
                    case 5: Vector(1.0f, 0.2f, 0.6f, vLight); break; // SU - Rosa
                    default: Vector(1.0f, 1.0f, 1.0f, vLight); break;
                }

                // Posicionamiento de los efectos de luz (un poco por encima de los pies)
                vPos[2] += 100.f;

                // Brillo principal (Sprite)
                CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);

                // Efecto de parpadeo (Luminosidad variable)
                float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                CreateSprite(BITMAP_LIGHT, vPos, 2.0f, vFinalLumi, o);

                // Renderizado del Aura Circular
                RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.5f, 2.5f, vFinalLumi);

                // Efectos de terreno (Rotación doble)
                fLumi = sinf(WorldTime * 0.0015f) * 0.3f + 0.5f;
                EnableAlphaBlend();
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
               
                // Capa 1: Gira a la derecha
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 2.0f, 2.0f, vFinalLumi, WorldTime * 0.01f);
                // Capa 2: Gira a la izquierda
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 1.f, 1.f, vFinalLumi, -WorldTime * 0.01f);
               
                // Aura GM extra para intensidad
                RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.5f, 1.5f, vFinalLumi);
               
                DisableAlphaBlend();
            }
        }

        // Renderizado normal del cuerpo del personaje
        if (o->Live)
        {
            if (o->Visible)
            {
                RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
                if (o->Type == MODEL_PLAYER)
                    battleCastle::CreateBattleCastleCharacter_Visual(c, o);
            }
        }
    }
}
Bon Dia

Dakosmu


Si es GM: Tendrá un aura de color Blanco/Plateado brillante (independientemente de su clase o nivel).

Si es Jugador Normal: Necesitará ser Nivel 400 para ver el aura de su clase.

Si es Nivel < 400: No verá ningún aura.

void RenderCharactersClient()
{
    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        // --- SISTEMA DE AURAS PRO (LEVEL 400 + GM SUPPORT) ---
        if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            // Verificamos si es un GM (comúnmente c->CtlCode == 32 o 8 según el server)
            bool isGM = (c->CtlCode == 32 || c->CtlCode == 8);

            // El efecto se activa si: Es GM O (Es Nivel >= 400 Y no está invisible)
            if (isGM || (c->Level >= 400 && !g_isCharacterBuff(o, eBuff_Cloaking)))
            {
                vec3_t vLight, vPos, vFinalLumi;
                VectorCopy(o->Position, vPos);

                if (isGM)
                {
                    // Color para Game Masters: Blanco puro / Plateado
                    Vector(1.0f, 1.0f, 1.0f, vLight);
                }
                else
                {
                    // Selector de colores por Clase para jugadores normales
                    switch (c->Class) {
                        case 0: Vector(0.2f, 0.4f, 1.0f, vLight); break; // Wizard - Azul
                        case 1: Vector(1.0f, 0.2f, 0.2f, vLight); break; // Knight - Rojo
                        case 2: Vector(0.2f, 1.0f, 0.4f, vLight); break; // Elf - Verde
                        case 3: Vector(0.7f, 0.2f, 1.0f, vLight); break; // MG - Morado
                        case 4: Vector(1.0f, 0.7f, 0.1f, vLight); break; // DL - Dorado
                        case 5: Vector(1.0f, 0.2f, 0.6f, vLight); break; // SU - Rosa
                        default: Vector(0.8f, 0.8f, 0.8f, vLight); break;
                    }
                }

                vPos[2] += 100.f;

                // 1. Brillo de ambiente (Sprite grande)
                CreateSprite(BITMAP_LIGHT, vPos, 6.0f, vLight, o, 0.5f);

                // 2. Efecto de pulsación
                float fLumi = sinf(WorldTime * 0.05f) * 0.4f + 0.9f;
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                CreateSprite(BITMAP_LIGHT, vPos, 2.0f, vFinalLumi, o);

                // 3. Aura circular de magia
                RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.5f, 2.5f, vFinalLumi);

                // 4. Efectos en el suelo con transparencia
                fLumi = sinf(WorldTime * 0.0015f) * 0.3f + 0.5f;
                EnableAlphaBlend();
                Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
               
                // Capas giratorias encontradas (Efecto Vortex)
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 2.0f, 2.0f, vFinalLumi, WorldTime * 0.01f);
                RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 1.2f, 1.2f, vFinalLumi, -WorldTime * 0.015f);
               
                // Refuerzo visual GM_AURORA estático
                RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.5f, 1.5f, vFinalLumi);
               
                DisableAlphaBlend();
            }
        }

        // --- RENDERIZADO DEL CUERPO ---
        if (o->Live)
        {
            if (o->Visible)
            {
                RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
               
                // Visuales de Battle Castle si aplica
                if (o->Type == MODEL_PLAYER)
                    battleCastle::CreateBattleCastleCharacter_Visual(c, o);
            }
        }
    }
}
Bon Dia

Dakosmu

Runne por nileves de personajes.5 niveles de progresión y el efecto especial para GM:


CitarLvl 10-49: Solo una pequeña marca de luz tenue en el suelo que gira lento.

Lvl 50-149: Se activa un punto de luz brillante en el centro del personaje.

Lvl 150-349: Aparece el primer anillo mágico (RING_OF_GRADATION) girando bajo los pies.

Lvl 350-399: El aura empieza a brillar con intensidad y se hace más grande (BITMAP_MAGIC).

Lvl 400+ / GM: Se activa el "Modo Full", con una explosión de luz constante, dos anillos girando en sentidos opuestos y el aura máxima.



void RenderCharactersClient()
{
    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        // --- SISTEMA DE EFECTOS ESCALONADOS POR NIVEL ---
        if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            if (!g_isCharacterBuff(o, eBuff_Cloaking))
            {
                vec3_t vLight, vPos, vFinalLumi;
                VectorCopy(o->Position, vPos);
                bool isGM = (c->CtlCode == 32 || c->CtlCode == 8);

                // 1. Selector de colores por Clase
                switch (c->Class) {
                    case 0: Vector(0.2f, 0.4f, 1.0f, vLight); break; // Wizard - Azul
                    case 1: Vector(1.0f, 0.2f, 0.2f, vLight); break; // Knight - Rojo
                    case 2: Vector(0.2f, 1.0f, 0.4f, vLight); break; // Elf - Verde
                    case 3: Vector(0.7f, 0.2f, 1.0f, vLight); break; // MG - Morado
                    case 4: Vector(1.0f, 0.7f, 0.1f, vLight); break; // DL - Dorado
                    case 5: Vector(1.0f, 0.2f, 0.6f, vLight); break; // SU - Rosa
                    default: Vector(1.0f, 1.0f, 1.0f, vLight); break;
                }
               
                if (isGM) Vector(1.0f, 1.0f, 1.0f, vLight); // GM siempre Blanco

                // --- LÓGICA DE ESCALADO POR NIVEL ---
               
                // Efecto Nivel 10+: Un brillo suave en los pies
                if (c->Level >= 10 || isGM)
                {
                    float fLumi = sinf(WorldTime * 0.05f) * 0.2f + 0.5f;
                    Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 0.8f, 0.8f, vFinalLumi, WorldTime * 0.005f);
                    DisableAlphaBlend();
                }

                // Efecto Nivel 50+: Se añade un destello de luz (Sprite)
                if (c->Level >= 50 || isGM)
                {
                    vPos[2] += 50.f;
                    CreateSprite(BITMAP_LIGHT, vPos, 1.5f, vLight, o);
                }

                // Efecto Nivel 150+: Se añade el Anillo de Gradación giratorio
                if (c->Level >= 150 || isGM)
                {
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_RING_OF_GRADATION, o->Position[0], o->Position[1], 1.2f, 1.2f, vLight, -WorldTime * 0.01f);
                    DisableAlphaBlend();
                }

                // Efecto Nivel 350+: El aura se vuelve más grande y parpadeante
                if (c->Level >= 350 || isGM)
                {
                    float fLumi = sinf(WorldTime * 0.05f) * 0.5f + 0.8f;
                    Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                    RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.8f, 1.8f, vFinalLumi);
                }

                // Efecto Nivel 400 (MÁXIMO): Efecto completo de impacto y aura doble
                if (c->Level >= 400 || isGM)
                {
                    vPos[2] += 50.f; // Subir un poco más la luz
                    CreateSprite(BITMAP_LIGHT, vPos, 4.0f, vLight, o, 0.5f);
                   
                    EnableAlphaBlend();
                    // Segundo anillo giratorio en sentido opuesto
                    RenderTerrainAlphaBitmap(BITMAP_SUMMON_IMPACT, o->Position[0], o->Position[1], 2.0f, 2.0f, vLight, WorldTime * 0.015f);
                    // Aura de GM reforzada
                    RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.2f, 2.2f, vLight);
                    DisableAlphaBlend();
                }
            }
        }

        // --- RENDERIZADO ESTÁNDAR ---
        if (o->Live && o->Visible)
        {
            RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
            if (o->Type == MODEL_PLAYER)
                battleCastle::CreateBattleCastleCharacter_Visual(c, o);
        }
    }
}
Bon Dia

Dakosmu

Para el Nivel 400, incluiremos un efecto de partículas ascendentes (estilo humo o energía que sube) y la ejecución de un sonido ambiental para que el personaje realmente destaque sobre los demás.

He estructurado el código para que las partículas se generen de forma continua mientras el jugador sea nivel 400, creando una columna de energía.


void RenderCharactersClient()
{
    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        // --- SISTEMA DE EFECTOS ESCALONADOS PRO ---
        if (o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            if (!g_isCharacterBuff(o, eBuff_Cloaking))
            {
                vec3_t vLight, vPos, vFinalLumi;
                VectorCopy(o->Position, vPos);
                bool isGM = (c->CtlCode == 32 || c->CtlCode == 8);

                // Selector de colores por Clase
                switch (c->Class) {
                    case 0: Vector(0.2f, 0.4f, 1.0f, vLight); break; // Wizard - Azul
                    case 1: Vector(1.0f, 0.2f, 0.2f, vLight); break; // Knight - Rojo
                    case 2: Vector(0.2f, 1.0f, 0.4f, vLight); break; // Elf - Verde
                    case 3: Vector(0.7f, 0.2f, 1.0f, vLight); break; // MG - Morado
                    case 4: Vector(1.0f, 0.7f, 0.1f, vLight); break; // DL - Dorado
                    case 5: Vector(1.0f, 0.2f, 0.6f, vLight); break; // SU - Rosa
                    default: Vector(1.0f, 1.0f, 1.0f, vLight); break;
                }
                if (isGM) Vector(1.0f, 1.0f, 1.0f, vLight);

                // LVL 10+: Brillo base
                if (c->Level >= 10 || isGM) {
                    float fLumi = sinf(WorldTime * 0.05f) * 0.2f + 0.5f;
                    Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 0.8f, 0.8f, vFinalLumi, WorldTime * 0.005f);
                    DisableAlphaBlend();
                }

                // LVL 50+: Punto de luz
                if (c->Level >= 50 || isGM) {
                    vPos[2] += 50.f;
                    CreateSprite(BITMAP_LIGHT, vPos, 1.5f, vLight, o);
                }

                // LVL 150+: Anillo de suelo
                if (c->Level >= 150 || isGM) {
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_RING_OF_GRADATION, o->Position[0], o->Position[1], 1.2f, 1.2f, vLight, -WorldTime * 0.01f);
                    DisableAlphaBlend();
                }

                // LVL 350+: Aura de energía
                if (c->Level >= 350 || isGM) {
                    float fLumi = sinf(WorldTime * 0.05f) * 0.5f + 0.8f;
                    Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                    RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.8f, 1.8f, vFinalLumi);
                }

                // LVL 400+: EFECTO MÁXIMO (Partículas + Sonido + Aura Doble)
                if (c->Level >= 400 || isGM)
                {
                    vPos[2] += 20.f;
                    CreateSprite(BITMAP_LIGHT, vPos, 4.0f, vLight, o, 0.5f);
                   
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_SUMMON_IMPACT, o->Position[0], o->Position[1], 2.0f, 2.0f, vLight, WorldTime * 0.015f);
                    RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.2f, 2.2f, vLight);
                    DisableAlphaBlend();

                    // --- NUEVO: PARTÍCULAS ASCENDENTES ---
                    // Crea partículas que suben desde los pies cada pocos frames
                    if (rand() % 3 == 0) {
                        vec3_t vParticlePos;
                        VectorCopy(o->Position, vParticlePos);
                        vParticlePos[0] += (rand() % 100 - 50); // Dispersión X
                        vParticlePos[1] += (rand() % 100 - 50); // Dispersión Y
                        vParticlePos[2] += (rand() % 20);       // Desde el suelo
                       
                        // BITMAP_SMOKE o BITMAP_FLARE suelen funcionar bien para esto
                        CreateParticle(BITMAP_SMOKE, vParticlePos, o->Angle, vLight, 1, 1.0f, o);
                    }

                    // --- NUEVO: SONIDO AMBIENTAL (Opcional) ---
                    // Nota: Se debe controlar para que no sature. Aquí un ejemplo:
                    // if (WorldTime % 5000 == 0) PlayBuffer(SOUND_RENEW_LEVEL);
                }
            }
        }

        // Renderizado normal
        if (o->Live && o->Visible) {
            RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
            if (o->Type == MODEL_PLAYER)
                battleCastle::CreateBattleCastleCharacter_Visual(c, o);
        }
    }
}

CitarPartículas (CreateParticle): He añadido una probabilidad (rand() % 3) para que no se sature la pantalla. Las partículas aparecen en posiciones aleatorias cerca de los pies del jugador y suben. He usado BITMAP_SMOKE, que con el color de la clase (vLight) crea un efecto de "fuego fatuo" o energía mística.

Estructura de Sonido: He dejado comentada la línea de PlayBuffer. Si quieres que los personajes nivel 400 emitan un zumbido o sonido mágico constante, puedes activarla, pero ten cuidado de no usar sonidos muy ruidosos.

Optimización: Las partículas están vinculadas al objeto (o), por lo que si el personaje se mueve, las partículas lo siguen de forma natural.
Bon Dia

Dakosmu

comando especial para que el GM pueda ocultar o mostrar todas las auras con una tecla


// Variable global (colócala al inicio de tu archivo .cpp o en un Header)
bool g_ShowAuras = true;

void RenderCharactersClient()
{
    // --- LÓGICA DE TECLADO (EJEMPLO) ---
    // Si presionas F10, se apagan/prenden las auras
    if (IsPress(VK_F10)) {
        g_ShowAuras = !g_ShowAuras;
    }

    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        // --- SISTEMA DE EFECTOS ESCALONADOS + CONTROL GM ---
        // Solo procesamos si las auras están activadas globalmente
        if (g_ShowAuras && o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            if (!g_isCharacterBuff(o, eBuff_Cloaking))
            {
                vec3_t vLight, vPos, vFinalLumi;
                VectorCopy(o->Position, vPos);
                bool isGM = (c->CtlCode == 32 || c->CtlCode == 8);

                // Selector de colores por Clase
                switch (c->Class) {
                    case 0: Vector(0.2f, 0.4f, 1.0f, vLight); break; // Wizard - Azul
                    case 1: Vector(1.0f, 0.2f, 0.2f, vLight); break; // Knight - Rojo
                    case 2: Vector(0.2f, 1.0f, 0.4f, vLight); break; // Elf - Verde
                    case 3: Vector(0.7f, 0.2f, 1.0f, vLight); break; // MG - Morado
                    case 4: Vector(1.0f, 0.7f, 0.1f, vLight); break; // DL - Dorado
                    case 5: Vector(1.0f, 0.2f, 0.6f, vLight); break; // SU - Rosa
                    default: Vector(1.0f, 1.0f, 1.0f, vLight); break;
                }
                if (isGM) Vector(1.0f, 1.0f, 1.0f, vLight);

                // --- ESCALADO DE EFECTOS ---

                // LVL 10+: Brillo base tenue
                if (c->Level >= 10 || isGM) {
                    float fLumi = sinf(WorldTime * 0.05f) * 0.2f + 0.5f;
                    Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 0.8f, 0.8f, vFinalLumi, WorldTime * 0.005f);
                    DisableAlphaBlend();
                }

                // LVL 50+: Punto de luz en el pecho
                if (c->Level >= 50 || isGM) {
                    vPos[2] += 50.f;
                    CreateSprite(BITMAP_LIGHT, vPos, 1.5f, vLight, o);
                }

                // LVL 150+: Anillo rúnico en el suelo
                if (c->Level >= 150 || isGM) {
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_RING_OF_GRADATION, o->Position[0], o->Position[1], 1.2f, 1.2f, vLight, -WorldTime * 0.01f);
                    DisableAlphaBlend();
                }

                // LVL 350+: Aura de energía ascendente (Magic)
                if (c->Level >= 350 || isGM) {
                    float fLumi = sinf(WorldTime * 0.05f) * 0.5f + 0.8f;
                    Vector(fLumi * vLight[0], fLumi * vLight[1], fLumi * vLight[2], vFinalLumi);
                    RenderAurora(BITMAP_MAGIC + 1, RENDER_BRIGHT, o->Position[0], o->Position[1], 1.8f, 1.8f, vFinalLumi);
                }

                // LVL 400+: EFECTO MÁXIMO (Aura doble + Partículas)
                if (c->Level >= 400 || isGM)
                {
                    vPos[2] += 20.f;
                    CreateSprite(BITMAP_LIGHT, vPos, 4.0f, vLight, o, 0.5f);
                   
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_SUMMON_IMPACT, o->Position[0], o->Position[1], 2.0f, 2.0f, vLight, WorldTime * 0.015f);
                    RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.2f, 2.2f, vLight);
                    DisableAlphaBlend();

                    // Generación de partículas de energía
                    if (rand() % 3 == 0) {
                        vec3_t vParticlePos;
                        VectorCopy(o->Position, vParticlePos);
                        vParticlePos[0] += (rand() % 60 - 30);
                        vParticlePos[1] += (rand() % 60 - 30);
                        vParticlePos[2] += (rand() % 30);       
                        CreateParticle(BITMAP_SMOKE, vParticlePos, o->Angle, vLight, 1, 0.8f, o);
                    }
                }
            }
        }

        // --- RENDER FINAL DEL CUERPO DEL PERSONAJE ---
        if (o->Live && o->Visible) {
            RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
            if (o->Type == MODEL_PLAYER)
                battleCastle::CreateBattleCastleCharacter_Visual(c, o);
        }
    }
}


CitarF10: Al presionar esta tecla, la variable g_ShowAuras cambia entre true y false.

Rendimiento: Cuando g_ShowAuras es falso, el cliente salta directamente toda la lógica de cálculo de colores y renderizado de bitmaps, lo que aumenta los FPS si hay muchos jugadores en el mapa.

Partículas: He ajustado la dispersión de las partículas (rand() % 60 - 30) para que salgan más cerca del cuerpo del personaje, dando un aspecto más compacto y elegante.
Bon Dia

Dakosmu

Detalles del Sistema de Corona:
Posicionamiento (vPos[2] += 230.f): He ajustado la altura para que quede justo por encima de los cascos más altos (como los de Dark Knight o Dark Lord).

Color de Ranking: He definido un color Dorado Brillante (1.0, 0.8, 0.0). Si quisieras un Top 2, podrías ponerlo en color plata (0.8, 0.8, 0.8).

Partículas de Éxito: He añadido BITMAP_SPARK (chispas) que caen desde la corona hacia los hombros, dando un efecto de "realeza".


void RenderCharactersClient()
{
    // Variable para controlar el encendido/apagado de auras (F10)
    if (IsPress(VK_F10)) {
        g_ShowAuras = !g_ShowAuras;
    }

    for (int i = 0; i < MAX_CHARACTERS_CLIENT; ++i)
    {
        CHARACTER* c = gmCharacters->GetCharacter(i);
        OBJECT* o = &c->Object;

        if (g_ShowAuras && o->Kind == 1 && SceneFlag == MAIN_SCENE && o->Live)
        {
            if (!g_isCharacterBuff(o, eBuff_Cloaking))
            {
                vec3_t vLight, vPos, vFinalLumi;
                VectorCopy(o->Position, vPos);
                bool isGM = (c->CtlCode == 32 || c->CtlCode == 8);

                // --- SELECTOR DE COLORES POR CLASE ---
                switch (c->Class) {
                    case 0: Vector(0.2f, 0.4f, 1.0f, vLight); break; // Wizard - Azul
                    case 1: Vector(1.0f, 0.2f, 0.2f, vLight); break; // Knight - Rojo
                    case 2: Vector(0.2f, 1.0f, 0.4f, vLight); break; // Elf - Verde
                    case 3: Vector(0.7f, 0.2f, 1.0f, vLight); break; // MG - Morado
                    case 4: Vector(1.0f, 0.7f, 0.1f, vLight); break; // DL - Dorado
                    case 5: Vector(1.0f, 0.2f, 0.6f, vLight); break; // SU - Rosa
                    default: Vector(1.0f, 1.0f, 1.0f, vLight); break;
                }
                if (isGM) Vector(1.0f, 1.0f, 1.0f, vLight);

                // --- EFECTOS DE NIVEL (RESUMIDOS) ---
                if (c->Level >= 10 || isGM) {
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_GM_AURORA, o->Position[0], o->Position[1], 0.8f, 0.8f, vLight, WorldTime * 0.005f);
                    DisableAlphaBlend();
                }

                if (c->Level >= 150 || isGM) {
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_RING_OF_GRADATION, o->Position[0], o->Position[1], 1.2f, 1.2f, vLight, -WorldTime * 0.01f);
                    DisableAlphaBlend();
                }

                if (c->Level >= 400 || isGM) {
                    // Aura máxima de suelo y partículas
                    EnableAlphaBlend();
                    RenderTerrainAlphaBitmap(BITMAP_SUMMON_IMPACT, o->Position[0], o->Position[1], 2.0f, 2.0f, vLight, WorldTime * 0.015f);
                    RenderAurora(BITMAP_GM_AURORA, RENDER_BRIGHT, o->Position[0], o->Position[1], 2.2f, 2.2f, vLight);
                    DisableAlphaBlend();
                }

                // --- NUEVO: EFECTO DE CORONA TOP 1 (SOBRE LA CABEZA) ---
                // Supongamos que c->Rank es una variable que indica el puesto en el ranking
                if (c->Rank == 1) // El mejor de su clase
                {
                    vec3_t vCrownPos, vCrownColor;
                    VectorCopy(o->Position, vCrownPos);
                    vCrownPos[2] += 230.f; // Posición sobre la cabeza
                   
                    // Color Dorado para el Ranking 1
                    Vector(1.0f, 0.8f, 0.0f, vCrownColor);
                   
                    float fLumi = sinf(WorldTime * 0.02f) * 0.2f + 0.8f;
                    Vector(vCrownColor[0] * fLumi, vCrownColor[1] * fLumi, vCrownColor[2] * fLumi, vFinalLumi);
                   
                    // Dibujamos un destello tipo corona
                    CreateSprite(BITMAP_FLARE, vCrownPos, 1.0f, vFinalLumi, o, WorldTime * 0.05f);
                   
                    // Partículas doradas cayendo de la corona
                    if (rand() % 5 == 0) {
                        vCrownPos[0] += (rand() % 40 - 20);
                        vCrownPos[1] += (rand() % 40 - 20);
                        CreateParticle(BITMAP_SPARK, vCrownPos, o->Angle, vCrownColor, 1, 0.5f, o);
                    }
                }
            }
        }

        // Renderizado normal del personaje
        if (o->Live && o->Visible) {
            RenderCharacter(c, o, (i == SelectedCharacter || i == SelectedNpc));
        }
    }
}


CitarEfecto de Corona: Un destello flotante sobre la cabeza (vPos[2] += 200.f).

Lógica de Top 1: He añadido una condición (que puedes conectar con tu base de datos o una variable del juego) para que la corona sea de Oro (Dorado) para el Top 1 y Plata (Blanco) para el Top 2.
Bon Dia

🡱 🡳
Real Time Web Analytics