Category Archives: Brainstorms

G.M.A.C. – Give-Me-A-Chance™ Hit System

When I started to think about the hit-chance system for combat, I wanted to address the ‘consecutive hit/miss’ issue that pops up in turn-based games. Even some of my own favourites games, like X-COM have players complaining of their questionable bad luck (naturally, no one complains of good luck).

Citizen is a real-time game. The random-hit issue presents itself differently from a turn-based game. I think the issue is more glaring in a turn-based game because every shot is felt. Nevertheless, as my iterative test showed, this issue is still relevantly noticeable in real-time game.

Luck vs skill

I think that the root of the issue with this game design is the distinction between luck vs skill. If we say that I have a 25% chance of hitting, and then proceed to roll a d8, and hope to get <= 2, then, that’s the concept of luck as all dice rolls are.

But in combat, it’s not all about luck; rather, it’s more about skill. When we say that we can hit a bulls-eye 25% of the time — contingent on factors affecting accuracy — we are likely referring to a fact, not a chance. To say that I can hit so-and-so 25% of the time means that I would have gone to a firing range, shot some rounds, and observed how many rounds have gone in the #1 ring and how many have landed outside. Then I would repeat this process until I get an average percentage, which becomes a quantitative representation of my skill level. This is the principle of skill, which I wished to represent in the Give-Me-A-Chance (GMAC) system.

Randomness

Some folks think that the problem with pseudo-random numbers (also referred to RNG — random number generation) is that they are ‘pseudo’, rather than ‘true’ random numbers. For the purposes of rolling for a hit that technical distinction is irrelevant. The important aspect about RNG is that whether they are ‘pseudo’ or ‘true’, they they depict luck rather than skill.

When you view a graphical representation of random numbers, they do not form any coherent shape, making them suitable for unpredictability.

But in a combat system some unpredictability is necessary. But the unpredictability needs to be felt in the details of combat to give it an organic touch. But unpredictability shouldn’t be felt on the wider scope of the whole combat system, which is the complaint of most players.

Give-Me-A-Chance

Enter GMAC, which tries to balance the hit-chance based on previous hits/misses. This means that the hit-chance (i.e. the skill level) is being modified based on the success rate of hits. In short, GMAC is intended to make hit-chance feel like the percentage it is depicting.

As an example of why RNG without modifications feels like luck, we run a random number generator for 100 iterations. For each iteration, it compares a random number to 75%; if the random number falls below 75%, then that’s considered a hit. Then the average percentage for those 100 iterations is shown.  Then we run it 10 more times (10 cycles). This is an example of the results:

Each line represents a single cycle. Remember that the percentages are averages of 100 comparisons per single cycle. This means that for cycle # 3, the average percentage was 7% higher than the target. And at cycle #9 it was 5% lower, giving a total range of 12% difference, which is rather a big deal.

Success rate

The principle behind GMAC is counting the success rate of shots. The basic principle is this: when you shoot the first round, you roll against your hit-chance, which is your basic skill level, which we’ll set as 50%. If you hit the target, you would have had a 100% success rate (1 out of 1 shot). Because you are 100% successful so far, and your skill level is 50%, your next hit-chance would be penalised to, say, 1%.  If you make the second shot (roll below 1% — yes it’s actually possible!), then you would continue to have a 100% success rate, and your hit-chance continues to be penalised to 1%, which you will likely not keep on hitting for long.

On the flip-side, if you missed the first shot, your success rate is 0%, and your hit-chance is given a slight bonus so as to make you more likely to hit.

Like a true socialist system, it helps you when you’re down, and when you’re getting too successful, it shuts you down.

Compared to the previous random hit-chance test, the above image shows GMAC modifying the hit-chance. hit_chance is incremented, and average is the result of the GMAC operation. The closer average is to hit_chance means that it is working.

However, you can see that in the hit_chance:12 there is a large 3% discrepancy. This occurs in the lower percentages (< 20%). GMAC represents higher percentages far more accurately than low ones.

Shot count

I had to decide how many shots I wanted to track in order to compute the percentages accurately. As I’m tracking each shot that fires, I have to know when to ‘give up’ and reset the counter. The primary reason for this is the assumption that the hit-chance changes over time. For example, if the Player is moving towards the enemy, the hit-chance increases. If I track too many shots, then I would potentially be comparing the success rate of another hit-chance that is drastically different from the current one.

