starlit  Diff

Differences From Artifact [fe75c1df99]:

To Artifact [910641ec39]:


55
56
57
58
59
60
61

62
63
64































65
66
67
68
69
70
71
72
73
74
75

76




77
78
79
80
81
82
83
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
...
124
125
126
127
128
129
130












131
132
133
134
135
136
137
138
139
140
141




142
143
144
145
146
147
148
...
380
381
382
383
384
385
386
387
388

389
390
391
392
393

394
395
396




397
398
399
400
401
402
403
...
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428

429




430
431
432







433
434
435
436
437
438
439
...
471
472
473
474
475
476
477

478
479
480
481

482
483
484
485
486
487
488
...
597
598
599
600
601
602
603

604









605
606
607
608
609
610
611
...
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
...
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
...
783
784
785
786
787
788
789


790
791
792
793
794
795
796
797
798














799
800
801
802
803
804
805
...
847
848
849
850
851
852
853




854
855







856



857

858
859
860

861


862



863
864


865
866














867
868
869
870
871
872
873
...
893
894
895
896
897
898
899

900
901
902
903
904
905
906
907
908




909

910
911

912
913

914
915
916

917
918
919
920
921
922
923
924
				weapon = {primary = nil, secondary = nil};
				psi = {primary = nil, secondary = nil};
				maneuver = nil;
			};
			pref = {
				calendar = 'commune';
			};

		}
	end;
	__index = {































		pullPersona = function(self)
			-- if later records are added in public updates, extend this function to merge them
			-- into one object
			local s = userStore(self.entity)
			self.persona = s.read 'persona'
			self.pheno = starlit.world.species.pheno(self.persona.species, self.persona.speciesVariant)
		end;
		pushPersona = function(self)
			local s = userStore(self.entity)
			s.write('persona', self.persona)
		end;

		uiColor = function(self) return lib.color {hue=238,sat=.5,lum=.5} end;




		statDelta = function(self, stat, d, cause, abs)
			local dt = self.persona.statDeltas
			local min, max, base = self:statRange(stat)
			if abs then
				if     d == true  then d = max
				elseif d == false then d = min end
			end
................................................................................
				self:pushPersona()
			end


			self:updateHUD()
			-- TODO trigger relevant animations?
		end;
		lookupSpecies = function(self)
			return starlit.world.species.lookup(self.persona.species, self.persona.speciesVariant)
		end;
		phenoTrait = function(self, trait, dflt)
-- 			local s,v = self:lookupSpecies()
-- 			return v.traits[trait] or s.traits[trait] or 0
			return self.pheno:trait(trait, dflt)
		end;
		statRange = function(self, stat) --> min, max, base
			return starlit.world.species.statRange(
				self.persona.species, self.persona.speciesVariant, stat)
		end;
		effectiveStat = function(self, stat)
			local val
			local min, max, base = self:statRange(stat)
................................................................................
			else
				val = base + self.persona.statDeltas[stat] or 0
			end

			local d = max - min
			return val, (val - min) / d
		end;












		damageModifier = function(self, kind, amt)
			if kind == 'bluntForceTrauma' then
				local std = self:phenoTrait 'sturdiness'
				if std < 0 then
					amt = amt / 1+std
				else
					amt = amt * 1-std
				end
			end
			return amt
		end;




		attachImage = function(self, def)
			local user = self.entity
			local img = {}
			img.id = user:hud_add {
				type = 'image';
				text = def.tex;
				scale = def.scale;
................................................................................
				align = {x=0, y=-1};
				z = -1;
				update = function(user, set)
					set('text', hudAdjustBacklight(hudCenterBG):render())
				end;
			};
		end;
		-- horrible horrible HACK
		setModeHand = function(self)

			local inv = self.entity:get_inventory()
			local hnd
			if self.actMode == 'off'
				then hnd = ItemStack('starlit:_hand_dig')
				else hnd = ItemStack()

			end
			inv:set_stack('hand', 1, hnd)
		end;




		onModeChange = function(self, oldMode, silent)
			self.hud.elt.crosshair.update()
			if not silent then
				local sfxt = {
					off = 'starlit-mode-off';
					nano = 'starlit-mode-nano';
					psi = 'starlit-mode-psi';
................................................................................
			local oldMode = self.actMode
			self.actMode = mode
			self:onModeChange(oldMode, silent)
			if mode ~= oldMode then
				starlit.ui.setupForUser(self)
			end
		end;
		deleteHUD = function(self)
			for name, e in pairs(self.hud.elt) do
				self:hud_delete(e.id)
			end
		end;
		updateHUD = function(self)
			for name, e in pairs(self.hud.elt) do
				if e.update then e.update() end
			end

		end;




		clientInfo = function(self)
			return minetest.get_player_information(self.name)
		end;







		onSignup = function(self)
			local meta = self.entity:get_meta()
			local inv = self.entity:get_inventory()
			-- 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
			inv:set_size('main', 6) -- carried items and tools. main hotbar.
			inv:set_size('hand', 1) -- horrible hack to allow both tools and intrinsics

................................................................................
			giveGifts('starlit_suit_canisters', gifts.suitCans)

			giveGifts('main', gifts.carry)

			self:reconfigureSuit()

			-- i feel like there has to be a better way

			local cx = math.random(-500,500)
			local iter, startPoint = 1
			repeat local temp = -100
				local cz = math.random(-500,500)

				local cy = minetest.get_spawn_level(cx, cz)
				if cy then
					startPoint = vector.new(cx,cy,cz)
					temp = starlit.world.climate.eval(startPoint,.5,.5).surfaceTemp
				end
				iter = iter + 1
				if iter > 100 then break end -- avoid infiniloop in pathological conditions
................................................................................
			}
			me:set_eye_offset(nil, vector.new(3,-.2,10))
			-- TODO set_clouds speed in accordance with wind
			starlit.world.species.setupEntity(me, self.persona)
			starlit.ui.setupForUser(self)
			self:createHUD()
			self:updateSuit()

		end;









		suitStack = function(self)
			return self.entity:get_inventory():get_stack('starlit_suit', 1)
		end;
		suitSound = function(self, sfx)
			-- trigger a sound effect from the player's suit computer
			minetest.sound_play(sfx, {object=self.entity, max_hear_distance=4}, true)
		end;
................................................................................
					sfx = 'starlit-power-up'
				elseif state == 'powerSave' or os == 'powerSave' then
					sfx = 'starlit-configure'
				end
				if sfx then self:suitSound(sfx) end
			end
		end;
		species = function(self)
			return starlit.world.species.index[self.persona.species]
		end;
		updateBody = function(self)
			local adornment = {}
			local suitStack = self:suitStack()
			if suitStack and not suitStack:is_empty() then
				local suit = suitStack:get_definition()._starlit.suit
				suit.adorn(adornment, suitStack, self.persona)
			end
................................................................................
				-- TODO display power use icon
			end
			return supply, wasteHeat
		end;
		naked = function(self)
			return self:suitStack():is_empty()
		end;
		onPart = function(self)
			starlit.liveUI     [self.name] = nil
			starlit.activeUI   [self.name] = nil
			starlit.activeUsers[self.name] = nil
		end;
		openUI = function(self, id, page, ...)
			local ui = assert(starlit.interface.db[id])
			ui:open(self, page, ...)
		end;
		onRespond = function(self, ui, state, resp)
			ui:action(self, state, resp)
		end;

		updateWeather = function(self)
		end;

		canInteract = function(self, with)
			return true; -- TODO
		end;

		trigger = function(self, which, how)
			local p
			local wld = self.entity:get_wielded_item()
			if which == 'maneuver' then
				p = self.power.maneuver
			elseif which == 'retarget' then
				self.action.prog = {}
................................................................................
							end
							local sw = starlit.item.sw.db[pgm.body.pgmId]
							run = assert(sw.run, 'missing run() for active software ability ' .. pgm.body.pgmId)
							break
						end
					end
				end


			else
				error('bad ability pointer ' .. dump(p))
			end
			if run then
				run(self, ctx)
				return true
			end
			return false
		end;














		give = function(self, item)
			local inv = self.entity:get_inventory()
			local function is(grp)
				return minetest.get_item_group(item:get_name(), grp) ~= 0
			end
			-- TODO notif popups
			if is 'specialInventory' then
................................................................................
		local dehydration = p:trait 'dehydration' * biointerval
		-- you dehydrate faster in higher temp
		dehydration = dehydration * math.max(1, starlit.world.climate.temp(u.entity:get_pos()) / 10)

		u:statDelta('nutrition', -bmr)
		u:statDelta('hydration', -dehydration)





		if u:effectiveStat 'nutrition' == 0 then
			-- starvation







		end





		if u:effectiveStat 'hydration' == 0 then
			-- dying of thirst
		end




		local rads = u:effectiveStat 'irradiation'



		if rads > 0 then
			u:statDelta('irradiation', -0.0001 * biointerval)


		end















	end
end)

