starlit  Check-in [d2ab51532e]

Overview
Comment:somewhat improve buttons
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA3-256: d2ab51532e382991cb3c83f280782dbfbd878f7bcc5b6a9cc623959e10c8059d
User & Date: lexi on 2025-03-25 17:47:31
Other Links: manifest | tags
Context
2025-03-25
17:47
somewhat improve buttons Leaf check-in: d2ab51532e user: lexi tags: trunk
2025-01-19
19:18
we have always been at war with east minecraft check-in: 4732f8d454 user: lexi tags: trunk
Changes

Modified asset.list from [33338e0378] to [7b9ff32a24].

   228    228   mods/starlit/textures/starlit-ui-alert.png
   229    229   mods/starlit/textures/starlit-ui-bar.png
   230    230   mods/starlit/textures/starlit-ui-bg-digital.png
   231    231   mods/starlit/textures/starlit-ui-bg-panel.png
   232    232   mods/starlit/textures/starlit-ui-button-hw-hover.png
   233    233   mods/starlit/textures/starlit-ui-button-hw-press.png
   234    234   mods/starlit/textures/starlit-ui-button-hw.png
          235  +mods/starlit/textures/starlit-ui-button-sw-active.png
   235    236   mods/starlit/textures/starlit-ui-button-sw-hover.png
   236    237   mods/starlit/textures/starlit-ui-button-sw-press.png
   237    238   mods/starlit/textures/starlit-ui-button-sw.png
   238    239   mods/starlit/textures/starlit-ui-crosshair-nano.png
   239    240   mods/starlit/textures/starlit-ui-crosshair-psi.png
   240    241   mods/starlit/textures/starlit-ui-crosshair-weapon.png
   241    242   mods/starlit/textures/starlit-ui-crosshair.png

Added mods/starlit-psionics/init.lua version [539da653d2].

            1  +local lib = starlit.mod.lib
            2  +
            3  +starlit.interface.install(starlit.type.ui {
            4  +	id = 'starlit_psionics:cultivate';
            5  +	pages = {
            6  +		index = {
            7  +			setupState = function(state, user) end;
            8  +			handle = function(state, user, act) end;
            9  +		};
           10  +	};
           11  +})
           12  +
           13  +starlit.world.psi.meld {
           14  +	cultivate = {
           15  +		name = 'Cultivate';
           16  +		desc = 'Invest numina to develop new powers';
           17  +		powerKind = 'direct';
           18  +		cost = {}; -- cultivate costs are applied in the interface
           19  +
           20  +		 -- all psions start with this power
           21  +		learnCost = 0, rarity = 0;
           22  +		ui = 'starlit_psionics:cultivate';
           23  +	};
           24  +}
           25  +
           26  +starlit.world.psi.meld {
           27  +	cultivate = {
           28  +		name = 'Mend';
           29  +		desc = 'Burn numina to rapidly heal yourself';
           30  +		powerKind = 'passive';
           31  +		cost = { stat = {numina = 10} }; -- ψ/s
           32  +
           33  +		learnCost = 1000, rarity = 5;
           34  +		bgProc = function(ctx) end;
           35  +	};
           36  +}

Added mods/starlit-psionics/mod.conf version [e85c7f4195].

            1  +name = starlit_psionics
            2  +title = starlit psionics
            3  +description = defines the base psi powers
            4  +depends = starlit

