Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Lua Tips and Tricks  (Read 517 times)

0 Members and 1 Guest are viewing this topic.

Offline Spots

  • *
  • +1% chance to make useful post
  • Spots has no influence.
Lua Tips and Tricks
« on: July 20, 2020, 04:15:36 AM »
Hey, everyone! I'm new to this forum, but I've been working on mapadds since late 2019. During this time, I've run into a fair few problems and had to learn some things the hard way, so I thought it would be cool to start a list of useful things I wish I knew when I started out. The list isn't very long right now, but I'm definitely going to update it as I continue to learn things and overcome problems.

One important thing to note about timers is that the value returned by a function that gets called by a timer is the number of seconds before it gets called again. Initially, I thought that it was either 0 or 1, with 0 cancelling the timer and 1 repeating it at the initially specified interval. Returning 0 does cancel the timer, but returning 1 will cause it to repeat in one second, which isn't always desirable. Here's an example of how to make something repeat every 5 seconds:
Code: [Select]
HL2.CreateTimer("ExampleFunction", 5)

function ExampleFunction()
    -- Do stuff.
    return 5

A nice thing about this is that the function called by the timer can have multiple return values in it and choose one based on some conditions. So, the interval could be changed by returning a different number or the timer could be cancelled by returning 0 whenever necessary. Also, the number doesn't have to be whole. It's totally possible to make something repeat several times a second.

Random Numbers
Random number generation has been a real doozy for the entire time I've been doing mapadds. The only issue that I've consistently noticed is that the numbers generated are too predictable for certain situations by default, but I've encountered some other issues that make me question reality a bit. I'll start off by addressing the predictability issue and then move on the other other weirdness that I'm uncertain about.

For most cases, it shouldn't be necessary to deliberately seed the random number generator, but there are some cases where just leaving it alone doesn't cut it. For example, one script I'm working on chooses a random number of enemies to spawn in and I noticed it flip-flopping between two different numbers every time I loaded the map. So, when computers can't be random enough, something that is random can be harnessed... the player! My solution is to wait a few seconds for the player to start moving at the start of the game and then get the dot product of their current origin vector and the origin vector of a random node in the map. So, this would be written as:
Code: [Select]
local randomNode = HL2.GetNodeData(HL2.RandomInt(0, HL2.GetNodeCounts() - 1))
HL2.RandomSeed(HL2.DotProduct(HL2.GetAbsOrigin(HL2.GetPlayer()), HL2.Vector(randomNode.x, randomNode.y, randomNode.z)))

The magic of this solution is that it's basically impossible to get exactly the same seed more than once due to the highly unpredictable movement of the player. However, it does require the script to initially do nothing for a few seconds which kinda sucks. Depending on the case, though, the player might not even notice it.

Other Stuff
Now I should mention the other weirdness I've run into with RNG. Spawning a bunch of enemies all at once is a very common thing to do and I have previously worked on a wave-based survival gamemode that required exactly that for each wave. Spawning in the first wave always worked, but subsequent waves proved problematic. Every other wave spawned every enemy on the same node even though the function used to spawn the wave was the same as the one that was used to successfully spawn in the first wave. I had to think about the issue for a while, but I finally came up with a theory that the built-in random number generator was being seeded with the current time every time it got called, thus causing a limit of only one random number per second. However, later testing seems to have disproved this theory and I still have no idea why the issue occurred. I got around the issue by creating a timer that spawned one enemy every second until the proper number had been reached instead of spawning them all at once. It's not an ideal solution and complicates the code, but at least it ended up not being noticeable on the surface. Anyway, if you run into the same problem, that's the only advice I can offer unless I figure out something better in the future.

Executing Console Commands
It's possible to execute console commands with Lua by spawning a point_clientcommand entity and then using the HL2.EntFire function. The clientcommand entity can either be spawned in the mapadd.txt file by putting this in the "entities" section:
Code: [Select]
"targetname" "console"
or in Lua by writing this:
Code: [Select]
local console = HL2.CreateEntity("point_clientcommand", HL2.Vector(0, 0, 0), HL2.Vector(0, 0, 0))
HL2.KeyValue(console, "targetname", "console")
Then, console commands can be executed with the HL2.EntFire function. For example, this would give the player a shotgun:
Code: [Select]
HL2.EntFire("console", "player", "Command", "give weapon_shotgun", "0")Also, the "0" at the end is a delay, so this would execute the command immediately.

General Errors
"attempt to call nil value"
I've found that if a script fails with the error "attempt to call nil value" in the console, it's usually due to a basic syntax error such as comparing values with a single = sign instead of two. Actually, I can't remember any occasion where it wasn't just a simple mistake like that.

Most of the times I've crashed the game with a script, it's been because of a node error. This is particularly frustrating because it can be hard to tell what exactly made the game crash. So far, I've encountered two major causes of node problems. The first one is trying to manually edit an snl file and messing something up along the way, which is something I've never been able to pull off correctly anyway, so I unfortunately can't offer any advice with that. The second cause is trying to spawn an entity on an invalid node. For me, this has always been caused by picking a random node and forgetting to subtract 1 from the HL2.GetNodeCounts() function. So, since node counting starts at 0, picking a random node number is done by writing:
Code: [Select]
HL2.RandomInt(0, HL2.GetNodeCounts() - 1)
This one shouldn't come as a surprise, but the game really doesn't like infinite loops and will completely freeze if one happens. Just a possible lead to look into if you encounter a freeze.

Anyway, that's all I've got for right now. In addition to keeping this list updated, I'm gonna see if I can make some in-depth tutorials/explanations on making various gamemodes later on as well as shorter tutorials on useful things that can be applied to a variety of gamemodes. Any questions, suggestions, etc. are welcomed!
« Last Edit: July 20, 2020, 06:19:04 PM by Spots »

Offline BIZ

  • 32-Bit Hobo
  • *
  • *
  • +0% chance to make useful post
  • BIZ hides in shadows.
Re: Lua Tips and Tricks
« Reply #1 on: July 20, 2020, 07:27:40 AM »
This is the one thing we are sorely lacking. Anything helps in this department. Thanks for sharing and welcome to the forums.


SimplePortal 2.3.6 © 2008-2014, SimplePortal