Mundo Maker
¡Bienvenid@ a Mundo Maker!

¿Quieres aprender todo sobre el RPG Maker?



Regístrate y forma parte de Mundo Maker.
Conectarse

Recuperar mi contraseña

Temas importantes
----------------------------------------
Páginas con recursos RPG Maker
----------------------------------------
----------------------------------------
----------------------------------------
----------------------------------------
----------------------------------------
----------------------------------------
----------------------------------------
----------------------------------------
----------------------------------------
Afiliados
Estadísticas
Tenemos 4133 miembros registrados.
El último usuario registrado es Neysersitoh.

Nuestros miembros han publicado un total de 84769 mensajes en 12064 argumentos.

[XP] Lunatic Enemy A.I.

Ver el tema anterior Ver el tema siguiente Ir abajo

RPG Maker XP [XP] Lunatic Enemy A.I.

Mensaje por Wecoc el 2017-01-18, 19:14

Éste script añade la opción de configurar la A.I. de los enemigos del juego, por ID de acción y por ID de enemigo.
Para quien no esté familiarizado con el término, la A.I. (Inteligencia Artificial) lo que hace es impedir que el enemigo haga cosas muy estúpidas y sin sentido como votar a Donald Trump. Está pensado para scripters de nivel medio. Si no sabes nada de scripts éste no te servirá de nada.

El script añade el parámetro conditions a las acciones del enemigo. Éste es una array con tantas strings (condiciones) como se quiera, que son evaluadas para saber cuando se podrá efectuar la acción y cuando no. Los parámetros que vienen por defecto al definir una acción (Turno, PV, Nivel, Interruptor) siguen funcionando como siempre, así como el rating (frecuencia) de dicha acción si se cumplen todos los parámetros, lo nuevo es poder definir strings que añadan parámetros totalmente personalizados. Todo eso se configura dentro del método set_custom_action_conditions.

Pero hay más, la A.I. no es solo saber cuando hacer una acción sinó también a quien hacerla.
Eso podéis configurarlo por ID de skill y por ID de enemigo dentro del método decide_target_for_enemy.

En el código está explicado de modo más detallado cómo hacer la configuración, con un ejemplo de cada cosa. Aún así os daré unos ejemplos prácticos para que veáis mejor el uso del script. Es compatible con casi todo, solo reescribe Game_Enemy:make_action y sus modificaciones a parte de ese método no son nada troncales.

Script

Ponedlo encima de main. No requiere créditos.

Código:
#==============================================================================
# ** [XP] Lunatic Enemy A.I. v1.0
#------------------------------------------------------------------------------
# Autor: Wecoc (No requiere créditos)
#==============================================================================

#==============================================================================
# ** Game_EnemyAction
#==============================================================================

class Game_EnemyAction
  attr_accessor :kind
  attr_accessor :basic
  attr_accessor :skill_id
  attr_accessor :rating
  attr_accessor :conditions
  #--------------------------------------------------------------------------
  # * Initialize
  #--------------------------------------------------------------------------
  def initialize
    @kind     = 0
    @basic    = 0
    @skill_id = 1
    @rating   = 5
    @conditions = []
  end
  #--------------------------------------------------------------------------
  # * Definir condición
  #--------------------------------------------------------------------------
  def set_condition(text)
    @conditions.push(text)
    @conditions.uniq!
    return @conditions
  end
  #--------------------------------------------------------------------------
  # * Definir condición de turnos
  # Por defecto: a = 0, b = 1
  #--------------------------------------------------------------------------  
  def set_turn_pattern(a, b)
    parameters = "n = $game_temp.battle_turn ; a = #{a} ; b = #{b} ;"
    set_condition(parameters + "!(b == 0 and n != a)")
    set_condition(parameters + "!(b > 0 and (n < 1 or n < a or n % b != a % b))")
  end
  #--------------------------------------------------------------------------
  # * Definir condición de % HP mínima del que usa la acción
  # Por defecto: hp = 100
  #--------------------------------------------------------------------------  
  def set_hp_condition(hp)
    set_condition("self.hp * 100.0 / self.maxhp <= #{hp}")
  end
  #--------------------------------------------------------------------------
  # * Definir condición de nivel mínimo de la party para realizar la acción
  # El nivel de la party se obtiene con el nivel más alto del grupo
  # Por defecto: level = 1
  #--------------------------------------------------------------------------  
  def set_level_condition(level)
    set_condition("$game_party.max_level < #{level}")
  end
  #--------------------------------------------------------------------------
  # * Definir condición de interruptor activado
  # Por defecto: switch_id = 0
  #--------------------------------------------------------------------------  
  def set_switch_condition(switch_id, value=true)
    if switch_id > 0
      set_condition("$game_switches[#{switch_id}] == #{value}")
    end
  end
end