local cbit = {
	up   = 0x001;
	down = 0x002;
	left = 0x004;
................................................................................
				return mustHalt
			else return doNothing end
		end
		local skipBits = 0
		if user.action.bits ~= bits then
			local mPrimary = what(cbit.dig)
			local mSecondary = what(cbit.put)

			if mPrimary == mustInit then -- ENGINE-BUG
				user.action.tgt = {type='nothing'}
				user.action.prog = {}
			elseif mPrimary == mustHalt then
				user:trigger('primary', {state='halt'})
			end
			if mSecondary == mustHalt then
				user:trigger('secondary', {state='halt'})
			end




		end

		--bits = bit.band(bits, bit.bnot(skipBits))
		if bit.band(bits, cbit.dig)~=0 then

			user:trigger('primary', {state='prog', delta=delta})
		end

		if bit.band(bits, cbit.put)~=0 then
			user:trigger('secondary', {state='prog', delta=delta})
		end

		user.action.bits = bits
		-- ENGINE-BUG: dig and put are not handled equally in the
		-- engine. it is possible for the put bit to get stuck on
		-- if the key is hammered while the player is not moving.
		-- the bit will release as soon as the player looks or turns
		-- nonetheless this is obnoxious
	end
end)







>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>

>
>
>
>







 







<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>











