Jump to content

Shrooblord

Members
  • Posts

    610
  • Joined

  • Last visited

  • Days Won

    4

Everything posted by Shrooblord

  1. Cool. Thanks for your effort, RoMonGerr. If ever you want to compare two files again, before and after changes, a very easy-to-use and even easier-to-read tool is DiffChecker. Just copy both files into its two windows (the before on the left and the after on the right is conventional) and set its expiry date to never / make it permanent, and people all over the Internet who flock to this post will be able to enjoy the fruits of your labour, even after many updates of code changes. ;) And the effort is much less on your side, too. It's literally like two copy-pastes and a couple of clicks.
  2. This is true, but we were talking about Captained ships that the player owns who've been given the Order to either Salvage or Mine - those don't fight back, and they indeed use their weapons to "salvage" or "mine" if they have any equipped, which is incredibly counter-productive both ways, whichever way you look at it.
  3. ooohh, yeah. There's an anti-telefragging script in-place. I remember that now. True. Aw, too bad. No trying to randomly teleport people inside asteroids and Stations to blow them up, then. :P
  4. Yes, but the fact that the Shipyard "forgets" what ship it was building on is the problem, because that way it never completes the build; and never toggles off the invincibility of the ship it was building. Furthermore, I've found that the work-around I mentioned is sub-optimal. The ship you commandeer this way will not have all the functionality it should have (i.e. its Interaction menu wasn't stocked with all the options it should have had, if I recall correctly). My memory is a bit hazy on this part though. It's been a couple of days since I triggered this behaviour.
  5. Cool! So a normal jump would always teleport you to a safe place in the target Sector, I assume, but /teleport could potentially warp you inside an asteroid? That sounds fun!!
  6. Let's see what Wayleran has to say about it: OK, so when it comes to "event Trader vessels" like the distress call Traders, this is the line to modify, in shiputility.lua, line 288. Meanwhile, he says the following about actual Traders that buy and sell cargo to Stations: So change tradingutility.lua, line 206, to change the amount that cargo ships that go to Stations to buy/sell goods carry on each trip. According to him, these vessels always drop 50% of what they were carrying when they die, no changing it. So it would seem there is no way to modify how much they drop when killed without inherently modifying how much they buy/sell (or rather: the relationship is inverse to that - how much they buy/sell directly influences how much they'll drop). Where did you learn this, Wayleran? Was it from experimentation? I'd love to find out. EDIT: Just to follow up on this, take a look at this code from civilship.lua. It details what happens if the Cargo Hauler decides to indeed dump their cargo when the player threatens to raid them: function CivilShip.dumpCargo() if onClient() then invokeServerFunction("dumpCargo") return end local ship = Entity() local cargos = ship:getCargos() for good, amount in pairs(cargos) do for i = 1, amount, 2 do Sector():dropCargo(ship.translationf, Faction(callingPlayer), Faction(ship.factionIndex), good, ship.factionIndex, 2) end ship:removeCargo(good, amount) end end The part where it reads for i = 1, amount, 2 do Sector():dropCargo(ship.translationf, Faction(callingPlayer), Faction(ship.factionIndex), good, ship.factionIndex, 2) end highly intrigues me. What you can see happening there is that the for loop iterates from 1 up to the amount of goods the Cargo Hauler has, and drops that item of cargo on an individual basis, but does this for every second item of cargo in their cargo bays. This means they'll also always drop half even if you threaten to destroy them, and they tell you they'll give you everything they've got! And then the script just plain deletes the rest, simulating that the ship did indeed drop all their cargo, when in fact they dropped half and crammed the rest into their Fusion Generators for fuel. Hahaha, what?? It would seem the devs want us to only ever raid 50% of what Cargo Haulers carry, no change to be made. I guess there's a lesson here: piracy doesn't pay! ;D Interesting. I will continue to investigate.
  7. Your best bet at learning new code is to use a couple of different resources: The Documentation has a great expansive list of all the functions and properties that are available to you. I recommend a text editor that allows you to search these documents, as finding your way around using the navigation buttons alone may not be as intuitive as you hope (and also, sometimes you simply don't know in what part of the Docs a specific function call would be housed). Study the vanilla code for behaviour that closely matches what you want to implement. If your idea is novel, there's a good chance you can still base part of it on how vanilla handles a couple of things. I found especially the Sector generation and Entity interaction commands to be quite informative on the whole structure of how most of Avorion operates. The Entity Debugger does a lot of wacky stuff that isn't scripted anywhere else, and it too is a good source for learning some neat tricks. Look at how some modders have applied new code to existing functionality, or found ways to integrate completely new code into the game. Some of the methodology they use is rather elegant and inspiring, and I for one can still take a good couple of notes from their work. Experiment. A lot. Adding a simple script and using print(...) commands is a great help to debug what is going on, since we don't have an actual debugger to use. You'll find, though, that even though this is true, you'll rarely actually crash the game. Often you'll find an error is explained in much detail, with a full stack traceback, in the Console (open by pressing the quote key (' and ")). I've only crashed my game once, and I'm still not sure how, when I did. xD Another great asset are functions like printTable(...), tostring(...) and the /run ... command in-game. I've also attached the Entity Debugger to my own ship by default by adding Config.Add("data/scripts/lib/entitydbg.lua") to my Ship Script Loader Config, which is an immense help. Good luck out there, and have fun modding!
  8. No, ships may still get stuck trying to follow you after a jump, even with all variables in-place that should ensure a safe jump. Often I find jumping back to the Sector in question with a Mining Drone already changes something that causes them to have jumped. That is to say, I never see them actually leave, but they're always already gone when I enter the Sector while backtracking. I'm not sure what causes this, but this behaviour sounds like an issue with the Sector unloading before the ship had time to warp out. I'm just guessing on that front, though. Absolutely. It's been confirmed that the devs plan on adding this in an upcoming update. Not sure if it was going to be this upcoming update, but soonTM at the very least.
  9. No, they're not. They'll sit there and get shot at while casually mining ore. Also, I've never seen a Captained mining ship with guns shoot back, unless I manually give it the Order Guard This Position. Maybe I need to experiment more in that field, though. Yes, as far as Pirate targeting is concerned, I don't think they automatically lock on to player vessels all the time. If they've got a target, they'll try to kill it, which gives you a little bit of a window to dash in and out for a quick salvaging raid. But I'm not sure if you can totally count on being safe - they may still decide to start targeting you on a whim.
  10. Yeowch. Now I kinda want to see a story mission like that in the game. Call it "Pilgrimage". We have to find a New Promised Land for these poor homeless Traders and escort them there.
  11. Yup, really looking forward to that update. To expand upon what Ravien said, the exact command is: /teleport PLAYER X Y Enter this in chat and you'll be immediately teleported there in your ship, almost like you just jumped there normally. For example: /teleport Shrooblord -77 -162 would teleport me to the Sector with coordinates -77:-162 To see all commands you have access to, type /help in chat. To view the help information about a specific command, type /help command in chat. In this case, that would be /help teleport
  12. For the first question, yes, absolutely. That's what we were discussing just above your post. I don't remember which of the two lines it is that controls this, but the two lines Wayleran mentions you need to edit drive the amount of goods dropped by killed traders, exactly as you request. I don't know about defining how well-armed trader vessels are. Look at some of the trader scripts and the plan generators in the vanilla script library. I'm pretty sure there should be something in there that matches what you're looking for.
  13. Meh, sorry about that. I must admit I pumped out an "update" in a hurry. You can also tell by the amount of edits I made to that post detailing some bugs I found right afterwards. My experiences with the "fix" so far are: It works if you have a lot of System Module Upgrades or Turrets to research. It breaks non-Auto Research (vanilla) functionality. It doesn't combine turrets of different types like players might; it only Auto-Researches duplicates (i.e. those made in a Turret Factory). On top of what you just added, I'd say the "fix" is rather buggy and shouldn't be installed, correct. x) I should take a look at this again in the future, but I remember having quite the headscratcher trying to resolve the original bugs I found. Perhaps it would be best to try and rewrite this entire thing from scratch. --- That said, I did have a little bit of a revelation which might work: instead of using the item.uvalue variables I mentioned giving me trouble before, one could use entity:getValue(...) and entity:setValue(...) calls to achieve the same effect. It may be worth a shot. To be continued...
  14. For anyone still looking for a mod like this in the Steam Workshop Era, why not check out Bubbet's Ranks Mod? It does everything this mod did, and more. ==== After a request made by death dealah for a mod that creates a welcome e-mail to first-timers on a server, I created this mod, which is able to send such welcome e-mail, and include resources like money and all the materials from Iron up to Avorion as an attachment to the e-mail. ==== WELCOME MAIL - v1.0 Introducing: Welcome Mail! ==== MODIFYING THE E-MAIL (SERVER OWNERS): You can change the contents of the e-mail, including the amount of resources the joining player will receive, by modifying the appropriate lines of code in the file /mods/WelcomeMail/scripts/player/welcomeMail.lua : invokeServerFunction("setTranslatedMailText", "Some Assets to Help You Get Rolling"%_t, --message header WelcomeMail.generateWelcomeMailText(Player().index), "Shrooblord /* The owner of this galaxy. Wait, what? */"%_t) --sender invokeServerFunction("generateWelcomeMailAttachments", 50, --money 100, --iron 200, --titanium 300, --naonite 400, --trinium 500, --xanion 600, --ogonite 700) --avorion local welcome_text = [[Welcome, ${player}, We see you are new here. Good to have you on board! Included in this e-mail is a bit of a start-up capital for you to work with. We hope this will help you get going more easily, and give you a little bit of a leg-up in this dangerous Galaxy... good luck out there! May our paths once cross. Until then! Best wishes, Your Galactic Overlords ]]%_t ==== INSTALLATION INSTRUCTIONS: This mod needs to be installed on both client and server. Unzip the contents of this .ZIP file into your (...)/Avorion/ installation directory. Modify (...)/Avorion/data/scripts/server/server.lua in the following way: Insert this code inside the onPlayerLogIn call. player:addScriptOnce("mods/WelcomeMail/scripts/player/welcomeMail.lua") --WelcomeMail If you had a vanilla server.lua file, that function will now look like this: function onPlayerLogIn(playerIndex) local player = Player(playerIndex) Server():broadcastChatMessage("Server", 0, "Player %s joined the galaxy"%_t, player.name) player:addScriptOnce("headhunter.lua") player:addScriptOnce("eventscheduler.lua") player:addScriptOnce("story/spawnswoks.lua") player:addScriptOnce("story/spawnai.lua") player:addScriptOnce("story/spawnguardian.lua") player:addScriptOnce("story/spawnadventurer.lua") player:addScriptOnce("mods/WelcomeMail/scripts/player/welcomeMail.lua") --WelcomeMail matchResources(player) end You're done! Have fun playing. 🙂 NOTE: A side effect of having to install this mod on both client and server means that new Galaxies created in singleplayer will also trigger the Welcome Mail. I'm looking into how to prevent this from happening. Changelog ==MOST RECENT VERSION: 1.0== 1.0 Initial release. ==OLDER VERSIONS== WelcomeMail-1.0.zip
  15. PART III (FINAL) package.path = package.path .. ";data/scripts/lib/?.lua" require ("galaxy") require ("utility") require ("stringutility") -- Don't remove or alter the following comment, it tells the game the namespace this script lives in. If you remove it, the script will break. -- namespace WelcomeMail WelcomeMail = {} WelcomeMail.welcomeMoney = 0 WelcomeMail.amountIron = 0 WelcomeMail.amountTitanium = 0 WelcomeMail.amountNaonite = 0 WelcomeMail.amountTrinium = 0 WelcomeMail.amountXanion = 0 WelcomeMail.amountOgonite = 0 WelcomeMail.amountAvorion = 0 local mailText = "" local mailHeader = "" local mailSender = "" function WelcomeMail.restore(values) WelcomeMail.welcomeMoney = values.welcomeMoney or 0 WelcomeMail.amountIron = values.amountIron or 0 WelcomeMail.amountTitanium = values.amountTitanium or 0 WelcomeMail.amountNaonite = values.amountNaonite or 0 WelcomeMail.amountTrinium = values.amountTrinium or 0 WelcomeMail.amountXanion = values.amountXanion or 0 WelcomeMail.amountOgonite = values.amountOgonite or 0 WelcomeMail.amountAvorion = values.amountAvorion or 0 mailText = values.mailText or "" mailHeader = values.mailHeader or "" mailSender = values.mailSender or "" end function WelcomeMail.secure() return { welcomeMoney = WelcomeMail.welcomeMoney, amountIron = WelcomeMail.amountIron, amountTitanium = WelcomeMail.amountTitanium, amountNaonite = WelcomeMail.amountNaonite, amountTrinium = WelcomeMail.amountTrinium, amountXanion = WelcomeMail.amountXanion, amountOgonite = WelcomeMail.amountOgonite, amountAvorion = WelcomeMail.amountAvorion } end -- this function gets called on creation of the entity the script is attached to, on client and server function WelcomeMail.initialize() if onServer() then print("Welcome Mail initialised!") end if onClient() then invokeServerFunction("setTranslatedMailText", "Some Assets to Help You Get Rolling"%_t, --message header WelcomeMail.generateWelcomeMailText(Player().index), "Shrooblord /* The owner of this galaxy. Wait, what? */"%_t) --sender invokeServerFunction("generateWelcomeMailAttachments", 50, --money 100, --iron 200, --titanium 300, --naonite 400, --trinium 500, --xanion 600, --ogonite 700) --avorion invokeServerFunction("onPlayerLogIn", Player().index) end end function WelcomeMail.setTranslatedMailText(header, text, sender) mailHeader = header mailText = text mailSender = sender end -- when the player logs in, this function is called function WelcomeMail.onPlayerLogIn(playerIndex) if onServer() then local player = Player(playerIndex) local server = Server() local playerLogIns = server:getValue("player_"..tostring(player.name).."_logIns") if playerLogIns == 0 or playerLogIns == nil then playerLogIns = 1 server:setValue("player_"..tostring(player.name).."_logIns", 1) else playerLogIns = playerLogIns + 1 server:setValue("player_"..tostring(player.name).."_logIns", playerLogIns) end if playerLogIns == 1 then local mail = Mail() mail.header = mailHeader mail.text = mailText mail.sender = mailSender mail.money = WelcomeMail.welcomeMoney mail:setResources(WelcomeMail.amountIron, WelcomeMail.amountTitanium, WelcomeMail.amountNaonite, WelcomeMail.amountTrinium, WelcomeMail.amountXanion, WelcomeMail.amountOgonite, WelcomeMail.amountAvorion) player:addMail(mail) else print("WelcomeMail: Number of times player "..tostring(player.name).." has logged in: "..tostring(playerLogIns).." times; not generating welcome mail.") end end --regardless of whether re run on server or client, we will end script execution as soon as we're done sending or not sending the mail terminate() end function WelcomeMail.generateWelcomeMailAttachments(attachMoney, attachIron, attachTitanium, attachNaonite, attachTrinium, attachXanion, attachOgonite, attachAvorion) WelcomeMail.welcomeMoney = attachMoney or 0 WelcomeMail.amountIron = attachIron or 0 WelcomeMail.amountTitanium = attachTitanium or 0 WelcomeMail.amountNaonite = attachNaonite or 0 WelcomeMail.amountTrinium = attachTrinium or 0 WelcomeMail.amountXanion = attachXanion or 0 WelcomeMail.amountOgonite = attachOgonite or 0 WelcomeMail.amountAvorion = attachAvorion or 0 end -- following are mail texts sent to the player function WelcomeMail.generateWelcomeMailText(playerIndex) local receiver = Player(playerIndex) local welcome_text = [[Welcome, ${player}, We see you are new here. Good to have you on board! Included in this e-mail is a bit of a start-up capital for you to work with. We hope this will help you get going more easily, and give you a little bit of a leg-up in this dangerous Galaxy... good luck out there! May our paths once cross. Until then! Best wishes, Your Galactic Overlords ]]%_t return welcome_text % {player = receiver.name} end function WelcomeMail.getValues() if onClient() then return end return { welcomeMoney = WelcomeMail.welcomeMoney, amountIron = WelcomeMail.amountIron, amountTitanium = WelcomeMail.amountTitanium, amountNaonite = WelcomeMail.amountNaonite, amountTrinium = WelcomeMail.amountTrinium, amountXanion = WelcomeMail.amountXanion, amountOgonite = WelcomeMail.amountOgonite, amountAvorion = WelcomeMail.amountAvorion } end return WelcomeMail So there you have it. We have generated a welcome mail. ---- INSTALLATION INSTRUTIONS TL; DR version: Finally, we register our script to be run by the server by editing the vanilla file Avorion/data/scripts/server/server.lua to include this script whenever it runs: player:addScriptOnce("mods/WelcomeMail/scripts/player/welcomeMail.lua") --WelcomeMail We insert this code inside the onPlayerLogIn call. If you had a vanilla server.lua file, that function will now look like this: function onPlayerLogIn(playerIndex) local player = Player(playerIndex) Server():broadcastChatMessage("Server", 0, "Player %s joined the galaxy"%_t, player.name) player:addScriptOnce("headhunter.lua") player:addScriptOnce("eventscheduler.lua") player:addScriptOnce("story/spawnswoks.lua") player:addScriptOnce("story/spawnai.lua") player:addScriptOnce("story/spawnguardian.lua") player:addScriptOnce("story/spawnadventurer.lua") player:addScriptOnce("mods/WelcomeMail/scripts/player/welcomeMail.lua") --WelcomeMail matchResources(player) end Sadly, we cannot make mods yet without editing even a little bit of vanilla code: c'est la vie. ---- Nice. But let's test this in action before we say "we did it!!" prematurely. I'm going to generate a new galaxy, and log in. Do we receive a mail? Hell, yeah, we do! What happens when we log in a second time? No e-mail, and Console prints the number of times we've logged in (2 in this case)! And we don't get an e-mail when we log into a galaxy we've already been playing in for a while. Excellent! Even when we log into a Galaxy within which we were piloting an Alliance ship, everything works as expected. Great! ---- All of that done, we have built ourselves your welcome mail mod! Let's install it on both our server and our client, and we're good to go. :) You can find the mod in this forum post. NOTE: A side effect of having to install this mod on both client and server means that new Galaxies created in singleplayer will also trigger the Welcome Mail. There's probably a check we can install to make sure this doesn't happen, but that was a little beyond the scope of this tutorial. P.S. There's an entry in the Documentation about mails. Look at Avorion/Documentation/Mail.html if you're curious where I got some of this info. In general, the Documentation is a great place to get started whenever you're curious about (adding) some functionality in Avorion.
  16. PART II -- if ship is destroyed this function is called function WelcomeMail.onDestroyed(index, lastDamageInflictor) if WelcomeMail.refundedValue == 0 then return end local faction = Faction() if not faction then return end local ship = Entity() -- don't pay if the faction destroyed his ship by himself local damagers = {ship:getDamageContributors()} if #damagers == 1 and damagers[1] == faction.index then WelcomeMail.sendInfo(faction, "Insurance Fraud detected. You won't receive any payments for %s."%_t, ship.name) return end if faction.isPlayer then local player = Player(faction.index) local mail = Mail() mail.header = mailHeader mail.text = mailText mail.sender = mailSender mail.money = WelcomeMail.refundedValue player:addMail(mail) else faction:receive(Format("Received insurance refund for %1%: %2% credits."%_T, ship.name), WelcomeMail.refundedValue) end end Right. Baby steps! First of all, we're not interested in the onDestroyed callback, but rather the onPlayerLogIn "callback" (remember this is not a true callback execution but rather a server function call that we are manually invoking). This callback has only one argument: playerIndex, which supplies the code contained within the function with the information about just who it was, who just logged in. Player(playerIndex) inside this function will point to that specific player. We'll use this in sending our mail to the player. Let's also change the comment above it to actually reflect when this function gets triggered. So -- if ship is destroyed this function is called function WelcomeMail.onDestroyed(index, lastDamageInflictor) turns into -- when the player logs in, this function is called function WelcomeMail.onPlayerLogIn(playerIndex) Looking at the rest of the body of this function, we can tell that all the handlers for a Faction are unnecessary for our code. After all, we never want to send our mail to a whole Faction (Alliance), but always to individual players. So, we can remove all code that checks for Factions and sends messages to Factions rather than players, and also remove the require("faction") part of the initialisation at the top of our script. Now, we want to add some resources in the mail to get the player started. But how do we do that? If we look at the Documentation, we'll see that the Mail() object has a function exposed to it that's called setResources(), but it doesn't tell us how many arguments it takes, or what its arguments are interpreted as. Furthermore, if you search the vanilla code for setResources(), you only get one entry from entitydbg.lua, but its use is a little... confusing, also given the comment that is set beside it. Oh well, we need to figure this one out ourselves. Time for some testing. if onServer() then local player = Player() local mail = Mail() mail.header = "Test" mail.text = "Here's some stuff." mail.sender = "Shrooblord" mail.money = 50 mail:setResources(100, 200, 300, 400, 500, 600, 700) player:addMail(mail) end Copy this code to your clipboard and start the game. Open Chat with Enter and type /run (with a space at the end) followed by your pressing CTRL+V to paste the clipboard into the Chat textbox. Don't worry about the text running off the screen. Chat don't care. He's cool like that. When you press Enter, you'll see you've received an e-mail from Shrooblord titled "Test", with the message body being "Here's some stuff." Furthermore, as an attachment to the e-mail is enclosed 50 Credits, 100 Iron, 200 Titanium, 300 Naonite, 400 Trinium, 500 Xanion, 600 Ogonite and 700 Avorion. Cool! So now we know how to use setResources(). To include any materials in the attachment to your e-mail, all you need to do is call mail:setResources(amountIron, amountTitanium, amountNaonite, amountTrinium, amountXanion, amountOgonite, amountAvorion) and we're done! Note the colon ( : ) instead of the period ( . ). This is because this is a function call on the object mail. Alright, so let's initialise some variables at the top of our script for these amounts, define them during the initialisation of the mail contents, and actually add them to the mail: WelcomeMail.refundedValue = 0 WelcomeMail.amountIron = 0 WelcomeMail.amountTitanium = 0 WelcomeMail.amountNaonite = 0 WelcomeMail.amountTrinium = 0 WelcomeMail.amountXanion = 0 WelcomeMail.amountOgonite = 0 WelcomeMail.amountAvorion = 0 local mailText = "" local mailHeader = "" local mailSender = "" function WelcomeMail.restore(values) WelcomeMail.refundedValue = values.refundedValue or 0 WelcomeMail.amountIron = values.amountIron or 0 WelcomeMail.amountTitanium = values.amountTitanium or 0 WelcomeMail.amountNaonite = values.amountNaonite or 0 WelcomeMail.amountTrinium = values.amountTrinium or 0 WelcomeMail.amountXanion = values.amountXanion or 0 WelcomeMail.amountOgonite = values.amountOgonite or 0 WelcomeMail.amountAvorion = values.amountAvorion or 0 mailText = values.mailText or "" mailHeader = values.mailHeader or "" mailSender = values.mailSender or "" end function WelcomeMail.secure() return { refundedValue = WelcomeMail.refundedValue, amountIron = WelcomeMail.amountIron, amountTitanium = WelcomeMail.amountTitanium, amountNaonite = WelcomeMail.amountNaonite, amountTrinium = WelcomeMail.amountTrinium, amountXanion = WelcomeMail.amountXanion, amountOgonite = WelcomeMail.amountOgonite, amountAvorion = WelcomeMail.amountAvorion } end ... function WelcomeMail.getValues() if onClient() then return end return { welcomeMoney = WelcomeMail.welcomeMoney, amountIron = WelcomeMail.amountIron, amountTitanium = WelcomeMail.amountTitanium, amountNaonite = WelcomeMail.amountNaonite, amountTrinium = WelcomeMail.amountTrinium, amountXanion = WelcomeMail.amountXanion, amountOgonite = WelcomeMail.amountOgonite, amountAvorion = WelcomeMail.amountAvorion } end There are some obvious ellipses - I have just shown what I updated to incorporate our new variables. After we do all that, and have removed the check for insurance fraud (which isn't necessary for what we want to do), and remove the check for whether the refundedValue == 0 (since we want to support welcome e-mails that don't give money but do give resources), we end up with a very simple function: -- when the player logs in, this function is called WelcomeMail.onPlayerLogIn(playerIndex) local player = Player(playerIndex) local mail = Mail() mail.header = mailHeader mail.text = mailText mail.sender = mailSender mail.money = WelcomeMail.refundedValue mail:setResources(WelcomeMail.amountIron, WelcomeMail.amountTitanium, WelcomeMail.amountNaonite, WelcomeMail.amountTrinium, WelcomeMail.amountXanion, WelcomeMail.amountOgonite, WelcomeMail.amountAvorion) player:addMail(mail) end Now, let's change the name of the WelcomeMail.refundedValue variable to something a little more sensible. Let's say WelcomeMail.welcomeMoney. A quick find-and-replace does this all for us. Don't forget to also find-and-replace for refundedValue just by itself, since there are the secure(), restore() and getValues() functions that use these names instead of WelcomeMail.refundedValue. That's all cool and all, but we haven't actually set any of the values for the attachments yet. Let's write a new function: function WelcomeMail.generateWelcomeMailAttachments(attachMoney, attachIron, attachTitanium, attachNaonite, attachTrinium, attachXanion, attachOgonite, attachAvorion) WelcomeMail.welcomeMoney = attachMoney or 0 WelcomeMail.amountIron = attachIron or 0 WelcomeMail.amountTitanium = attachTitanium or 0 WelcomeMail.amountNaonite = attachNaonite or 0 WelcomeMail.amountTrinium = attachTrinium or 0 WelcomeMail.amountXanion = attachXanion or 0 WelcomeMail.amountOgonite = attachOgonite or 0 WelcomeMail.amountAvorion = attachAvorion or 0 end Our function has 8 arguments that can be passed to it. However, because we define inside the function that the local variables are either the value given by the argument; or 0, we could theoretically call our function using fewer than 8 arguments, and it would work (just keep in mind you'll have to call them in-order, so if you want to give 0 Iron but 100 Titanium, you'll have to call WelcomeMail.generateWelcomeMailAttachments(0, 0, 100)). This isn't strictly necessary, since we only call this function once, and we know we're giving it all the values explicitly (see our rewrite of initialize(), below), but it's good coding practise to create failsafes nonetheless. And don't forget to change the initialize() function, where we'll set these values: -- this function gets called on creation of the entity the script is attached to, on client and server function WelcomeMail.initialize() if onServer() then print("Welcome Mail initialised!") end if onClient() then invokeServerFunction("setTranslatedMailText", "Some Assets to Help You Get Rolling"%_t, --message header WelcomeMail.generateWelcomeMailText(Player().index), "Shrooblord /* The owner of this galaxy. Wait, what? */"%_t) --sender invokeServerFunction("generateWelcomeMailAttachments", 50, --money 100, --iron 200, --titanium 300, --naonite 400, --trinium 500, --xanion 600, --ogonite 700) --avorion invokeServerFunction("onPlayerLogIn", Player().index) end end You can change the values of all the attachments yourself, of course, but I just did it this way so it's clear for testing purposes which values corresponded to which materials (so basically: that we can test and see that it works correctly). All we need to do at this point is change the body of the mail: -- following are mail texts sent to the player function WelcomeMail.generateInsuranceMailText() local entity = Entity() local receiver = Faction() local insurance_loss_payment = [[Dear ${player}, We received notice of the destruction of your craft '${craft}'. Very unfortunate! As you are insured at our company you shall receive enclosed the sum insured with us as a loss payment. The contract for your craft '${craft}' is now fulfilled. We hope we can be of future service to you. Best wishes, Ship Insurance Intergalactical ]]%_t return insurance_loss_payment % {player = receiver.name, craft = entity.name} end Remember we changed the function name to WelcomeMail.generateWelcomeMailText()? Let's not forget to include that change here. Furthermore, we don't need to include the Faction or Entity calls, as we're addressing the player directly and don't need any information about the ship they're currently flying. Instead, we only want to know their player name. We can retrieve this if we know the player's index, which we are going to pass to it as an argument. So let's change this block of code into: -- following are mail texts sent to the player function WelcomeMail.generateWelcomeMailText(playerIndex) local receiver = Player(playerIndex) local welcome_text = [[Welcome, ${player}, We see you are new here. Good to have you on board! Included in this e-mail is a bit of a start-up capital for you to work with. We hope this will help you get going more easily, and give you a little bit of a leg-up in this dangerous Galaxy... good luck out there! May our paths once cross. Until then! Best wishes, Your Galactic Overlords ]]%_t return welcome_text % {player = receiver.name} end Naturally, you can change it into whatever you want. This is just an idea and mostly a placeholder for whatever it is you want to put in there. Remember to also change the WelcomeMail.generateWelcomeMailText() call in initialize() into WelcomeMail.generateWelcomeMailText(Player().index), or you will have a very unhappy game. This is the final piece of code to run through: function WelcomeMail.getUpdateInterval() local minutes = 5 return minutes * 60 end function WelcomeMail.getValues() if onClient() then return end return { welcomeMoney = WelcomeMail.welcomeMoney, amountIron = WelcomeMail.amountIron, amountTitanium = WelcomeMail.amountTitanium, amountNaonite = WelcomeMail.amountNaonite, amountTrinium = WelcomeMail.amountTrinium, amountXanion = WelcomeMail.amountXanion, amountOgonite = WelcomeMail.amountOgonite, amountAvorion = WelcomeMail.amountAvorion } end We won't need the getUpdateInterval() function because we only intend to execute this function once, on player log in. We will terminate() after we're done, so the script will end (and remove itself from the player, too). I added our new variables to the getValues() function. To be honest, I'm not quite sure when this function is called, but I kept it in-place just in case the game needs access to these values for some reason. Ah, but wait! We have forgotten one crucial part of this process! We do not check yet whether the player has logged in for the first time, or whether they are returning to the server. Remember that block of code we prepared way at the start? Time to integrate it into our script now. Let's go back to our WelcomeMail.onPlayerLogIn(playerIndex) function definition and put our check in place: -- when the player logs in, this function is called function WelcomeMail.onPlayerLogIn(playerIndex) if onServer() then local player = Player(playerIndex) local server = Server() local playerLogIns = server:getValue("player_"..tostring(player.name).."_logIns") if playerLogIns == 0 or playerLogIns == nil then playerLogIns = 1 server:setValue("player_"..tostring(player.name).."_logIns", 1) else playerLogIns = playerLogIns + 1 server:setValue("player_"..tostring(player.name).."_logIns", playerLogIns) end if playerLogIns == 1 then local mail = Mail() mail.header = mailHeader mail.text = mailText mail.sender = mailSender mail.money = WelcomeMail.welcomeMoney mail:setResources(WelcomeMail.amountIron, WelcomeMail.amountTitanium, WelcomeMail.amountNaonite, WelcomeMail.amountTrinium, WelcomeMail.amountXanion, WelcomeMail.amountOgonite, WelcomeMail.amountAvorion) player:addMail(mail) else print("WelcomeMail: Number of times player "..tostring(player.name).." has logged in: "..tostring(playerLogIns).." times; not generating welcome mail.") end end --regardless of whether re run on server or client, we will end script execution as soon as we're done sending or not sending the mail terminate() end Our last effort of labours: we now only execute aaaalll the functionality in this script if the player logs in, and at that time their total number of log-ins is 1. In other words, they've just joined the server for the first time, so they get a welcome e-mail! Finally, we terminate() the script whether they got an e-mail or not, because we only want to run this script once: on player log-in. The if onServer() then check is probably redundant, but it can't hurt to make sure. ---- All-in-all, our full script is: TO BE CONTINUED...
  17. This explanation is going to need multiple posts. Turns out the forum has a 20k character limit. Yay, forum breaker is I! ---- TL; DR: I made you a mod that does what you want. I walk you through the steps on how I created the mod, but you can also just install it on the server and client and it should work. Follow the installation instructions at the bottom of my last post in this chain of posts. Enjoy! ---- PART I I didn't reply at first because I didn't know. But, I've looked at the insurance system code , so let's build what you want together: Basically, we're just going to hack together a welcome e-mail by copying what the insurance mail does, changing the contents, and changing the conditions that trigger it. On top of that, the insurance system is quite complex, and we don't need nearly all of that behaviour for our purposes. So, let's start by copy-pasting the whole insurance.lua script, and then delete what we don't need. We do it this way, so we ensure that all requires, variables and utility functions that are required by the script to function are present, and we don't forget to copy crucial parts of the script that aren't in the main body of our attention. Basically, it's just a safer approach to copy-pasting code, but you can do it however you want. Next, we're going to implement some code to track how often players have logged in to your server, and only deliver the mail when they log in for the first time. local player = Player(playerIndex) local server = Server() local playerLogIns = server:getValue("player_"..tostring(player.name).."_logIns") if playerLogIns == 0 or playerLogIns == nil then playerLogIns = 1 server:setValue("player_"..tostring(player.name).."_logIns", 1) else playerLogIns = playerLogIns + 1 server:setValue("player_"..tostring(player.name).."_logIns", playerLogIns) end if playerLogIns == 1 then ... end Don't worry about where to put this code for now. We'll get to that later. But this is the driving force of our mail: we only send it once, and that's on the first log in, then never again. What does this code do? Let's take a look. First, we set some local variables, to more easily reference certain things like the player, and make the code more readable. Then we use the special functions getValue("VALUENAME") and setValue("VALUENAME", "VALUE") in order to get or set custom values to the server itself. These values will be read later in order to determine how often the player has logged in. Finally, we perform some checks to see if the player hasn't logged in yet (either by the value being 0 somehow or by way of the value not existing yet), and trigger some behaviour if this is indeed the first time the player logs in. Additionally, we count up the amount of times the player has logged in after this event. Perhaps we want to use the total amount of times the player has logged in at some point? But more importantly, this ensures that the code knows when the player logs in after the first time. We could also have used a boolean here (true/false), but I decided to go with counting, instead. A side effect of using this functionality is that it will only start to work after this script is installed on the server. This means that, on existing servers, all players who log on after this script is installed, will get a Welcome Email. This can be circumvented by manually setting the custom values for all players who you know log in to your server, as long as you know their name: /run Server():setValue("player_PLAYERNAME_logIns", 1) Executing this by entering it in Chat before any of the players in question have joined the server will ensure that they never get the e-mail (because the value is set to 2 the first time they log in after installing this script). I'm afraid this is a rather manual approach, but there's nothing else to it if we use the set-up we have. Incidentally, you can remove the server custom value by passing the value nil to it. This special lua object will cause the custom value to be deleted, if ever you want to do so. /run Server():setValue("player_PLAYERNAME_logIns", nil) Fortunately, even if existing players get this mail, you can instruct them to delete the mail rather than pressing Take All and they shouldn't receive the free materials. That is, if you trust your players enough to actually do so. Deleting the mail without pressing Take All will not grant the recipient of the mail the materials or money enclosed within. OK, so we've got the check in place for when we want to deliver the mail. But, we don't have the mail itself, yet. So let's start hacking away at insurance.lua until only the areas of code remain that we are interested in: package.path = package.path .. ";data/scripts/lib/?.lua" require ("galaxy") require ("utility") require ("stringutility") require ("faction") -- Don't remove or alter the following comment, it tells the game the namespace this script lives in. If you remove it, the script will break. -- namespace Insurance Insurance = {} Insurance.refundedValue = 0 local mailText = "" local mailHeader = "" local mailSender = "" function Insurance.restore(values) Insurance.refundedValue = values.refundedValue or 0 mailText = values.mailText or "" mailHeader = values.mailHeader or "" mailSender = values.mailSender or "" end function Insurance.secure() return { refundedValue = Insurance.refundedValue } end -- this function gets called on creation of the entity the script is attached to, on client and server function Insurance.initialize() if onServer() then local entity = Entity() entity:registerCallback("onDestroyed" , "onDestroyed") end if onClient() then invokeServerFunction("setTranslatedMailText", "Loss Payment enclosed"%_t, Insurance.generateInsuranceMailText(), "S.I.I. /* Abbreviation for Ship Insurance Intergalactical, must match with the email signature */"%_t) end end function Insurance.setTranslatedMailText(header, text, sender) mailHeader = header mailText = text mailSender = sender end -- if ship is destroyed this function is called function Insurance.onDestroyed(index, lastDamageInflictor) if Insurance.refundedValue == 0 then return end local faction = Faction() if not faction then return end local ship = Entity() -- don't pay if the faction destroyed his ship by himself local damagers = {ship:getDamageContributors()} if #damagers == 1 and damagers[1] == faction.index then Insurance.sendInfo(faction, "Insurance Fraud detected. You won't receive any payments for %s."%_t, ship.name) return end if faction.isPlayer then local player = Player(faction.index) local mail = Mail() mail.header = mailHeader mail.text = mailText mail.sender = mailSender mail.money = Insurance.refundedValue player:addMail(mail) else faction:receive(Format("Received insurance refund for %1%: %2% credits."%_T, ship.name), Insurance.refundedValue) end end -- following are mail texts sent to the player function Insurance.generateInsuranceMailText() local entity = Entity() local receiver = Faction() local insurance_loss_payment = [[Dear ${player}, We received notice of the destruction of your craft '${craft}'. Very unfortunate! As you are insured at our company you shall receive enclosed the sum insured with us as a loss payment. The contract for your craft '${craft}' is now fulfilled. We hope we can be of future service to you. Best wishes, Ship Insurance Intergalactical ]]%_t return insurance_loss_payment % {player = receiver.name, craft = entity.name} end function Insurance.getUpdateInterval() return 1 --Run once per second end function Insurance.getValues() if onClient() then return end return { refundedValue = Insurance.refundedValue } end We copy this code and put it in a new file that we call welcomeMail.lua, and place this file inside a new directory in the Avorion installation space that has the following path: Avorion/mods/WelcomeMail/scripts/player/welcomeMail.lua. We could theoretically write part of this code's functionality by editing vanilla files, but I'm a huge fan of doing as little in-place editing as we need, since it leads to easier installation of the mod later down the line. Now, let's go through the code itself. Let's go through this code one step at a time. I've already made some adjustments, but not all. package.path = package.path .. ";data/scripts/lib/?.lua" require ("galaxy") require ("utility") require ("stringutility") require ("faction") -- Don't remove or alter the following comment, it tells the game the namespace this script lives in. If you remove it, the script will break. -- namespace Insurance Insurance = {} Insurance.refundedValue = 0 local mailText = "" local mailHeader = "" local mailSender = "" The very first part of the code is the initialisation. This is important for the script to run properly. At the top, we are importing local variables and functions from other scripts. We will need all of these to remain for our functionality to work, so we're leaving that untouched. Next up, we have the namespace of the script. Currently, it still reads Insurance, so we're going to change that into WelcomeMail. Finally is variable initialisation. I got rid of some of the variables that had to do with paying for the insurance, but left the one that's about refunding money, since wherever in the code it is that the insurance mail gives you your money, we want to implement the same behaviour, so for now, we're keeping that variable just where it is (we can always get rid of it later). You'll notice that throughout the code it says namespace.variableName or namespace.functionName. We're going to do a quick find-and-replace to change all instances of Insurance. into WelcomeMail. so now our namespace changes are completed. Next up, there's the following lines of code: function WelcomeMail.restore(values) WelcomeMail.refundedValue = values.refundedValue or 0 mailText = values.mailText or "" mailHeader = values.mailHeader or "" mailSender = values.mailSender or "" end function WelcomeMail.secure() return { refundedValue = WelcomeMail.refundedValue } end These are important for storing and retrieving data whenever the server performs a save. It basically ensures that data is preserved when the game loads data back into memory (i.e. when the server starts). Now, I'm not sure we're going to need to store data in our case, but we're going to leave it intact just in case. Better be safe than sorry. (I should get more knowledgeable on how these functions work and when they are needed - but c'est ça: I simply am not right now, so let's just keep these functions as they are.) I have removed all variables that we also removed from the initialisation block of the code and kept only what we are initialising in the first place. Next up: -- this function gets called on creation of the entity the script is attached to, on client and server function WelcomeMail.initialize() if onServer() then local entity = Entity() entity:registerCallback("onDestroyed" , "onDestroyed") end if onClient() then invokeServerFunction("setTranslatedMailText", "Loss Payment enclosed"%_t, WelcomeMail.generateInsuranceMailText(), "S.I.I. /* Abbreviation for Ship Insurance Intergalactical, must match with the email signature */"%_t) end end function WelcomeMail.setTranslatedMailText(header, text, sender) mailHeader = header mailText = text mailSender = sender end OK, so here is our first bit of actual functionality. initialize() is run - as explained by the comment - whenever the script is run for the first time. It sets the (default) values for our variables, and registers callbacks to make the script execute whenever a certain server callback is triggered. Let's look at the first item on our list: entity:registerCallback. We're not going to want to register a callback in our case, but instead directly call the functions we want to call from inside this initialize() function. This is because the way we will install our script (see the Installation Instructions at the end of this chain of posts) will inherently imply the callback in place here for the original insurance code: this script is run when the player logs in. So, we can remove the line local entity = Entity() entirely and change the line entity:registerCallback("onDestroyed" , "onDestroyed") into print("Welcome Mail initialised!"). Basically, all we're doing if we're running on the Server, is letting the Console (and server logs) know that this script was successfully loaded and has started executing. That's all we care about on the server-side, at this point. We can see if the script is actually running on our server by opening Console with the quote key ( ' and " ) (that's the default keybinding at least: the user may have changed this in the Key Layout Settings). Next, we add another invokeServerFunction(...) call to the if onClient() then test: invokeServerFunction("onPlayerLogIn", Player().index) OK, so what have we done? We have made sure that instead of calling a function onDestroyed(...) whenever the entity this script would have been attached to dies (as would be the case for the insurance code), that a function named onPlayerLogIn(...) is called whenever a player logs in, and gets passed the player's index (so the script knows which specific player to target later on). It's the convention for scripts in Avorion to name the function that is registered to trigger on a callback the same as that callback's name, for ease of reading. However, theoretically you could call it whatever you want, especially considering we're not registering a callback at all, but rather just manually invoking a function. Still, for ease of reading and comprehension, I recommend we follow the convention. The onPlayerLogIn(...) function that should supposedly be executed whenever the invokeServerFunction calling it is triggered doesn't exist in our code yet. Don't worry about that. We'll get to it in a second. Let's move on to a part of the mail's contents. if onClient() then invokeServerFunction("setTranslatedMailText", "Loss Payment enclosed"%_t, WelcomeMail.generateInsuranceMailText(), "S.I.I. /* Abbreviation for Ship Insurance Intergalactical, must match with the email signature */"%_t) end ... function WelcomeMail.setTranslatedMailText(header, text, sender) mailHeader = header mailText = text mailSender = sender end We see here that on the client-side, we make the server run a function that sets the variables we initialised at the top of the code, thereby creating the subject, body and sender of the mail's text. Change this to whatever you want. I'm just providing an example. WelcomeMail.generateInsuranceMailText() is what is now generating the body of the mail, but we'll get to that later. if onClient() then invokeServerFunction("setTranslatedMailText", "Some Assets to Help You Get Rolling"%_t, --message header WelcomeMail.generateWelcomeMailText(Player().index), "Shrooblord /* The owner of this galaxy. Wait, what? */"%_t) --sender end I've also changed WelcomeMail.generateInsuranceMailText() into WelcomeMail.generateWelcomeMailText() for consistency (and to reduce confusion). With all our changes in place, this block of our code now looks like this: -- this function gets called on creation of the entity the script is attached to, on client and server function WelcomeMail.initialize() if onServer() then print("Welcome Mail initialised!") end if onClient() then invokeServerFunction("setTranslatedMailText", "Some Assets to Help You Get Rolling"%_t, --message header WelcomeMail.generateWelcomeMailText(Player().index), "Shrooblord /* The owner of this galaxy. Wait, what? */"%_t) --sender invokeServerFunction("onPlayerLogIn", Player().index) end end function WelcomeMail.setTranslatedMailText(header, text, sender) mailHeader = header mailText = text mailSender = sender end After this is run, the mail is primed for reception - its data is all generated -, but it has not yet been sent. Let's see what happens when the player logs in. TO BE CONTINUED...
  18. Nah I agree: only Credits isn't enough. But I feel like System Module Upgrades should still drop. After all, there has to be a little fun in killing other players' ships... >:D
  19. Yes, this please. I find it a little frustrating that simply trying to defend myself against military vessels can end up with me losing a lot of reputation points with other Factions in the Sector because my Captained ships decided they wanted to attack a Trader, too.
  20. Haha I wouldn't worry about posts not getting enough exposure around here. These forums don't move that quickly. I would like to hear other people's feelings on the subject, though?
  21. Hmm a bit tricky depending on which mods you want installed, and how much effort you want to put into investigating the mods in question. I'll leave my thoughts. 1. Your most obvious indicator is if the mod maker puts in their mod post's description whether this is the case or not (a good point actually: I'll update my own mod post to reflect this). 2. You can ask the modders themselves (or people in the community who might know) by asking in the mod's thread. 3. You can go through the mod code and see if there are any calls using the specific if onServer() then or if onClient() then methods. These are indicators that the code expects to be run on both, with different functionality running depending on which of the two is the case (i.e. "this is being run on the server" or "this is being run on a client"). invokeServerFunction(...) and invokeClientFunction(...) are also good indicators of the script expecting to be run on both. 4. If the mod code scripts only UI things, it might be client-only. Probably not though, because the functionality that the UI is trying to drive or get information from has to come from somewhere, and that somewhere is the server. If the mod code scripts anything at all that you can think of that shouldn't go out-of-synch between clients / be hackable by a client, you can expect this to be run by the server, but this sadly doesn't give you much indication about whether or not the client needs a copy, too. 5. You can try and see (trial-and-error approach): load the server up with the mod installed, start a client without the mod installed and try and login to the server. See if the game breaks if you try and do anything that the mod supposedly adds to the game. Ultimately, the answer is no (or: not really). There is no obvious surefire way to tell whether a mod needs to be installed both client- and server-side just from looking at the mod itself. However, there are some indicators you can follow to try and figure this out yourself, or you could just launch the server with the mod installed, launch a client without the mod installed, try and join and see if anything breaks. Testing it yourself is usually a good way to go. I would also go asking around on the forums, myself. All-in-all I'd say I can't think of many mods out there that the client will not also need installed for one part of functionality or another, but don't let my inexperience in that matter cloud your judgment. I'd love to hear more about this, actually.
  22. Yeah, until you need to / want to update, and you basically have to reinstall everything from scratch to ensure your mods don't overwrite updated game assets. There's a lot of room for improvement on this part of the modding experience. That aside - not a bad idea. I guess that's kind of what I do already: I have a separate folder somewhere with all the ZIP files of the mods I have installed.
  23. hahaha ok I get it - you're right. But more to the point: I was wondering if the C++ side of things didn't freak out / plain wouldn't work if you didn't dock at the "appropriate distance". If not - and clearly you're saying indeed, not -, then the "hard-codedness" I previously assumed the docking procedure to be is in reality slightly more lenient to change. This is good news for modders. Thanks for the info!
  24. Intriguing. You have an unmodded game? I have had a similar experience with my friend on a server we started from scratch, so a whole new galaxy. However, I was under the impression that was due to the many balancing and game-changing mods I have installed on that server, but if you're running vanilla, then that means that the spawning rate has been altered in the original game code, too. Curious. We have had a bad time trying to deal with the amount of stuff assailing us / Sectors we're in pretty much continuously, and are barely scratching up the galactic food chain. I agree with you: this is a much different experience to my own first playthrough. My time was pretty chill, only getting into fights when I wanted to, and only infrequently getting ambushed or surprised by a fake distress call. But what happens now, both in your anecdote and in our own experiences, is the lot of us landing in a much more hostile galaxy with a taste for player blood. Yikes!
  25. I like the concept. But how do you explain this? Credits I can see being sent via an "e-mail"; it's literally virtual currency and it's just wired to you, plain-as. Registers updated: boom - the system acknowledges you have more money now. But materials? How do you teleport those aboard through an "e-mail"? Fluff aside, I see value in this idea. It is a bit of a nuisance to go get all the materials back after an unfortunate run-in with a ... giga cannon. Maybe the % of materials you get back should be scaled with the availability of the material? I can imagine Space Insurance Company MK Ultra doesn't stock up on 1,000,000 Avorion to refund you for your all-Avorion build you lost to unfortunate... giga cannon accidents, but perhaps a build with 40,000 Iron could easily be refunded for all its materials. After all, Avorion and the less obviously amazing materials are supposed to be scarcer and harder to come by, and I think an insurance firm that somehow has a massive stockpile of these things sort of defeats the purpose of that idea.
×
×
  • Create New...