CreatureFactory.java :  » Game » yarg-roguelike » creid » mythos » util » Java Open Source

Java Open Source » Game » yarg roguelike 
yarg roguelike » creid » mythos » util » CreatureFactory.java
/********************************************************************
 * CreatureFactory
 * 
 * Defines creature types and provides methods to generate creatures
 * with given constraints (level, rarity, etc)
 *******************************************************************/

package creid.mythos.util;

import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import creid.mythos.*;

public class CreatureFactory
{
  //--------------------------------------------------------
  // Constants
  //--------------------------------------------------------

  //Game file columns
  //KEY  NAME  SYMBOL  COLOR  LEVEL  RARITY  
  //HEALTH  BODY  COORDINATION  MIND  SPEED  PERCEPTION  
  //FACTION  NATURAL_ARMOR  AI_TYPE  PROPERTIES
  public static final int CREATURE_COLUMNS = 17;
  public static final int COL_KEY = 0;
  public static final int COL_NAME = 1;
  public static final int COL_SYMBOL = 2;
  public static final int COL_COLOR = 3;
  public static final int COL_LEVEL = 4;
  public static final int COL_RARITY = 5;
  public static final int COL_HEALTH = 6;
  public static final int COL_BODY = 7;
  public static final int COL_COORD = 8;
  public static final int COL_MIND = 9;
  public static final int COL_SPEED = 10;
  public static final int COL_PERCEPTION = 11;
  public static final int COL_FACTION = 12;
  public static final int COL_AI = 13;
  public static final int COL_MOVE_MASK =  14;
  public static final int COL_PROPS =  15;
  
  public static final char ANY_SYMBOL = ' ';

  
  //--------------------------------------------------------
  // Attributes
  //--------------------------------------------------------

  //Creature Master List
  public HashMap<String, Creature> bestiary;

  //--------------------------------------------------------
  // Constructor
  //--------------------------------------------------------
  
  //populates the geography and catalogue
  public CreatureFactory()
  {  
    //Load creature data from file
    LinkedList<Creature> creatures = loadCreatures();
    
    //Populate the geography and count the occurences of the 
    //different rarities
    bestiary = new HashMap<String, Creature>();
        
    for (Creature c : creatures)
    {
      //add to bestiary
      bestiary.put(c.id, c);
    }
  }
  
  //--------------------------------------------------------
  // genCreature
  // Return a randomly-chosen creature adhering to the given
  // constraints.
  //
  // level: maximum allowed level. If ANY, accept all levels
  // symbol: specific type.  If ANY, accept all types.
  // rarity: specific rarity.  If ANY, a random rarity will be chosen
  // requiredProps: if null, accept all. Otherwise only accept items
  //   with that have one or all of the properties
  // matchAllProps: if false, allow items with one or more required props
  //   if true, match all of the listed properties
  // matchValues: if true, properties must have same values as requiredProps,
  //   otherwise just require the properties to exist.
  //--------------------------------------------------------
    
  public Creature genCreature(int level, char symbol, int rarity)
  {
    char[] typeArr = { ANY_SYMBOL };
    return genCreature(level, typeArr, rarity, new Property[0], new char[0], 
        new Property[0], false, false);
  }
  