>
>
>
>







 







<
|
>
|
|
|
|
|
>

<

>
>
>
>







 







|
|
|
|
|
|
<
<

>

>
>
>
>



>
>
>
>
>
>
>







 







>
|


<
>







 







>

>
>
>
>
>
>
>
>
>







 







<
<
<







 







|
|
|
|
<







<
<
<
<
<
<
<
<







 







>
>









>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
|
<
>
>
>
>
>
>
>
|
>
>
>

>
|
<
<
>

>
>

>
>
>


>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>









>
>
>
>
|
>

<
>
|

>
|
<
<
>








55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
134
135
136
137
138
139
140








141
142
143
144
145
146
147
...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
...
425
426
427
428
429
430
431

432
433
434
435
436
437
438
439
440

441
442
443
444
445
446
447
448
449
450
451
452
...
462
463
464
465
466
467
468
469
470
471
472
473
474


475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
...
530
531
532
533
534
535
536
537
538
539
540

541
542
543
544
545
546
547
548
...
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
...
700
701
702
703
704
705
706



707
708
709
710
711
712
713
...
793
794
795
796
797
798
799
800
801
802
803

804
805
806
807
808
809
810








811
812
813
814
815
816
817
...
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
...
921
922
923
924
925
926
927
928
929
930
931
932

933
934
935
936
937
938
939
940
941
942
943
944
945
946


947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
....
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024

1025
1026
1027
1028
1029


