Wednesday, May 15, 2019

battleMETAL - And Yet, Somehow it all works - part 5 - Arming Menu

Load and lock…


On the heels of talking about the Deployment Menu, there’s another menu that I had a lot of fun building and consider a technical achievement: the Arming Menu. This menu is a staple of old vehicle combat games, and mech games in particular like in Battletech, it's called the ‘Mech Lab’. This is the menu where the player gets to exercise a level of creativity, creating a loadout of weapons and equipment that they are most comfortable with. In battleMETAL, every playable ‘Metal (mech) has a set of Equipment Slots. These slots may contain Weapons or Equipment but limited to a set of factors such as item size and the type of item. Here’s a few examples from old school games to showcase what my starting ideas were.

Mech Lab as seen in Mechwarrior 4: Mercenaries


Weapon Menu as seen in Earthsiege 2


These menus can be fairly daunting for a newer player or someone not used to this genre of game. A ton of information is thrown at the player all-at-once, and many decisions here will directly impact the player’s experience when they actually deploy to the game space. However, this is a menu that the game will return to many times, and Players are encouraged to spend all the time they want here testing out ideas. To that end, I tried to make the Arming Menu as straightforward as possible given the limited UI tools at hand.



Proof of Concept Arming Menu ~2016



...Ok, we can do a little better than that, heh. This image was the very first take on the Arming Menu. Although it lacks a lot of graphical icons, one can see the roots of the menu are here. Weapons and Equipment have a UID or unique ID that the game uses when looking up data for the item. When the Arming Menu is called, it grabs all relevant data for each UID, and the data is stored in separate .item files. These .item files are text files renamed to be better understood, and contain a JSON-style data language.



Add in data loaded for every mech in the game and the end result is a wonderful menu that looks like this:





Now this is looking pretty good. We can see the list of available equipment for the Player. We can see the hardpoints that the chosen mech possesses. There’s some other important panels here as well: the ‘Weapon Group’ panel in the lower left allows the Player to set their fire groups. A fire group is a collection of weapons bound to a single fire key. Next to this panel, is the ‘Energy Drain’ panel. This important panel ties into battleMETAL’s Player resource mechanics. In addition to health, Players must be aware of how much Energy their ‘Metal currently has. Energy is used to power weapons, shields (a rechargeable health pool), and sprinting. This panel tells the player how much energy their Weapon Groups are going to draw when used. This is helpful to Player if they’re trying to balance their equipment selections.


In the middle-right of the screen is a very important set of panels, the Weapon Info panels. The top one shows the statistics for the Weapon that a Player has chosen in the menu. The bottom panel is for the Weapon in the selected hardpoint. They’re over/under so that Players can compare directly, the stats of 2 different weapons before mounting any weapon to a hardpoint.


Now for some neat code notes. Every mech has a list of Hardpoints that can hold a piece of Equipment. The data for this is stored in a text file with a custom .unit extension. When the Player selects a mech in the Hangar Menu, that data is loaded into a set of global vars. These vars are not static, if the Player chooses a different mech, the globals are updated with the data of the next mech the Player chose. Part of the data loaded are the hardpoints, Quake C being more primitive means that hardpoint data is loaded into a series of Arrays[] dedicated to each data point. The index of the array is used as the UID of the Hardpoint:


float MECH_DATA_H_TYPE[10];denotes the allowable Types of equipment for the Hardpoint.

float MECH_DATA_H_SIZE[10]; maximum Size of the Equipment.

vector MECH_DATA_H_ARM[10];

vector MECH_DATA_H_HNG[10];


The two vector arrays are specifically for the UI. Currently, I hardcoded the screen-position of the Hardpoints when the Arming Menu needs to draw these on-screen. The Arming Menu code loops through all the hardpoints (game maximum of 9 possible hardpoints) and pulls the position for each Hardpoint from those vector arrays. When the Player clicks the mouse, the code checks to see if the mouse position is within the area near of each vector to determine if the Player has clicked on a Hardpoint. If the Player has, the code uses the UID of the Equipment in the Hardpoint to load the .item data file for that Equipment.


Another great feature I implemented recently was the ability for the Player to save up to 6 different configurations per ‘Metal. I realized that Players would prefer to be able to save their changes to the mechs for later use. Well if we’re going through the trouble of saving something once, then we now have the functionality to save anytime (isn’t programming grand?). The save feature is autonomic and triggered by natural Player interaction with the Arming Menu. Things like tabbing between configs, or switching from different menus and the Arming Menu. The save files are then stored in folders labelled via ‘Metal UID, and then their own number. The save files itself is a plaintext file, and is loaded on-demand. This means that savvy Players can noodle around with just the text file and have it load by simply changing configs in-game. I did put in some validations though, so no breaking a ‘Metal’s Hardpoint values by putting in larger-than-allowed weapons or such.


Finally, once the Player has tweaked the ‘Metal to their liking, they proceed to the Deployment Menu. When the Player hits the ‘Launch’ button, the CSQC takes all their selection data and pushes it up to the Server for parsing. The Player’s chosen mech UID is sent, along with Hardpoint data and Weapon Group options, the server than takes this data and builds the Player’s entity into that mech. Overall, this was a really fun feature to build for battleMETAL, and fairly radical for any Quake mod at all.

