0 votes
by (870 points)

I'm using MadExile's gregorian time and date widget and I was wondering how I'd go about setting it so certain variables change based on time?

For instance, I want to set things up so that as time progresses the player's character becomes more hungry. What I have so far keeps resetting after 23 hours, as a new day comes about. I'm not sure how to make it add time.

:: PassageFooter
<<set $lastmeal to clone($gameDate.getHours())>>

:: PassageHeader
<<if $lastmeal gte 5 lt 10>>
You're a bit hungry.
<<elseif $lastmeal gte 10 lt 15>>
Your stomach can't stop growling.
<<elseif $lastmeal gte 15 lt 24>>
You're officially hangry.
<</if>>

:: StoryCaption
You ate $lastmeal hours ago.

I think if I can figure out how to set this up for hours I should be able to figure it out for birthdays and expired food, since it'd just be a matter of changing things to days? But for my items, I'm using HiEv's universal inventory system, so my food looks like this:

Items['apple pie'] = {
  name : "apple pie",
  plural : "apple pies",
  type : "food",
  size : 2,
  cost : 10,
  nutrition : 3,
  expires : 5,
  expired : false,
  magic : false,
  description : "A sweet and tart pie."
};

so "expires: 5" would mean that I'd like the item to expire and go bad after five days and set "expired" to true so the character is forced to toss it or risk eating it and getting sick. As for birthdays, I'd just like to age someone up after a year has passed. 

<<set $children to []>>
<<set $child to {
name: "",
age: $age,
gender: "",
birthday: "$birthday",
bthlbs: 0,
bthoz: 0,
hair: "$hairColor",
hairtexture: "$hairTexture",
eye: "$eyeColor",
skin: "$skinColor",
persona: "$persona"
}>>

Thanks in advance for any help!

1 Answer

0 votes
by (44.7k points)
edited by
 
Best answer

When you say, "What I have so far keeps resetting after 23 hours, as a new day comes about," well, that shouldn't happen if you're using the <<addmins>> or <<addhours>> widgets to add to the time, as they were given here.  Adding time with those widgets should also add to the date.

As for making food expire, you'd need to check to see when a new day happens, and then update any expiration dates.  So, for example, you could update your widgets like this:

/* Adds the specified number of minutes. */
<<widget "addmins">>\
	<<set _tempDay = $gameDate.getDay()>>\
	<<run $gameDate.setUTCMinutes($gameDate.getUTCMinutes() + $args[0])>>\
	<<if _tempDay != $gameDate.getDay()>>\
		<<NewDay>>\
	<</if>>\
<</widget>>

/* Adds the specified number of hours. */
<<widget "addhours">>\
	<<set _tempDay = $gameDate.getDay()>>\
	<<run $gameDate.setUTCHours($gameDate.getUTCHours() + $args[0])>>\
	<<if _tempDay != $gameDate.getDay()>>\
		<<NewDay>>\
	<</if>>\
<</widget>>

/* Call this when a new day occurs. */
<<widget "NewDay">>\
	<<set _tempItems = UInv.GetItemsArrayByProperty("inventory", "expires")>>\
	<<for _i, _ItemName range _tempItems>>\
		<<set UInv.AddToItemPropertyValue("inventory", _ItemName, "expires", -1)>>\
		<<if UInv.GetItemPropertyValue("inventory", _ItemName, "expires") < 0>>\
			<<set _tempCount = UInv.BagHasItem("inventory", _ItemName)>>\
			<<if _tempCount == 1>>\
				<<run alert("1 " + UInv.GetItemPropertyValue("inventory", _ItemName, "name") + " has expired.")>>\
			<<else>>\
				<<run alert(_tempCount + " " + UInv.GetItemPropertyValue("inventory", _ItemName, "plural") + " have expired.")>>\
			<</if>>\
			<<set UInv.DeleteItem("inventory", _ItemName)>>\
		<</if>>\
	<</for>>
<</widget>>

You'll need to change "inventory" to the name of whatever bag you're using.  That should decrease the "expire" property on items that have an "expire" property by one, and if that number goes below zero, then it alerts the player that the items have gone bad and deletes them.