Modified mods/starlit-scenario/init.lua from [8a376bbedc] to [339ae3b1f1].

     2      2   local scenario = starlit.world.scenario
     3      3   local log = starlit.logger 'scenario'
     4      4   
     5      5   local function makeChip(label, schem, sw)
     6      6   	local E = starlit.mod.electronics
     7      7   	local files = {}
     8      8   	local sz = 0
            9  +	for _, e in ipairs(sw) do
           10  +		local file = {
           11  +			kind = 'sw', name = '', drm = e[2];
           12  +			body = {pgmId = e[1], conf = {}};
           13  +		}
           14  +		table.insert(files, file)
           15  +		sz = sz + E.chip.fileSize(file)
           16  +	end
     9     17   	for _, e in ipairs(schem) do
    10     18   		local p = E.sw.findSchematicFor(e[1])
    11     19   		if p then
    12     20   			local file = {
    13     21   				kind = 'sw', name = '', drm = e[2];
    14     22   				body = {pgmId = p, conf = {}};
    15     23   			}
    16     24   			table.insert(files, file)
    17     25   			sz = sz + E.chip.fileSize(file)
    18     26   		end
    19     27   	end
    20         -	for _, e in ipairs(sw) do
    21         -		local file = {
    22         -			kind = 'sw', name = '', drm = e[2];
    23         -			body = {pgmId = e[1], conf = {}};
    24         -		}
    25         -		table.insert(files, file)
    26         -		sz = sz + E.chip.fileSize(file)
    27         -	end
    28     28   	local chip = ItemStack(assert(E.chip.findBest(function(c)
    29     29   		return c.flash >= sz, c.ram + c.clockRate
    30     30   	end)))
    31     31   	local r = E.chip.read(chip)
    32     32   	r.label = label
    33     33   	r.files = files
    34     34   	E.chip.write(chip, r)

