Tuesday, January 29, 2019

Pirated Breach patch notes from an alternate future reality

I've been playing Breach (the async action pvp rpg) for a little over a week now. I have over 100 hours into it already according to Steam. Obviously this completely qualifies me as an expert in the game's system and mechanics, and that my ideas are superior to the developers. /sarcasm.

The game itself is really fun, has some basis in mythology, and is currently in beta/early access as I type this. I am really excited to see the game succeed, and in the spirit of beta testing, I have decided to write up this list of suggestions and requests in the form of a set of patch notes.

These "patch notes from the future" are simply a list of features, requests, suggestions, and bug fixes that I feel should be implemented. Obviously the dev's will likely already have some ideas of their own and my ideas may conflict with theirs, or maybe they won't. Maybe they will take some of my ideas and make them happen, maybe they wont. Maybe they already know about every bug I'm about to list. Maybe they don't. Also, 1 or 2 of these items are already on their to-do list (like the ability to map/remap all the buttons), but I have included them here for completeness.

I understand some people won't agree with some, much, or all of this. That's ok. I'm not saying I am perfect or know everything. Game balancing is a very tricky artform. I've been doing it for a while, and I still get stuff wrong. We all do. This is just a list of stuff I think would put the game in a relatively balanced state (Heroes vs. VD's wise), make some of the items, builds, and tactics less frustrating for everyone involved, and improve the quality of life for a few classes. Not to mention improve build and team comp diversity.

So here are the patch notes that have been pirated from an alternate future reality.

 

General:

  • Added support for mice with extra buttons on them.
  • Added support for remapping all the buttons, including the ability to change the self-cast button to something other than alt.
  • Added the ability to make "self-cast" the default action/cast-style on a per-spell basis, in this case holding alt (or w/e) when casting will make it function the "normal" way, requiring a target to be under the player's reticle.
  • To reward teams/demons that perform extremely well, we have added an additional in-mission level for all players/characters both hero and demon: Upon reaching L5, you will be presented with an additional opportunity to purchase a level-up-talent via the usual F1/2/3 buttons. However, rather than adding a 5th piece of gear to pull these talents from, the 3 options will be randomly pulled from the 6 gear-based options that were not selected when that person leveled up from levels 1-3. Any unselected talents from the weapon item (L4) will not appear here for balance reasons.
  • Ability damage amounts, cooldowns, and durations will now be displayed on the 'B' screen.
  • Ability damage amounts, cooldowns, and durations will now be updated on both the 'B' screen and in the class editor/active spellbook screens when they have been modified by a gem.
  • Added the ability to /whisper other players.
  • Added the ability to report other players.
  • Added the ability to ignore/block other players. This will not impact your matchmaking and you can still end up in the same match as somone you have blocked. We will look into changing this in the future, but it is unlikely as it would cause many matchmaking issues.
  • The current picks and bans order often leaves the Veil Demon feeling frustrated and powerless, and the match feeling too easy for the Heroes since they can easily counter pick the VD and shut him down early. To mitigate this, we have changed the VD pick order from Elite-VD-Elite to Elite-Elite-VD. This should reduce the frustration felt by the VD as the number of opportunities for the Heroes to choose hard counters is reduced from 2 to 1, and will encourage the top 3 Hero picks to be more diverse in their picks in anticipation of what the VD pick may be.
  • Added an audio alert for when the queue is ready to be entered.
  • Added a 0.5 second cooldown for using potions to prevent players from accidentally using more charges than intended
  • Color coded all schools and classes so that now the color of the border around each class's icon will represent the school they are in. Arcane = pink, Dark = green, etc.

 

Maps:

  • Modified existing maps so that they are more dynamic and the next area you go to is based on who won the last event - kind of like a choose your own adventure book. Some of these rooms have multiple ways to get to them, others will only be reached when 1 side is doing really well, or really poorly. Each map will still only have 1 boss room.
  • Modified existing maps so that each room has a randomly generated event/objective, rather than having each room be the same every time you come to it. This should increase replayability exponentially.
  • Adjusted enemy difficulty (via increased quantities of enemies, increased base health pools, damage output, etc.) on a per room basis to help reduce feelings of powerlessness encountered by players on either side when they consistently lose objectives. This should help to reduce snowballing as well as act as a low-grade "come-back" mechanic for both heroes and demons.
  • Added a whole bunch of map-specific and thematically appropriate monsters to each map so that you aren't fighting the same old satyrs and wolves in every room on every map.

 

Gem Changes:

  • Sapphire and Ruby of the Cheetah effects swapped for both hero and veil demon versions of the gems.
  • Sapphire and Ruby of the Vulture effects swapped for both hero and veil demon versions of the gems.
  • Vigilant Light Sapphire and Ruby effects swapped for both hero and veil demon versions of the gems.
  • Vigilant Light Ruby and Sapphire have had their Haste effects increased from 3.75% and 7.5% to 5% and 10% respectively (in addition to having their effects swapped) for both hero and veil demon versions of the gems.
  • Empowering Dark Lethal Conjuring Ruby (the one that grants +2.5% Elite accrual rate per team level) name has been fixed. Its proper name should have been (and is now) Empowering Dark Ruby of Advancement.
  • All other "Dark Lethal Conjuring" and "Empowering Dark Lethal Conjuring" gems have had their tooltips corrected to accurately state that they are increasing conjuring damage rather than breach damage.
  • Draining Light Sapphire and Ruby have had their lifesteal effects increased from 2.5% and 1.25% to 5% and 2.5% respectively for both hero and veil demon versions of the gems.
  • Gems will continue to only be purchased a single time rather than our original plan for players to be able to buy more than of a single gem and stack the effects. This is to ensure build diversity.
  • Before the next major patch, we will be evaluating which gems are used the most and least used and plan to improve on those gems that are least used - and possibly reduce the effectiveness of the most used gems - in order to continue to improve game balance and build diversity in the future.

 

Spells and Item changes by School:

  • Added a 0.5 second global cooldown for all abilities to prevent players from accidentally using more charges than intended by tapping a button quickly, and also to prevent cancelling abilities by pressing several buttons in too quick of succession.

Non-School Specific Items:

  • "Basic attack clip size increased by 50%" mod can no longer appear on items for melee classes.
  • "Dodge automatically reloads basic attacks" can no longer appear on items for melee classes.
  • "All spell cooldowns are reset and charges set to max. Does not include dodge or ultimate spells" talent has been combined with "Instantly grants +1 health potion" talent. It now reads: "Instantly grants +1 health potion and all spell cooldowns are reset and charges set to max. Does not include dodge or ultimate spells."
  • "Health potion now also cleanses all negative effects" talent has gained the following effect: "Instantly grants +1 health potion." It now reads: "Instantly grants +1 health potion, and health potion now also cleanses all negative effects."
  • "Health potion now grants all healing instantly, instead of over 3 seconds" talent has gained the following: effect "and instantly grants +1 health potion." It now reads: "Instantly grants +1 health potion, and health potion now grants all healing instantly, instead of over 3 seconds."
  • "Health potion also grants 2s of damage immunity" talent has gained the following effect: "Instantly grants +1 health potion." It now reads: "Instantly grants +1 health potion, and health potion now also grants 2s of damage immunity."
  • "You are invisible to the veil demon while reviving an ally" talent has been combined with the "You are invisible to the veil demon while downed" talent. It now reads: "You are invisible to the veil demon while reviving an ally or while downed."

Arcane:

  • Transcendent Blast: range increased by 10m and the tooltip now includes a description of the shape and size it covers as well as the range.
  • Mending Wave: Passive now also grants Light Impact Resistance.
  • Stasis Beam: Passive now also grants Light Impact Resistance.
  • Glacial Armor: Passive now also grants Light Impact Resistance.
  • Chronoleap: Now also grants Moderate Impact Resistance for 4 seconds after emerging from Chronoleap.
  • Quake: If stumbled, knocked down, or knocked back while casting this spell 75% of the cooldown is refunded. This does not occur if you were "Interrupted".
  • Meteor: If stumbled, knocked down, or knocked back while casting this spell 75% of the cooldown is refunded. This does not occur if you were "Interrupted".
  • Temporal Field: If stumbled, knocked down, or knocked back while casting this spell 75% of the cooldown is refunded. This does not occur if you were "Interrupted".
  • Aqueous Globe: If stumbled, knocked down, or knocked back while casting this spell 75% of the cooldown is refunded. This does not occur if you were "Interrupted".
  • "Guardian Sprite now applies to all allies with a damage shield that blocks up to 3000 damage, but the duration is reduced to 3s" talent changed to: "Guardian Sprite now applies to all allies with a damage shield that blocks up to 1500 damage."

Battle:

  • Guard: effective distance increased from 10m to 20m, but you may only have guard active on a single target. Casting Guard on a new target removes Guard from your previous target.
  • Intervene: cooldown reduced from 12 seconds to 10.
  • Arcane Charge: cooldown increased from 8 seconds to 10.
  • Cyclone Blade: cooldown increased from 10 seconds to 12.
  • Mana Strike: cooldown increased from 4 seconds to 6.
  • Resolute Block: duration increased from 2 seconds to 3.
  • Resolute Block: cooldown reduced from 10 seconds to 9.
  • Bastard Cleave: gaining lifesteal now requires 3 stacks of "Bastard Cleave" rather than 2.
  • War Cry: duration decreased from 8s to 6.
  • Zephyr Strike: cooldown increased from 8s to 10. 
  • Zephyr Strike: aerial damage resistance reduced from 75% to 50%.
  • Zephyr Strike: fixed bug where enemies launched into the air via Zephyr Strike would sometimes fall back down to the ground for no apparent reason.

Dark:

  • Haunting Wraiths: increased range by 5m.
  • Death Touch: reduced the casting animation/delay that occurs after you release the mouse button but before you teleport from 2 seconds to 0.5 seconds.
  • Death Touch: fixed the bug where you would cast the spell but not teleport to the target, resulting in a wasted cast.
  • Death Touch: added the ability to self-cast this (via the alt key). This option will only work if you currently have an active Phylactery in play. When you self-cast Death Touch, instead of teleporting to an enemy, you will instead teleport to your Phylactery, dealing Death Touch's damage to all enemies within 5m of your Phylactery.
  • Raise Dead: duration increased from 20 seconds to 25.
  • Arcane Siphon: upon casting, enemies are pulled towards the caster for 1 second. After 1 second all enemies nearby are knocked down, and are then also Silenced and Slowed for 10s.
  • Arcane Siphon: any enemy killed while Silenced or Slowed by this ability will heal the caster for a percentage of their maximum health. Grunts provide 5%, Strong enemies provide 10%, and Elites provide 20%.
  • Sow: Grunt enemies killed by Sow, or while they have the Sow debuff on them, automatically heal the caster for 50hp without the need to cast Reap.
  • Sow: corrected the tooltip to explain how many Sow stacks an enemy can have at once, and the duration of those stacks.
  • Sow: now also grants the caster 20% damage resistance against any attacks made by enemies that currently have a Sow debuff.
  • "Reap no longer consumes stacks of Sow but instead refreshes the duration. Reap now has a cooldown of 15 seconds" talent cooldown reduced from 15 seconds to 12.
  • "Reap no longer consumes stacks of Sow but instead refreshes the duration. Reap now has a cooldown of 15 seconds" talent can now appear on tertiary items as well as weapons.

Hedge:

  • Siphoning Skull: now grants Heavy Impact Resistance while being channeled.
  • Siphoning Skull: tooltip clarified to indicate that it does 335 damage per second for 8s, and that the target is stunned for the full 8s as well.
  • Siphoning Skull: increased range by 5m.
  • Exilium Daemonium: The souls of any enemies killed by Exilium Daemonium are converted to "Exorcism Stacks" for the caster (max 10 stacks). The next time the caster successfully applies the Expel debuff to a possessed enemy, he deals additional damage to the Expelled target equal to 100 per stack and heals himself for 100 per stack.
  • Banishing Relic: increased damage from 225 to 250.
  • Hallowed Spike: decreased cooldown from 21s to 20.
  • Confound: increased damage from 80 to 100.
  • Confound: decreased cooldown from 20s to 18.
  • Marked Shot: increased cooldown from 12s to 13.
  • Hellfire: increased aoe radius by +1m.
  • Tar Jar: increased range by 5m.
  • Tar Jar: increased damage from 75 to 100.
  • Tar Jar: increased aoe radius by +2.5m.
  • Tar Jar: increased cooldown from 6s to 8.
  • Sticky Grenade: increased cooldown from 3.5s to 5.

Shadow:

  • Mob of Ravens: fixed bug that could result in clones disappearing even if you selected the "permanent clones" talent.
  • Mob of Ravens: fixed bug that could result in your clones casting "Mob of Ravens".
  • Bird of Prey: increased cooldown from 8s to 10.
  • Bird of Prey: fixed bug where enemies launched into the air via Bird of Prey would sometimes fall back down to the ground for no apparent reason.
  • Ambush: fixed the bug where you would cast the spell but not teleport to the target, resulting in a wasted cast.

Tech:

  • Kinetic Absorbtion: fixed the 0 stacks lockout bug.
  • Recall Dart: cooldown increased from 10s to 15.
  • Snare Trap: root duration reduced from 3s to 2.
  • Snare Trap: cooldown increased from 13s to 15.
  • Snare Trap: max charges reduced from 6 to 5.
  • Rapid Fire Clip: cooldown increased from 15s to 18.
  • Quick Feet: reduced duration from 8s to 6.
  • Precision Barrage: increased duration from 6s to 8.
  • Precision Barrage: cooldown reset threshold reduced from 50% to 25%.
  • Lead Storm: increased duration from 5s to 6.
  • Boom Shot: increased cooldown from 8s to 10.
  • Turret: reduced turrets health by 25%.
  • Ricochet: fixed bug that sometimes causes ricochet to only hit 1 target, or none, or just stop for no reason.
  • Ricochet: increased base bounce range by 1m.

Veil Demon Spells:

  • Veil Rift: can no longer target anything other than players. Yes that means you can no longer Veil Rift a zombie. Sorry.
  • Veil Rift: increased "throw force" by 10%.
  • Veil Rift: fixed bug where the veil demon would exit the veil rift while the target was still in the veil.
  • Veil Shock: fixed bug that resulted in "this power requires a target" error message. 
  • Veil Shock: now correctly has its damage increased from Gems (like Ruby of the Wolf & Lethality Ruby).
  • Veil Shock: is now automatically applied to anyone who has been affected by Veil Rift for as long as they are in the Veil.
  • Pain Conduit: increased aura threshold from 100 health to a percentage based value: 25%.
  • Pain Conduit: reduced cooldown from 60s to 50.
  • Translocate: can now be cast in the air, but the target will appear on the ground below your current location.
  • Translocate: increased heal threshold from 100 health to a percentage based value: 20%.
  • Translocate: increased cooldown from 8s to 12.
  • Sacrifice: reduced ally animation time from 'trigger' to explode from 1s to 0.6.
  • Sacrifice: now applies a 10% slow for 10s to anyone who takes damage from the explosion.
  • Grael Heart: Increased base health by 25%.
  • Grael Heart: Reduced cooldown from 60s to 50.
  • Watcher: increased base health by 50%.
  • Aethyr Coil: removed delay between cast and "hit". This should now function like an instant cast spell similar to Veil Rift, Translocate, and Winds of Auros.
  • Aethyr Coil: now applies a 10% slow for 10s.
  • Sheaths of Sorrow: increased cooldown from 33s to 36.
  • Inspiring Presence: Allies are now granted 25% bonus damage if they are near an Elite, a possessed ally, or the disembodied Veil Demon himself. This effect does not stack.
  • Cursed Siphoning: Elite minion allies now gain 75% lifesteal on targets that are affected by root, stun, or any ailment - as well as slow. This effect does not stack.
  • Grael Vitals: bonus health increased from 10% to 20%.
  • Grael Cage: can no longer be placed in the transition areas between event rooms.
  • Chronos Gem: increased duration from 20s to 25.
  • Chronos Gem: decreased cooldown from 60s to 50.
  • Chronos Gem: increased base health by 50%.
  • Command: allies in the area now also receive 15% haste in addition to bonus movement speed.
  • Command: reduced 60% bonus movement speed to 35% (making the total movement speed increase only 50% when combined with the haste, down from 60%). 
  • Veil Shroud: now applies a 25% slow to any enemies (heroes) while they are in the AoE.

Veil Demon Items:

  • "Every enemy captured within Grael Cage grants +0.5 charge for that breach" talent has been reduced from +0.5 to +0.35.
  • "Every enemy hit with Sheaths of Sorrow grants +0.5 charge for that breach" talent has been reduced from +0.5 to +0.35.
  • "Veil Shroud has reduced cooldown and reduced duration, but you can now have multiple active at one time" talent description edited to specify how much the cooldown and duration are each decreased.
  • "Damaging enemies while possessing a minion grants 1s of recharge time on all _____ spells" talents have all been changed to "... 1s of recharge time on all spells".
  • "Cooldown and Charge Accrual for all spells reduced by 12.5%. Stacks with similar gems and talents. Maximum Bonus is 50% from all sources." talent has been improved from 12.5% to 18%.
  • "Cooldown and Charge Accrual for ______ spells reduced by 15%..." talents have all been changed to/replaced by "... for all spells by 18%..." talents. This is to simplify items, reduce the number of possible "waste" combos on talent items.
  • The weapon talent: "Cooldown and Charge Accrual for all spells reduced by 25%. Stacks with similar gems and talents. Maximum Bonus is 50% from all sources." talent has been increased from 25% to 32%.
  • "Dodge automatically reloads basic attacks" can no longer appear on Veil Demon items.
  • "Basic attack clip size increased by 50%" can no longer appear on Veil Demon items.
  • "Expel will no longer force de-possess" talent can now appear on tertiary items as well as weapons. It has also been reworded for clarity and now reads: "While possessing you are immune to expel effects."
  • A few new weapon talents have been added as well:
  • "While possessing you are immune to slow and root effects."
  • "While possessing you are immune to stun and 'launched into the air' effects."

Wednesday, May 9, 2018

Persistent Data on website (Cookies, Sessions, and Hidden Fields, Oh My!)

The ability to “save” a user’s data when browsing a website has been a long sought after ability beginning with the efforts of the Roman argentarii of the 3rd century BCE; since then, e-commerce has come a long way. It got a little rocky in the dark ages, but really opened up during the renaissance, and greatly accelerated in the last decades of the 20th century. Today we will learn three different ways to save user data: with hidden fields, cookies, and session data.


Our plan today:

1. Overview/definitions
2. Hidden Fields
3. Cookies
4. Sessions
5. Wrap-up


Overview / Definitions


So first, let’s talk about what these three things are. ‘Hidden fields’ are an object you can use on your page that can hold text data, much like an (invisible) label object, but the data can be stored via accessing a “built-in” data array called ViewState. A ‘Cookie’ is an array of variables you can use in your code behind that are stored (effectively) as a plain .txt file on the users/clients machine. Finally, sessions are a way to store data server side in a structure that is similar to the ViewState object.


Hidden Fields


By far the simplest and most straight forward of the options to implement (imho). All you really need to do is include an object on the webpage via either “asp:HiddenField ID="HiddenField1" runat="server"“ or “input id=”HiddenField1” type="hidden"” if using pure html5. After creating the (input) object itself, you can store whatever data/objects you would like in the object/array. In order to store the data, all you need to do is access the “ViewState” object like you would a dictionary object, or an array with a string/text based index, where said index is made up on-the-fly, like this: “ViewState[“myCustomObjectName”]”. You can assign anything you want to the object, including class objects (as long as they are [Serializable]). For our practice/example code today, we will be using a Person class that defined in its own .cs file in the App_Code folder of an ASP web-page using the master/content schema. It looks like this:

[Serializable]
public class Person
{
   public string FirstName, LastName;
   public Person(string FirstName, string LastName)
   {
      this.FirstName = FirstName;
      this.LastName = LastName;
   }
}

Then, in the content-page, we define the input object like this:

<asp:HiddenField ID="HiddenField1" runat="server">

And finally, in the code behind (or in the server side script section), we have the following code:

protected void Page_Load(object sender, EventArgs e)
{
   if (IsPostBack)
   {
      Label1.Text = HiddenField1.Value;
   }
   else
   {
      Label1.Text = "Page_Load: IsPostBack == false";
   }
}

protected void Button1_Click(object sender, EventArgs e)
{
   if (!string.IsNullOrWhiteSpace(TextBox1.Text) &amp;&amp; !string.IsNullOrWhiteSpace(TextBox2.Text))
   {
      ViewState["objP"] = new Person(TextBox1.Text, TextBox2.Text);
      string viewStateData = ((Person)ViewState["objP"]).FirstName;
      viewStateData += "," + ((Person)ViewState["objP"]).LastName;
      HiddenField1.Value = viewStateData;
   }
}

So, when the page 1st loads, if it’s a PostBack (aka, we’ve been here before), it will put the text data we’ve stored in the HiddenField1 into Label1 (don’t forget to put that somewhere on the page) so we can see what we put in there, in this case, the name of the Person. If this is not a PostBack, we set the label.Text to some debug code so we can see better what is going on.

On the same page we also have 2 textboxes and a button (make sure you stick those in there too if you’re following along). The button, after checking to make sure that the 2 text boxes aren’t empty, will store a new Person object into the “objP” ‘dictionary slot’. What we do then, thanks to the [Serializable] attribute of our Person class, is to save the various members of Person into a comma-separated string; and finally, we store that string into the value attribute of our HiddenField1 input object.

Now, when the form is submitted via PostBack from clicking button1, the ViewState item “objP” gets sent with it, and returned with the new page; that way, in the page_load event we can simply access the HiddenField1 object and set the Label equal to its Value property.


Cookies


Cookies are a simple way to store (non-critical, non-security) user data on a client machine for future retrieval. Often, this takes the form of preferences, and form input fields that haven’t yet been submitted or aren’t stored on the server. To a user, a cookie is a text file that gets stored on the local machine; to a programmer, it is an object that holds an array of strings, or an array of arrays of strings, or an array of arrays of arrays that hold … well, you get the idea. To use them we just create an “HttpCookie” object, and then store strings in it via the following syntax:

HttpCookie thisIsACookieObject = new HttpCookie("myCookieName");
thisIsACookieObject.Expires = DateTime.Now.AddSeconds(10);
thisIsACookieObject["ClickTime"] = DateTime.Now.ToString();
Response.Cookies.Add(thisIsACookieObject);

When accessing the data on subsequent visits, you can call a cookie request and store it in a cookie object. As you can see, we can set the expiration of the cookie relative to the DateTime.Now function, as well as simply setting a static datetime if desired. You could also store sets of info in a complex array such as “thisIsACookieObject[“userData”][“firstName”] = “Bob””. Anyway, back to the cookie we’re trying to request: If that cookie is null, then that cookie doesn’t exist on the client machine and you can create one as normal; if it returns non-null, you can pull the string data from it as follows:

HttpCookie myCookie = Request.Cookies["myCookieName"];
if (myCookie != null)
{
   HttpCookie thisIsACookieObject = Request.Cookies["myCookieName"];
   Label1.Text = thisIsACookieObject["ClickTime"];
}
else
{
   //No Cookie found
}

Because all key value pairs in a cookie are stored as strings, you should always perform validation on the data before you use it to make sure you aren’t accidentally trying to use a name as an int, or a double as Date, etc. You can delete a cookie by using the .Remove command, or by setting the expiration of the cookie to be DateTime.Now.AddDays(-1); - just make sure to do a Response.Cookies.Add(thisIsACookieObject); after setting the expiration date.


Sessions


Sessions are a very user-friendly and easy way to store data. The session is stored on the server, and in most regards functions very similarly to the Hidden-Field method of storing data with the ViewState object. The session is initialized and “saved” as an object with nearly identical syntax, all we would need to do is to revise the code for our button in our hiddenField example as follows:

protected void Button1_Click(object sender, EventArgs e)
{
   if (!string.IsNullOrWhiteSpace(TextBox1.Text) &amp;&amp; !string.IsNullOrWhiteSpace(TextBox2.Text))
   {
      Session["objP"] = new Person(TextBox1.Text, TextBox2.Text);
   }
}

As you can see, we are no longer using the hiddenField in this example. Now, rather than saving all of our data in a string format, we are simply saving the session object to it (technically still as a string behind the scenes, but don’t sweat that). Then, it is accessed much like a server-side cookie, if you will, via casting the strings in the Session object back to the appropriate objects, as shown here:

protected void Page_Load(object sender, EventArgs e)
{
   if (Session["objP"] == null)
   {
      Response.Redirect("HiddenFields.aspx");
   }
   else
   {
      Label1.Text = ((Person)Session["objP"]).LastName + ", " + ((Person)Session["objP"]).FirstName;
   }
}


Wrap-up


So when should we use which method? Well, as a quick and dirty rule of thumb: Page specific data should not be stored in Session, since that can cause issues if multiple tabs/windows are open, so in that case using ViewState is a good alternative. If you only need to access the values in page-specific inputs from the client side, then using Hidden Fields should work fine. Cookies are a good way to store user specific data that relates to the web-site as a whole that you may want to remember for a non-trivial duration. Things related the user as an individual – things like font preference, or color schemes, or a name, or other non-security related info – are good candidates of data to store in a cookie.

Wednesday, May 2, 2018

jQuery or Bust

Welcome back!

jQuery is an open source JavaScript Library that is useful for both confusing English majors who always capitalize the first letter in a sentence, and simplifying a great many tasks that might normally take several additional lines of code were it not employed. It does not, however, do anything new; just about anything you can do with jQuery, you can also accomplish without jQuery. Additionally, there are actually times when using jQuery will slow down your website rather than speeding it up. You can see more about this at “http://youmightnotneedjquery.com”. That being said – jQuery is incredibly popular and useful and we are going to learn how to use a few of its ‘super-powers’.

First, we will start off simple, mainly because I’m not pro at this yet myself, and partly because, well, that’s just usually the best place to start.

One of the first things to note is that you need to have the jQuery library ‘enabled’ on the website you are going to use it on. This is accomplished fairly easily. One way to do it is to install it locally on/in the project that you will be using it with. If you are using VS, you can install jQuery pretty quickly though nuGet. Alternatively, although it is a tad slower, you can have jQuery loaded from an online source, called a “Content Delivery Network”. The way to do this is quite similar to how you would include a css or javascript file in your project, except – there’s no file! Here’s how:

Navigate to the head section of your html file where you have these lines of code.
<script src="scripts/scripts.js"></script>
<link href="styles/style.css" rel="stylesheet" />

Then, just above those add
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>


You can test to ensure jQuery is working on your page by opening the console window in Firefox, Chrome, or most other browsers, and typing “jQuery” into the console command line. If you don’t get an error, everything is working fine. Ok, so now that we have jQuery working, lets look at some of the nifty things we can do with it.

The first thing we are going to do is change the color of some text when we click on it. So, in your project, in your JavaScript tag, go ahead and copy this in (without the enclosing quotes of course): “$(function () {$("body").click(function () {$("body").css("color", "#003b6f");});});”. Looks totally easy and obvious with no explanation needed right? Totally self-explanatory? Great, moving on. What’s that? You want me to go over it anyway, just in case your friend doesn’t understand? Ok, fine.

First off, jQuery seems to mostly be done as inline anonymous functions. I don’t know why, and it seems like a horrible way to code, but it’s what the vast majority of websites seem to do with jQuery. There are a few proponents out there of making the code be more readable by using named functions and classes, and calling them at the appropriate times with arguments, much like you would do for, you know, any other programming language. But we’re not going do that here because, well, I’m not that savvy yet. We are stuck with this “spaghetti code”. That being said, lets see what we have.

Our first bit of code is the jQuery Selector “$”, you use this to let the compiler know that you’re about to throw some spaghetti at it. Then we open our parenthesis to start out code. The first “function() {}” is to tell the compiler that this is an inline anonymous function that should be run when triggered. This first function() is actually optional, but if you don’t include it then you have to include your code in the html page where you want it to run, and that script block has to be at the bottom/end of the page.

I’m not sure 100% why it works like this, but I imagine it has to do with needing all the id’s, div’s, classes, css, etc. to be in-place already before it tries to execute the code. Either way, it is generally “better” to put in this opening anonymous “function()” at the start of your script to ensure that it runs when and where you want it to with the least amount of headache.

Which brings us from “$(function(){});” to “$(function() {$(“body”).click();});”. Here, the Selector symbol (still ‘$’), is letting us know that we want to perform the click event when something is clicked. When what is clicked? “body”. So this is telling us that whenever a user clicks anywhere on the “body” of the html page (which is pretty much anywhere at all), that we should do something. What should we do? We should run a function, which brings us to “$(function() {$(“body”).click(function(){});});”. What should that function do you ask? Let me tell you: it should run the following simple jQuery Command: “$(‘x’).css(‘var’, ‘val’);”.

This is a prototypal jQuery command. In this case you would replace x with whichever html or css identifier you want to manipulate, such as ‘div’, ‘a’, ‘body’, ‘#id’,’.class’, ‘nav ul li’, etc. pretty much any way you would define a css style, you put in here as “what to act upon”. Or more accurately, “what to select”. Var would be replaced with the css tag, like ‘color’, or ‘padding’, or ‘height’ (if the css uses a dash, like ‘font-weight’, you replace it with a camel-cased version, a-la ‘fontWeight’). Then, finally, you replace val with the value you want.

So in our case we end up with “$(‘body’).css(‘color’, ‘003d9b’);”. Then we stick that into our $paghetti, to get the final product of: “$(function () {$("body").click(function () {$("body").css("color", "#003b6f");});});”. Now, on that page, when you click (almost) anywhere, all the text turns #003d9b, or, “Tardis Blue”.

I actually have more to say, and can explain 2 more changes/pages, but I’m running out of time since I’m super behind this week due to my Grad school classes sucking super bad, so I will have to wrap this up for now, and turn it in. The code is done and has all the changes done for the full 3 pages, but since I’m out of time to turn it in, this will have to do for now, and when I have time to update this blog post with the code for the other pages, I will do so.

Monday, April 23, 2018

Spirit Island Companion App

Greetings and Salutations!

I've recently finished a "Beta" / prototype of my Spirit Island companion app. It is not the full game - you must still own a full copy of the physical game to make use of the app. It is pretty much just a digitized invader board. It handles building the fear deck, event deck, invader deck, blight card, etc. and allows you to gain fear, add and remove blight, draw cards, advance invader cards, etc. It does pretty much everything you do on the invader board only without needing to take up the table space and take out all the cardboard bits and all that. Makes set up and take down faster and easier, and is rather convenient IMHO. It still has some limitations, (and possibly a few minor bugs), but I just tossed it together in my spare time for fun. It works great on my MS Surface.

You can find the base board game on Amazon - it's called Spirit Island (duh), as well as the Expansion - Branch & Claw. The Board Game Geek listing is here.

It should be noted that I am fully adhering to the terms creating for "fan creation of game elements", as I understand them, and hereby post the following as proof/acceptance:
@Eric: I agree to the terms for creating Spirit Island game elements set forth in the FAQ.
If I am somehow not following the rules, or have misunderstood them, please contact me and I will fix the issue or remove the download link to this program as soon as possible.


The Spirit Island Companion App is totally free and can be acquired here:
This is the download link for the Spirit Island Companion App.


If you find this program enjoyable, useful, and/or helpful, and you would like to support further development of this app or others like it, aka: bug fixes, updates when new expansions come out, added features based on user-submitted suggestions, and possibly apps for other games, you can find a donation link near the bottom of the left hand column of my blog under my "profile".


ASP.NET Week 4 - Stored Procedures w/ OleDb

Welcome back Sportsball Fans! Today we’re going to review the basics of using a stored procedure in a database using and OleDb command / connection object. This is pretty much the same as using an SQLCommand, all the syntax is (seems to be) the same, so this information should work whether you are using Ole, or the regular kind of connection. Here is a brief overview of what you're going to learn:

Our outline

1. Creating the necessary command and connection objects & Setting them up.
2. Configuring those objects to use a stored procedure.
3. Setting up variables for passing arguments to a stored procedure.
4. Putting it all in a try catch block for safety.

Here we go!


Section 1:

You create the objects, and initialize them to connect to the database, in pretty much the exact same way you would for any other C# database access in code.

1
2
3
4
5
6
7
8
OleDbConnection conn = new OleDbConnection();
OleDbCommand cmd = new OleDbCommand();
string strOledbConnection = @"Provider=SQLOLEDB;
                            Data Source=.;
                            Integrated Security=SSPI;
                            Initial Catalog=yourDbNameHere";
conn.ConnectionString = strOledbConnection;
conn.Open();



Section 2:

Configuring the object(s) is actually also very easy and fast. First, we need to set the connection and commandText of the cmd object. The first is trivial, as we have all done many times before, however setting the command text might be new-ish to some, so just remember that when setting the commandText to use only the name of the stored procedure – no arguments or parameters, and it is case sensitive (of course). After you’ve set these, all you have to do is set the CommandType to “CommandType.StoredProcedure” and you’re ready to move on to the next (slightly more complicated) step.

1
2
3
cmd.Connection = conn;
cmd.CommandText = "nameOfStoredProcedure";
cmd.CommandType = CommandType.StoredProcedure;



Section 3:

In order to pass arguments to our stored Procedure, we first need to set up a few temporary variables, these should be of the ‘OleBdParameter’ type and will have data stored in them the ‘cmd.Parameters.Add()’ method. You will need one variable per parameter. In this example we have four.

1
2
3
4
OleDbParameter strName = cmd.Parameters.Add("@Name", OleDbType.VarChar, 50);
OleDbParameter strEmail = cmd.Parameters.Add("@Email", OleDbType.VarChar, 50);
OleDbParameter strHandle = cmd.Parameters.Add("@Handle", OleDbType.VarChar, 50);
OleDbParameter strReason = cmd.Parameters.Add("@Reason", OleDbType.VarChar, 50);

Each Parameter has 3 … well, … parameters. The 1st is a handle for when you want to pass them in as a combined string format when creating “standard” SQL commands, as in: “Insert into [Logins] ([Name],[EmailAddress],[LoginName],[ReasonForAccess]) Values (@Name, @EmailAddress, @LoginName, @ReasonForAccess );”. The 2nd and 3rd parameters should match the variable type in the db in type and size. After setting them up, you will want to set each one’s ‘direction’ to ‘input’ as shown.

1
2
3
4
strName.Direction = ParameterDirection.Input;
strEmail.Direction = ParameterDirection.Input;
strHandle.Direction = ParameterDirection.Input;
strReason.Direction = ParameterDirection.Input;

After successfully setting up your Stored Procedure input variables as above, you are now ready to assign data to them in a very simple C# fashion, and can then run your (non)Query as usual:

1
2
3
4
5
6
strName.Value = tbName.Text;
strEmail.Value = tbEmail.Text;
strHandle.Value = tbHandle.Text;
strReason.Value = tbReasons.Text;
 
cmd.ExecuteNonQuery();



Section 4:

All this should really be done in a try catch block for safety. I don’t think I should really need to explain why this is the case, but I will show you a complete code block here below so that you can see out “final product”.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
protected void Button1_Click(object sender, EventArgs e)
    {
        OleDbConnection conn = new OleDbConnection();
        OleDbCommand cmd = new OleDbCommand();
        try
        {   //1. Make a Connection
            string strOledbConnection = @"Provider=SQLOLEDB;
                                    Data Source=.;
                                    Integrated Security=SSPI;
                                    Initial Catalog=ASPNetHomework";
            conn.ConnectionString = strOledbConnection;
            conn.Open();

            //2. Issue a Command
            cmd.Connection = conn;
            cmd.CommandText = "pInsLogins";
            cmd.CommandType = CommandType.StoredProcedure;

            OleDbParameter strName = cmd.Parameters.Add("@Name", OleDbType.VarChar, 50);
            OleDbParameter strEmail = cmd.Parameters.Add("@Email", OleDbType.VarChar, 50);
            OleDbParameter strHandle = cmd.Parameters.Add("@Handle", OleDbType.VarChar, 50);
            OleDbParameter strReason = cmd.Parameters.Add("@Reason", OleDbType.VarChar, 50);
            strName.Direction = ParameterDirection.Input;
            strEmail.Direction = ParameterDirection.Input;
            strHandle.Direction = ParameterDirection.Input;
            strReason.Direction = ParameterDirection.Input;

            strName.Value = tbName.Text;
            strEmail.Value = tbEmail.Text;
            strHandle.Value = tbHandle.Text;
            strReason.Value = tbReasons.Text;

            cmd.ExecuteNonQuery();

            //3. Process the Results
            Label1.Text += "Request Submitted Successfully.";
        }
        catch (Exception ex)
        {
            Label1.Text += "<b>" + cmd.CommandText.ToString() + "</b><br /><br />";
            Label1.Text += ex.ToString();
        }
        finally { conn.Close(); } //4. Run clean up code
    }


Best of luck to you in your coding.
See you next time!

Tuesday, April 17, 2018

Creating an ASP webpage using a Master and Content model with html, css, & C#

Greetings!

Have you ever wondered how to create a webpage using a Master and Content model with html, css, & C#? How about how to make your own personal stash of ibuprofen in the comfort of your living room? Ever thought how cool it would be if you could use lasers and tinfoil to increase your wi-fi's output by 350%? Well, if you want to learn the 1st one, I've got you covered. Not so much on the other two.

So, what we're gonna do today is learn the basics of creating a webpage by using a model that includes a "MasterPage" and "Content Pages". This is different from making a 'regular' html/css webpage because the pages themselves don't all include all the usual markup that you may be used to seeing. At least, not all in the same place. Here is a brief overview of what you're going to learn:

Our outline

One. Starting a new Master/Content project.
Too. How the master page looks (and how it works with content pages).
3. How the content pages look (and how they work with the master page).
Fore!. How the css and code-behind pages work with this type of project.
VI. Bonus: Proof that aliens exist.


Section One:
The standard way to create this type of project is actually quite straightforward, as is most of the process of this entire process/method. The first thing you'll want to do is of course open up your favorite IDE, mine being Visual Studio 2017. Choose to make a new project and select the "ASP.NET Empty Web Site"; The exact wording of the project might differ based on what IDE you are using and its version. After your project is opened, right click on the project name (Website1(1) by default), and click Add | Master Page. You will now see... a master page. With some html code and stuff.
A super exciting screen shot of visual studio!


Section Too:
So, what is this master page? Well, if you look closely you will see that it looks pretty much the same as a basic regular html webpage that you might create in a really simple text editor for a very simple website. There are, however, two things that might stick out to your trained and expert eagle eyes: the code that reads something like this "asp:contentplaceholder id="ContentPlaceHolder1" runat="server" "/asp:contentplaceholder". This is half of the linking between master and content pages. "But, why?" you might ask.

What you want to do here is write the basic framework for a web site wherein all the pages will look the same, or at least have a "theme" that they all share. If all the pages will use the same (exact) look/theme, then you can replace the 1st occurrence of the asp:ContentPlaceHolder (the one in the "head" tag section) with your standard css ref link as I have done in the following example. For each other occurrence of the asp:ContentPlaceHolder, you will be able to add whatever you would normally - but we'll worry about that later.

For now, just fill in all the "boiler plate" that you need for your page without any of the actual content - that is, no 's, no div tables, no text - just the basic html code you need to make the page layout how you want it. Also, feel free to add more asp:ContentPlaceHolder tags as necessary, if you copy and paste the one in the main body, it will automatically change/fix the syntax and naming for you to make them usable without errors.
An incredibly creative website template, if I do say so myself.


Section 3:
Now that you have a Master page all laid out with framework code for your website, now we will start making content. My content here is rather sparse (don't tell, but all of this is really just for homework credit), but you can obviously get pretty complex here. For example, all my pages have the same h1 heading in the header section/tag of the page, but you could just as easily have a different 'title' there for each page, or even a different Title (from the head section/tag) by using another asp:ContentPlaceHolder there. Anyway, now that all that is done, go over to the solution explorer again and right click on your "MasterPage.master" file - and then click "Add Content Page".

Once done processing, which generally should be very fast, you will see that VS has created a new file for you named "Default.aspx". If you open this file you will notice that for each of your asp:ContentPlaceHolder tags that you set up in your master page, you will now have a "asp:Content" tag with a ContentPlaceHolderID property set to its corresponding ContentPlaceHolder. Each of these will, once the page is rendered from the server, be inserted into its appropriate place in the master page, and then made into a more-or-less standard webpage on the clients machine. Note that these content pages do not need any html tags or framework, they are content only, using only the html content tags necessary for your css to work, such as "p", "h1", etc.
Maybe I should start posting extra random pictures just to make this more interesting...


Section Fore!:
If you right click  on the project name and click Add | Style Sheet, you can add a standard css style sheet to the project/web site and use it as you normally would. Just make sure you add the link ref to the head of each content page that should use it, or to the master page if every page will be using the same styles. Furthermore, if you expand the arrow next to any given page (Master or Content) you will find a .cs file associated with each one. This is the code behind file for that given page, and can be used as you would normally use a code behind page for any given web page.
Look Mom! Codebehind!


Section VI:
Aliens!
"It's an older meme sir, but it checks out."
~The End~


I hope you have enjoyed reading my incredibly helpful, insightful and exciting blog post about making a basic html/css website using the Master/Content model. If you have any questions or comments, please feel free to do the thing with the stuff down below. By the way, here is how you make ibuprofen.

Thursday, December 20, 2012

Since the world is going to end tomorrow...

Since the world is going to end tomorrow, I figured now would be a good time to add a post. I really should do this more often. I'm not sure why I should do this more often, but I suppose it is kind of fun in a way. The reason I haven't posted anything in a long time is that I've been away doing school things. Also I just plain forgot and or got lazy. But mostly school things. That's my story and I'm sticking to it.

I'm currently attending the University of Washington for a double BA in Psychology and Sociology with a minor in ASL. I plan to stick around for a double MA in mental health and nonverbal communication. Although I may end up moving down to northern Oregon and trying to get my MA there. We shall see. Why would I move to Oregon you ask? Well...

About 3 years ago I met someone at con. We didn't really chat at all for the year following our meeting due to her already having partners and being in a closed group, so we didn't exchange numbers. She lived in Oregon anyway, so the odds of hanging out were slim. The following year at con we did hang out a little more and chat some and it was fun and flirty. And flirting is always fun. And so, if i recall correctly, we exchanged numbers and we did a little chatting during the year long break between con's that followed, but not very much. This most recent year the flirting reached critical mass. We weren't really able to spend very much time together at con, but it was fairly obvious that sparks were there. We have chatted much since last con. And even finally were able to see each other outside of con when she came up into the city to visit. Since then, it's been kind of crazy. Tons of texting, pictures sent back and forth, and keeping in constant contact about whats going on with each other day to day and week to week. She is awesome. So are her partners. I can't say enough good things. Finally being twitterpated again feels good. It's different every time, and it's awesome every time, and this time is very different, and very the same. It makes me grin like an idiot.

The quad consists of the girl I'm twitterpated with, her girlfriend, whom I am also twitterpated with in equal amount, (different but equal), and each of their boyfriends. The girls are both amazing and I can't describe or explain how great it feels to have met them both. I feel like this very well might be a thing, *the* thing, that I could do for long term. At the very least I want to try. I just hope that the two boys in the quad accept me soon for I very much would like to join them all. You would think that 3 years would be long enough for them to know that I'm not just trying to hook up with the girls and bail out, but oh well. They care about the girls, and that is certainly not something I'm going to complain about at all, especially since I feel the same way. I can't wait to call them my girlfriends. Just thinking of being able to do that gives me butterflies in my stomach and makes me feel a little protective of them, in a good way.

Here's hoping that it's not the end of the world, if for no other reason than for the sake of love.

Thursday, June 30, 2011

All alone

Today my new roommates are on vacation. So I have the new place all to myself for a week. This would normally mark me trying to figure out what to do with my week that would be fun that I normally couldn't do with them here. But other than throw a party, which I don't even want to do, and wouldn't out of respect anyway, I can't think of anything.

I've also been feeling rather lonely lately, and have been trying to figure out ways to see people and hang out and be more social, but I haven't been able to create any opportunities, and none have fallen in my lap either. I wonder if this week of solitude will help a little, as sometimes solitude seems to do, or if it will just intensify the feelings I'm having. I guess we'll see.

Wednesday, June 29, 2011

In the new place

Much better living arrangement here. There's more space, it's quieter, and the only real complaint is the cats, which isn't really a complaint at all. Mal and Sammy seem to be tolerating each other for the most part. I was really worried that Sammy was going to be a big poop head when it came to getting along with Mal since this marks the first time he's ever had to live with another cat. I am surprised that Sammy seems to be completely content and at ease and I've only heard of him hissing once so far. Mal on the other hand doesn't really like Sammy, despite Sammy being really laid back and acting very passive/non-alpha. The few times I've seen the two of them in close quarters Sammy just lies on the ground and looks really calm and relaxed, and Mal is hissing and growling at him. I can't blame him, this is his house after all, and Sammy is something akin to an invader. I'm sure it will settle down though, Mal just needs to realize that Sammy is a non-threat. I just wish I knew how to get him to realize that. Anyway, I really hope Mal relaxes a bit, I really want him to be happy and relaxed like Sammy has been, and really hope it happens soon, for everyone's sake.

Had a good talk with ex-roommates. I think things will be fine now. We seem to get a long fine when we're not living together, and I think that over time we can be friends again. Me and my friend that is, I really doubt I'll ever want to see or talk to his wife again - even in the talk we had there was an air of "she did nothing wrong" even though the entire situation was completely her doing. I still can't believe how blind he can be to her lies, manipulations and craziness. Oh well, that's love I guess. I'm just happy that he's happy, even if that means I don't get to see him as much as I would like. /sigh

Monday, June 20, 2011

Invasion of Privacy

I can't begin to describe how annoyed I am right now. I want to say pissed-off but I'm too amazed at the balls it took to invade my privacy in such a way... So, I'm moving out of my room right now and into another room. I have most of my stuff packed. Only have some furniture and my computer still up since I don't have a car big enough to haul the bigger items. So, while I've been out the last while, either yesterday, or today at some point, my computer was used to print off some stuff without my permission.

I just don't even know what to say about this. After all the accusations of all the crap that I've supposedly done to her, and the passive aggressive implications of a whole bunch of other stuff that is apparently all my fault, she has the balls to use my printer. Or have her husband do it, since I wouldn't put it past her to make him do it so she has plausible denyability. Enough was printed that my brand new cyan and magenta ink cartridges are now at 50% each, and I'm completely out of blank ink. What. The. Fuck. ?

I'm speechless, well, sorta. I'm in a daze at least. I can't even fathom what twisted bullshit must be running through their minds that makes them think this is even remotely okay. Obviously they just honestly don't care about me at all, and have no more regard for me than some random person off the street. But, shit, I wouldn't even do stuff like that to people I don't know. I'm not even sure if I would do that kinda crap to someone I actively disliked. So I can't imagine what amount of utter lack of respect they must feel for me. I thought these people were my friends. I guess I just learned better. At least now I know. Thank god I'm out of here in a week or so.

Saturday, June 18, 2011

Packing today is hard

I'm having a really really hard day today. For a few different reasons. Mainly it's this feeling of aloneness. I don't think there's anything that can make you feel more alone than packing up all your stuff, by yourself, to move away from what you've become familiar and comfortable with. It pretty much the same vibe as this article I read about feeling broken. It was about this Indian goddess, I don't recall the name, but it was long and I'm sure I would mispronounce it if I tried to say it out loud. Anyway, the point was about how this goddess represents the power that is inherent in lying on the ground in your room on the floor in a broken mess. the example they gave was "such as just after a break up". How droll I thought.

Anyway, the premise was that as you lie there, a broken mess on the ground, you are actually at your most powerful, that is, you are in a state of being that has the most potential. Because you are, at that point, not limited by what you thought you were, you can now rebuild yourself into whatever you want to be, because there are no strictures at that moment. The article was good, and I enjoyed it, but there was one line that really struck home with me. It said that when you distill it down to its very core, the reason breaking up (and this could be extended to moving, I'm sure) was so hard and so emotional is that it is a form of losing your future.

That struck me in a certain light that day, when I read it. I was amazed. I never quite understood why breaking up was so hard to do, even when you're not really happy in the relationship. You have a scene, an ideal, or even just a fantasy, in your head of what you think your life is going to play out like. You think you know whats going to happen, who you're going to be with, and for how long. You have plans. You have dreams. And many of them are shared with your partner. You have a future. And when you break up, that future, such as you imagined it, is now gone. ... And that's hard. But it's not the end of the world. you still have a future. And perhaps even a better one than previously supposed. But it is often hard to see that when you're on the ground in your room sobbing into a pillow.

Oddly enough, that is how my current living situation is. I'm not happy here, but I still find it incredibly depressing to be moving. I thought I was going to be happy here. I had planned on staying for at least 5 more months. I know that's not a lot, but having guarantees in life are comforting. Five months guaranteed is better than 5 years of "maybe", right? Well, I thought so, but as I type this, my roommates are coming home, and I am realizing that that is not the case. It was definitely comforting to have a future, such as it was, but I am not really happy here, and that makes all the difference.

The new place I'm moving to will have more room, my own bathroom, my own space, access to a kitchen that I don't necessarily have to share. the works. Now, keep in mind, I do have to share - I am technically just renting a room from a friend (a really good friend, and possibly my best friend at the moment), but their house has 2 bathrooms, 2 kitchens, 2 living rooms, etc. It's a nice house. And the best part? They won't invade my privacy by going in my room when I'm not home. they won't get upset at stuff that they imagine I have done, or let little things pile up until they explode and cause huge shouting matches. In short, they are reasonable, level headed people who act like adults and actually know how to communicate rather than just act like a child and give me the silent treatment. And then yell and scream and throw a tantrum when they don't get their way. More to the point, they will let their needs be known and not expect me to read their minds. And that is a glorious thing.

In short, I have been really really depressed today to the point of crying. But now that I think about it, I'm going to be changing from being "not really happy, but comfortable" into "unsure of my future, but happy". And to me, this giant pain in the butt of moving, the shouting matches, the crying... it's all worth it. Because now I have a chance at being a lot happier.

Friday, June 17, 2011

Longing

So, this morning I talked to my Mom, and my Grandma. It was good to chat with them both, especially since we didn't discuss any of my money issues. I hate having money issues and I feel bad that half the time I talk to my family that it ends up being the topic of conversation. However, now that I am done with my forms that needed filling out for the Dr.'s appointment I have today, I am left sitting here with nothing to do but think.

I will of course need to write up the paper I have that's due today at some point today, and email it in, and I will go see if any of the local restaurants are hiring bus boys, but for now, I seem to be stuck, in my head anyway. Oddly enough, much as I still have thoughts and craziness about moving dancing through my head, and how I'm going to live, and what to do about money if I can't find a job fast enough, but my mind keeps going back to one thing, over and over, amidst all the other thoughts that make perfect sense at the moment.

So, I talked to that girl again (finally) yesterday.I know I keep referring to her as that girl, which I suppose isn't very nice, but I don't want to name her, just in case. Although, thinking back, maybe I already did... Crap, well, anyway, screw it, her name is Thorn. So, I talked to Thorn yesterday on the phone. And now, I can't stop thinking about her again. We had a really nice conversation, and it lasted a really long time, although it felt really short. Time flies. Upon checking the call log it certainly didn't seem like 90 minutes.

Anyway, the point is, I really want to talk to her again. Now. But I can't. I don't want to text or call. I don't want to leave her an email. I mean, I want to, I really want to. But I shouldn't, so I wont. Besides, she'll probably read this, and that will makes thing complicated enough as it is. We agreed that it would be better and easier for us to have minimal contact, mainly because she wants to give her current relationship an honest and real second chance, 100%, nothing half-assed. And I completely respect that, and wish them the best. In a way. I can't help but be human, and humans are selfish, and so there is that little kernel of hope that maybe she'll come back to me. But I really do send them my best wishes.

That being said however, I am hurting right now. It really sucks not being able to talk to her, to tell her how much I miss her every day, how often I think about her, and how crushing this sometimes can feel. Funny I should use that word. Crushing. Crushed. About a week ago, I was in ASL class and was thinking of her. At that exact moment I was thinking of how much fun we had one day while walking and talking, and a classmate got my attention and pulled me out of my day dream to ask me what was wrong. Apparently, according to her, I looked "crushed". It kinda caught me off guard, I generally tend to be on the easy side of my emotions being readable by others, but I figured since I was thinking of a good memory that I would look happy at that moment. But apparently I was missing that moment more than i was relishing the memory, and it showed. I also had my teacher ask me what was wrong, and he commented 3 times that week about how sad I looked.

I think I've been doing better lately, no one has told me I look like hell this week, so either I'm hiding it better, or maybe slowly getting over it, or maybe I just have other things to worry about now. I suspect all three are true to varying degrees. But that doesn't stop me from wishing I had another chance. It doesn't stop me from opening my phone, typing a text to her, and then deleting it. Over and Over. It doesn't stop me from longing to see her, and talk to her, and touch her and hold her. Sometimes I wish I was a robot - that way I wouldn't have to feel. I know that it's better to have loved and lost than to never have loved at all, and I believe it, and I stick by it. But knowing that doesn't make right now, dealing with loss, any easier. Knowing that a hardship is only temporary doesn't make the hardship any less hard. But I guess it's nice to have hope. Sorta.

Ok, I think I'm done being emo now. Well, I'm done for now anyway.