Of course, the downside of tracking too little is that I won’t be able to represent the accuracy of certain the percentages  because there wouldn’t be enough ‘resolution’ for the math to compute.

In the actual GMAC v1 algorithm, however, once properly tweaked, it seemed like 10 shots satisfied all requirements; resolution was good especially when I simulated the shot series over a thousand times.

Low percentage

There was a peculiarity with the algorithm, which I think was related to the fact that I decided on counting 10 shots.

The lowest percentage I could track was 10%, which is logical, since any hit-chance above 0% is going to be adjusted to have at least once in 10 shots, giving it always a minimum of 10%. Thus, combat skills should always resolve to anything above 10%.

 

Hit-chance and GMAC

Note that GMAC is a modifier. The actual hit-chance is calculated by the factors governing the combat mechanics; weapon accuracy, weapon range, Player skills, etc. Then that ‘base’ hit-chance is passed onto GMAC which takes care of the balancing for success rate.

I think the GMAC modifier is a good RPG combat concept. It allows randomisation on a shot-by-shot basis, but will endeavour to reflect your actual skill level by increasing or decreasing likelihood of hitting based on on your success rate. Hopefully, this replaces the feeling of being unlucky with the feeling of being unskilled.

 

 

Advertisements

Inventory

One of Citizen’s gameplay themes is the limitation of what weapons you can use. As a citizen of a dystopian, Robot-controlled city, you couldn’t freely carry dangerous goods and not expect to be accosted by Robot patrollers.

It was in the INV system that I first wanted to express that game concept. The INV system was originally conceived so that the Player can carry only a few weapons. So I delineated certain types of items can only be placed in certain ‘slots’. I also limited the number of slots for a particular category.

However, in time, I came to think that the limitation was a bit too extreme. It was complicated from the point of view of mechanism, but it also had logical game problems.


Nearly 3 years ago I was working on an RnD game project (dubbed Henry) which was supposed to feature multiple characters. The system allowed the viewing of different inventories within the same interface, and allowed trading between characters through a drag-and-drop mechanism. It featured multiple pages and a categorisation of items; weapons and armour were automatically put into the upper slots, and other adventure items were put underneath.

The Inventory system from the RnD project called ‘Henry’. I think there’s a bit of Jagged Alliance in the graphic design decisions…

It was Henry‘s Inventory system that gave me my first experience in the in the difficulty in doing inventories, from the organisation of items, to the behaviours of drag-and-drop and how the logic of how things are arranged and displayed.

Also from Henry I took the idea of categorisation, which is the exclusive placement of items of a certain type into a section of slots in the interface.

But categorisation, I later decided, was not necessary if I was just simply gunning for weapon limitations. There were other ways of discouraging the Player from carrying too many, from the increased likelihood of getting checked by Robots, or simply the inability to use them effectively once the shooting started.

Because there were hundreds of ways to skin the limitation cat, I eased my rigid rules in the INV system. However, unlike Henry I had two other Inventory-related concepts that I had to address to introduced their own complexity: Readyslot, and Trade.

The Citizen INV (right), Trade( left) and Readyslot (bottom-right) interfaces.

Readyslot

Without going into too much details about the the Readyslot’s mechanics, it is simply the place where weapons that will be used immediately for combat are put.

Switching weapons that are already in the Readyslot are done immediately. However, there’s a time-delay when you try equipping weapons from the INV, which may be akin to taking something from your backpack. This is how the game discourages the Player from swapping weapons from the INV which may potentially contain a lot of different weapons in the game.

There are other characteristics: there are only 3 slots in the Readyslot area, and that is significant. Pistols are 1-slot weapons, subguns (i.e. SMG) are 2-slot weapons, and rifles are 3-slot weapons. You can mix and match any weapon configuration that the number of slots numerically allows.

But there are special considerations for pistols, too: you can dual-wield pistols.

If you choose to equip a subgun, you can carry another pistol as ‘backup’.

If you choose a rifle, the most powerful weapons in the game, you are limited to that weapon only, and if you try to change weapons from the backpack, there’s a time-delay to get it.