You can change that to set the "expired" property and change other food values when they expire instead.  This is just an example.

As for birthdays, just don't bother to keep the "age" property.  Only keep a "birthdate" property, and calculate the age from that.  If you pass their birthday then it will automatically show the correct age, without even needing to modify anything.

Hope that helps!  :-)

 

by (870 points)
Thank you so much! c:

I figured out my issue with the "disappearing hours" issue of the last eaten variable, I was using the wrong object. The food expiration is great! There's just one issue I've noticed with the alert, in that it will say "2 NaN have expired" rather than "2 apple pies have expired". This only seems to happen when the item is more than 1, and I've doublechecked that I changed "inventory" to "backpack" like you noted, and all of the items have a "plural" property for the code to pull from, so I'm not sure why the NaN.

On that note, is there any way to ensure food items expire based on when they were purchased specifically? For example, I have a shop passage where one can buy an apple, the game advances a day, and then the player is on another passage where they find an apple and it's added to their bag. The next day, both apples expire. I understand why that happens, because I don't think there's any property set to distinguish the apples as being picked up at different times... Would adding a new property to each item resolve that even if the items have the same name?
by (44.7k points)
edited by

The "NaN" is due to a typo.  This line:

<<run alert(_tempCount + " " +  + UInv.GetItemPropertyValue("inventory", _ItemName, "plural") + " have expired.")>>\

should be like this:

<<run alert(_tempCount + " " + UInv.GetItemPropertyValue("inventory", _ItemName, "plural") + " have expired.")>>\

Just one extra "+" caused that.  :-P  (I edited the earlier post to fix that.)

Also, if you use "UInv.SetMergeItemMethod(UInv.MERGE_RENAME_SOURCE_ITEMNAME)" it will make it so that if any of the properties are different (such as the expiration date) the items won't stack.  Just edit the very last line of the UInv JavaScript to change the merge item method.

There's a missing feature in the version of UInv that you have which will make the renamed items stack properly, so you'll need to patch part of the UInv code.  (This will be built into the next version.)  Search for "case UInv.MERGE_RENAME_SOURCE_ITEMNAME:" and then just replace this:

											case UInv.MERGE_RENAME_SOURCE_ITEMNAME:  // Rename the source's unique identifier so that it's stored separately in the destination bag.
												NewItemName = UInv.GetUniqueItemName();
												Item.UInvDefaultItemType = ItemType;
												UInv.RemoveItemObjectsDefaultProperties(Item, ItemType);
												if (Item.UInvQuantity === 1) {
													delete Item.UInvQuantity;
												}
												State.variables.UInvBags[DestinationBagName][NewItemName] = Item;  // copy item 
												UInv.SetCurrentItemName(NewItemName);
												UInv.SetCurrentBagName(DestinationBagName);
												UInv.SetBagTouched(DestinationBagName);
												Result = NewItemName;
												break;  // Success

with this:

											case UInv.MERGE_RENAME_SOURCE_ITEMNAME:  // Rename the source's unique identifier so that it's stored separately in the destination bag.
												NewItemName = UInv.GetMatchingItemsArray(SourceBagName, ItemName, DestinationBagName);
												if (NewItemName.length == 0) {
													NewItemName = UInv.GetUniqueItemName();
													Item.UInvDefaultItemType = ItemType;
													UInv.RemoveItemObjectsDefaultProperties(Item, ItemType);
													if (Item.UInvQuantity === 1) {
														delete Item.UInvQuantity;
													}
													State.variables.UInvBags[DestinationBagName][NewItemName] = Item;  // copy item
												} else {
													NewItemName = NewItemName[0];
													UInv.AddToItemPropertyValue(DestinationBagName, NewItemName, "UInvQuantity", Item.UInvQuantity);  // Increase quantity of existing matching items
												}
												UInv.SetCurrentItemName(NewItemName);
												UInv.SetCurrentBagName(DestinationBagName);
												UInv.SetBagTouched(DestinationBagName);
												Result = NewItemName;
												break;  // Success