  public static final int ANY = -99;
  public Creature genCreature(int level, char[] types, int rarity, Property[] requiredProps, 
      char[] forbidTypes, Property[] forbidProps,
      boolean matchAllProps, boolean matchValues)
  {
    if (rarity == ANY)
    {
      rarity = Mythos.callRNG(1, 31);
      switch (rarity)
      {
        case 1:
          rarity = Entity.UNHEARD_OF;
          break;
        case 2:
        case 3:
          rarity = Entity.VERY_RARE;
          break;
        case 4:
        case 5:
        case 6:
        case 7:
          rarity = Entity.RARE;
          break;
        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:
        case 15:
          rarity = Entity.UNCOMMON;
          break;
        default:
          rarity = Entity.COMMON;
      }
    }
    
    LinkedList<Creature> list = new LinkedList<Creature>();
    for (String id : bestiary.keySet())
    {
      Creature i = bestiary.get(id);
      
      //Skip items that do not match our criteria
      if (level != ANY && i.level > level)
        continue;
      
      if (types[0] != ANY_SYMBOL)
      {
        boolean match = false;
        
        for (char t : types)
          if (i.symbol == t)
            match = true;
        
        if (!match) continue;
      }
      
      //Check for forbidden types
      if (forbidTypes != null && forbidTypes.length > 0)
      {
        for (char t: forbidTypes)
          if (t == i.symbol)
            continue;
      }
      
      if(rarity < i.rarity || i.rarity == Entity.NEVER_SPAWN)
        continue;
      
      //Check properties
      if (requiredProps != null && requiredProps.length > 0)
      {
        boolean hasAny = false;
        boolean missing = false;
        boolean mismatch = false;
        
        for (Property prop : requiredProps)
        {
          if (i.hasProperty(prop.name))
            hasAny = true;
          else
            missing = true;
          
          if (prop instanceof SProperty)
          {
            if (matchValues && !i.getSProperty(prop.name).value.equals(((SProperty)prop).value))
              mismatch = true;
          }
          else if (prop instanceof NProperty)
          {
            if (matchValues && i.getNProperty(prop.name).value != ((NProperty)prop).value)
              mismatch = true;
          }
        }
        
        //Did we pass?
        if (matchAllProps && missing)
          continue;
        
        if (matchValues && mismatch)
          continue;
        
        if (!hasAny)
          continue;
      }
      
      //Check for forbidden props
      if (forbidProps != null && forbidProps.length > 0)
      {
        for (Property p : forbidProps)
          if (i.hasProperty(p.name))
            continue;
      }
      
      //This item is acceptable to us
      list.add(i);
    }
    
    //If there are any items, choose one to return
    if (list.size() > 0)
    {
      int choice = Mythos.callRNG(0, list.size() - 1);
      
      return new Creature(list.get(choice));
    }
    else
      //No items exist that match the specifications
      return null;
  }
  
  //--------------------------------------------------------
  // randomCreature(int min, int max, bool ood)
  // 
  // Spawn a random creature within the given level range
  // and toggle allowing (rarely) something unusually deeper 
  // to be spawned.
  //--------------------------------------------------------
  
  public Creature randomCreature(int minLvl, int maxLvl, boolean allowOOD)
  {
    //Select how unusual of a creature we will create; each successive rarity
    //is twice as unlikely to be included.
    //Note that we do not pick a rarity and choose something with that rarity,
    //rather we pick a rarity and disallow anything more rare than that, so
    //with unheardOf rarity selected, we allow common, uncommon, rare, very rare
    //and unheardOf creatures, so unheardOf is very rare indeed!
    int rarity = Mythos.callRNG(1, 31);
    switch (rarity)
    {
      case 1:
        rarity = Entity.UNHEARD_OF;
        break;
      case 2:
      case 3:
        rarity = Entity.VERY_RARE;
        break;
      case 4:
      case 5:
      case 6:
      case 7:
        rarity = Entity.RARE;
        break;
      case 8:
      case 9:
      case 10:
      case 11:
      case 12:
      case 13:
      case 14:
      case 15:
        rarity = Entity.UNCOMMON;
        break;
      default:
        rarity = Entity.COMMON;
    }
    
    //Small chance for out-of-depth generation
    while (allowOOD && Mythos.callRNG(0, Creature.ODDS_OUT_OF_DEPTH) == 0)
      maxLvl += 5;
    
    //Now use the rarity to build our pool of creatures
    LinkedList<Creature> pool = new LinkedList<Creature>();
    
    for (Iterator<Creature> it = bestiary.values().iterator(); it.hasNext(); )
    {
      Creature candidate = it.next();

      if (candidate.level <= maxLvl && candidate.level >= minLvl &&
          candidate.rarity <= rarity)
        pool.add(candidate);
    }

    //Create a clone of the selected creature in the geography
    Creature c = new Creature(pool.get(Mythos.callRNG(0, pool.size() - 1)));
    
    //Any individualization will be done here
    
    //Return the finished product
    return c;
  }
  