#==============================================================================
# ** Game_Enemy
#==============================================================================

class Game_Enemy < Game_Battler
  attr_accessor :actions
  #--------------------------------------------------------------------------
  # * Initialize
  #--------------------------------------------------------------------------
  alias action_support_ini initialize unless $@
  def initialize(troop_id, member_index)
    action_support_ini(troop_id, member_index)
    @actions = []
    for i in 0...$data_enemies[@enemy_id].actions.size
      data = $data_enemies[@enemy_id].actions[i]
      action = Game_EnemyAction.new
      action.kind     = data.kind
      action.basic    = data.basic
      action.skill_id = data.skill_id
      action.rating   = data.rating
      action.set_turn_pattern(data.condition_turn_a, data.condition_turn_b)
      action.set_hp_condition(data.condition_hp)
      action.set_level_condition(data.condition_level)
      action.set_switch_condition(data.condition_switch_id)
      @actions.push(action)
    end
    set_custom_action_conditions
  end
  #--------------------------------------------------------------------------
  # * Definir las condiciones no predeferminadas
  #--------------------------------------------------------------------------
  def set_custom_action_conditions
    case @enemy_id
    when 1 # ID del Enemigo
      #------------------------------------------------------------------------
      # Aplicar condiciones personalizadas aquí, siguiendo el esquema:
      # @actions[Action ID].set_condition "Condición"
      # La condición puede tener más de una línea
      #------------------------------------------------------------------------
      # Ejemplo: Aplicar acción Cura solo si un enemigo tiene <= 25% de HP
      #
      # @actions[1].set_condition "
      #   $game_troop.enemies.any? do |enemy|
      #     !enemy.dead? && enemy.hp * 100.0 / enemy.maxhp <= 25}
      #   end"
      #------------------------------------------------------------------------
    # when ...
    end
  end
  #--------------------------------------------------------------------------
  # * Obtener las acciones
  #--------------------------------------------------------------------------
  def actions
    return @actions
  end
  #--------------------------------------------------------------------------
  # * Hacer acción más viable según sus condiciones
  #--------------------------------------------------------------------------
  def make_action
    self.current_action.clear
    return unless self.movable?
    available_actions = []
    rating_max = 0
    for action in self.actions
      for condition in action.conditions
        next action if eval(condition) == false
      end
      available_actions.push(action)
      if action.rating > rating_max
        rating_max = action.rating
      end
    end
    ratings_total = 0
    for action in available_actions
      if action.rating > rating_max - 3
        ratings_total += action.rating - (rating_max - 3)
      end
    end
    if ratings_total > 0
      value = rand(ratings_total)
      for action in available_actions
        if action.rating > rating_max - 3
          if value < action.rating - (rating_max - 3)
            self.current_action.kind     = action.kind
            self.current_action.basic    = action.basic
            self.current_action.skill_id = action.skill_id
            self.current_action.decide_target_for_enemy(@enemy_id)
            return
          else
            value -= action.rating - (rating_max - 3)
          end
        end
      end
    end
  end
end

#==============================================================================
# ** Game_BattleAction
#==============================================================================

class Game_BattleAction
  #--------------------------------------------------------------------------
  # * Decidir el target más inteligente para una acción
  #--------------------------------------------------------------------------
  def decide_target_for_enemy(enemy_id)
    if @kind == 0 # Habilidad básica
      #------------------------------------------------------------------------
      # Puse que al atacar se elija siempre al azar pero se puede cambiar
      # según el enemy_id, por ejemplo
      #------------------------------------------------------------------------
      return decide_random_target_for_enemy
    end
    battler = nil
    case @skill_id
    when 1 # ID de la Skill
      #------------------------------------------------------------------------
      # Aplicar condiciones personalizadas aquí, siguiendo el esquema:
      # @actions[Action ID].set_condition "Condición"
      # La condición puede tener más de una línea
      #------------------------------------------------------------------------
      # Ejemplo: Aplicar habilidad Cura al enemigo con menor HP
      # targets = $game_troop.enemies.clone
      # targets.delete_if {|target| target.dead?} # Quitar los muertos
      # battler = targets.sort{|b,a| b.hp - a.hp}[0] # El de menor HP
      #------------------------------------------------------------------------
    end
    if battler != nil
      @target_index = battler.index
    else
      clear
    end
  end
end
avatar
Wecoc
Administrador
Administrador



Créditos 9217

Gracias : 493

Volver arriba Ir abajo

RPG Maker XP Re: [XP] Lunatic Enemy A.I.

Mensaje por Producciones The Golden el 2017-01-18, 19:20

Y como siempre... yo voy por mi lado en el mapa XD, ahora estoy preparando un engine de distintos enemigos con distintas mecánicas en mapa... obviamente pienso subir esto al foro,  en algunas horas probaré tu script y editaré este comentario dejando mi impresión, saludos!
avatar
Producciones The Golden
Veterano
Veterano

