Tuesday, November 29, 2016

Code Test, Part 2

hey all,

Here is a second test to see if I can get more code to show up nicely. I'm super new to HTML coding, and getting C# code to show up nicely on the web is not the easiest thing in the world. So, here goes nothing. Check below all the code for some commentary.

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

//set up a callback registration with emotions, moods, and personality

//amount of sleep
//amount of food
//sickness
//injuries
//exercise
public class PhysicalState : ScriptableObject
{

 PhysicalNeed[] needs = new PhysicalNeed[6];
 BodyState bodyState = BodyState.Resting;

 List < injury > injuries;

 List < disease > diseases;

 #region Setters and Getters


 public BodyState Body
 {
  get { return bodyState; }
  protected set { }
 }

 public float Hunger
 {
  get { return needs[(int)PhysicalNeedType.Food].severity; }
  protected set { }
 }

 public float Tiredness
 {
  get { return needs[(int)PhysicalNeedType.Rest].severity; }
  protected set { }
 }

 public float Thirst
 {
  get { return needs[(int)PhysicalNeedType.Hydration].severity; }
  protected set { }
 }

 public float Lust
 {
  get { return needs[(int)PhysicalNeedType.Sex].severity; }
  protected set { }
 }

 public float Comfort
 {
  get { return needs[(int)PhysicalNeedType.Comfort].severity; }
  protected set { }
 }

 public float Cleanliness
 {
  get { return needs[(int)PhysicalNeedType.Cleanliness].severity; }
  protected set { }
 }

 #endregion


 //use PhysicalEventArgs here
 public delegate void PhyscialStateChangeHandler(object source, PhysicalEventArgs args);//set up delegate
 public event PhyscialStateChangeHandler PhysicalStateChanged;//define event based on event
                 //raise the event

 float timer = 5f;

 // Use this for initialization
 void Start ()
 {
  injuries = new List < injury > ();
  diseases = new List < disease > ();
  SetupPhysicalState();
 }
 