The technical challenged associated with the Readyslot is how that in itself is an extension of the INV system even though it may not look like it visually. The Readyslot is a categorisation, so only weapons can be placed in there.

Trade

The Trade system is essentially the INV system, but using a different source for the contents of the INV. For example, NPCs have their own INV database, and even scene elements, like a rubbish bin that can potentially hold items, have their own INV system.

The Trade system is a little different from INV in that there is a variable slot designation that is dictated by some database (in this case it’s specified in Tiled). For example, a rubbish bin will have 2×2 Trade INV (a.k.a. TINV). A dead robot will have 1×1. Some may have 3×1, or 4×2, etc. And thus there were many considerations about how the system will respond if there was an attempt to populate the TINV with more slots that it could hold, or items that wouldn’t fit the dimension of the slots. For example, a 3-slot rifle cannot fit in a 2×2 TINV.

Slot size, width and height

In games like Diablo, items occupy ‘slots’ in the inventory. But Diablo’s system is very elaborate, as items have both width and height. For Citizen, I decided only to consider how many slots a certain item will occupy. For the most part, only rifles and subguns occupy more than one slot. This greatly simplified the system.

The reason why this is a big deal is because one of the challenges of making an INV is the correct display of items in their proper slots. When dragging a rifle (3-slots) at the right-most slot of the INV, you expect the system to compensate for the size; it must not place it the right-most slot, but 3-slots to the left in order to the rifle to fit the intended placement location.

Also, you have to consider if there are items currently in place in those slots. Will you allow items to be displaced? If so, how do you logically re-position them that makes intuitive sense?

If dragging a rifle from the INV to the Readyslot that is already full of weapons, will you make a swap? Or disallow it?

It’s questions like that, and every conceivable permutation of how one item is dragged from one place or another, dropped onto itself, or another dropped onto it, or something else entirely, all those things filled the 2 weeks I spent designing and iterating through the INV and Readyslot systems

The multi-page function isn’t yet implemented, but I have to make sure to what extent I implement it. How many items will I end up implementing in the game’s narrative and combat? How many pages will it fill? Should I have unlimited pages? Or is one page a good simply limit?

Lots of questions. But it’s all part of the fun, right?

 

Look and feel

Though I’ve already done a bit of art for the game, I’ve honestly been fluffing about this one for quite some time. I didn’t have a definitive look and feel so i’m setting one up now.

I had been discussing with B about how to really get into terms with this. I think my main problem, which includes the art I’ve already made, is that the inspiration came from simply an attraction to a particular artwork (in this case Gray Shuko), but had nothing to do with the theme or the feel of the game itself as I want to depict it.

This is 2400 AD:

Playing it when I was but a babe, I can summarise the overall feeling in several points.

  • Dark
  • Bright neon lights
  • Dirty and clean contrasts
  • Feeling of unease in the environment

Note that while certain feelings are not readily expressible from a lookdev perspective, it helped to focus on the overall emotion/feeling I was having when I played the game, because that is the sort of thing that motivated me to develop CITIZEN. Perhaps what seems unrelated might actually be useful when the time comes. Who knows?

As B pointed out, the darkness comes from the black background, and the ‘neon’ probably comes from the magenta-coloured gui frames.

The ‘clean’ is obvious, because the graphics are simple. But I think I there was a ‘dirtiness’ in line with what was going on as a game, so that there are those contrasting elements, especially when going from one area to another (eg entering a room from exterior).

The feeling of unease is harder to track down, and I think that’s more of a gameplay issue, at least from how it looks like right now. I think however, that it will also inform other design aspects, like how the robots will look like, and perhaps even how sound, or the writing is done.

With those 4 points guiding me I scoured for anything in the web for indicators that may give me an idea how to hammer home those things. The thing was, I didn’t necessarily look for the most beautiful images, because I knew that beautiful images were stuffing up my efforts to have an actual theme to the game.

Good examples and why

