Sunday, August 31, 2014

Hero Worship - Time + Distance = Fun?

Spent some some time doing a little housekeeping.  You know, cleaning a few things up, adding some additional feedback, etc.  Much of this was focused on the build buttons.  Now they highlight on mouseover and alpha out if a building option isn't available.  It's little stuff that will likely be tossed out when I redo the whole control scheme.  But I think it's very important to do this early and often.

I have encountered projects where things like UI and player feedback aren't really considered until the last possible minute.  Then towards the end of the project, they only take one or two passes at it, and it ends up pretty crappy.  And a players' understanding of what's going on in the game is at least as important as the game mechanics, no?

So, to recap.  I have buildings:  house, sawmill, stoneworks, watchtowers, and (now) barracks.  Workers appear from the sawmills and stoneworks, seek out the nearest resources, walk over, gather some, walk back, drop them off, and so on.  They move around obstacles, avoid enemies, and react appropriately when things don't go according to play... for the most part :)

But what about construction of the actual buildings?  In a full-on city building game, the player would select a location on the map, the little villagers would scurry about collecting the required resources from some stockpile, carry everything over, and then slowly build it. I initially thought this was what I was going to do, but before committing to a completely new set of on-screen villagers with their own set of logic, I figured it would be a good idea to think it through.

Right now, building placement is somewhat arbitrary. As long as you're not trying to place a new building on an existing one or on top of some other object, you're free to plop it down wherever you like.  It will instantly appear and begin to function.  This works but doesn't give the player that much in the way of making interesting choices. Some thought needs to be given when placing a sawmill or a stoneworks because the villagers need to travel back and forth from resource to building.  Build it too far away and your economy will be sluggish.



So what can we do about this? As I mentioned, one thing would be to have villagers travel from the town center to the build location.  That would require another villager type and I don't think I want to do that.  What happens if the build site gets attacked/destroyed? What if the villagers get ambushed on the way? What happens to the resources?  One thing I don't want to do is give the player direct control over the villagers so I can't just have them drag select some idle villagers and assign them build duty. Nor do I want the game to just generate new villagers from the town center and send them off to their doom.

No.  In the end, I implemented a build timer.  Once you place a building, your resources are committed.  The building is then slowly constructed.  It's weak in this state and if attacked it will likely be destroyed before it's completed.  I messed around with it and so far I like the delay.

But that still doesn't address the problem of where you can place your buildings.  For that I look our real-time strategy friend, StarCraft.  (Go ahead. Play it. I'll wait.)  For the uninitiated there are three playable "races" in StarCraft: Terran, Zerg, and Protoss.  Each have unique way to build their bases.  The Terrans have no build restrictions and in fact many of their buildings are mobile. That's close to what I've got now.  The Zerg can only build on an expanding carpet of "creep" that gushes forth from certain constructions.  Hmmm, interesting.  And the Protoss may only build near special "pylon" building that are designed to power everything within their radius.  Again, interesting.  It's really a pretty neat set of systems that have all kinds of advantages and ramifications for players.

For me, I'm most interested in limiting the player from instantly expanding all over the map, but requiring the player to eventually expand.  Here's where I think I'll be starting:

  • Buildings need to be constructed within a certain radius of the town center.
  • (Potentially) A certain type of building will provide an additional pocket of build-able area... like the Protoss pylon.  It can be built anywhere.




  • (Potentially) Any building provides their own build-able radius but needs to be constructed within the radius of another building.



I still think there needs to be something more than wandering monsters to push the player back and give him something to struggle against.  I've got an idea, but that is for another post.   :)

Sunday, August 17, 2014

Hero Worship - Now You See Me...

There's no fog of war in Actraiser.  You can see everything that's happening on the map at all times. I wanted to mess around with it for Hero Worship, but I've never attempted anything remotely like it before.  I could have researched the "best" solution or sent out a plea on the numerous coding forums, but sometimes it's fun figuring stuff out by yourself.  And sure enough, I came up with a working system that ain't fancy, but it does get the job done in the way I imagined.

It's really very simple.  So simple I call it the "Poor Man's Fog of War".

