starlit  Diff

Differences From Artifact [fe75c1df99]:

To Artifact [910641ec39]:


    55     55   				weapon = {primary = nil, secondary = nil};
    56     56   				psi = {primary = nil, secondary = nil};
    57     57   				maneuver = nil;
    58     58   			};
    59     59   			pref = {
    60     60   				calendar = 'commune';
    61     61   			};
           62  +			overlays = {};
    62     63   		}
    63     64   	end;
    64     65   	__index = {
           66  +		--------------
           67  +		-- overlays --
           68  +		--------------
           69  +		updateOverlays = function(self)
           70  +			local phys = {
           71  +				speed = self.pheno:trait('speed',1);
           72  +				jump = self.pheno:trait('jump',1);
           73  +				gravity = 1;
           74  +				speed_climb = 1;
           75  +				speed_crouch = 1;
           76  +				speed_walk = 1;
           77  +				acceleration_default = 1;
           78  +				acceleration_air = 1;
           79  +			}
           80  +			for i, o in ipairs(self.overlays) do o(phys) end
           81  +			self.entity:set_physics_override(phys)
           82  +		end;
           83  +		overlay = function(self, o)
           84  +			local id = #self.overlays+1
           85  +			self.overlays[id] = o
           86  +			self:updateOverlays()
           87  +			return id
           88  +		end;
           89  +		deleteOverlay = function(self, id)
           90  +			table.remove(self.overlays, id)
           91  +			self:updateOverlays()
           92  +		end;
           93  +
           94  +		--------------
           95  +		-- personae --
           96  +		--------------
    65     97   		pullPersona = function(self)
    66     98   			-- if later records are added in public updates, extend this function to merge them
    67     99   			-- into one object
    68    100   			local s = userStore(self.entity)
    69    101   			self.persona = s.read 'persona'
    70    102   			self.pheno = starlit.world.species.pheno(self.persona.species, self.persona.speciesVariant)
    71    103   		end;
    72    104   		pushPersona = function(self)
    73    105   			local s = userStore(self.entity)
    74    106   			s.write('persona', self.persona)
    75    107   		end;
          108  +
    76    109   		uiColor = function(self) return lib.color {hue=238,sat=.5,lum=.5} end;
          110  +
          111  +		-----------
          112  +		-- stats --
          113  +		-----------
    77    114   		statDelta = function(self, stat, d, cause, abs)
    78    115   			local dt = self.persona.statDeltas
    79    116   			local min, max, base = self:statRange(stat)
    80    117   			if abs then
    81    118   				if     d == true  then d = max
    82    119   				elseif d == false then d = min end
    83    120   			end
................................................................................
    97    134   				self:pushPersona()
    98    135   			end
    99    136   
   100    137   
   101    138   			self:updateHUD()
   102    139   			-- TODO trigger relevant animations?
   103    140   		end;
   104         -		lookupSpecies = function(self)
   105         -			return starlit.world.species.lookup(self.persona.species, self.persona.speciesVariant)
   106         -		end;
   107         -		phenoTrait = function(self, trait, dflt)
   108         --- 			local s,v = self:lookupSpecies()
   109         --- 			return v.traits[trait] or s.traits[trait] or 0
   110         -			return self.pheno:trait(trait, dflt)
   111         -		end;
   112    141   		statRange = function(self, stat) --> min, max, base
   113    142   			return starlit.world.species.statRange(
   114    143   				self.persona.species, self.persona.speciesVariant, stat)
   115    144   		end;
   116    145   		effectiveStat = function(self, stat)
   117    146   			local val
   118    147   			local min, max, base = self:statRange(stat)
................................................................................
   124    153   			else
   125    154   				val = base + self.persona.statDeltas[stat] or 0
   126    155   			end
   127    156   
   128    157   			local d = max - min
   129    158   			return val, (val - min) / d
   130    159   		end;
          160  +
          161  +		---------------
          162  +		-- phenotype --
          163  +		---------------
          164  +		lookupSpecies = function(self)
          165  +			return starlit.world.species.lookup(self.persona.species, self.persona.speciesVariant)
          166  +		end;
          167  +		phenoTrait = function(self, trait, dflt)
          168  +-- 			local s,v = self:lookupSpecies()
          169  +-- 			return v.traits[trait] or s.traits[trait] or 0
          170  +			return self.pheno:trait(trait, dflt)
          171  +		end;
   131    172   		damageModifier = function(self, kind, amt)
   132    173   			if kind == 'bluntForceTrauma' then
   133    174   				local std = self:phenoTrait 'sturdiness'
   134    175   				if std < 0 then
   135    176   					amt = amt / 1+std
   136    177   				else
   137    178   					amt = amt * 1-std
   138    179   				end
   139    180   			end
   140    181   			return amt
   141    182   		end;
          183  +
          184  +		---------
          185  +		-- HUD --
          186  +		---------
   142    187   		attachImage = function(self, def)
   143    188   			local user = self.entity
   144    189   			local img = {}
   145    190   			img.id = user:hud_add {
   146    191   				type = 'image';
   147    192   				text = def.tex;
   148    193   				scale = def.scale;
................................................................................
   380    425   				align = {x=0, y=-1};
   381    426   				z = -1;
   382    427   				update = function(user, set)
   383    428   					set('text', hudAdjustBacklight(hudCenterBG):render())
   384    429   				end;
   385    430   			};
   386    431   		end;
   387         -		-- horrible horrible HACK
   388         -		setModeHand = function(self)
   389         -			local inv = self.entity:get_inventory()
   390         -			local hnd
   391         -			if self.actMode == 'off'
   392         -				then hnd = ItemStack('starlit:_hand_dig')
   393         -				else hnd = ItemStack()
          432  +		deleteHUD = function(self)
          433  +			for name, e in pairs(self.hud.elt) do
          434  +				self:hud_delete(e.id)
   394    435   			end
   395         -			inv:set_stack('hand', 1, hnd)
   396    436   		end;
          437  +		updateHUD = function(self)
          438  +			for name, e in pairs(self.hud.elt) do
          439  +				if e.update then e.update() end
          440  +			end
          441  +		end;
          442  +
          443  +		---------------------
          444  +		-- actions & modes --
          445  +		---------------------
   397    446   		onModeChange = function(self, oldMode, silent)
   398    447   			self.hud.elt.crosshair.update()
   399    448   			if not silent then
   400    449   				local sfxt = {
   401    450   					off = 'starlit-mode-off';
   402    451   					nano = 'starlit-mode-nano';
   403    452   					psi = 'starlit-mode-psi';
................................................................................
   413    462   			local oldMode = self.actMode
   414    463   			self.actMode = mode
   415    464   			self:onModeChange(oldMode, silent)
   416    465   			if mode ~= oldMode then
   417    466   				starlit.ui.setupForUser(self)
   418    467   			end
   419    468   		end;
   420         -		deleteHUD = function(self)
   421         -			for name, e in pairs(self.hud.elt) do
   422         -				self:hud_delete(e.id)
          469  +		setModeHand = function(self) -- horrible horrible HACK
          470  +			local inv = self.entity:get_inventory()
          471  +			local hnd
          472  +			if self.actMode == 'off'
          473  +				then hnd = ItemStack('starlit:_hand_dig')
          474  +				else hnd = ItemStack()
   423    475   			end
          476  +			inv:set_stack('hand', 1, hnd)
   424    477   		end;
   425         -		updateHUD = function(self)
   426         -			for name, e in pairs(self.hud.elt) do
   427         -				if e.update then e.update() end
   428         -			end
   429         -		end;
          478  +
          479  +		---------------------
          480  +		-- intel-gathering --
          481  +		---------------------
   430    482   		clientInfo = function(self)
   431    483   			return minetest.get_player_information(self.name)
   432    484   		end;
          485  +		species = function(self)
          486  +			return starlit.world.species.index[self.persona.species]
          487  +		end;
          488  +
          489  +		--------------------
          490  +		-- event handlers --
          491  +		--------------------
   433    492   		onSignup = function(self)
   434    493   			local meta = self.entity:get_meta()
   435    494   			local inv = self.entity:get_inventory()
   436    495   			-- the sizes indicated here are MAXIMA. limitations on e.g. the number of elements that may be carried are defined by your suit and enforced through callbacks and UI generation code, not inventory size
   437    496   			inv:set_size('main', 6) -- carried items and tools. main hotbar.
   438    497   			inv:set_size('hand', 1) -- horrible hack to allow both tools and intrinsics
   439    498   
................................................................................
   471    530   			giveGifts('starlit_suit_canisters', gifts.suitCans)
   472    531   
   473    532   			giveGifts('main', gifts.carry)
   474    533   
   475    534   			self:reconfigureSuit()
   476    535   
   477    536   			-- i feel like there has to be a better way
   478         -			local cx = math.random(-500,500)
          537  +			local posrng = starlit.world.seedbank[0x13f19] -- TODO player-specific seed
          538  +			local cx = posrng:int(-500,500) --math.random(-500,500)
   479    539   			local iter, startPoint = 1
   480    540   			repeat local temp = -100
   481         -				local cz = math.random(-500,500)
          541  +				local cz = posrng:int(-500,500)
   482    542   				local cy = minetest.get_spawn_level(cx, cz)
   483    543   				if cy then
   484    544   					startPoint = vector.new(cx,cy,cz)
   485    545   					temp = starlit.world.climate.eval(startPoint,.5,.5).surfaceTemp
   486    546   				end
   487    547   				iter = iter + 1
   488    548   				if iter > 100 then break end -- avoid infiniloop in pathological conditions
................................................................................
   597    657   			}
   598    658   			me:set_eye_offset(nil, vector.new(3,-.2,10))
   599    659   			-- TODO set_clouds speed in accordance with wind
   600    660   			starlit.world.species.setupEntity(me, self.persona)
   601    661   			starlit.ui.setupForUser(self)
   602    662   			self:createHUD()
   603    663   			self:updateSuit()
          664  +			self:updateOverlays()
   604    665   		end;
          666  +		onPart = function(self)
          667  +			starlit.liveUI     [self.name] = nil
          668  +			starlit.activeUI   [self.name] = nil
          669  +			starlit.activeUsers[self.name] = nil
          670  +		end;
          671  +
          672  +		-----------------------------
          673  +		-- environment suit & body --
          674  +		-----------------------------
   605    675   		suitStack = function(self)
   606    676   			return self.entity:get_inventory():get_stack('starlit_suit', 1)
   607    677   		end;
   608    678   		suitSound = function(self, sfx)
   609    679   			-- trigger a sound effect from the player's suit computer
   610    680   			minetest.sound_play(sfx, {object=self.entity, max_hear_distance=4}, true)
   611    681   		end;
................................................................................
   630    700   					sfx = 'starlit-power-up'
   631    701   				elseif state == 'powerSave' or os == 'powerSave' then
   632    702   					sfx = 'starlit-configure'
   633    703   				end
   634    704   				if sfx then self:suitSound(sfx) end
   635    705   			end
   636    706   		end;
   637         -		species = function(self)
   638         -			return starlit.world.species.index[self.persona.species]
   639         -		end;
   640    707   		updateBody = function(self)
   641    708   			local adornment = {}
   642    709   			local suitStack = self:suitStack()
   643    710   			if suitStack and not suitStack:is_empty() then
   644    711   				local suit = suitStack:get_definition()._starlit.suit
   645    712   				suit.adorn(adornment, suitStack, self.persona)
   646    713   			end
................................................................................
   726    793   				-- TODO display power use icon
   727    794   			end
   728    795   			return supply, wasteHeat
   729    796   		end;
   730    797   		naked = function(self)
   731    798   			return self:suitStack():is_empty()
   732    799   		end;
   733         -		onPart = function(self)
   734         -			starlit.liveUI     [self.name] = nil
   735         -			starlit.activeUI   [self.name] = nil
   736         -			starlit.activeUsers[self.name] = nil
   737         -		end;
          800  +
          801  +		--------
          802  +		-- ui --
          803  +		--------
   738    804   		openUI = function(self, id, page, ...)
   739    805   			local ui = assert(starlit.interface.db[id])
   740    806   			ui:open(self, page, ...)
   741    807   		end;
   742    808   		onRespond = function(self, ui, state, resp)
   743    809   			ui:action(self, state, resp)
   744    810   		end;
   745         -
   746         -		updateWeather = function(self)
   747         -		end;
   748         -
   749         -		canInteract = function(self, with)
   750         -			return true; -- TODO
   751         -		end;
   752         -
   753    811   		trigger = function(self, which, how)
   754    812   			local p
   755    813   			local wld = self.entity:get_wielded_item()
   756    814   			if which == 'maneuver' then
   757    815   				p = self.power.maneuver
   758    816   			elseif which == 'retarget' then
   759    817   				self.action.prog = {}
................................................................................
   783    841   							end
   784    842   							local sw = starlit.item.sw.db[pgm.body.pgmId]
   785    843   							run = assert(sw.run, 'missing run() for active software ability ' .. pgm.body.pgmId)
   786    844   							break
   787    845   						end
   788    846   					end
   789    847   				end
          848  +			elseif p.ref then
          849  +				run = p.ref.run
   790    850   			else
   791    851   				error('bad ability pointer ' .. dump(p))
   792    852   			end
   793    853   			if run then
   794    854   				run(self, ctx)
   795    855   				return true
   796    856   			end
   797    857   			return false
   798    858   		end;
          859  +
          860  +		-------------
          861  +		-- weather --
          862  +		-------------
          863  +		updateWeather = function(self)
          864  +		end;
          865  +
          866  +		canInteract = function(self, with)
          867  +			return true; -- TODO
          868  +		end;
          869  +
          870  +		---------------
          871  +		-- inventory --
          872  +		---------------
   799    873   		give = function(self, item)
   800    874   			local inv = self.entity:get_inventory()
   801    875   			local function is(grp)
   802    876   				return minetest.get_item_group(item:get_name(), grp) ~= 0
   803    877   			end
   804    878   			-- TODO notif popups
   805    879   			if is 'specialInventory' then
................................................................................
   847    921   		local dehydration = p:trait 'dehydration' * biointerval
   848    922   		-- you dehydrate faster in higher temp
   849    923   		dehydration = dehydration * math.max(1, starlit.world.climate.temp(u.entity:get_pos()) / 10)
   850    924   
   851    925   		u:statDelta('nutrition', -bmr)
   852    926   		u:statDelta('hydration', -dehydration)
   853    927   
   854         -		if u:effectiveStat 'nutrition' == 0 then
   855         -			-- starvation
   856         -		end
          928  +		local moralePenalty = -1 -- 1min/min
          929  +		local fatiguePenalty = 1 -- 1min/min
          930  +		local heatPenalty = 1 -- stamina regen is divided by this
   857    931   
   858         -		if u:effectiveStat 'hydration' == 0 then
   859         -			-- dying of thirst
          932  +		do local warmth = u:effectiveStat 'warmth'
          933  +			local tempRange = u:species().tempRange
          934  +			local tComfMin, tComfMax = tempRange.comfort[1], tempRange.comfort[2]
          935  +			local tempDiff = 0
          936  +			if warmth < tComfMin then
          937  +				tempDiff = math.abs(warmth-tComfMin)
          938  +			elseif warmth > tComfMax then
          939  +				tempDiff = math.abs(warmth-tComfMax)
          940  +			end
          941  +			moralePenalty = moralePenalty + tempDiff
          942  +			heatPenalty = heatPenalty + tempDiff
   860    943   		end
   861    944   
          945  +		-- penalize heavy phys. activity
          946  +		local stamina, sp = u:effectiveStat 'stamina'
          947  +		fatiguePenalty = fatiguePenalty * (1 + 9*(1-sp))
          948  +
          949  +		local food = u:effectiveStat 'nutrition'
          950  +		local water = u:effectiveStat 'hydration'
   862    951   		local rads = u:effectiveStat 'irradiation'
          952  +		if food < 1000 then moralePenalty = moralePenalty + (1 - (food/1000)) * 5 end
          953  +		if water < 1   then moralePenalty = moralePenalty + (1 - (water/1)) * 10 end
          954  +
   863    955   		if rads > 0 then
   864    956   			u:statDelta('irradiation', -0.0001 * biointerval)
          957  +			local moraleDrainFac = 2^(rads / 2)
          958  +			moralePenalty = moralePenalty * moraleDrainFac
          959  +		end
          960  +
          961  +		u:statDelta('morale', moralePenalty * biointerval)
          962  +		u:statDelta('fatigue', fatiguePenalty * biointerval)
          963  +
          964  +		if food == 0 then -- starvation
          965  +			u:statDelta('health', -5*biointerval)
          966  +		end
          967  +
          968  +		if water == 0 then -- dying of thirst
          969  +			u:statDelta('health', -20*biointerval)
   865    970   		end
   866    971   
          972  +		if sp < 1.0 then
          973  +			u:statDelta('stamina', u:effectiveStat 'staminaRegen' / heatPenalty)
          974  +		end
   867    975   	end
   868    976   end)
   869    977   
   870    978   local cbit = {
   871    979   	up   = 0x001;
   872    980   	down = 0x002;
   873    981   	left = 0x004;
................................................................................
   893   1001   				return mustHalt
   894   1002   			else return doNothing end
   895   1003   		end
   896   1004   		local skipBits = 0
   897   1005   		if user.action.bits ~= bits then
   898   1006   			local mPrimary = what(cbit.dig)
   899   1007   			local mSecondary = what(cbit.put)
         1008  +			local mManeuver = what(cbit.manv)
   900   1009   			if mPrimary == mustInit then -- ENGINE-BUG
   901   1010   				user.action.tgt = {type='nothing'}
   902   1011   				user.action.prog = {}
   903   1012   			elseif mPrimary == mustHalt then
   904   1013   				user:trigger('primary', {state='halt'})
   905   1014   			end
   906   1015   			if mSecondary == mustHalt then
   907   1016   				user:trigger('secondary', {state='halt'})
   908   1017   			end
         1018  +			if mManeuver == mustInit then
         1019  +				user:trigger('maneuver', {state='init'})
         1020  +			elseif mManeuver == mustHalt then
         1021  +				user:trigger('maneuver', {state='halt'})
         1022  +			end
   909   1023   		end
   910   1024   		--bits = bit.band(bits, bit.bnot(skipBits))
   911         -		if bit.band(bits, cbit.dig)~=0 then
   912         -			user:trigger('primary', {state='prog', delta=delta})
         1025  +		local function prog(what)
         1026  +			user:trigger(what, {state='prog', delta=delta})
   913   1027   		end
   914         -		if bit.band(bits, cbit.put)~=0 then
   915         -			user:trigger('secondary', {state='prog', delta=delta})
   916         -		end
         1028  +		if bit.band(bits, cbit.dig)~=0  then prog 'primary'   end
         1029  +		if bit.band(bits, cbit.put)~=0  then prog 'secondary' end
         1030  +		if bit.band(bits, cbit.manv)~=0 then prog 'maneuver'  end
   917   1031   		user.action.bits = bits
   918   1032   		-- ENGINE-BUG: dig and put are not handled equally in the
   919   1033   		-- engine. it is possible for the put bit to get stuck on
   920   1034   		-- if the key is hammered while the player is not moving.
   921   1035   		-- the bit will release as soon as the player looks or turns
   922   1036   		-- nonetheless this is obnoxious
   923   1037   	end
   924   1038   end)