Dark, neon, simple shapes. This is one of the prime lookdev reference images. Note that I’m not aiming for pixel art, but rather, aiming for the simplicity in detail, as well as non-assuming design.
The colours are muted in the darkness, and the outlines are aesthetically-pleasing.
From the game ‘The Last Night’; again, not about pixel art — and not even about the sidescroller viewpoint, which is not what CITIZEN is, but rather, the dark mood.
From the game Black Annex, I keep this peg to remind myself of simplicity in rendering. Granted, this is rather austere, but the game (gameplay itself) gives a connection between the look and how the game responds and feels spatially, so that simple renders can be given a lot of life, and other considerations and compromises can be made depending on how the graphics are used.
The space is cozy; the rendering is more in line with my initial art. The outlines are pleasing (again), and the proportions are not so realistic.
Not every location in CITIZEN is in darkness, so in exterior situations, I think this lighting condition, and overall feel, is close to the feel I’m going for. I won’t be able to really represent atmospherics, but the cyan haze is appealing.
Though not dark, I can imagine myself filling in the darkness and the mood and tone. The appeal of this image is of the minimum detail to represent the scene. It looks very game-y, The colours are represented more matter-of-factly; while I prefer grading the artwork to fit the darkness, I keep this peg to remind me that I don’t have to go too overboard with it.

Lighting considerations

In respect to the Good Examples shown above, the most use of lighting was the picture of The Last Night which features area lighting, where one portion of the screen will affect a surface’s diffuse colour. This is fine as long as no shadows are implicitly cast (ie by use of explicit harsh lighting).

Note that even when neons were used in the abovementioned image, the overall effect was still diffused; as it is pixel art, there’s an acceptance of these compromises. The artwork that I have to develop should also have the same acceptability using those compromises.

A good example of area-based lighting. It’s basically a masked colour layer that sits on a particular region, affecting those elements that come underneath it.

Proportional considerations

The image shown on the right is probably the kind of character proportions/shape that I want. This means it is realistically proportioned, but not particularly realistically shaped or accurate.

Bad examples (to avoid)

While appealing in of itself, the rendering is a tad too realistic: reflections, soft and hard shadows which require any character in the scene to be rendered as such. Textures are also too subtle. I would like use gradient of colours more often than textures.
From ShadowRun, this is too realistic.
While I love this game, the rendering — like Fallout — is done with too many hi-freq textures.
Though I had originally imagined a scene in the Hills in the same mood and feel as this, this is the sort of lighting that is not practical to do, so I avoid not only these types of lights, but a pitch black exterior, where the only light will inevitably have to come from point sources.
Too simple to the point where I won’t get my tone. Outlines, when too thick, end up brightening the colours, which is the reverse of what I want to do.
Though a beautiful image, the grading is too biased and ultimately too realistic.
Amazing to look at, nevertheless, even static light sources where pronounced shadows are produced is going to be a problem. However, lights above the reach of the main level (eg player, npc, ground-level env), might be ok, even when animated like this picture. Rain might also be a good thing to explore at a later time.

Thoughts on displaying adventure text

Adventure text can be long, and should be variable as much as possible.

In the RND test, I’ve formulated an initial design that has the following aspects.

Terms (TextTerms)

The use of terms mean that a keyword is used as a topic. So if I used the term ‘missionsuccess’, then I could define that the displayed text is ‘Mission successful! Congratulations!’

Furthermore, I can use a TMX entity, for example an npc with the name ‘letigus’, and use that as my term. And thus define ‘letigus’ as ‘Hi, my name is Letigus. What can I do you for?’

Also, through the use of terms, I can encapsulate a variations into one term. This is the syntax which I use to implement this. This is written in a separate text file which read by the AJAX object and then put into a Dictionary using the CSV2Dictionary plugin by Rex.

Each term is defined by at least 2 lines. One line is a declaration of how many variations there are. And the succeeding lines are the content of the term.

<term>|<default index>/<maximum index>
<term>.<index>.<text type>.<duration>|<text>

The first line’s <term> is the declaration of the term’s name. The default index refers to which index it should start with. The maximum index is expected maximum variations of the term.

The second line’s <term> signifies this is the term being used. The <index> is defining this particular term’s index. <text type> can be anything that identifies the kind of text it is supposed to be. For example, the text could refer to a person speaking, or it could refer to a ‘narrator’, or it could refer to a description of something. This <text type> is used to colour the text according.y The <duration> is the number of seconds that the text will hold. If 0, it will hold indefinitely, which is useful in cases, for example, if a player is killed, and a ‘defeat’ term is used. Then there is a bar ‘|’ and the <text> follows afterwards.

