My Shortie: Ray Camden's Beginner ColdFusion Contest (Monster Maker)

<cfcomponent
	output="false"
	hint="This is your shortie; treat her well.">

	<!---
		Run pseudo constructor. Here is where we can set
		up default data structures and data values.
	--->

	<!---
		Create a private data structure to hold all instance
		related data.
	--->
	<cfset VARIABLES.Instance = StructNew() />

	<!---
		This is the brain that the shortie will use to persist
		data. This is a place holder. The actual data fill be
		passed in during construction.
	--->
	<cfset VARIABLES.Instance.Brain = "" />


	<!---
		These are the base line values on the shortie
		properties. These are the values that will be updated
		by the interactions. These will be used to calculate
		the overall mood and health of your shortie. A positive
		number indicates "more" of that property where as a
		negative value indicates "less" of that property.

		For a more in-depth explanation of what these
		properties mean, check out the Interaction.cfc code.
	--->
	<cfset VARIABLES.Instance.Anger = 0 />
	<cfset VARIABLES.Instance.Energy = 0 />
	<cfset VARIABLES.Instance.Hunger = 0 />
	<cfset VARIABLES.Instance.Happiness = 0 />
	<cfset VARIABLES.Instance.Love = 0 />


	<!---
		This is the current time in which the Shortie exists.
		This is not necessarily reflective of the real world
		time and it might be overriden by the Brain.
	--->
	<cfset VARIABLES.Instance.Time = Now() />

	<!---
		This is the real world time. We need to keep track of
		this so we can later on figure out how much time has
		passed since we last calculated the time.
	--->
	<cfset VARIABLES.Instance.RealTime = Now() />

	<!---
		This is the time scale to days of real world time
		to shortie world time. Hour shortie hours are going
		to equal 30 real world seconds. Since our time
		difference are going to be calculated in fractions
		of a day, we need to figure out how to multiple our
		day fractions to get shortie hours.

		H = Hour
		D = Day
		SH = Shortie Hour
		SD = Shortie Day

		1 H == 60 * 2 SH
		24 H == 60 * 2 * 24 SH
		1 D == 60 * 2 * 24 / 24 SD
		1 D == 60 * 2 SD
		1 D == 120 SD
	--->
	<cfset VARIABLES.Instance.DayScale = 120 />

	<!---
		This is the time at which the next interaction will
		be available. This is to prevent you from abusing your
		shortie and to take into account that each interaction
		does require some time to enact. For now, we will just
		set it to now(), but that can be overridden.
	--->
	<cfset VARIABLES.Instance.NextInteractionTime = Now() />


	<cffunction
		name="Init"
		access="public"
		returntype="any"
		output="false"
		hint="Returns an initialized shortie instance.">

		<!--- Define arguments. --->
		<cfargument
			name="Brain"
			type="any"
			required="true"
			/>

		<!---
			Store the passed in brain object. The brain object
			should already have the persisted data loaded into
			it, so no need to alter the Brain in any way.
		--->
		<cfset VARIABLES.Instance.Brain = ARGUMENTS.Brain />

		<!--- Load the brain data. --->
		<cfset VARIABLES.LoadData() />

		<!---
			Adjust time to get the Shortie's time scale the
			current time (if time has elapsed since that last
			time the Application was fired up).
		--->
		<cfset THIS.AdjustTime() />


		<!--- Return This reference. --->
		<cfreturn THIS />
	</cffunction>


	<cffunction
		name="AdjustTime"
		access="public"
		returntype="void"
		output="false"
		hint="This adjusts the shortie time to make sure it scales to regular world time increments.">

		<!--- Define the local scope. --->
		<cfset var LOCAL = StructNew() />

		<!--- Get the current time. --->
		<cfset LOCAL.Now = Now() />

		<!---
			Get the difference in real world time that has
			elapsed since our last time adjustment. This will
			give us the fraction of days that have passed.
			Then, we need to multiple that by are day factor to
			get how many shortie hours have passed.
		--->
		<cfset LOCAL.DayDiff = (
			(LOCAL.Now - VARIABLES.Instance.RealTime) *
			VARIABLES.Instance.DayScale
			) />

		<!---
			This is the actual date that we need to adjust
			our internal time value to.
		--->
		<cfset LOCAL.TargetTime = (
			VARIABLES.Instance.Time +
			LOCAL.DayDiff
			) />

		<!---
			Increment the time. This will take care of
			actually updating the internal time value. We want
			in increase the time only that is required.
		--->
		<cfset VARIABLES.IncrementTime(
			(LOCAL.TargetTime - VARIABLES.Instance.Time)
			) />

		<!---
			Update the real time so that our date-diffs don't
			lose track of our scaled time.
		--->
		<cfset VARIABLES.Instance.RealTime = LOCAL.Now />

		<!--- Return out. --->
		<cfreturn />
	</cffunction>


	<cffunction
		name="CommitData"
		access="private"
		returntype="void"
		output="false"
		hint="This commits short term memory to the brain.">

		<!--- Commit data to brain. --->
		<cfset VARIABLES.Instance.Brain.Set(
			Property = "Anger",
			Value = VARIABLES.Instance.Anger,
			CommitData = false
			) />

		<cfset VARIABLES.Instance.Brain.Set(
			Property = "Energy",
			Value = VARIABLES.Instance.Energy,
			CommitData = false
			) />

		<cfset VARIABLES.Instance.Brain.Set(
			Property = "Hunger",
			Value = VARIABLES.Instance.Hunger,
			CommitData = false
			) />

		<cfset VARIABLES.Instance.Brain.Set(
			Property = "Happiness",
			Value = VARIABLES.Instance.Happiness,
			CommitData = false
			) />

		<cfset VARIABLES.Instance.Brain.Set(
			Property = "Love",
			Value = VARIABLES.Instance.Love
			) />

		<cfset VARIABLES.Instance.Brain.Set(
			Property = "Time",
			Value = VARIABLES.Instance.Time
			) />

		<!--- Return out. --->
		<cfreturn />
	</cffunction>


	<cffunction
		name="GetNextInteractionTime"
		access="public"
		returntype="numeric"
		output="false"
		hint="Gets the time at which the Shortie can next be interacted with.">

		<cfreturn VARIABLES.Instance.NextInteractionTime />
	</cffunction>


	<cffunction
		name="GetProperties"
		access="public"
		returntype="struct"
		output="false"
		hint="Returns the 'mental' properties of the shortie.">

		<!--- Define the local scope. --->
		<cfset var LOCAL = StructNew() />

		<!--- Store properties locally. --->
		<cfset LOCAL.Anger = VARIABLES.Instance.Anger />
		<cfset LOCAL.Energy = VARIABLES.Instance.Energy />
		<cfset LOCAL.Hunger = VARIABLES.Instance.Hunger />
		<cfset LOCAL.Happiness = VARIABLES.Instance.Happiness />
		<cfset LOCAL.Love = VARIABLES.Instance.Love />

		<!--- Return local property copy. --->
		<cfreturn LOCAL />
	</cffunction>


	<cffunction
		name="GetTime"
		access="public"
		returntype="string"
		output="false"
		hint="Returns the shortie time (internal time model).">

		<!---
			Return the time as a string with full date / time
			stamp formatting.
		--->
		<cfreturn (
			DateFormat(
				VARIABLES.Instance.Time,
				"mm/dd/yyyy "
				) &
			TimeFormat(
				VARIABLES.Instance.Time,
				"hh:mm TT"
				)
			) />
	</cffunction>


	<cffunction
		name="IncrementTime"
		access="private"
		returntype="void"
		output="false"
		hint="This increments time.">

		<!--- Define arguments. --->
		<cfargument
			name="TimeSpan"
			type="numeric"
			required="false"
			default="#CreateTimeSpan( 0, 1, 0, 0 )#"
			hint="The time to increase for this iteration (defaults to an hour)."
			/>


		<!--- Increment the internal time. --->
		<cfset VARIABLES.Instance.Time = (
			VARIABLES.Instance.Time +
			ARGUMENTS.TimeSpan
			) />

		<!---
			Every time we increment the time, it takes a
			toll on the shortie. Time makes you tired, it
			makes you hungry. This is on top of any
			interactions that have just taken place. For every
			four hours that pass, the shortie loses 1 energy
			units (scaled to our time span).
		--->
		<cfset VARIABLES.Instance.Energy = (
			VARIABLES.Instance.Energy -
			(
				1 *
				ARGUMENTS.TimeSpan / CreateTimeSpan( 0, 4, 0, 0 )
			)
			) />

		<cfset VARIABLES.Instance.Hunger = (
			VARIABLES.Instance.Hunger +
			(
				1 *
				ARGUMENTS.TimeSpan / CreateTimeSpan( 0, 4, 0, 0 )
			)
			) />


		<!--- Commit the data changes to the brain. --->
		<cfset VARIABLES.CommitData() />

		<!--- Return out. --->
		<cfreturn />
	</cffunction>


	<cffunction
		name="Interact"
		access="public"
		returntype="void"
		output="false"
		hint="This is how you can pass in an interaction. This will update the internal state.">

		<!--- Define arguments. --->
		<cfargument
			name="Interaction"
			type="any"
			required="true"
			hint="This is an interaction that must implement the interaction interface."
			/>

		<!--- Define the local scope. --->
		<cfset var LOCAL = StructNew() />

		<!---
			Before we do any interaction, we have to adjust
			the time. We might not even be able to perform an
			interaction and we will need to adjust time in
			order to figure that out.
		--->
		<cfset THIS.AdjustTime() />


		<!---
			Now that we have the proper time, check to see if
			we can perform an interaction.
		--->
		<cfif (VARIABLES.Instance.NextInteractionTime GT VARIABLES.Instance.Time)>

			<!---
				We have not yet reached our time of next
				possible interaction. Just return out of the
				interaction method call.
			--->
			<cfreturn />

		</cfif>


		<!---
			ASSERT: At this point, we know that we have waited
			long enough to perform another interaction.
		--->


		<!--- Get the time required for this interaction. --->
		<cfset LOCAL.TimeRequired = ARGUMENTS.Interaction.GetTimeRequired() />

		<!---
			This interactoin will "lock" the shortie's ability
			to interact for a given amount of time. Keep track
			of that time by adding the time required to the
			current shortie time.
		--->
		<cfset VARIABLES.Instance.NextInteractionTime = (
			VARIABLES.Instance.Time +
			LOCAL.TimeRequired
			) />


		<!---
			With all interactions, there is some degree of
			error and randomness. We are going to produce
			this randomness and we are going to do it on
			several levels.

			Create a general multiplier. We are going to
			default this to one, but one time out of 30
			(statistically) this is going to be totally
			haywire and flip.
		--->
		<cfif (RandRange( 1, 30 ) EQ 15)>

			<!---
				This interaction is NOT going to make any
				sense. But that's ok - that is life sometimes.
			--->
			<cfset LOCAL.Multiplier = -1 />

		<cfelse>

			<!--- This is a standard interaction. --->
			<cfset LOCAL.Multiplier = 1 />

		</cfif>


		<!---
			ASSERT: At this time, we have determines if this
			going to be a completely non-sensical interaction
			or a normal interaction. Now, we can determine the
			minor randomness.
		--->


		<!---
			Create an interum struct that will hold the deltas
			for our internal properties.
		--->
		<cfset LOCAL.Delta = StructNew() />

		<!---
			Get the delta values from the interaction. When
			getting the base data, add some minor noise to
			each value and multiple the random multiplier.
		--->
		<cfset LOCAL.Delta.Anger = (
			(
				ARGUMENTS.Interaction.GetAnger() *
				LOCAL.Multiplier
			) +
			RandRange( -1, 1 )
			) />

		<cfset LOCAL.Delta.Energy = (
			(
				ARGUMENTS.Interaction.GetEnergy() *
				LOCAL.Multiplier
			) +
			RandRange( -1, 1 )
			) />

		<cfset LOCAL.Delta.Hunger = (
			(
				ARGUMENTS.Interaction.GetHunger() *
				LOCAL.Multiplier
			) +
			RandRange( -1, 1 )
			) />

		<cfset LOCAL.Delta.Happiness = (
			(
				ARGUMENTS.Interaction.GetHappiness() *
				LOCAL.Multiplier
			) +
			RandRange( -1, 1 )
			) />

		<cfset LOCAL.Delta.Love = (
			(
				ARGUMENTS.Interaction.GetLove() *
				LOCAL.Multiplier
			) +
			RandRange( -1, 1 )
			) />

		<cfset LOCAL.Delta.IsPositive = ARGUMENTS.Interaction.GetIsPositive() />


		<!---
			Now that we have the delta values with random
			noise, update the internal data model.
		--->
		<cfset VARIABLES.Instance.Anger = (
			VARIABLES.Instance.Anger +
			LOCAL.Delta.Anger
			) />

		<cfset VARIABLES.Instance.Energy = (
			VARIABLES.Instance.Energy +
			LOCAL.Delta.Energy
			) />

		<cfset VARIABLES.Instance.Hunger = (
			VARIABLES.Instance.Hunger +
			LOCAL.Delta.Hunger
			) />

		<cfset VARIABLES.Instance.Happiness = (
			VARIABLES.Instance.Happiness +
			LOCAL.Delta.Happiness
			) />

		<cfset VARIABLES.Instance.Love = (
			VARIABLES.Instance.Love +
			LOCAL.Delta.Love
			) />


		<!--- Commit the data changes to the brain. --->
		<cfset VARIABLES.CommitData() />

		<!--- Return out. --->
		<cfreturn />
	</cffunction>


	<cffunction
		name="LoadData"
		access="private"
		returntype="void"
		output="false"
		hint="Loads the property data from the brain (persisted data).">

		<!---
			From the brain, set the initial values for our
			internal state properties. Since we might not have
			all of these value set, be sure to Val() each
			value that gets returned.
		--->
		<cfset VARIABLES.Instance.Anger = Val(
			VARIABLES.Instance.Brain.Get( "Anger" )
			) />

		<cfset VARIABLES.Instance.Energy = Val(
			VARIABLES.Instance.Brain.Get( "Energy" )
			) />

		<cfset VARIABLES.Instance.Hunger = Val(
			VARIABLES.Instance.Brain.Get( "Hunger" )
			) />

		<cfset VARIABLES.Instance.Happiness = Val(
			VARIABLES.Instance.Brain.Get( "Happiness" )
			) />

		<cfset VARIABLES.Instance.Love = Val(
			VARIABLES.Instance.Brain.Get( "Love" )
			) />


		<!--- Get the shortie's scaled time. --->
		<cfset VARIABLES.Instance.Time = Val(
			VARIABLES.Instance.Brain.Get( "Time" )
			) />

		<!---
			Make sure our time is valid. If for some reason
			we are not starting out with a valid time, then
			set it equal to the real world time.
		--->
		<cfif NOT VARIABLES.Instance.Time>
			<cfset VARIABLES.Instance.Time = VARIABLES.Instance.RealTime />
		</cfif>


		<!--- Return out. --->
		<cfreturn />
	</cffunction>


	<cffunction
		name="Labotomize"
		access="public"
		returntype="void"
		output="false"
		hint="Clears the brain data, thereby, resenting the properties.">

		<!--- Clear the brain data. --->
		<cfset VARIABLES.Instance.Brain.ClearData() />

		<!--- Load the blank data into the shorti. --->
		<cfset VARIABLES.LoadData() />

		<!--- Clear next interaction time. --->
		<cfset VARIABLES.Instance.NextInteractionTime = Now() />

		<!--- Return out. --->
		<cfreturn />
	</cffunction>

</cfcomponent>

For Cut-and-Paste