1030
1031
1032
1033
1034
1035
1036
1037
1038
				weapon = {primary = nil, secondary = nil};
				psi = {primary = nil, secondary = nil};
				maneuver = nil;
			};
			pref = {
				calendar = 'commune';
			};
			overlays = {};
		}
	end;
	__index = {
		--------------
		-- overlays --
		--------------
		updateOverlays = function(self)
			local phys = {
				speed = self.pheno:trait('speed',1);
				jump = self.pheno:trait('jump',1);
				gravity = 1;
				speed_climb = 1;
				speed_crouch = 1;
				speed_walk = 1;
				acceleration_default = 1;
				acceleration_air = 1;
			}
			for i, o in ipairs(self.overlays) do o(phys) end
			self.entity:set_physics_override(phys)
		end;
		overlay = function(self, o)
			local id = #self.overlays+1
			self.overlays[id] = o
			self:updateOverlays()
			return id
		end;
		deleteOverlay = function(self, id)
			table.remove(self.overlays, id)
			self:updateOverlays()
		end;

		--------------
		-- personae --
		--------------
		pullPersona = function(self)
			-- if later records are added in public updates, extend this function to merge them
			-- into one object
			local s = userStore(self.entity)
			self.persona = s.read 'persona'
			self.pheno = starlit.world.species.pheno(self.persona.species, self.persona.speciesVariant)
		end;
		pushPersona = function(self)
			local s = userStore(self.entity)
			s.write('persona', self.persona)
		end;

		uiColor = function(self) return lib.color {hue=238,sat=.5,lum=.5} end;

		-----------
		-- stats --
		-----------
		statDelta = function(self, stat, d, cause, abs)
			local dt = self.persona.statDeltas
			local min, max, base = self:statRange(stat)
			if abs then
				if     d == true  then d = max
				elseif d == false then d = min end
			end
................................................................................
				self:pushPersona()
			end


			self:updateHUD()
			-- TODO trigger relevant animations?
		end;








		statRange = function(self, stat) --> min, max, base
			return starlit.world.species.statRange(
				self.persona.species, self.persona.speciesVariant, stat)
		end;
		effectiveStat = function(self, stat)
			local val
			local min, max, base = self:statRange(stat)
................................................................................
			else
				val = base + self.persona.statDeltas[stat] or 0
			end

			local d = max - min
			return val, (val - min) / d
		end;

		---------------
		-- phenotype --
		---------------
		lookupSpecies = function(self)
			return starlit.world.species.lookup(self.persona.species, self.persona.speciesVariant)
		end;
		phenoTrait = function(self, trait, dflt)
-- 			local s,v = self:lookupSpecies()
-- 			return v.traits[trait] or s.traits[trait] or 0
			return self.pheno:trait(trait, dflt)
		end;
		damageModifier = function(self, kind, amt)
			if kind == 'bluntForceTrauma' then
				local std = self:phenoTrait 'sturdiness'
				if std < 0 then
					amt = amt / 1+std
				else
					amt = amt * 1-std
				end
			end
			return amt
		end;

		---------
		-- HUD --
		---------
		attachImage = function(self, def)
			local user = self.entity
			local img = {}
			img.id = user:hud_add {
				type = 'image';
				text = def.tex;
				scale = def.scale;
................................................................................
				align = {x=0, y=-1};
				z = -1;
				update = function(user, set)
					set('text', hudAdjustBacklight(hudCenterBG):render())
				end;
			};
		end;

		deleteHUD = function(self)
			for name, e in pairs(self.hud.elt) do
				self:hud_delete(e.id)
			end
		end;
		updateHUD = function(self)
			for name, e in pairs(self.hud.elt) do
				if e.update then e.update() end
			end

		end;

		---------------------
		-- actions & modes --
		---------------------
		onModeChange = function(self, oldMode, silent)
			self.hud.elt.crosshair.update()
			if not silent then
				local sfxt = {
					off = 'starlit-mode-off';
					nano = 'starlit-mode-nano';
					psi = 'starlit-mode-psi';
................................................................................
			local oldMode = self.actMode
			self.actMode = mode
			self:onModeChange(oldMode, silent)
			if mode ~= oldMode then
				starlit.ui.setupForUser(self)
			end
		end;
		setModeHand = function(self) -- horrible horrible HACK
			local inv = self.entity:get_inventory()
			local hnd
			if self.actMode == 'off'
				then hnd = ItemStack('starlit:_hand_dig')
				else hnd = ItemStack()


			end
			inv:set_stack('hand', 1, hnd)
		end;

		---------------------
		-- intel-gathering --
		---------------------
		clientInfo = function(self)
			return minetest.get_player_information(self.name)
		end;
		species = function(self)
			return starlit.world.species.index[self.persona.species]
		end;

		--------------------
		-- event handlers --
		--------------------
		onSignup = function(self)
			local meta = self.entity:get_meta()
			local inv = self.entity:get_inventory()
			-- 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
			inv:set_size('main', 6) -- carried items and tools. main hotbar.
			inv:set_size('hand', 1) -- horrible hack to allow both tools and intrinsics

................................................................................
			giveGifts('starlit_suit_canisters', gifts.suitCans)

			giveGifts('main', gifts.carry)

			self:reconfigureSuit()

			-- i feel like there has to be a better way
			local posrng = starlit.world.seedbank[0x13f19] -- TODO player-specific seed
			local cx = posrng:int(-500,500) --math.random(-500,500)
			local iter, startPoint = 1
			repeat local temp = -100

				local cz = posrng:int(-500,500)
				local cy = minetest.get_spawn_level(cx, cz)
				if cy then
					startPoint = vector.new(cx,cy,cz)
					temp = starlit.world.climate.eval(startPoint,.5,.5).surfaceTemp
				end
				iter = iter + 1
				if iter > 100 then break end -- avoid infiniloop in pathological conditions
................................................................................
			}
			me:set_eye_offset(nil, vector.new(3,-.2,10))
			-- TODO set_clouds speed in accordance with wind
			starlit.world.species.setupEntity(me, self.persona)
			starlit.ui.setupForUser(self)
			self:createHUD()
			self:updateSuit()
			self:updateOverlays()
		end;
		onPart = function(self)
			starlit.liveUI     [self.name] = nil
			starlit.activeUI   [self.name] = nil
			starlit.activeUsers[self.name] = nil
		end;

		-----------------------------
		-- environment suit & body --
		-----------------------------
		suitStack = function(self)
			return self.entity:get_inventory():get_stack('starlit_suit', 1)
		end;
		suitSound = function(self, sfx)
			-- trigger a sound effect from the player's suit computer
			minetest.sound_play(sfx, {object=self.entity, max_hear_distance=4}, true)
		end;
................................................................................
					sfx = 'starlit-power-up'
				elseif state == 'powerSave' or os == 'powerSave' then
					sfx = 'starlit-configure'
				end
				if sfx then self:suitSound(sfx) end
			end
		end;



		updateBody = function(self)
			local adornment = {}
			local suitStack = self:suitStack()
			if suitStack and not suitStack:is_empty() then
				local suit = suitStack:get_definition()._starlit.suit
				suit.adorn(adornment, suitStack, self.persona)
			end
................................................................................
				-- TODO display power use icon
			end
			return supply, wasteHeat
		end;
		naked = function(self)
			return self:suitStack():is_empty()
		end;

		--------
		-- ui --
		--------

		openUI = function(self, id, page, ...)
			local ui = assert(starlit.interface.db[id])
			ui:open(self, page, ...)
		end;
		onRespond = function(self, ui, state, resp)
			ui:action(self, state, resp)
		end;








		trigger = function(self, which, how)
			local p
			local wld = self.entity:get_wielded_item()
			if which == 'maneuver' then
				p = self.power.maneuver
			elseif which == 'retarget' then
				self.action.prog = {}
................................................................................
							end
							local sw = starlit.item.sw.db[pgm.body.pgmId]
							run = assert(sw.run, 'missing run() for active software ability ' .. pgm.body.pgmId)
							break
						end
					end
				end
			elseif p.ref then
				run = p.ref.run
			else
				error('bad ability pointer ' .. dump(p))
			end
			if run then
				run(self, ctx)
				return true
			end
			return false
		end;

		-------------
		-- weather --
		-------------
		updateWeather = function(self)
		end;

		canInteract = function(self, with)
			return true; -- TODO
		end;

		---------------
		-- inventory --
		---------------
		give = function(self, item)
			local inv = self.entity:get_inventory()
			local function is(grp)
				return minetest.get_item_group(item:get_name(), grp) ~= 0
			end
			-- TODO notif popups
			if is 'specialInventory' then
................................................................................
		local dehydration = p:trait 'dehydration' * biointerval
		-- you dehydrate faster in higher temp
		dehydration = dehydration * math.max(1, starlit.world.climate.temp(u.entity:get_pos()) / 10)

		u:statDelta('nutrition', -bmr)
		u:statDelta('hydration', -dehydration)

		local moralePenalty = -1 -- 1min/min
		local fatiguePenalty = 1 -- 1min/min
		local heatPenalty = 1 -- stamina regen is divided by this

		do local warmth = u:effectiveStat 'warmth'

			local tempRange = u:species().tempRange
			local tComfMin, tComfMax = tempRange.comfort[1], tempRange.comfort[2]
			local tempDiff = 0
			if warmth < tComfMin then
				tempDiff = math.abs(warmth-tComfMin)
			elseif warmth > tComfMax then
				tempDiff = math.abs(warmth-tComfMax)
			end
			moralePenalty = moralePenalty + tempDiff
			heatPenalty = heatPenalty + tempDiff
		end

		-- penalize heavy phys. activity
		local stamina, sp = u:effectiveStat 'stamina'


		fatiguePenalty = fatiguePenalty * (1 + 9*(1-sp))

		local food = u:effectiveStat 'nutrition'
		local water = u:effectiveStat 'hydration'
		local rads = u:effectiveStat 'irradiation'
		if food < 1000 then moralePenalty = moralePenalty + (1 - (food/1000)) * 5 end
		if water < 1   then moralePenalty = moralePenalty + (1 - (water/1)) * 10 end

		if rads > 0 then
			u:statDelta('irradiation', -0.0001 * biointerval)
			local moraleDrainFac = 2^(rads / 2)
			moralePenalty = moralePenalty * moraleDrainFac
		end

		u:statDelta('morale', moralePenalty * biointerval)
		u:statDelta('fatigue', fatiguePenalty * biointerval)

		if food == 0 then -- starvation
			u:statDelta('health', -5*biointerval)
		end

		if water == 0 then -- dying of thirst
			u:statDelta('health', -20*biointerval)
		end

		if sp < 1.0 then
			u:statDelta('stamina', u:effectiveStat 'staminaRegen' / heatPenalty)
		end
	end
end)