  //--------------------------------------------------------------------------
  // loadCreatures
  //
  // Extract creature data from creature.game file
  //--------------------------------------------------------------------------
  public LinkedList<Creature> loadCreatures()
  {
    //Extract the monsters from the saved file
    LinkedList<Creature> monsterList = new LinkedList<Creature>();
    try
    {
      BufferedReader fin = Mythos.readDataFile(Mythos.CREATURE_GAME_FILE);
            
      //read in creatures from the file
      while (fin.ready())
      {
        String line = fin.readLine().trim();
        
        //skip blank lines
        if (line.length() < 1)
          continue;
        
        //skip comments
        if (line.charAt(0) == '#')
          continue;
        
        String[] input = line.split("\t");
        
        //skip badly formatted lines
        if (input.length != CREATURE_COLUMNS)
        {
          Mythos.logger.debugLog("Invalid input line:" + line);
        }
        
        //KEY  NAME  SYMBOL  COLOR  LEVEL  RARITY  
        //HEALTH  BODY  COORDINATION  MIND  SPEED  PERCEPTION  
        //FACTION  NATURAL_ARMOR  AI_TYPE  PROPERTIES
        String id = input[COL_KEY];
        String name = input[COL_NAME];
        char symbol = input[COL_SYMBOL].charAt(0);
        int color = Entity.parseColor(input[COL_COLOR]);
        int level = Integer.parseInt(input[COL_LEVEL]);
        int rarity = Entity.parseRarity(input[COL_RARITY]);
        int health = Integer.parseInt(input[COL_HEALTH]);
        int body = Integer.parseInt(input[COL_BODY]);
        int coordination = Integer.parseInt(input[COL_COORD]);
        int mind = Integer.parseInt(input[COL_MIND]);
        int speed = Integer.parseInt(input[COL_SPEED]);
        int perception = Integer.parseInt(input[COL_PERCEPTION]);
        int faction = Entity.parseFaction(input[COL_FACTION]);
        //int ai = Integer.parseInt(input[COL_AI]);
        int moveMask = Entity.parseMoveMask(input[COL_MOVE_MASK]);
        
        String[] properties = null;
        if (!input[COL_PROPS].trim().equals("-"))
          properties = input[COL_PROPS].split(":");
        
        Creature c = new Creature(id, name, symbol, color, health, body, coordination,
            mind, speed, moveMask, 0, perception, faction, -99, level,
            rarity, false, false);
        
        if (properties != null)
          for (String prop : properties)
          {
            String[] part = prop.split("=");
            
            //Natural Armor
            if(part[0].equals("naturalArmor"))
            {
              StatEffect narmor = new StatEffect("naturalArmor", StatEffect.STAT_ARMOR, Integer.parseInt(part[1]), Effect.FOR_DURATION, Effect.UNLIMITED, Effect.UNLIMITED);
              narmor.apply(c);
            }
            else
            {
              //Is value a number?
              try
              {
                c.setProperty(part[0], Integer.parseInt(part[1]));
              }
              catch(NumberFormatException ex)
              {
                c.setProperty(part[0], part[1]);
              }
            }
          }
        
        monsterList.add(c);
      }
    }
    catch (Exception ex)
    {
      Mythos.logger.debugLog(ex.toString());
      System.err.println(ex);
      System.exit(1);
    }
    
    //return as array
    return monsterList;
  }
  
  //--------------------------------------------------------------------------
  // spawnedUnique:
  // some monsters have limited generation. Decrement their unique value each
  // time one is spawned, and remove them from the bestiary when unique=0
  //--------------------------------------------------------------------------
  
  public void spawnedUnique(Creature unique)
  {
    //Paranoia: don't do anything to a non-unique critter
    if (!unique.hasProperty("unique"))
      return;
    
    NProperty uniqueness = unique.getNProperty("unique");
    uniqueness.value--;
    
    if (uniqueness.value < 1)
    {
      Mythos.logger.debugLog(unique.name + " will no longer be spawned");
      
      //Remove them from our choice of creatures
      bestiary.remove(unique.id);
    }
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.