First, here's what I wanted:

  • Monsters and enemy buildings are unseen unless a villager or village building is near.
  • If monsters move further away, they become unseen.
  • Once monster buildings have been "scouted" they stay visible on the map.
  • Terrain is not affected by any of this.  It's always viable.

And here's what I knew how to do:

  • Get nearest instance of any object or category of objects.
  • Determine the distance between a monsters (or building) and any near object.
  • Adjust alpha of the sprite based on distance.

And that's it.

It works the same way with enemy buildings.  In fact, the code only exists on the enemy units and structures because those are the only ones that need to know if they can be seen.  The Villager AI doesn't know or care either way.  But that's ok, because they're reaction to enemy proximity is based on their own "sight" radius.

Here's the script.  It's in the step event of the monster:

This will hold the friendly object that's closest to the monster.
var alphaMod;  


Gather our data
person = instance_nearest(x,y, obj_walkers);
personDist = distance_to_object(person);
personDist = floor(personDist);

place = instance_nearest(x,y, obj_buildings);
placeDist = distance_to_object(place);
placeDist = floor(placeDist);

This determines if it's a building or a villager.
if(personDist < placeDist) 
    {
    alphaMod = personDist;
    }
    else
    {
    alphaMod = placeDist; 
    }

Here's where it will adjust the alpha based on distance.
The formula "(alphaMod - 300)*(-.01)" makes the transition smooth.  It looks pretty neat.
    
if(alphaMod >= 300)
    {
    image_alpha = 0;
    }
else if(alphaMod < 300 && alphaMod > 201)
    {
    image_alpha = (alphaMod - 300)*(-.01);
    }
else if(alphaMod <= 200)
    {
    image_alpha = 1;
    }


The only difference between the script on the monster and the one on the monster building is an additional "Detected" boolean.  This flags true once any villager object gets within 200px of it.  If it's true, it sets the alpha to 1 and then stops checking for distances (since it's been located).

There are still some things to consider:

  • If I introduce creeping corruption, should that be affected by fog of war?  Right now I think so.  If players can see the corruption spreading they will be able to figure out the location of the source and I don't think we want that.
  • What about line of sight?  Mountains, forests, etc.  Should that have an effect.  I don't know how much value it will add the to the game.  The interactions aren't really about the tactics of unit position or movement.  It's more of an economic / exploration thing, so I'm going to table it unless it becomes something I absolutely have to deal with.
For now, we soldier on.


Sunday, August 10, 2014

Hero Worship - Deep Thoughts

So far everything functions fine, but there's no game.  No goal, no challenge, no progression.  No meaningful choice.  In fact, it's all rather pedestrian, with me relying on tried and true concepts from other titles.  But I don't just want to make a complete knock-off of some generic strategy game. But I want to use the great Actraiser as inspiration.  And I really need to look more closely at what makes that game tick.

What the hell is an "Actraiser" anyway?

If you're not at all familiar with the game, Actraiser was a mix of god-sim and side scrolling action.  The game progression goes something like this:



It was a fairly linear game.  From what I remember, access to more difficult territories was based on your level, so your progression from one place to the next was fairly fixed.  For Hero Worship I think I'll be tossing out the progression from one land to the next in favor on a single, one map / one fight experience.  The game will begin in the town building phase and eventually end with an action phase once all requirements are met. Let's ignore the action phase for now and focus on the town.

Despite its simplicity, the sim phase in Actraiser was really pretty interesting. Let's look at what you did as a player and how I think I'm going to approach it in Hero Worship.

Here's the first map of the game.  The town starts in the center and is represented by a temple surrounded by a road.  On the outer edges of the map are monster spawn points.  As you are building your town, monsters will constantly emerge from these locations to wreak havoc.

Although the game runs in real-time, your villagers can only build once at the beginning of the day.  You can direct where they should be building. One of the goals for each map was to direct your villagers to build up their town right on a monster spawner.  Once they were close enough, they would destroy it, making the land a little safer.