0/3

Créditos 639

Gracias : 60

Volver arriba Ir abajo

RPG Maker XP Re: [XP] Lunatic Enemy A.I.

Mensaje por PKTrainer el 2017-01-18, 19:24

Como siempre tus script son interesantes que a la vez de útiles,espero que a pesar de no saber una M de scripts pueda hacerlo funcionar a mi gusto.
avatar
PKTrainer
Iniciado
Iniciado

0/3

Créditos 0

Gracias : 4

Volver arriba Ir abajo

RPG Maker XP Re: [XP] Lunatic Enemy A.I.

Mensaje por Wecoc el 2017-01-18, 20:46

Producciones The Golden: Pues vuelvo a decirte lo mismo en éste caso, porque vuelve a ser un script básicamente de batalla.
Wecoc escribió:Piensa que las batallas en mapa son otro mundo totalmente distinto, a parte que hay varias y cada una usa cosas de la base distintas así que es muy difícil hacer un script que sirva para todo. (...) Las batallas en mapa y otras batallas muy alejadas de la default requieren que éste tipo de add-ons se hagan explícitamente para las mismas.
PKTrainer: Por probarlo no pierdes nada pero ya verás con los ejemplos que pondré a continuación que en éste script sin saber RGSS es dificilillo lograr lo que buscas.

1A. Atacar si todos los miembros estan bien de salud, curar si no
Como es el primero lo explicaré más detalladamente a modo de tutorial. De hecho es el ejemplo que hay en el script.
Supongamos que queremos que si todos los enemigos están bien de salud la ratio Atacar - Curar sea de 7 - 3, mientras que si no sea al revés, de 3 - 7.

Lo que haremos es crearlas por duplicado en el enemigo, pero con las frecuencias que he dicho.
Pongo en el esquema como van las ID para el script.

Spoiler:

Luego vamos dentro de set_custom_action_conditions. La ID del enemigo en el ejemplo es la 1 (es su número en la Base de Datos)

Para las acciones 0 y 1 (Cuando debe atacar) pondremos que la condición sea que la HP de ninguno de los miembros sea inferior al 40%, mientras que para las acciones 2 y 3 (Cuando debe curar) pondremos que la condición sea que la HP de al menos uno de los miembros sea inferior al 40%.

Código:
     for i in [0, 1] # IDs de acciones 0 y 1
        @actions[i].set_condition "
        not($game_troop.enemies.any? do |enemy| # No hay ninguno con <= 40%
          !enemy.dead? && enemy.hp * 100.0 / enemy.maxhp <= 40
        end)"
      end
      for i in [2, 3] # IDs de acciones 2 y 3
        @actions[i].set_condition "
        $game_troop.enemies.any? do |enemy| # Hay alguno con <= 40%
          !enemy.dead? && enemy.hp * 100.0 / enemy.maxhp <= 40
        end"
      end

Spoiler:

1B. Aplicar la skill de curación siempre al de menor HP
Siguiendo con el mismo ejemplo, pero ahora la parte de escojer bien el target de la acción. Lo lógico es curar a quien más lo necesita.
Nota: En la base de datos por defecto, la habilidad de curación afecta a todo el grupo así que ésta parte no tendría sentido.

Para editar el modo en el que se escoje el target iremos dentro del método decide_target_for_enemy.
Supongamos que la ID de la skill es 1 (eso es su órden en la Base de Datos), pues dentro del case @skill_id obtenemos los targets disponibles, quitamos los que estén muertos y luego elegimos el que tenga menor HP. El mejor modo es ordenar la array de los targets con sort teniendo en cuenta el parámetro que nos interesa, en éste caso la HP, pero hay otros modos de hacerlo. Una vez ordenada esa array usamos [0] para obtener el primero, es decir el de menos HP.

Código:
     targets = $game_troop.enemies.clone
      targets.delete_if {|target| target.dead?} # Quitar los muertos
      battler = targets.sort{|b,a| b.hp - a.hp}[0] # El de menor HP

Spoiler:

Tanto un código como el otro son bastante "macarrónicos" pero así explicados espero que no os parezcan tan raros ni difíciles de editar para adaptar a vuestra situación.

2A. Aplicar skill anti-veneno solo si hay álguien del grupo envenenado
Pues aunque no lo parezca es un caso bastante similar al primero (1A), pero más sencillo en el sentido de que no hay que duplicar acciones aquí, solo crear una con una frecuencia muy alta pero con una nueva condición: que al menos uno del grupo esté envenenado.

Supondremos que es la acción 3.

Código:
     @actions[3].set_condition "
        $game_troop.enemies.any?{|enemy| enemy.state?(2)} # ID del Estado Veneno
      "

