Files
source/gamedir/Base/Scripts/initmines.lua
T
d683cdeb64 Run from gamedir (#686)
* prepare gamedir for running the game

* Stop telling installers to overwrite the vanilla game

The release no longer contains anything that lands on a vanilla file:
its base data lives in Base, so unpacking it over the game directory
and copying the game's Data into the release now amount to the same
installation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Give the mod's base data its own VFS profile

Its files ship in Base now, so that a vanilla Data directory can be
copied in whole without a single collision. Mount Base right above the
vanilla dirs in every profile stack — the slot where the installer's
overwrites used to end up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Mount Base in the language overlays' VFS configs too

Assembling a release copies a <Language>_Version directory over
gamedir, its own copies of the configs included, so without the same
Base profile every non-English release would mount nothing from Base.

Their libraries move along with the rest of the base data: each
language slf, and the Russian data.slf that shadows the vanilla
Data.slf by name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Ignore a vanilla Data directory dropped into gamedir

Running the game here needs one copied in, and it is an untouched copy
of somebody's retail install — nothing this repository should track.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Ignore only the player profile directory, not every Profiles

The unanchored pattern also swallowed Base/TableData/Profiles, the
mod's soldier profile XMLs, which are tracked and belong in a release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* move gamedir/Data to gamedir/Base

* move gamedir-languages' Data to Base

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:05:15 -03:00

354 lines
10 KiB
Lua

--[[
*************
** Remarks **
*************
The purpose of this Lua script is to provide the code and data needed to
initialize the strategic mines. There are two reasons for the game to make
calls to this script:
1. The strategic layer gets initialized.
Right after a new game is started, the program calls InitializeMines to
gather the data defining the mines (see §1.). It then uses that data to
actually create the mines.
2. The player enters a mine sector.
When a mine sector gets loaded (tactical mode), the program calls
InitializeHeadMiners to collect information on creation of head miners
and how to distribute them among the mines (see §2.).
**********************
** Global variables **
**********************
- newDIFFICULTY_LEVEL (Start new game setting)
1 = easy
2 = experienced
3 = expert
4 = insane
- iniWHICH_MINE_SHUTS_DOWN (ja2_options.ini setting)
-1 = Game chooses a mine randomly.
0 = No mine will shut down!
2 = Drassen
3 = Alma
4 = Cambria
5 = Chitzena
6 = Grumm
]]
-- For debugging purposes you might want to have some basic logging output
-- from the game. Uncommenting the following line will create "initmines.log"
-- in your profile directory.
--logging = true
----------------------------------------
-- §0. Some setup code to get started --
----------------------------------------
-- Initialize the pseudo random number generator
math.randomseed( os.time() ); math.random(); math.random(); math.random()
-- http://lua-users.org/wiki/MathLibraryTutorial
MineType = { Silver = 0, Gold = 1 }
-----------------------------
-- §1. Mine initialization --
-----------------------------
--[[
function InitializeMines()
Gets called on initialization of strategic layer.
This function is supposed to return an array table of initialization data
for each mine. Every element itself must be a table consisting of the
following members:
- Location (required)
String, defining the sector to place the mine, e.g. "A10".
- Type
Use MineType.Gold or MineType.Silver (cp. §0.).
- MaxRemovalRate
Amount of income to be gained at each production period ( = 1/4 of
daily income).
- RemainingOreSupply
Total amount of money available to be extracted.
- Infectible (0/1)
Boolean, specifying whether or not a mine can be infected by crepitus.
This is kind of a hack until creature quest becomes externalized.
Do not set this to 1 for mines other than Drassen, Alma, Cambria, Grumm.
- AssociatedUnderground
Array table of strings specifying sector coordinates, e.g. "A10-1".
This relates underground sectors to the mine. In these sectors the game
adds miners working underground, and also uses that information to build
the sector name.
]]
function InitializeMines()
--------------------------------------
-- §1.1. Set up initial mine status --
--------------------------------------
local mines =
{
-- San Mona
{
Location = "D4", Type = MineType.Gold,
MinimumProduction = 0,
AssociatedUnderground = { "D4-1", "D5-1" },
},
-- Drassen
{
Location = "D13", Type = MineType.Silver,
MinimumProduction = 1000,
AssociatedUnderground = { "D13-1", "E13-1" },
Infectible = 1,
},
-- Alma
{
Location = "I14", Type = MineType.Silver,
MinimumProduction = 1500,
AssociatedUnderground = { "I14-1", "J14-1" },
Infectible = 1,
},
-- Cambria
{
Location = "H8", Type = MineType.Silver,
MinimumProduction = 1500,
AssociatedUnderground = { "H8-1", "H9-1" },
Infectible = 1,
},
-- Chitzena
{
Location = "B2", Type = MineType.Silver,
MinimumProduction = 500,
AssociatedUnderground = { "B2-1" },
},
-- Grumm
{
Location = "H3", Type = MineType.Gold,
MinimumProduction = 2000,
AssociatedUnderground = { "H3-1", "I3-1", "I3-2", "H3-2", "H4-2" },
Infectible = 1,
},
}
-----------------------------------
-- §1.2. Vanilla JA2 logic below --
-----------------------------------
-- create variables for easy identification at quest related stuff
local DrassenMine = mines[2]
local AlmaMine = mines[3]
-- more initialization
for i = 1, #mines do
local m = mines[i]
m.MaxRemovalRate = m.MinimumProduction
if m.MinimumProduction > 0 then
m.RemainingOreSupply = 999999999
else
m.RemainingOreSupply = 0
end
end
-- Randomize the exact size of each mine. The total production is always
-- the same and depends on the game difficulty, but some mines will
-- produce more in one game than another, while others produce less.
local numProductionIncreases = ({ 25, 25, 20, 15 })[newDIFFICULTY_LEVEL]
while numProductionIncreases > 0 do
local m
-- pick a producing mine at random and increase its production
repeat
m = mines[math.random(#mines)]
until m.MinimumProduction > 0
-- increase mine production by 20% of the base (minimum) rate
m.MaxRemovalRate = m.MaxRemovalRate + m.MinimumProduction / 5
numProductionIncreases = numProductionIncreases - 1
end
-- Incorporate cheat option...
-- switch on iniWHICH_MINE_SHUTS_DOWN
-- 0: no mine runs out, skip this altogether
-- -1: choose mine randomly
-- >0: ID of the mine that shall run out
if iniWHICH_MINE_SHUTS_DOWN ~= 0 then
local m
if iniWHICH_MINE_SHUTS_DOWN == -1 then
-- Choose which mine will run out of production.
-- This will never be the Alma mine or an empty mine (San Mona)...
-- choose randomly
repeat
m = mines[math.random(#mines)]
-- Alma mine can't run out for quest-related reasons
until m.MinimumProduction > 0 and m ~= AlmaMine
else
-- Select mine by given index
-- No safety checks whatsoever, any error will be pretty much in
-- your face. If it breaks it's all their fault, I don't care...
m = mines[iniWHICH_MINE_SHUTS_DOWN]
end
if m == DrassenMine then
minDaysBeforeDepletion = 20
else
minDaysBeforeDepletion = 10
end
m.RemainingOreSupply = minDaysBeforeDepletion * 4 * m.MaxRemovalRate
-- We don't want the creatures to infect the mine that runs out.
m.Infectible = 0
end
return mines;
end -- function InitializeMines
------------------------------------
-- §2. Head miners initialization --
------------------------------------
--[[
function InitializeHeadMiners(mines, currentMine)
This function is called when the player enters a mine sector.
It can return either nil or the head miners initialization and
distribution. If it returns nil, it will be called again when a mine
sector gets loaded. If it returns a value other than nil, it will not be
called again.
Parameters:
- mines
An array of tables featuring only the following member:
- MaxRemovalRate
- currentMine
This is the index of the mine being entered by the player (cp. §1.1.).
If the function does not return nil but the head miners distribution, it
must return an array of tables consisting of the following members:
- Profile
The profile ID of the head miner.
- Quotes
Array of exactly four integers defining the quote IDs of the head miner
for the following strategic events:
1. RUNNING_OUT
2. CREATURES_ATTACK
3. CREATURES_GONE
4. CREATURES_AGAIN
It is valid to insert -1 for missing quotes.
- MineID
The index of the mine the head miner is assigned to (cp. §1.1.).
]]
function InitializeHeadMiners(mines, currentMine)
local headMinerInfo = {
-- Fred
{ Profile = 106, Quotes = { 17, 18, 27, 26 }, },
-- Matt
{ Profile = 148, Quotes = { -1, 18, 32, 31 }, },
-- Oswald
{ Profile = 156, Quotes = { 14, 15, 24, 23 }, },
-- Calvin
{ Profile = 157, Quotes = { 14, 15, 24, 23 }, },
-- Carl
{ Profile = 158, Quotes = { 14, 15, 24, 23 }, },
}
local Fred = headMinerInfo[1]
local Matt = headMinerInfo[2]
local AlmaID = 3 -- cp. §1.1.
-- don't place miners until player enters a producing mine
if mines[currentMine].MaxRemovalRate == 0 then
return nil
end
-- Matt is always head miner in Alma
Matt.MineID = AlmaID
-- Fred is the first miner the player encounters unless it's Alma
if currentMine ~= AlmaID then
Fred.MineID = currentMine
end
-- collect IDs of all producing mines except Alma mine and Fred's
local MinesLeft = {}
for i = 1, #mines do
if i ~= AlmaID and i ~= currentMine then
if mines[i].MaxRemovalRate > 0 then
table.insert(MinesLeft, i)
end
end
end
-- randomly distribute miners
for i = 1, #headMinerInfo do
if headMinerInfo[i].MineID == nil then
local index = math.random(#MinesLeft)
headMinerInfo[i].MineID = MinesLeft[index]
table.remove(MinesLeft, index)
end
end
return headMinerInfo
end -- function InitializeHeadMiners