Note that in the text file, there are no newlines, so the use of ‘\n’ is just a marker for a C2 text replacement for an actual C2 newline character.

To recap, here’s an example:

look_r1|1/2
look_r1.1.desc.0|The place is a smelly hole.\nAnd a sight for sore eyes.
look_r1.2.desc.0|You don't know how anyone could live here.

So at the start, ‘look_r1’ is defined as having 2 variations, and it starts at the first index.

Then look_r1’s two texts are defined, each specifying their own index, and type, and duration, and finally, the text.

Displaying the text

The display of the text is then offloaded to another function (DisplayText) which sets the duration and colour based on the data it receives from the Term itself.

For example, if the type is an NPC speaking, the DisplayText function will wrap the text in quotation marks, and maybe give it a different colour.

External text file, map-specific

As I said before, the AJAX and CSV2Dictionary plugins are used to transfer an external text file’s contents to a dictionary, and following the format above, this is relatively trivial.

CSV2Dictionary requires a pair separation by a specified delimiter. I chose the default (comma) and used the  bar ‘|’ as a separator within the tokens.

In the RND test, there was no distinction between map-centric adventure text and more generic/global text. However, this should be implemented as it makes most sense.

Thoughts on accomps

Accomps is ‘accomplishments’ and I’ve separated map-related accomps (m_accomps) with global accomps (g_accomps). Global accomps persist, while m_accomps are re-populated  when a map loads.

This separation may not be necessary, but for the meantime, there it is.

accomps have the key as the accomplishment, and the value is either 0 or 1. 0 if not yet accomplished (default value), and 1 if it is.

In the RND test, the accomp related to exiting an area was triggered 1 every time the player was on top of the designated area (ie current_trigger variable). But when  the current_trigger="", it triggered (using TOWT) a reset to 0. So as long as the player was not on the trigger, the accomps will always revert to 0.

On the map logic script, the ‘success’ of a map is checked whether all (or some) of the accomps have been set to 1. Because it is map specific, anything can be written here.

In the TMX side, there are some inputs that need to be done. First, the map’s accomps are written in the Map’s custom properties called ‘accomps’. Eg, accomps=talk:npc1,kill:guard1,remain:exit

It is done in the TMX because it is only relevant for this map. There are other data that could be offloaded here.

In this case, when player talks to an NPC, this interaction is marked as ‘talk’, and thus the trigger is sent as ‘talk’. If player talks to ‘npc1′ then this is sent to npc1. Now in the TOWT trigger for f_trigger_receiver.f_name==’npc1’, it is responsible for writing the proper key to the accomps. The syntax is free-form so care must be taken.

In the RND test, the killing of an enemy was not broadcast, but this is a good example of implementing such a broadcast (eg BroadcastTrigger(“killed”,f_trigger_receiver.uid)) within the main logic.

Then in the main logic, a trigger receiver is actually waiting to receive the news of death. When it does, it takes care of its own key in the accomps: accomps["kill:guard1"] = 1.

 

Thoughts on triggers

On the RND test, here are some thoughts on triggers.

Triggers are broadcast by a function. Triggers may have a targeted object/instance. In order to target any potential object, they’re put into a Family, which I’ll refer to as f_trigger_receiver (f_tr, for short).

There are 2 parts to triggers. The ‘main’ logic, and the ‘map’ logic. The main logic handles generic logic of triggers.

Main logic

f_trigger_receiver

The Family for all trigger receivers. Requires f_name, and f_receivedtrigger variables. f_name is the name of the entity which a trigger will use to refer to this instance. f_receivedtrigger is the string identifying the trigger that has been sent out.

On player GridMove reach target

Fired every time the player moves into a tile. This queries if a trigger area was stepped on.

Also, the player must have a current_trigger variable which keeps track of the trigger area it is on at any given time. This prevents re-triggering when the trigger area covers adjacent tiles. Also, this allows to find out if the player has stepped out of a trigger.

BroadcastTrigger