2B. Aplicar la skill anti-veneno a un aliado solo si está envenenado
Sería tonto después de hacer el paso 2A que resultara que al final la acción se aplicara a uno de los enemigos que no está envenenado.

Dentro de decide_target_for_enemy ponemos lo siguiente:

Código:
     targets = $game_troop.enemies.clone
      targets = targets.select{|target| target.state?(2)} # ID del estado Veneno
      if targets.size > 0 # Ya definimos antes que al menos uno está envenenado
        battler = targets[rand(targets.size)] # Uno al azar (de los envenenados)
      end

Éstos son los dos ejemplos más típicos, y también los más completos porque requieren las dos fases.

3. Aplicar una skill de ataque siempre al de menor HP
Muy similar al 1B pero cambiando los targets por los actores de la party, al ser una skill ofensiva.
Se vuelve a usar sort para ordenar por HP y luego [0] para pillar el primero de la lista, es decir el de menor HP. Para atacar al que más HP tiene (otro tipo de estrategia) se usaría lo mismo pero con [-1] para seleccionar el último (en Array índice negativo significa contar desde atrás)

Código:
      if enemy_id == 1 # ID del enemigo
        targets = $game_party.actors.clone
        targets.delete_if {|target| target.dead?} # Quitar muertos
        battler = targets.sort{|b,a| b.hp - a.hp}[0] # El de menos HP
      end

4. Curar siempre al boss en vez de a los esbirros o a uno mismo
Otra modificación del target. En éste caso usamos la ID del grupo enemigo para saber si estamos luchando contra el boss y los esbirros. Si no es el caso, la selección será al azar como siempre. Lo mismo si sí que es el grupo pero el Boss está muerto. Por fin un código mucho más ameno en éste caso xD

Código:
      if enemy_id == 4 # ID del enemigo
        if $game_temp.battle_troop_id == 17 # ID del grupo enemigo
          battler = $game_troop.enemies[1] # ID del Boss
          if battler.dead? # Si el Boss está muerto se selecciona normal
            return decide_random_target_for_enemy
          end
        end
      else # Si no es el grupo enemigo con el Boss se selecciona normal
        return decide_random_target_for_enemy
      end

5. No usar una habilidad que daña a todo el grupo si solo hay un miembro vivo en éste
Una nueva condición para skill. Sería más fácil si no tuviéramos que tener en cuenta si están vivos o no, pero bueno viene a siendo del estilo que ya se ha visto en otros ejemplos.

Código:
      @actions[3].set_condition "
        actors = $game_party.actors.clone # Grupo de héroes
        actors.delete_if{|target| target.dead?} # Quitar los que están muertos
        actors.size > 1 # Solo se efectúa si hay más de uno
      "

6. Escapar si el nivel de la party es muy alto y el monstruo es una mierdica
Claro, también se pueden poner condiciones a la acción de Escapar del enemigo del mismo modo, eso es bastante útil. En cuanto la party ha llegado a cierto nivel no tiene sentido que los cutre-limos sigan intentando atacarla.

Código:
      # Escapar si el nivel es más alto de 10
      # (se usa el del actor con nivel más alto)
      @actions[2].set_condition "$game_party.actors.max_level > 10"

7. No aplicar magia de veneno a álguien que ya esté envenenado
Un clásico. Muy parecido al ejemplo anterior de veneno (2B) pero cambiando los objetivos puesto que es una acción ofensiva en éste caso.
Además en éste caso interesa que el objetivo no tenga el estado, al contrario de en el otro caso.

Código:
      targets = $game_party.actors.clone
      targets = targets.select{|target| !target.state?(2)} # ID del estado Veneno
      if targets.size > 0 # Ya definimos antes que al menos uno está envenenado
        battler = targets[rand(targets.size)] # Uno al azar (de los envenenados)
      end

Esos son todos los ejemplos prácticos que se me ocurren.
Ojalá ésto os ayude.
avatar
Wecoc
Administrador
Administrador



Créditos 9217

Gracias : 493

Volver arriba Ir abajo

RPG Maker XP Re: [XP] Lunatic Enemy A.I.

Mensaje por Producciones The Golden el 2017-01-18, 23:33

Recién voy a probarlo, pero... las dos veces que te dije lo de que estoy haciendo combates en mapa no fue una queja XD fue solo un comentario jajaaja.
avatar
Producciones The Golden
Veterano
Veterano

0/3

Créditos 639

Gracias : 60

Volver arriba Ir abajo

RPG Maker XP Re: [XP] Lunatic Enemy A.I.

Mensaje por Contenido patrocinado


Contenido patrocinado


Volver arriba Ir abajo

Ver el tema anterior Ver el tema siguiente Volver arriba


Permisos de este foro:
No puedes responder a temas en este foro.