 // Update is called once per frame
 void Update ()
 {
  timer -= Time.deltaTime;
  if (timer < 0)
  {
   timer = 5f;
   bodyState = (BodyState)UnityEngine.Random.Range(0, 5);
  }
  switch(bodyState)
  {
   case BodyState.Bathing:
    ChangePhysicalState(PhysicalNeedType.Cleanliness, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Comfort, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Rest, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Food, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Hydration, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Sex, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    break;
   case BodyState.Drinking:
    ChangePhysicalState(PhysicalNeedType.Cleanliness, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Comfort, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Rest, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Food, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Hydration, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Sex, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    break;
   case BodyState.Eating:
    ChangePhysicalState(PhysicalNeedType.Cleanliness, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Comfort, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Rest, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Food, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Hydration, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Sex, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    break;
   case BodyState.Resting:
    ChangePhysicalState(PhysicalNeedType.Cleanliness, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Comfort, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Rest, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Food, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Hydration, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Sex, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    break;
   case BodyState.Sexing:
    ChangePhysicalState(PhysicalNeedType.Cleanliness, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Comfort, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Rest, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Food, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Hydration, +Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    ChangePhysicalState(PhysicalNeedType.Sex, -Time.deltaTime * needs[(int)PhysicalNeedType.Cleanliness].changeRate);
    break;
  }
 }

 void SetupPhysicalState()
 {
  //warmth = 100f;
  //lust = 0f;
  //thirst = 0f;
  //hunger = 0f;
  //tiredness = 0f;
  for (int i = 0; i < needs.Length; i++)
  {
   needs[i] = new PhysicalNeed(0f, 1f, 1000f, (PhysicalNeedType)i);
  }
  Debug.Log("Physical needs setup");
 }

 public void ChangePhysicalState(PhysicalNeedType ty, float amount)
 {
  switch (ty)
  {
   case PhysicalNeedType.Food:
    needs[(int)PhysicalNeedType.Food].severity = Mathf.Clamp(needs[(int)PhysicalNeedType.Food].severity + amount, 0f, 100f);
    //needs[(int)PhysicalNeedType.Food].severity += amount;
    break;
   case PhysicalNeedType.Hydration:
    needs[(int)PhysicalNeedType.Hydration].severity = Mathf.Clamp(needs[(int)PhysicalNeedType.Hydration].severity + amount, 0f, 100f);
    //needs[(int)PhysicalNeedType.Hydration].severity += amount;
    break;
   case PhysicalNeedType.Rest:
    needs[(int)PhysicalNeedType.Rest].severity = Mathf.Clamp(needs[(int)PhysicalNeedType.Rest].severity + amount, 0f, 100f);
    //needs[(int)PhysicalNeedType.Rest].severity += amount;
    break;
   case PhysicalNeedType.Sex:
    needs[(int)PhysicalNeedType.Sex].severity = Mathf.Clamp(needs[(int)PhysicalNeedType.Sex].severity + amount, 0f, 100f);
    //needs[(int)PhysicalNeedType.Sex].severity += amount;
    break;
   case PhysicalNeedType.Comfort:
    needs[(int)PhysicalNeedType.Comfort].severity = Mathf.Clamp(needs[(int)PhysicalNeedType.Comfort].severity + amount, 0f, 100f);
    //needs[(int)PhysicalNeedType.Comfort].severity += amount;
    break;
   case PhysicalNeedType.Cleanliness:
    needs[(int)PhysicalNeedType.Cleanliness].severity = Mathf.Clamp(needs[(int)PhysicalNeedType.Cleanliness].severity + amount, 0f, 100f);
    //needs[(int)PhysicalNeedType.Cleanliness].severity += amount;
    break;
   default:
    break;
  }
  OnPhysicalStateChanged();
 }

 //for use with the delegate
 //any thing that changes the physical state should call this function
 protected void OnPhysicalStateChanged()
 {
  if (PhysicalStateChanged  != null)
  {
   //treating it like a function
   //this is used to notify subscribers to this event
   PhysicalEventArgs p = new PhysicalEventArgs();
   p.phys = this;
   PhysicalStateChanged(this, p);
   //Debug.Log("Physical State changed");
  }
 }

 
}

//set up new class for EventArgs
public class PhysicalEventArgs : EventArgs
{
 public PhysicalState phys { get; set; }
}

public class PhysicalNeed:Need
{
 //[Range(0, 100)]
 //public float severity;
 //research the actual rate at which each physical need must be met
 //water: about 250ml every 3 hours, for a total of 2,000ml per day. more could cause problems. but depends on exercise level and temperature.
 //food: 2,000~3,000kC every day, spread between 2~5 meals. also depends on exercise
 //rest: between 6~9 hours per day. 
 //sex:??? once/twice per week?
 //cleanliness: about once per day, depending on exercise
 //comfort: ???increases if:
 //                          standing
 //                          working
 //                          sitting on ground
 //                          
 //
 //
 //public float changeRate;
 public PhysicalNeedType type;

 public PhysicalNeed(float sev, float ch, float mult, PhysicalNeedType ty):base(sev,ch,mult)
 {
  //base.Need(sev, ch, mult);
  type = ty;
 }
}

public enum PhysicalNeedType
{
 Food,
 Hydration,
 Rest,
 Sex,
 Comfort,
 Cleanliness
}

public enum BodyState
{
 Resting,
 Eating,
 Drinking,
 Sexing,
 Bathing
}

public struct Injury
{
 [Range(0, 100)]
 float severity;
 InjuryType type;
 Location loc;
}

public enum InjuryType
{
 Burn,
 Cut,
 Puncture,
 Bruise,
 Fracture
}

public struct Disease
{
 [Range(0, 100)]
 float severity;
 DiseaseVector cause;
 bool isInfectious;
 Location loc;
}

public enum Location
{
 Head,
 Neck,
 Chest,
 Abdomen,
 RightArm,
 LeftArm,
 Back,
 RightLeg,
 LeftLeg
}

public enum DiseaseVector
{
 Bacteria,
 Virus,
 Fungus
}

Hopefully what you saw was a lot of decently formatted C# code. One thing to note is that right now the rate at which each physical need increases is linear. In fact, after one second of time in the appropriate body state, the opposite physical needs will have increased by one.


This is a really boring relationship, and what I'd really like is something more like this:




With this, you set the maximum need level, max, and play around with the steepness and x mid-point values. For example, for water, we need about 250ml every three hours. After drinking, our need for water should be basically zero, and it will slowly increase. Eventually, after long enough without water, our thirst will get worse and worse at an increasing rate, until the derivative of the curve switches sign and we are so close to dying of thirst that a little more time barely changes our thirst level. The max level should be set to 100, but what about the x mid-point value? Assuming that all those medical studies about dehydration are true, lets say the x mid-point value should be 12 hours. What about the steepness level? Again, playing around with some different values, 0.5 produces an OK looking curve. Most probably, I am going to create a separate class to hold an array of floats, then use some form of interpolation to get any values in between them.

The next part of this whole deal is how should the level of need affect one's mood? 

This particular graph here from GameAI.com is the closest I've seen to anyone trying to figure this out in a concrete way. However, please stop and try to figure out what the graph means. What is on the X-axis for instance? Is it displaying the amount of food you have or is it displaying how hunger affects happiness? Either way, it makes no sense. Assuming that the X-axis shows how much food you have, if hunger is at +100, it might be reasonable to assume that it has a small positive affect on mood, but if hunger is at -100, why should it have a large positive affect on mood?  And assuming that mood is on the X-axis, if you have a small amount of food, why would that produce only a small positive affect on mood, while having lots of food would produce a large negative affect on mood? 

This is my own attempt at it and I think it makes a lot more sense. Need is on the X-axis, and mood is on the Y-axis. If your need is below 50, you get a positive bonus to mood, which increases the lower your need. If your need is above 50, however, you get increasing penalties to your mood. 








Why bother with all this? In this talk about a procedural game system, Ken Levine talks about the possibility of having NPCs in the game world that have desires and ambitions that the player can help or frustrate through actions.

 Since I am making the game by myself at this point, I cannot spend hours and hours writing scripted story lines for hundreds of characters. I'd much rather do something like what Ken Levine says, or take yet another page from Mount & Blade: Warband and have NPCs that react to what the player does based on their "wants and desires." The end goal is to have a system where the NPCs can generate quests for the player dynamically based on what they want and need at the moment: like a peasant asking the player to kill some monsters infesting a field, preventing the peasant from farming and getting food to feed the family. The need is for food, but the desire for safety prevents the peasant from going and getting it him/herself.

Thursday, November 24, 2016

Ancient Water

hey all,

I haven't had much time to continue working on my RPG project, but I have gotten a little work done on a small prototype board game. The theme is about water and population management. The play set up is like this: you have four boxes representing the amount of water contained in the river during each season of the year. The blue slips of paper represent water, the green ones crops, and the blank ones people. The cards with the I's represent levels of irrigation.


During each turn, the player roles the dice to determine how many water slips to put in the top box. The player must also decide where to put the people. Putting all the people in one area will allow for faster construction of irrigation, while spreading the people out will allow for gathering water in more places and thus growing more crops sooner. In the picture above, I've placed all my people in the first area and have build one irrigation. Without the irrigation, I would be limited to collecting one water slip per turn, but with irrigation I can receive two slips. It takes two slips of water to be converted into one crop, so this allows for more concentrated crop growth, and in turn higher population growth.

Each turn, all the boxes are moved down to the next row, and the bottom box is emptied and returned to the top to be filled.

I haven't decided if the crops will be consumed by the people yet, nor if the people will die after X turns, nor if the player must have a certain number of crops to feed their people. 

There are currently no end game goals, but I imagine reaching a certain population by X turns, or maintaining a certain population for X turns might be good ones.

Similar to my digestion game, this is designed to teach about the importance of water management for the Mesopotamian, Egyptian, Indus Valley, and Chinese civilizations.

If you use the Machinations tool by Joris Dormans, you can try a rough playable version by opening this file.

Any suggestions or comments?

Tuesday, November 22, 2016

Test

hey all, This is mostly just a test to see if I can get my C# code to show up.
    [Range(0, 100)]
    float hunger;
    [Range(0, 100)]
    float tiredness;
    [Range(0, 100)]
    float thirst;
    [Range(0, 100)]
    float lust;
    [Range(0, 100)]
    float warmth;

I originally started with something like the above.But as I thought more about it, I realized that a struct is probably closer to what is needed.

PhysicalNeed[] needs = new PhysicalNeed[6];

public struct PhysicalNeed
{
    [Range(0, 100)]
    float severity;
    float changeRate;
    PhysicalNeedType type;
}

public enum PhysicalNeedType
{
    Food,
    Hydration,
    Rest,
    Sex,
    Comfort,
    Cleanliness
}

Upon starting, the array of physical needs needs to be initialized, and during each update loop, the NPC's physical needs are updated depending on the NPC's physical state: if the NPC is sleeping, the need for rest will decrease, while if working, all the needs will slowly increase.

I also have some callbacks setup between the PhysicalState class and the Mood class, so that whenever the PhysicalState class is updated, it will update the NPC's mood.

Sunday, November 20, 2016

First Post

hey all,

It feels weird to be typing on blogger again after so long absent. My previous blog, Blog of An ALT, focused on my personal life here in Japan as an English teacher. Thing is, I'm not a teacher any more, or at least, not for much longer. I've quit my job and am looking to get into the game industry. As part of that new direction in my life, I've decided to start this new blog.

It will focus on the games I'm working on, the games I'm playing, and of course the trials and tribulations of both. The current plan is to post every Thursday about what I've been working on and playing during the rest of the week.

For example, I am a huge fan of Mount & Blade: Warband, and I've spent a few hours this week getting some ground work in on an RPG that I would like to model after it. Most of this involved setting up the basic classes that I need, like Weapon, Item, Attribute, Skill, Player, etc. I've also been doing some work on modelling emotions, moods, physical state, personality, and opinions, but this has not progressed too far. The goal is to have the NPCs in my RPG work something like the Sims in The Sims:  they will have different personalities, and basic physical, emotional, and social needs to be met, and different opinions about objects, other NPCs (or groups of NPCs), and the player. The player will be able to assist or frustrate those needs by interaction with the NPCs and objects in the world, but they will also be able to try to meet their own needs.

More to come next week.