That should make it so that the items will stack based on expiration date.

Hope that helps!  :-)

by (870 points)
edited by

It's always the smallest things that cause problems...

I made all the necessary changes and the multiple expiration problem's been solved, thanks! Only now the duplicate item has a funny name? The first apple is the usual, "apple," and the second is listed in the inventory as "item562016." Did I mess something up or is there a certain property I need to edit? When they expire, it does say the correct "1 apple expired" despite the weird label.

ETA: actually, I'm reading through - that's exactly what it's supposed to do, yeah? So I guess my question actually should be: is there a way to print the items to have a different title in my inventory/home/storage passage(s)?

by (44.7k points)

Yup, that's what it's supposed to do, that way you can tell the items apart.

As for printing them out, just note the days till expiration if the item has an "expires" property.  Perhaps something like this:

Your backpack contains:
<<set _tempItems = UInv.GetItemsArraySortedByProperty("backpack", "UInvDefaultItemType")>>\
<<for _i, _ItemName range _tempItems>>\
	<<set _quant = UInv.BagHasItems("backpack", _ItemName)>>\
	<<if _quant == 1>>\
		<<set _tempName = UInv.GetItemPropertyValue("backpack", _ItemName, "name")>>\
	<<else>>\
		<<set _tempName = UInv.GetItemPropertyValue("backpack", _ItemName, "plural")>>\
	<</if>>\
	<<set _quant = UInv.numberToAPString(_quant)>>\
	<<if UInv.ItemHasProperty("backpack", _ItemName, "expires")>>\
* _quant _tempName (days till expiration: <<=UInv.GetItemPropertyValue("backpack", _ItemName, "expires")>>)
	<<else>>\
* _quant _tempName
	<</if>>\
<</for>>\

That way your users can tell which stack is closest to expiring and eat from that one.

I plan to have display functions which will make that easier in the future, but for now you'll need to do something like the above.

Have fun!  :-)

by (44.7k points)

Sorry.  Patch to the earlier patch.  Change:

UInv.AddItem(DestinationBagName, NewItemName, Item.UInvQuantity);  // Increase quantity of existing matching items

to:

UInv.AddToItemPropertyValue(DestinationBagName, NewItemName, "UInvQuantity", Item.UInvQuantity);  // Increase quantity of existing matching items

That should fix a little problem caused by AddItem indirectly calling itself before the item is ready.

(The earlier code has been updated as well.)

by (870 points)

Ahh, it's working! It's perfect! Youuu are the absolute best, thank you so very much for your help and your time! 

by (44.7k points)
No problem.  :-)

Hopefully I'll have the next pre-release version of UInv out before the end of the month.
by (870 points)

Oh, that's awesome! I saw you mention working on a system for equipping items and clothes with a mannequin to alter stats while I was trolling through questions and I'm super excited for that because all of that has gone over my head so far lol. 

One more question if you don't mind? I tried to input the cases I had had in my original inventory. They aren't working properly for some reason? If my inventory is

apple [ inspect | eat | toss all ]

apple pie [ inspect | eat | toss all ]

belt [ inspect | eat | toss all ]

and I click on "eat" for apple, "apple pie" is what's deleted. If I click "inspect" for apple or apple pie, I get the description for belt, or whatever's the last item on the list.

This is my code for my inventory:

:: inventory widget [widget nobr]
<<widget "secondinv">>
	<span id="inventory"><<include "secondinv">></span>
<</widget>>

:: secondinv
<div id="inventory">
<<include "last meal">>
<<include "hunger">>
<<include "body">>
<<set _tempItems = UInv.GetItemsArraySortedByProperty("backpack", "plural")>>\
<<if _ItemsName == 0>>
	Your backpack is empty.