Wednesday, May 8, 2019

battleMETAL - And Yet, Somehow it all works - part 4 - Deployment Menu

Prepare for drop!

Checking my post timeline and realized that this blog series has now been running for a year, huzzah! Also, core coding of battleMETAL appears complete. The last few months have been cleaning up the map pipeline (don’t worry friends, we’ll get there…) which indicates that the main coding is stable! It does feel a little anticlimactic, so many weekends spent hammering this game together in a cave with a box of scraps….


Anyhoo, this article is about one of the in-mission menus. These menus are located in the CSQC module which is run exclusively client side. As described in a previous post about CSCQ, this menu system is brought up with the tab key (default key) after the player has connected to the server. One of these menus is the Deployment menu.


The design scope for the Deployment menu went something like this - Originally I wanted the player to be able to pick from a few different spawn points on any given mission. Previous mech games either lacked this feature or featured it loosely. 2 main impulses pushed me to implement such a feature in battleMETAL with the first being multiplayer. Originally I believed it was possible to make battleMETAL work in multiplayer as well as it did in singleplayer. Unfortunately due to engine limitations across multiple features, this was discovered to not be feasible given the time and manpower limits of myself (I’ll do a post eventually on what happened to multiplayer).


The second impetus came from adapting the multiplayer idea. I considered the Player’s ability to pick their spawn point to be a nice layer of tactical control to give the player in lieu of trying something like an open map or semi-open world. The spawn point selection also allows for levels to contain more than 1 scenario setup - the mission briefing can give clues to the player about the combat conditions at any of the spawn points. Once the player chooses their spawn point, they press the [launch] button, become the mech they chose in the Hangar menu and begin play.


So, how did the code get there? As seen in a previous article, a battleMETAL CSQC menu can be quickly spun up from the functional map. The file acts like a pseudo class where the primary menu functions for the desired menu are kept. I’ll cover the most interesting tidbit which are the deploy points.

Pictured is the Deployment menu with Deploy Points depicted as (A), (B), ( C).


To begin, when the Player connects to the Server, there’s a function call;

client_push_mapobjects()

This queries all the entities on the server that have the FL_NODE flag in their .flags member variable. When such entities are found, their data is piped to the Client via client_send_deployPoint() which bundles the relevant deploy point info into a SVC_TEMPENTITY game packet. The client then receives this data, checking its own global DEPLOY_POINTS_ACTIVE[]. This global array maps its array index to the Deploy Point Id of deploy point entities on the server. The game has an artificial limit of 32 total deploy point entities because of this (not sure if any given map needs more than 32 deploy points).


The ‘important info’ that the server sends to the client is the following:

        Deploy id - float, the assigned id number for the node ( this is not the same as the entity id)

         isActive - boolean, 1 or 0. Players can only see and use ‘
isActive == true’ deploy points.

        Origin - the server-side location of the entity in game space as a vector, 'X Y Z'


Quick side note, deploy points can be given an .faction member var. The server only sends deploy points whose faction value matches the specified Player’s faction value. This is a holdover from the multiplayer functionality where originally PvP was going to be a thing.


Once the client has the list of deploy points, the next step is how to render them. I really wanted a clean and programmatic way to render any deploy points anywhere on the given map space. Other considerations included the fact the player in a singleplayer-context only really ever sees the Deployment Menu once per map (even if they die, the map just restarts). Due to this, there wasn’t a need to constantly send live updates of deploy point data to the client. Knowing this, drawing the deploy points onto the menu screen was fairly straightforward.


Of the few things that CSQC gets ‘for free’ from the framework is data about the world entity. I can’t remember in the C-code all of the vars in world that are sent over, but there’s one important set - .mins and .maxs. These two variables contain the size of world’s total bounding box, just like other entities have bounding boxes. Knowing the bounding box of the world allows us to compare entity locations to some sort of pseudo-constant for game space. Using this info, I created an algorithm for projecting a map coordinate onto a 2D UI panel.

  1. Find the map’s total size
    1. Take the mins value as a positive number and add the maxs value
    2. Find the center of this new size
  2. Take the mins value as a positive number, then divide it over the total size
    1. This returns a percentage value ( see where I’m going here?)
  3. Apply the percentage to the UI panel’s screen size
  4. profit.

This algorithm allows us to display any game coordinates onto any-sized 2D space and you can see this functionality on the Briefing menu as well. Nav Points are player guide posts for navigating around the map and the same algorithm renders these points onto the Briefing menu just like the Deployment menu. Now that the Player can see where on the map a Deploy Point will place them, the next part is spawning the Player at the point they have chosen. When the player clicks the Launch button in the upper right-hand corner, the CSQC fires off a message back to the server. This message contains the player’s chosen mech, the mech’s equipment id’s, and the Deploy Point to spawn at.


When the Server receives this message, it goes and looks for the desired Deploy Point, and if it finds the point, will then move the Player to that point. The fallback scenario is that the Deploy Point is not found, so the code defaults to the map object Info Player Start which every map needs regardless of usage. That’s all I have for this post, an interesting small feature that was fun to implement and fairly unique for Quake mods. There’s probably some refactoring that could be done on the algorithm but I figure its low-cost enough to stay as it is.