Modified mods/starlit-suit/init.lua from [078f484c1f] to [ef81e2c820].

    57     57   		tex = {
    58     58   			plate = {
    59     59   				id = 'starlit-suit-survival-plate';
    60     60   				tint = lib.color {hue = 210, sat = .5, lum = .5};
    61     61   			};
    62     62   			lining = {
    63     63   				id = 'starlit-suit-survival-lining';
    64         -				tint = lib.color {hue = 180, sat = .2, lum = .7};
           64  +				tint = lib.color {hue = 260, sat = .3, lum = .3};
    65     65   			};
    66     66   		};
    67     67   		tints = {'suit_plate', 'suit_lining'};
    68     68   		temp = {
    69     69   			desc = facDesc('survival','cc');
    70     70   			maxHeat = 0.7; -- can produce a half-degree Δ per second
    71     71   			maxCool = 0.5;

Modified mods/starlit/food.lua from [09c96accb0] to [ee714c2ec0].

    34     34   		props = props;
    35     35   		color = f.color;
    36     36   	};
    37     37   end
    38     38   
    39     39   F.foreach('starlit:gen-food', {}, function(id, f)
    40     40   	core.register_item(id, {
    41         -		type = f.itemType or 'none';
           41  +		type = f.itemType or 'craft';
    42     42   		inventory_image = f.tex;
    43     43   		short_description = f.name;
    44     44   		description = foodTip(nil, f);
    45     45   		on_use = function(st, luser)
    46     46   			local user = starlit.activeUsers[luser:get_player_name()]
    47     47   			st:take_item(1)
    48     48   			local imp = F.impact(st,f)

Modified mods/starlit/init.lua from [ae90c8058c] to [ae88cd06e3].

    92     92   			trees = lib.registry.mk 'starlit:trees';
    93     93   			biomes = lib.registry.mk 'starlit:biome';
    94     94   		};
    95     95   		climate = {
    96     96   			weather = lib.registry.mk 'starlit:weather';
    97     97   			weatherMap = {}
    98     98   		};
           99  +		psi = lib.registry.mk 'starlit:psi';
    99    100   		scenario = {};
   100    101   		planet = {
   101    102   			gravity = 7.44;
   102    103   			orbit = 189; -- 1 year is 189 days
   103    104   			revolve = 20; -- 1 day is 20 irl minutes
   104    105   		};
   105    106   		fact = lib.registry.mk 'starlit:fact';
................................................................................
   458    459   		-- of the default hp_max. since we crank up
   459    460   		-- hp by a factor of 50~40, damage should be
   460    461   		-- cranked by similarly
   461    462   	end
   462    463   	return delta
   463    464   end, true)
   464    465   
   465         -function core.handle_node_drops(pos, drops, digger)
   466         -	local function jitter(pos)
          466  +do local function jitter(pos)
   467    467   		local function r(x) return x+math.random(-0.01, 0.01) end
   468    468   		return vector.new(
   469    469   			r(pos.x),
   470    470   			r(pos.y),
   471    471   			r(pos.z)
   472    472   		)
   473    473   	end
   474         -	for i, it in ipairs(drops) do
   475         -		if type(it) == 'string' then it = ItemStack(it) end
   476         -		if not it:is_empty() then
   477         -			local ent = core.add_item(jitter(pos), it)
   478         -			if ent ~= nil then -- avoid crash when dropping unknown item
   479         -				local dp = vector.new(0,0,0)
   480         -				if digger then dp = digger:get_pos() end
   481         -				local delta = dp - ent:get_pos()
   482         -				ent:add_velocity(vector.new(delta.x,0,delta.z));
          474  +
          475  +	function starlit.throwItem(luser, stack)
          476  +			local ent = core.add_item(jitter(luser:get_pos()), stack)
          477  +			a = luser:get_look_dir()
          478  +			a = a * math.random(1, 10);
          479  +			a.y = a.y + 0.5
          480  +			ent:add_velocity(a)
          481  +	end
          482  +
          483  +	function core.handle_node_drops(pos, drops, digger)
          484  +		for i, it in ipairs(drops) do
          485  +			if type(it) == 'string' then it = ItemStack(it) end
          486  +			if not it:is_empty() then
          487  +				local ent = core.add_item(jitter(pos), it)
          488  +				if ent ~= nil then -- avoid crash when dropping unknown item
          489  +					local dp = vector.new(0,0,0)
          490  +					if digger then dp = digger:get_pos() end
          491  +					local delta = dp - ent:get_pos()
          492  +					ent:add_velocity(vector.new(delta.x,0,delta.z));
          493  +				end
   483    494   			end
   484    495   		end
   485    496   	end
   486    497   end
   487    498   
   488         -
   489         --- TODO timer iterates live UI
          499  +	-- TODO timer iterates live UI
   490    500   

Modified mods/starlit/interfaces.lua from [7199aa7738] to [51ed588904].

     1      1   local lib = starlit.mod.lib
     2      2   
     3      3   function starlit.ui.setupForUser(user)
     4      4   	local function cmode(mode)
     5         -		if user.actMode == mode then return {hue = 150, sat = 0, lum = .3} end
            5  +		if user.actMode == mode then return {hue = 290, sat = 0, lum = .1} end
     6      6   	end
     7      7   	user.entity:set_inventory_formspec(starlit.ui.build {
     8      8   		kind = 'vert', mode = 'sw';
     9      9   		padding = .5, spacing = 0.1;
    10     10   		{kind = 'hztl';
    11     11   			{kind = 'contact', w=1.5,h=1.5, id = 'mode_nano',
    12     12   				img='starlit-ui-icon-nano.png', close=true, color = cmode'nano'};
................................................................................
    37     37   		if user.actMode == mode then
    38     38   			user:actModeSet 'off'
    39     39   		else
    40     40   			user:actModeSet(mode)
    41     41   		end
    42     42   	end
    43     43   
           44  +	-- disable suit modes if the suit is off or the user is naked
    44     45   	local modes = { nano = true, psi = false, weapon = true }
    45     46   	for e,s in pairs(modes) do
    46     47   		if fields['mode_' .. e] then
    47     48   			if s and (user:naked() or user:getSuit():powerState() == 'off') then
    48     49   				user:suitSound 'starlit-error'
    49     50   			else
    50     51   				setSuitMode(e)
................................................................................
   503    504   				local suit = user:getSuit()
   504    505   				local suitDef = suit:def()
   505    506   				local chipW, chipH = listWrap(suitDef.slots.chips, 5)
   506    507   				local batW, batH = listWrap(suitDef.slots.batteries, 5)
   507    508   				local canW, canH = listWrap(suitDef.slots.canisters, 5)
   508    509   				local suitMode = suit:powerState()
   509    510   				local function modeColor(mode)
   510         -					if mode == suitMode then return {hue = 180, sat = 0, lum = .5} end
          511  +					local c = {
          512  +						off = {hue = 0, sat = 0, lum = 0};
          513  +						powerSave = {hue = 60, sat = 0, lum = 0};
          514  +						on = {hue = 100, sat = 0, lum = 0};
          515  +					}
          516  +					if mode == suitMode then return c[mode] end
   511    517   				end
   512    518   				return starlit.ui.build {
   513    519   					kind = 'vert', mode = 'sw';
   514    520   					padding = 0.5, spacing = 0.1;
   515    521   					{kind = 'hztl',
   516    522   						{kind = 'img', desc='Batteries', img = 'starlit-item-battery.png', w=1,h=1};
   517    523   						{kind = 'list', target = 'current_player', inv = 'starlit_suit_bat',

Added mods/starlit/psi.lua version [8b15304149].

            1  +local lib = starlit.mod.lib

Modified mods/starlit/suit.lua from [d40c92ebbb] to [0ecf5e03a5].

    82     82   					user:suitSound('starlit-insert-snap')
    83     83   				elseif list == 'starlit_suit_chips' then
    84     84   					--user:suitSound('starlit-suit-chip-out')
    85     85   				elseif list == 'starlit_suit_canisters' then
    86     86   					user:suitSound('starlit-insert-snap')
    87     87   				end
    88     88   			end
           89  +			return true -- move allowed
    89     90   		end;
    90     91   		def = function(self)
    91     92   			return self.item:get_definition()._starlit.suit
    92     93   		end;
    93     94   		--[[
    94     95   		pullCanisters = function(self, inv)
    95     96   			starlit.item.container.dropPrefix(inv, 'starlit_canister')
................................................................................
   284    285   			};
   285    286   			suit = def;
   286    287   		};
   287    288   	});
   288    289   end)
   289    290   
   290    291   local slotProps = {
          292  +-- 	main = {
          293  +-- 		free = true;
          294  +-- 	};
   291    295   	starlit_cfg = {
   292    296   		itemClass = 'inv';
   293    297   	};
   294    298   	starlit_suit_bat = {
   295    299   		suitSlot = true;
   296    300   		powerLock = true;
   297    301   		itemClass = 'dynamo';
................................................................................
   375    379   	return true
   376    380   end)
   377    381   
   378    382   core.register_on_player_inventory_action(function(luser, act, inv, p)
   379    383   	local user = starlit.activeUsers[luser:get_player_name()]
   380    384   	local function slotChange(slot,a,item)
   381    385   		local s = slotProps[slot]
          386  +		local allow = true
   382    387   		if slot == 'starlit_suit' then
   383    388   			user:updateSuit()
   384    389   			if user:naked() then
   385    390   				starlit.type.suit.purgeInventories(user.entity)
   386    391   				user.power.nano = {}
   387    392   			end
   388    393   		elseif s and s.suitSlot then
   389    394   			local s = user:getSuit()
   390         -			s:onItemMove(user, slot, a, item)
   391         -			s:onReconfigure(user.entity:get_inventory())
   392         -			user:setSuit(s)
          395  +			if s:onItemMove(user, slot, a, item) then
          396  +				s:onReconfigure(user.entity:get_inventory())
          397  +				user:setSuit(s)
          398  +			else return end
   393    399   		else return end
   394    400   		user:updateHUD()
   395    401   	end
   396    402   
   397    403   	if act == 'put' or act == 'take' then
   398    404   		local item = p.stack
   399    405   		slotChange(p.listname, act, item)

Modified mods/starlit/user.lua from [1a0e392600] to [8da9d84fa1].

   214    214   			return val, (val - min) / d
   215    215   		end;
   216    216   
   217    217   		---------------
   218    218   		-- phenotype --
   219    219   		---------------
   220    220   		lookupSpecies = function(self)
   221         -			return starlit.world.species.lookup(self.persona.species, self.persona.speciesVariant)
          221  +			return starlit.world.species.lookup(
          222  +				self.persona.species,
          223  +				self.persona.speciesVariant)
   222    224   		end;
   223    225   		phenoTrait = function(self, trait, dflt)
   224    226   -- 			local s,v = self:lookupSpecies()
   225    227   -- 			return v.traits[trait] or s.traits[trait] or 0
   226    228   			return self.pheno:trait(trait, dflt)
   227    229   		end;
   228    230   		damageModifier = function(self, kind, amt)
................................................................................
   352    354   			local luser = self.entity
   353    355   			local bar = {def = def}
   354    356   			local img = lib.image 'starlit-ui-bar.png'
   355    357   			local colorized = img
   356    358   			if type(def.color) ~= 'function' then
   357    359   				colorized = colorized:shift(def.color)
   358    360   			end
          361  +
          362  +			local function adjustNumber(n)
          363  +				-- produce a value that is realized as whole number
          364  +				-- of images, rather than half-images
          365  +				return math.floor(n/2)*2;
          366  +			end
   359    367   
   360    368   			bar.id = luser:hud_add {
   361    369   				type = 'statbar';
   362    370   				position = def.pos;
   363    371   				offset = def.ofs;
   364    372   				name = def.name;
          373  +				number = adjustNumber(def.size);
   365    374   				text = colorized:render();
   366    375   				text2 = img:tint{hue=0, sat=-1, lum = -0.5}:fade(0.5):render();
   367         -				number = def.size;
   368    376   				item = def.size;
   369    377   				direction = def.dir;
   370    378   				alignment = def.align;
   371    379   				size = {x=4,y=24};
   372    380   			}
   373    381   			bar.update = function()
   374    382   				local sv, sf = def.stat(self, bar, def)
   375         -				luser:hud_change(bar.id, 'number', def.size * sf)
          383  +				luser:hud_change(bar.id, 'number', adjustNumber(def.size * sf))
   376    384   				if type(def.color) == 'function' then
   377    385   					local clr = def.color(sv, luser, sv, sf)
   378    386   					luser:hud_change(bar.id, 'text', img:tint(clr):render())
   379    387   				end
   380    388   			end
   381         -			return bar, {x=3 * def.size, y=16} -- x*2??? what
          389  +			return bar, {x=3 * def.size, y=16} -- x*3??? what
   382    390   		end;
   383    391   		createHUD = function(self)
   384    392   			local function basicStat(statName)
   385    393   				return function(user, bar)
   386    394   					return self:effectiveStat(statName)
   387    395   				end
   388    396   			end

Modified starlit.ct from [f4a136801c] to [259abbd299].

   146    146   ** naturally diminishes, but very slowly
   147    147   * [*illness]: not everything on Farthest Shadow's ecosystem is, shall we say, biocompatible. measured in percent
   148    148   ** affects [*morale]: the higher your illness, the more quickly you bleed morale
   149    149   
   150    150   your primary stats are shown on your HUD. ancillary stats can be viewed in the "body" panel.
   151    151   
   152    152   ### psionics
          153  +psionics draws from your reserve of numina to allow your spirit to directly affect the material world, or other spirits. numina accretes slowly, though high morale will speed this up.
          154  +
   153    155   there are four types of psionic abilities: Manual, Maneuver, Ritual, and Contextual.
   154    156   
   155    157   you can assign two Manual abilities at any given time and access them with the mouse buttons in Psionics mode.
   156    158   
   157    159   you can select a Psi Maneuver in the Psionics panel and activate it by holding [*Aux1].
   158    160   
   159         -a Ritual is triggered directly from the psionics menu. as the name implies, these are complex, powerful abilities that require large amounts of Psi and time to meditate before they trigger, and any interruption will cancel the ability (though it will not restore any lost psi). the most famous Ritual is of course Conjoin Metric, which Starlit astropaths use in conjunction with powerful amplifiers to perform long-distance FTL jumps -- but without centuries of dedication to the art, the best you can hope for if you manage to learn this storied power is to move yourself a few kilometers.
          161  +a Ritual is triggered directly from the psionics menu. as the name implies, these are complex, powerful abilities that require large amounts of Psi and time to meditate before they trigger, and any interruption will cancel the ability (though it will not restore any lost psi). the most famous Ritual is of course Rift, which Starlit astropaths use in conjunction with powerful amplifiers to perform long-distance FTL jumps -- but without centuries of dedication to the art, the best you can hope for if you manage to learn this storied power is to move yourself a few kilometers.
   160    162   
   161    163   a Contextual ability is triggered in a specific situation, usually by interacting with a certain kind of object. Contextual abilities often require specialized equipment, to the point that many Starlit practitioners maintain their own Psionics Lab.
          164  +
          165  +at the beginning of the game, you start with the power "Cultivate", which is used to develop your psionic talents.
          166  +
          167  +to gain new psionic abilities, invest numina by triggering the Cultivate power. gaining new powers requires much more numina than using them, so you'll want to be careful about how much power you expend. once you have invested enough numina in your psionic advancement, you will be given a list of up to three new powers to choose from. the specific powers are partially randomized, though some have additional prerequisites. the environment in which you meditate can affect which powers are offered; some powers can only be achieved in certain biomes or circumstances. make sure you have a secure location to meditate in, however, as any interruption will disrupt your meditation, wasting the invested numina.
          168  +
          169  +you can also invest numina to strengthen a power you already possess. this can be done using the powers "Fortify" and "Advance". Fortify produces extremely strong temporary power boosts without affecting the base power cost; these wear off as you use the Fortified power. Advance permanently enhances the power in small increments, but the base power cost increases for every Advancement. note, however, that the further you Advance a power, the more efficient its use of numina becomes.
          170  +
          171  +there is a maximum amount of numina that a spirit can hold onto. this is determined by your species and sex, with females generally having larger numina pools. if this becomes limiting, it is possible to stockpile numina through the use of numinium crystals, a sort of psychic battery. these are not cheap to build, however, and doing so requires a psi-forge.
          172  +
          173  +a manual psionic power belongs to one of three categories:
          174  +* [*Currents]: a [*Current] is a modifier that can be toggled on or off. quale determine the effect produced by a Carrier; for instance, you can produce a healing shockwave centered on your character by activating [*Purify] and firing [*Pulse]. multiple currents can be active at a time, though this consumes proportionally further power.
          175  +* [*Carriers]: a [*Carrier] determines how the selected [*Currents] are expressed into the world, both in terms of targeting and efficiency. Advancing a Carrier  improves its efficiency, and where relevant, range.
          176  +* [*]: a [*] power can be expressed in only one way; its Carrier is indivisble from its Current.
          177  +
          178  +psionic powers you can learn include, but are not limited to:
          179  +
          180  +#### Carriers
          181  +* [*Infuse]: applies Current directly to the user. this is the most efficient of all effectors.
          182  +* [*Reach]: affect a single target on an ongoing basis. less efficient with distance, but almost as efficient as [*Infuse] when the target is right beside you.
          183  +* [*Pulse]: release a wave of psionic force, applying Current to everything in range except yourself. this is the least efficient power.
          184  +* [*Lance]: produces a narrow beam that delivers the full force of its Current to the first thing it hits. Lances are chargeable; you can invest an arbitrary amount of numina into the chosen Currents by holding the bound key for a time before releasing it. Lances travel further the stronger their charge.
          185  +* [*Torrent]: produces a wide beam that weakens the further it travels, but affects all in its path
          186  +* [*Tide]: like [*Torrent], but even less efficient; the width of the beam expands as it travels, making it useful for targeting groups of enemies.
          187  +* [*Vortex]: like Lance, but explodes when it hits its target, affecting everything in range of the blast.
          188  +* [*Pierce]: fire off homing orbs that track nearby targets and applies Current on impact. the more Pierce is charged, the more orbs will be launched, and the greater their efficiency will be.
          189  +* [*Arc]: affects the nearest target, then jumps to the target nearest them, and so on. the more powerful Arc becomes, the greater the jump range, branching factor, and maximum jump count.
          190  +
          191  +#### Currents
          192  +* [*Shatter]: a powerful destructive force. useful for cutting open passageways quickly, and harming your enemies.
          193  +* [*Purify]: banish ailments affecting you or nearby players
          194  +* [*Nullify]: weaken or break active psionic powers nearby
          195  +* [*Confine]: temporarily block a psionic being from being able to use their powers. to break a confinement, they will have to an amount of numina proportional to what you invested into it; the more powerful this ability becomes, the greater the multiplier.
          196  +* [*Siphon]: drain health and stamina from a target
          197  +* [*Reave]: destroy some of your target's numina
          198  +* [*Lift]: produces antigravity. the specific effect depends on the Carrier. Lift is very useful in combination with other Currents, especially with [*Reach] carriers, as it can render foes helpless for the duration of the effect.
          199  +** a [*Lift Lance], [*Pulse], or [*Vortex] tosses targets high into the air
          200  +** a [*Lift Infusion] allows you to raise yourself into the air. this is not as flexible as true psionic flight, as it can only raise you upwards.
          201  +** a [*Lift Reach] allows telekinetic control of the target
          202  +** a [*Lift Torrent] or [*Tide] work like Reach, but can affect multiple targets. extremely useful when used alongside Repulse.
          203  +** [*Persisted] Lift currents can leave targets helplessly trapped until the Current runs out of power
          204  +* [*Repulse]: like Lift, but throws targets back instead of up
          205  +** a [*Repulsion Lance] throws a target back violently
          206  +** a [*Repulsion Pulse] flings everything away from you
          207  +** a [*Repulsion Infusion] works like a charged psionic jump, as opposed to the hover effect of [*Lift].
          208  +** a [*Repulsion Reach] charges an object with latent velocity, which is applied all at once when the effect is released
          209  +** a [*Repulsion Vortex] hurls its victims away from the point of detonation
          210  +** a [*Repulsion Torrent] or [*Tide] creates a powerful gale pushing targets back
          211  +* [*Draw]: pull things toward you. can be used to disarm enemies
          212  +* [*Reflect]: intercepts projectiles and causes them to ricochet or dissolve. this can only be used with certain Carriers:
          213  +** [*Reflect Pulse]: produces a protective dome
          214  +** [*Reflect Torrent]: produces a circular barrier in front of you
          215  +** [*Reflect Tide]: produces a broad barrier in front of you
          216  +* [*Inflict]: severely damages target morale and stamina; increases fatigue and illness. a sadistic technique devised by the Kuradoqše.
          217  +* [*Broil]: increases the target's body heat dramatically
          218  +* [*Lattice]: a meta-Current, causes effects to persist for a given amount of time, to, for instance, suspend an enemy in the air and keep them trapped there long enough to give you a head start, or apply a damage-over-time effect. Lattice Currents only have a small effect from moment to moment, but they are more efficient than momentane Currents. the more you charge a persisted effect, the longer it will last and the more efficient it will become.
          219  +
          220  +####
          221  +* [*Blip]: short-range teleportation. can be used to break free of psionic holds
          222  +
          223  +#### Rituals
          224  +* [*Cultivate]
          225  +* [*Fortify]
          226  +* [*Advance]
          227  +* [*Coalesce]: manifest a spiritual object in the physical world by condensing your numina into a matter matrix. this is how numinium is created.
          228  +* [*Rift]: teleport directly to a memorized location.
          229  +* [*Ward]: surround yourself with a psionic barrier that must be worn down before anything can affect you.
          230  +* [*Shard]: create psionic projectiles that orbit you until you are attached; the projectiles will then home in on the attacker and do significant damage.
          231  +* [*Meditate]: sacrifice numina to improve your morale
          232  +
          233  +#### Manuvers
          234  +* [*Rush]: psionic sprint
          235  +* [*Soar]: psionic flight -- much faster than suit flight systems, but too power-hungry to use over long distances.
          236  +* [*Shift]: teleport randomly a short distance to evade incoming attacks.
          237  +* [*Shroud]: make yourself imperceptible
   162    238   
   163    239   ## legal
   164    240   starlit source code (*.lua, *.conf, *.txt, *.csd files) is released under the GNU AGPLv3.
   165    241   assets (images, sounds, models, and anything else in the repo that doesn't qualify as source code) are released under the CC-BY-NC-SA 3.0 license.
   166    242   sound files with the prefix `default-` are taken from Luanti Game, whose assets are available under the CC-BY-SA 3.0 license.