As your villagers made their way around the map, they would discover special technologies (such as bridge building) that they could use to further progress.  They would also find "spells" that they would offer to you so that you could help them out as well.  With these spells you could summon rain to help crops grow or put out fires, or lighten bolts to kill enemies or clear away rock.  There was a very light puzzle-solving element involving locating these technologies and spells in each map, and using them correctly.  It important to note that the whole thing was very simple and very linear in terms of progression.

The other thing the player would do is direct the little cherub guy in the center of the screen around the map and use his (her? its?) arrows to destroy any monsters wandering around.  The villagers are completely defenseless are were prone to being kidnapped or having their houses destroyed.

Here's a quite decent let's play I found on the You Tubes.  The sim stuff comes in at around the 5 minute mark.

Once all of the spawn points were destroyed, the villagers would pinpoint the "ultimate source of EVIL" on the map and request that you come down and squash it (which is something you're all too happy to do during the action phase).

So how do I plan on keeping the spirit of Actraiser while making this game its own thing?  Here are some thoughts:


  • Direct control of town building.  This is a pretty major departure from Actraiser.  But I think there's more moment-to-moment engagement to be had from the player choosing which buildings to place when and where.
  • No direct villager control.  This is not a game about amassing a huge army and steamrolling over the enemy.  The villagers will react and defend.  There may be offensive units, but they will mostly act on their own.
  • God powers to assist and influence.  Like Actraiser, the god spells will assist the villagers in achieving their goals. It can be direct like a lightning bolt to take out enemies or it can be a subtle suggestion to investigate an area.  I'll need an additional resource (mana, belief, etc) to cover this.
  • Minion for direct control. Right now I'm thinking no.  The cherub in Actraiser worked in part because there wasn't much else for the player to do.  
  • Encroaching doom.  I had been playing a lot of the excellent Creeper World 3 and I just love love love the idea of fighting against this overwhelming wave of... stuff.   My initial thought is that the spawners, in addition to creating monsters, will spread corruption around the map.  This would slow units, destroy buildings, and render resources unclaimable. God powers and perhaps a special building would be able to counter the effect.
  • Destroying the monster spawners.  Same basic idea. However, in Hero Worship they will have to be discovered.   I've implemented something I call the "Poor Mans Fog of War" which I'll discuss in a later post.  Basically, these spots will remain hidden until a villager gets close to it.  Once discovered, it will be permanently visible on the map.  A discovered spawner will cause a specific building in the town to create a raiding party to travel over to the location and destroy it.
  • Summoning of the Hero.  This is what will end the "sim" part of the game and begin the action phase. In Actraiser, the action phase was triggered once the location of the "Ultimate Source of EVIL" was discovered.  In Hero Worship a shrine will need to be erected at a special location on the map.  The location will be revealed by destroying the monster spawners.  The shrine itself will require massive amounts of resources to be ferried to the location. Once built, the hero (the embodiment of god) will be summoned and the action phase will begin.
  • Additional villager types. A generic warrior/scout will take care of exploring and patrolling the map.  A druid will "seal" the monster spawners and build the shrine.
Obviously there are many more details to consider, but this should keep me busy.







Thursday, August 7, 2014

Hero Worship - Tower Defense

With villagers running around properly and monsters happily eating them, it was time to get some kind of defensing structure working.  The answer of course was that tried and true RTS mainstay, the tower.  Really nothing fancy here.  It's a building that requires resources to construct.  It has a range and a fire rate.  Once an enemy comes into range, the tower "locks on" to it and will fire exclusively at that enemy until it's either destroyed and it moves out of range.

The bullets generated by the tower move towards the x/y coordinates passed by the tower.  They do damage on contact so depending on the speed of the bullets and/or the enemy, you can make the towers more or less accurate.

That's one way to do it.  You could also have the tower insta-hit any enemy that's been locked on so you wouldn't even need to bother with collision detection at all.  Or you could base your hit on a percent chance modified by things that "accuracy", "distance", or "armor".  All pretty easy to implement, but I wanted to see the bullets fly and it's kind of neat to see them miss every once in a while.  Just remember to kill the bullets if they don't hit anything.


curDist = distance_to_point(originX, originY);

if(curDist > 150)
    {
    with(self)
        {
        instance_destroy();
        }
    }

The biggest takeaway from getting the towers in and working was not just how easy it was.  It was how simple it was to slip it into the existing game.  I have building selection and placement set up so once I have all the sprites and objects build, I just add the new one to my "buildings array" and it just works (with minor tweaking).

Right now my buttons appear just above my town center in a straight line.  I'd like them to appear in a circle.  I'm sure there's some simple math here but I haven't stumbled upon an answer in my journeys.  It's super low priority as I think the whole selection process will be changing when I move to controller input.

With the basics in place, it's time to make it work more like a game...


Sunday, August 3, 2014

Hero Worship - Here There Be Monsters (Part 2)

When we last met, our intrepid monster could successfully wander around aimlessly and eventually head towards civilization. The next bit was to get them to attack buildings and people.  It started simply, but then I quickly got lost in numerous "What if?" scenarios.

To shake the monsters out of their funk, I needed to add an instanace_nearest check for either a building or a person of any kind.  Game Maker allows you to parent objects, so I created generic "obj_building" and "obj_person" objects and childed my buildings and villages.  If there's one within a certain distance, the monster will cease wandering and start moving to attack.

The monster stuff was easy.  Once it collided with a villager or building, it would enter an attack mode, slowly depleting the hp of the collided object.  Once the hp reached 0, the object would be destroyed and the monster would seek a new target or go back to wandering. In order to achieve this, the monster has three states:


  • Moving - I'm wandering around looking for stuff.
  • Aggro - I've found something I don't like and I'm heading towards it.
  • Attacking - I'm in combat mode.

Under a simple test with a building, it all worked perfectly.  I added a health bar to the building with the Draw event so I could track hp depletion.  Here's the code.  The numbers are placeholder until I get them all stored somewhere else:

draw_self(); // you need this so it will still draw the sprite

if(hp < MaxHp) // only show health bar if damaged
    {
    draw_set_color(c_gray);
    draw_rectangle(x-32,y-27,x+32,y-16,false); //healthbar background

    draw_set_color(c_red);
    draw_rectangle(x-30,y-25, x+(hp*6)-30, y-18,false); //healthbar: this gets smaller with less hp.
    }

Here's where it got tricky.  Villages don't stand still.  They are off doing things, so they also need to know they are being attacked (or they are in danger) under different circumstances.  At first, the villages were completely stupid.  They did not react to a monster nearby.  This lead to villagers running right into a monster already occupied.



They needed a panic mode.  The initial implementation was actually pretty simple.  If there's a monster nearby, run in the opposite direction until you are far away, then resume what you're doing. Again, instance_nearest comes to our aid.  Here's the basic script:

monster = instance_nearest(x,y,obj_monster);
monsterDist = distance_to_object(monster);

if(monsterDist < 50 && !panic)
    {
    //panic mode
    path_end(); //stop following current path
    path_delete(myPath); //delete path: you need to do this to prevent memory leaks
    myPath = path_add(); //prepare for new path.
    speed = 0; //stop moving for a moment.
    panic = true; //you are now in a state of panic

//get point to run to
    dir = point_direction(monster.x, monster.y, x, y);  // gets a vector pointing from monster to villager
    radius = random_range(dir+45, dir-45); // this helps randomize the final direction to run in.
    
    targetX = x + lengthdir_x(200,radius);
    targetY = y + lengthdir_y(200,radius);
    targetX = floor(targetX);
    targetY = floor(targetY);
    
    //check to see if it's a valid destination and then go there
    r = mp_grid_path(grid, myPath, x, y, targetX, targetY,1);

        if(r)
        {
        path_start(myPath, GameControl.villagerSpeed,0,0);
        alarm[2] = 90;
        }
        else
        {
        panic = false;  // if not valid path, setting panic to false resets this condition and it starts over again
        }
    }
 


That worked ok.  But what if the villager was heading to a resource?  What if the villager was heading back to his building?  What if the villager was actively gathering resources?  All of these things need to be accounted for.  I already had variables I was tracking to cover these possibilities.  Here are the ones for the lumberjack:

  • onTree - I'm busy chopping down a tree.  At first I just made it so the villager would not run away.  But what if the villager finished chopping before the monster is done killing him?  He starts walking away, but the monster remains frozen in place because he hasn't completed his task.  So I also made the villager stop gathering.
  • myTree - The holds the instance_id of the tree to be chopped.  Zero means it's not an instance_id and any other number means it's an instance_id.  Now I know which path I need to create if the lumberjack leaves panic mode.  If it's zero, create a path back to the sawmill.  If it's any other number, create a path to that instance.
But what if the sawmill is destroyed by a monster?  What do the lumberjacks do?  It depends. Are there other sawmills?  Was the lumberjack already headed to that destination?  My solution was this:

The lumberjack does an initial check for the existence of any sawmill (and then the nearest one) and then goes there.  If no sawmill exists, the town center becomes the destination.  When the lumberjack reaches the town center, the wood is deposited, and the lumberjack disappears.

If the lumberjack is already heading back to a sawmill, there is a check that happens every couple of seconds to see if it's still there.  If it's not, recalculate a path based nearest sawmill or town center.

All of this took a lot more time than I expected, but at this point it all works.  Monsters and villagers swarm around doing what they should all on their own.


Friday, August 1, 2014

Hero Worship - Here There Be Monsters (Part 1)


What if?

What if? What if? What if? What if? What if?

That quickly became my mantra.

When implementing any kind of enemy behavior, simple or no, you wind up asking yourself "What if?" a lot.

And not just "What if?".  You have to think about a lot of things.  I'm always reminded of Liz England's excellent The Door Problem on what game designers actually do all day. There's just a lot of stuff to consider.  And when one small part of it doesn't work, it all comes crashing down.

In coming up with behavior for my generic monster type I had two basic states in mind:


  1. The monster had to wander around the map in a seemingly aimless manner, yet also slowly make his way towards the village.
  2. When close to a villager or building, go destroy it.

Wandering Monsters

For the first state, I already had some experience with this very behavior from my game Truffle Shuffle. (WARNING: doesn't work in Win8 for some reason...)   In it, the trippy shroom clouds float around the level and eventually make their way to your base to damage it.  The logic is pretty simple.  For 80 percent of the time, the cloud will choose a random direction to move.  The other 20 percent has the cloud moving towards the base.

With that as my starting place I created a case statement to fire off every 2 seconds or so.  It has three states:  wander aimlessly, head to the town center, and go idle.  The idea here is to give a sense that the monsters are just doing their thing, and yet they will eventually force a confrontation with the player.  I can tweak the percentages to get more or less aggressive monster types.  Right not the idle state doesn't appear to do much, but when animations are in, they could growl, look about, scratch an itch, etc.

The resulting behavior is pretty good.  I set the time interval  between changing states to be based on distance traveled (which is assigned through a random_range).  This way the monster doesn't walk the same distance each time.

Random distance and direction is accomplished by picking a point on a circle of a random size around the monster.  Here's the script:


//move in a random direction.

point = irandom(360);

radius = random_range(50,100);
radius = floor(radius);

targetX = x + lengthdir_x(radius,point);
targetY = y + lengthdir_y(radius,point);

targetX = floor(targetX);
targetY = floor(targetY);

//check to make sure destination is within the room boundaries.
if(targetX>0 && targetX<room_width && targetY>0 && targetY<room_height)
    {
    moving = true;
    move_towards_point(targetX, targetY, GameControl.monsterSpeed);
    }
    else
    {
    event_user(0); //not a valid destination.  Try again!
    }

There's a conditional in the step event (AKA the update function for your non Game Maker people) that checks to see of you've reached the destination and re-fires the case statement to give the monster new orders:

if(moving)
    {
    dist = point_distance(x,y,targetX,targetY); 
    if(dist < 5)
        {
        moving = false;
        speed = 0;
        event_user(0);
        }
    }

This all came together pretty fast.  I should have known the other part would be far more complicated.

What if? What if? What if? ...