A function which handles the send off to f_trigger_receiver. It accepts a trigger_name, and a trigger_target. The trigger_name is the identifier of the trigger. The trigger_target is a comma-delimited string that identifies the objects/instances that the trigger will be sent to. The f_trigger_receiver family is used in order to go across different object types.

Other interactions

Any other interactions deemed worthy of a trigger only has to call the BroadcastTrigger function, and feed it an object that can accept a trigger.

The RND test, for example, had broadcast an NPC interaction generically by feeding it trigger_name="npctalk", trigger_target="npc1". Then the trigger was broadcast only on npc1 and processed accordingly.

There are no  ‘global’ triggers (ie triggers must always have a target). If a ‘global’-like trigger is needed, it might be better to use the player’s mover token as that, since it’s as global as you’re going to get.

Map logic

Map logic refers to the map/room-specific stuff.

Typically, the triggers for a particular room are stored in a separate event sheet (which I call scripts).

Time triggers

I put the time triggers in the map because it’s more specific to the map/mission. I still call BroadcastTrigger, but the trigger_name is specific to the map, of course.

Time triggers include ‘per-tick’ or any kind of time-related triggers.

Self-initialisation

Some instances need to init themselves before going into play. For example, a waypoint traveller needs to init the first waypoint index. This is done using the post_tmx boolean check, which is basically a switch that tells that the TMX has been completely read, and all objects have been created (and thus referenceable).

Other triggers and functions

Any other kind of triggers, whether they’re from FSM or TOWT, can be put in the map logic script. In the RND test, I’ve put in unique FSM states (eg “reachpath”) to put it in a special state so that the rest of AI can contextualise itself.

Map-specific functions are put here as well.

 

 

 

 

Converting angles to isometric angles

I don’t think ‘isometric angles’ is even a term, but I’m referring to how an angle should appear like in isometric view.

The image below describes the situation.

Problem

Given an  angle (75deg and 45deg depicted in red arrows above), what is computed angle if the circle was compressed by half (ie an isometric view). The small ellipse depict a circle in isometric view.

The yellow arrows depict the scaling effect of the point on the big white circle and is projected to touch the isometric circle.

The green arrows represent the vectors whose angles I am computing.

Solution

I must apologise to any potential researcher that this solution is entirely homemade and there might be more elegant solutions out there.

  • With a given angle, determine the normalised length of the Adjacent side A. This is done by cos(angle).
  • The length of the A enables the measurement of the Opposite side O.
  • O is computed by multiply A with the tan() ratio of the angle: O = tan(angle)*A
  • Now that O has been found out (the yellow arrow line depicted above), scale the length down as per the isometric projection. For simplicity, I’m scaling it by half (eg for 256×125 tile): O2=O/2
  • Then recompute the ratio of the known A and new O2: ratio=O2/A
  • The new ratio can be used to determine angle using atan(): new_angle=atan(ratio)
  • If angle reaches 90, then A has no length, and this corresponds with the nominal orthographic angle.
  • If angle reaches 180, then O has no length, and so the same thing.
  • If angle > 90 and < 180 the result of A=cos(angle) will be negative.  The same with tan(angle).
  • If angle > 180 and < 270 then A=cos(angle) will be negative still, but tan(angle) will be positive.
  • If  angle > 270 and < 360 then A=cos(angle) will be positive, and tan(angle) will be negative.
  • When result angle is reached, modify the value based on the quadrant of the original angle:
    • If cos(angle) is negative, and tan(angle) is negative, add 180 degrees. (Quadrant 2)
    • If cos(angle) is negative, and tan(angle) is positive, add 180 degrees. (Quadrant 3)
    • If cos(angle) is positive, and tan(angle) is negative, add 360 degrees. (Quadrant 4)
    • If cos(angle) is positive, and tan(angle) is positive, then keep result. (Quadrant 1)

C2 angle

The images above show 45 degrees as pointing NE. In C2, however, it’s pointing SE. But this orientation allowed me to understand what was going on.

C2 Implementation

Thus.

Working rather well.

To reverse the operation, to get the orthogonal angle from an isometric angle, reverse tile’s width/height in O2: O2=O/(SquareTx.Height/SquareTx.Width)