local cbit = {
	up   = 0x001;
	down = 0x002;
	left = 0x004;
................................................................................
				return mustHalt
			else return doNothing end
		end
		local skipBits = 0
		if user.action.bits ~= bits then
			local mPrimary = what(cbit.dig)
			local mSecondary = what(cbit.put)
			local mManeuver = what(cbit.manv)
			if mPrimary == mustInit then -- ENGINE-BUG
				user.action.tgt = {type='nothing'}
				user.action.prog = {}
			elseif mPrimary == mustHalt then
				user:trigger('primary', {state='halt'})
			end
			if mSecondary == mustHalt then
				user:trigger('secondary', {state='halt'})
			end
			if mManeuver == mustInit then
				user:trigger('maneuver', {state='init'})
			elseif mManeuver == mustHalt then
				user:trigger('maneuver', {state='halt'})
			end
		end
		--bits = bit.band(bits, bit.bnot(skipBits))

		local function prog(what)
			user:trigger(what, {state='prog', delta=delta})
		end
		if bit.band(bits, cbit.dig)~=0  then prog 'primary'   end
		if bit.band(bits, cbit.put)~=0  then prog 'secondary' end


		if bit.band(bits, cbit.manv)~=0 then prog 'maneuver'  end
		user.action.bits = bits
		-- ENGINE-BUG: dig and put are not handled equally in the
		-- engine. it is possible for the put bit to get stuck on
		-- if the key is hammered while the player is not moving.
		-- the bit will release as soon as the player looks or turns
		-- nonetheless this is obnoxious
	end
end)