<<else>>
	Your backpack contains:
	<<capture _i>>
	<<for _i, _ItemName range _tempItems>>\
		<<set _quant = UInv.BagHasItem("backpack", _ItemName)>>\
		<<if _quant == 1>>\
			<<set _tempName = UInv.GetItemPropertyValue("backpack", _ItemName, "name")>>\
		<<else>>\
			<<set _tempName = UInv.GetItemPropertyValue("backpack", _ItemName, "plural")>>\
		<</if>>\
	<<if UInv.ItemHasProperty("backpack", _ItemName, "expires")>>\
 _tempName <<if _quant gt 1>>(_quant)<</if>>
 (expires in: <<=UInv.GetItemPropertyValue("backpack", _ItemName, "expires")>> days)
	<<else>>\
 _tempName <<if _quant gt 1>>(_quant)<</if>>
	<</if>>\
	[ 
				\<<link "inspect">>
					\<<run alert(UInv.GetItemPropertyValue("backpack", _ItemName, "description"))>>
				\<</link>>
				\<<switch UInv.GetItemPropertyValue("backpack", _ItemName, "type")>>
				\<<case "food">>
				\  | <<link "eat">>
<<if UInv.GetItemPropertyValue("backpack", _ItemName, "nutrition") is 1>><<hunger -2>><<eat>>
<<elseif UInv.GetItemPropertyValue("backpack", _ItemName, "nutrition") is 2>><<hunger -10>><<eat>>
<<elseif UInv.GetItemPropertyValue("backpack", _ItemName, "nutrition") is 3>><<hunger -20>><<eat>>
<</if>>					\
<<set UInv.DeleteItem("backpack", _ItemName, 1)>>\
					<<replace "#inventory">><<include "secondinv">><</replace>>\
				\<</link>>
				\<<case "drink">>
				\  | <<link "drink">>
<<if UInv.GetItemPropertyValue("backpack", _ItemName, "nutrition") is 1>><<hunger -2>>
<<elseif UInv.GetItemPropertyValue("backpack", _ItemName, "nutrition") is 2>><<hunger -10>>
<<elseif UInv.GetItemPropertyValue("backpack", _ItemName, "nutrition") is 3>><<hunger -20>>
<</if>>					<<set UInv.DeleteItem("backpack", _ItemName, 1)>>
					<<replace "#inventory">><<include "secondinv">><</replace>>\
				\<</link>>
				\<<case "clothes">>
				\  | <<link "wear">>
\/* wear clothing */
<<set UInv.SetItemPropertyValue("backpack", _ItemName, "wearing", "yes")>>\
<<set UInv.MoveItem("backpack", "body", _ItemName, 1)>>\
\<<replace "#inventory">><<include "secondinv">><</replace>>
<</link>>\
				\<<case "weapons">>
				\  | <<link "equip">>
\/* equip weapons */
				<</link>>\
				\<<default>>\
					\/* handle default switch case here */
				\<</switch>>
				\ | <<link "toss all">>
					<<set UInv.DeleteItem("backpack", _ItemName)>>\
					<<replace "#inventory">><<include "secondinv">><</replace>>\
				\<</link>>
			 \ ]	
			 /* NECESSARY SPACE */
<</for>>
<</capture>>
<</if>>
</div>\

<<link "bookstore boi">><<goto "bookshop">><</link>>

:/ not sure what i borked up

by (44.7k points)
edited by

Any time it seems to be doing something only on the last item in the loop, you can pretty much guarantee that the <<capture>> macro needs to be added or fixed.

In this case you need to change:

<<capture _i>>

to:

<<capture _ItemName>>

Basically, any changing variables used inside of a <<link>>, <<button>>, or other similar macro which gets triggered by the user after the loop is complete, need to be "captured" so they have the right value.

You don't need to capture _i because it's not used inside any of those <<link>>s.

Anyways, looks like the next version of UInv is just going to be adding pockets, because it's taking waaaay longer than I expected to implement them.  (Which I guess is a good thing, since that means it really needed to be implemented properly in UInv, since a beginner could easily screw them up.)

More display stuff will be in the update after that, since I need to get the display stuff to handle pockets as well, thus pockets have to be working first.  The clothing mannequin is definitely a high priority addition.

Have fun!   :-)

by (870 points)
Ohh, I see! Thanks for the explanation. It's perfect now.

:D that's rad! I can't wait to see the updated version. Good luck on all your coding!
...