Comment: | rename again |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
187bf04c875d238d14e4ccedc13983bf |
User & Date: | lexi on 2024-04-29 22:49:02 |
Other Links: | manifest | tags |
2024-05-01
| ||
13:46 | cleanups, fixes, begin canister rework, begin ecology check-in: a810a756ce user: lexi tags: trunk | |
2024-04-29
| ||
22:49 | rename again check-in: 187bf04c87 user: lexi tags: trunk | |
2024-03-29
| ||
22:43 | fix batteries, silence debug noise check-in: 6469428393 user: lexi tags: trunk | |
Modified dev.ct from [3b1604bc1d] to [912982b94d].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# starsoul development this file contains information meant for those who wish to develop for Starsoul or build the game from trunk. do NOT add any story information, particularly spoilers; those go in src/lore.ct. ## tooling starsoul uses the following software in the development process: * [*csound] to generate sound effects * [*GNU make] to automate build tasks * [*lua] to automate configure tasks ## building to run a trunk version of Starsoul, you'll need to install the above tools and run `make` from the base directory. this will: * run lua scripts to generate necessary makefiles * generate the game sound effects and install them in mods/starsoul/sounds ## policy * copyright of all submitted code must be reassigned to the maintainer. * all code is to be indented with tabs and aligned with spaces; formatting is otherwise up to whoever is responsible for maintaining that code * use [`camelCase], not [`snake_case] and CERTAINLY not [`SCREAMING_SNAKE_CASE] * sounds effects should be contributed in the form of csound files; avoid adding audio files to the repository except for foley effects |
| | | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# starlit development this file contains information meant for those who wish to develop for Starsoul or build the game from trunk. do NOT add any story information, particularly spoilers; those go in src/lore.ct. ## tooling starlit uses the following software in the development process: * [*csound] to generate sound effects * [*GNU make] to automate build tasks * [*lua] to automate configure tasks ## building to run a trunk version of Starsoul, you'll need to install the above tools and run `make` from the base directory. this will: * run lua scripts to generate necessary makefiles * generate the game sound effects and install them in mods/starlit/sounds ## policy * copyright of all submitted code must be reassigned to the maintainer. * all code is to be indented with tabs and aligned with spaces; formatting is otherwise up to whoever is responsible for maintaining that code * use [`camelCase], not [`snake_case] and CERTAINLY not [`SCREAMING_SNAKE_CASE] * sounds effects should be contributed in the form of csound files; avoid adding audio files to the repository except for foley effects |
Modified game.conf from [d6b8d1d1d1] to [b1638bce61].
1 2 3 4 5 |
title = Starsoul
author = velartrill
description = High-tech survival on a hostile alien world
allowed_mapgens = v7
disabled_settings = !enable_damage, creative_mode
|
| |
1 2 3 4 5 |
title = Starlit
author = velartrill
description = High-tech survival on a hostile alien world
allowed_mapgens = v7
disabled_settings = !enable_damage, creative_mode
|
Added mods/starlit-building/init.lua version [7b434e8bee].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
local lib = starlit.mod.lib local B = {} starlit.mod.building = B B.path = {} -- this maps stage IDs to tables of the following form --[[ { part = { ['starlit_building:pipe'] = 'myMod:stage3'; }; tool = { ['starlit:scredriver'] = 'myMod:otherThing_stage1'; ['starlit:saw'] = function(node, tool) minetest.replace_node(node, {name='myMod:stage1'}) minetest.drop_item(node, 'starlit_building:pipe') end; ['myMod:laserWrench'] = { allow = function(node, tool) ... end; handle = function(node, tool) ... end; }; }; } ]] -- it should only be written by special accessor functions! B.stage = lib.registry.mk 'starlit_building:stage' -- a stage consists of a list of pieces and maps from possible materials -- / tool usages to succeeding stages in the build tree. note that all -- used pieces must be defined before a stage is defined currently, due -- to a lack of cross-registry dependency mechanisms. i will hopefully -- improve vtlib to handle this condition eventually. --[[ starlit.mod.building.stage.link(id, { pieces = { 'starlit_building:foundation'; 'starlit_building:insulation'; -- offset ofsFac {'starlit_building:pipe', vector.new(-.5, 0, 0), 0};-- {'starlit_building:pipe', vector.new(-.5, 0, 0), 1}; 'starlit_building:insulation'; 'starlit_building:panel'; }; }) ]] B.piece = lib.registry.mk 'starlit_building:piece' -- a piece is used to produce stage definitions, by means of appending -- nodeboxes with appropriate offsets. it also lists the recoverable -- materials which can be obtained by destroying a stage containing -- this piece using nano. part IDs should correspond with piece IDs -- where possible --[[ starlit.mod.building.piece.link(id, { tex = 'myMod_part.png'; height = 0.1; -- used for auto-offset fab = { element = {iron=10}; }; shape = { type = "fixed"; fixed = { ... }; }; }) ]] B.part = lib.registry.mk 'starlit_building:part' -- a part is implemented as a special craftitem with the proper callbacks -- to index the registries and place/replace noes by reference to the -- build tree. --[[ starlit.mod.building.part.link(id, { name = ''; -- display name desc = ''; -- display desc img = ''; -- display image }) ]] B.stage.foreach('starlit:stageGen', {}, function(id, e) local box = {type = 'fixed', fixed = {}} local tex = {} local ofs = vector.new(0,0,0) for idx, p in ipairs(e.pieces) do local ho, pieceID, pos if type(p) == 'string' then pieceID, pos, ho = p, vector.zero(), 1.0 else pieceID, pos, ho = pc[1],pc[2],pc[3] end local pc = B.piece.db[pieceID] pos = pos + ofs if ho ~= 0.0 then ofs = vector.offset(ofs, 0, pc.height) end local sh = lib.node.boxwarped(pc.shape, function(b) -- { -x, -y, -z; -- +x, +y, +z } b[1] = b[1] + ofs.x b[4] = b[4] + ofs.x b[2] = b[2] + ofs.y b[5] = b[5] + ofs.y b[3] = b[3] + ofs.z b[6] = b[6] + ofs.z end) table.insert(box, sh) if type(pc.tex) == 'string' then table.insert(tex, pc.tex) else for i,t in ipairs(pc.tex) do table.insert(tex, t or '') end end end minetest.register_node(id, { description = 'Construction'; drawtype = 'nodebox'; paramtype = 'light'; paramtype2 = e.stateful or 'none'; textures = tex; node_box = box; group = { stage = 1 }; _starlit = { stage = id; }; }) end) function B.pathLink(from, kind, what, to) if not B.path[from] then B.path[from] = {part={}, tool={}} end local k = B.path[from][kind] assert(k[what] == nil) k[what] = to end function B.pathFind(from, kind, what) if not B.path[from] then return nil end return B.path[from][kind][what] end |
Added mods/starlit-building/mod.conf version [dd26a16b6d].
> > > > |
1 2 3 4 |
name = starlit_building title = starlit building depends = starlit_electronics, starlit description = implements construction elements |
Added mods/starlit-electronics/init.lua version [67a7c4791a].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 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 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 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 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 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 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 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 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 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 880 881 882 883 884 885 886 887 888 889 890 891 892 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 |
local lib = starlit.mod.lib local E = {} starlit.mod.electronics = E --------------------- -- item registries -- --------------------- -- a dynamo is any item that produces power and can be slotted into a power -- source slot. this includes batteries, but also things like radiothermal -- dynamos. starlit.item.dynamo = lib.registry.mk 'starlit_electronics:dynamo' -- batteries hold a charge of power (measured in kJ). how much they can hold -- (and how much power they can discharge?) depends on their quality starlit.item.battery = lib.registry.mk 'starlit_electronics:battery' -- a battery has the properties: -- class -- |- capacity (J ): amount of energy the battery can hold -- |- dischargeRate (W ): rate at which battery can supply power/be charged -- |- decay (J/J): rate at which the battery capacity degrades while -- discharging. decay=0 batteries require no maintenance; -- decay=1 batteries are effectively disposable -- |- leak (fac): charging inefficiency. depends on the energy storage -- technology. when N J are drawn from a power source, -- only (N*leak) J actually make it into the battery. -- leak=0 is a supercapacitor, leak=1 is /dev/null -- |- size (m): each suit has a limit to how big of a battery it can take -- instance -- |- degrade (mJ): how much the battery has degraded. instance max charge is -- | determined by $capacity - @degrade -- |- %wear ÷ 2¹⁶ : used as a factor to determine battery charge -- chips are standardized data storage hardware that can contain a certain amount -- of software. in addition to their flash storage, they also provide a given amount -- of working memory and processor power. processor power speeds up operations like -- crafting, while programs require a certain amount of memory. -- chips have a variable number of program slots and a single bootloader slot -- starlit.item.chip = lib.registry.mk 'starlit_electronics:chip' -- software is of one of the following types: -- schematic: program for your matter compiler that enables crafting a given item. -- output: the result -- driver: inserted into a Core to control attached hardware -- suitPower: provides suit functionality like nanoshredding or healing -- passive powers are iterated on suit application/configuration and upon fst-tick -- cost: what the software needs to run. some fields are fab-specific -- energy: for fab, total energy cost of process in joules -- for suitPassive, added suit power consumption in watts starlit.item.sw = lib.registry.mk 'starlit_electronics:sw' -- chip = lib.color(0, 0, .3); E.schematicGroups = lib.registry.mk 'starlit_electronics:schematicGroups' E.schematicGroupMembers = {} E.schematicGroups.foreach('starlit_electronics:ensure-memlist', {}, function(id,g) E.schematicGroupMembers[id] = {} end) function E.schematicGroupLink(group, item) table.insert(E.schematicGroupMembers[group], item) end E.schematicGroups.link('starlit_electronics:chip', { title = 'Chip', icon = 'starlit-item-chip.png'; description = 'Standardized data storage and compute modules'; }) E.schematicGroups.link('starlit_electronics:battery', { title = 'Battery', icon = 'starlit-item-battery.png'; description = 'Portable power storage cells are essential to all aspects of survival'; }) E.schematicGroups.link('starlit_electronics:decayCell', { title = 'Decay Cell', icon = 'starlit-item-decaycell.png'; description = "Radioisotope generators can pack much more power into a smaller amount of space than conventional batteries, but they can't be recharged, dump power and heat whether they're in use or not, and their power yield drops towards zero over their usable lifetime."; }) ------------------------- -- batteries & dynamos -- ------------------------- E.battery = {} local function accessor(ty, fn) return function(stack, ...) local function fail() error(string.format('object %q is not a %s', stack:get_name(), ty)) end if not stack or stack:is_empty() then fail() end if minetest.get_item_group(stack:get_name(), ty) == 0 then fail() end return fn(stack, stack:get_definition()._starlit[ty], stack:get_meta(), ...) end end -- return a wear level that won't destroy the item -- local function safeWear(fac) return math.min(math.max(fac,0),1) * 0xFFFE end -- local function safeWearToFac(w) return w/0xFFFE end E.battery.update = accessor('battery', function(stack, batClass, meta) -- local cap = E.battery.capacity(stack) local charge = meta:get_float 'starlit_electronics:battery_charge' meta:set_string('count_meta', string.format('%s%%', math.floor(charge * 100))) meta:set_int('count_alignment', 14) end) -- E.battery.capacity(bat) --> charge (J) E.battery.capacity = accessor('battery', function(stack, batClass, meta) local dmg = meta:get_int 'starlit_electronics:battery_degrade' -- µJ/μW local dmg_J = dmg / 1000 return (batClass.capacity - dmg_J) end) -- E.battery.charge(bat) --> charge (J) E.battery.charge = accessor('battery', function(stack, batClass, meta) local fac = meta:get_float 'starlit_electronics:battery_charge' -- local fac = 1 - safeWearToFac(stack:get_wear()) return E.battery.capacity(stack) * fac end) -- E.battery.dischargeRate(bat) --> dischargeRate (W) E.battery.dischargeRate = accessor('battery', function(stack, batClass, meta) local dmg = meta:get_int 'starlit_electronics:battery_degrade' -- µJ/μW local dmg_W = dmg / 1000 return batClass.dischargeRate - dmg_W end); -- E.battery.drawCurrent(bat, power, time, test) --> supply (J), wasteHeat (J) -- bat = battery stack -- power J = joules of energy user wishes to consume -- time s = the amount of time available for this transaction -- supply J = how much power was actually provided in $time seconds -- wasteHeat J = how heat is generated in the process -- test = if true, the battery is not actually modified E.battery.drawCurrent = accessor('battery', function(s, bc, m, power, time, test) local ch = E.battery.charge(s) local maxPower = math.min(E.battery.dischargeRate(s)*time, power, ch) ch = ch - maxPower if not test then local degrade = m:get_int 'starlit_electronics:battery_degrade' or 0 degrade = degrade + maxPower * bc.decay -- for each joule of power drawn, capacity degrades by `decay` J -- this should ordinarily be on the order of mJ or smaller m:set_int('starlit_electronics:battery_degrade', degrade) -- s:set_wear(safeWear(1 - (ch / E.battery.capacity(s)))) m:set_float('starlit_electronics:battery_charge', ch / E.battery.capacity(s)) E.battery.update(s) end return maxPower, 0 -- FIXME specify waste heat end) -- E.battery.recharge(bat, power, time) --> draw (J) -- bat = battery stack -- power J = joules of energy user wishes to charge the battery with -- time s = the amount of time available for this transaction -- draw J = how much power was actually drawn in $time seconds E.battery.recharge = accessor('battery', function(s, bc, m, power, time) local ch = E.battery.charge(s) local cap = E.battery.capacity(s) local maxPower = math.min(E.battery.dischargeRate(s)*time, power) local total = math.min(ch + maxPower, cap) -- s:set_wear(safeWear(1 - (total/cap))) m:set_float('starlit_electronics:battery_charge', total/cap) E.battery.update(s) return maxPower, 0 -- FIXME end) E.battery.setCharge = accessor('battery', function(s, bc, m, newPower) local cap = E.battery.capacity(s) local power = math.min(cap, newPower) -- s:set_wear(safeWear(1 - (power/cap))) m:set_float('starlit_electronics:battery_charge', power/cap) E.battery.update(s) end) E.battery.setChargeF = accessor('battery', function(s, bc, m, newPowerF) local power = math.min(1.0, newPowerF) m:set_float('starlit_electronics:battery_charge', power) E.battery.update(s) end) E.dynamo = { kind = {} } E.dynamo.drawCurrent = accessor('dynamo', function(s,c,m, power, time, test) return c.vtable.drawCurrent(s, power, time, test) end) E.dynamo.totalPower = accessor('dynamo', function(s,c,m) return c.vtable.totalPower(s) end) E.dynamo.dischargeRate = accessor('dynamo', function(s,c,m) return c.vtable.dischargeRate (s) end) E.dynamo.initialPower = accessor('dynamo', function(s,c,m) return c.vtable.initialPower(s) end) E.dynamo.wasteHeat = accessor('dynamo', function(s,c,m) return c.vtable.wasteHeat(s) end) -- baseline waste heat, produced whether or not power is being drawn. for batteries this is 0, but for -- radiothermal generators it may be high E.dynamo.kind.battery = { drawCurrent = E.battery.drawCurrent; totalPower = E.battery.charge; initialPower = E.battery.capacity; dischargeRate = E.battery.dischargeRate; wasteHeat = function() return 0 end; }; starlit.item.battery.foreach('starlit_electronics:battery-gen', {}, function(id, def) minetest.register_tool(id, { short_description = def.name; groups = { battery = 1; dynamo = 1; electronic = 1; }; inventory_image = def.img or 'starlit-item-battery.png'; description = starlit.ui.tooltip { title = def.name; desc = def.desc; color = lib.color(0,.2,1); props = { { title = 'Optimal Capacity', affinity = 'info'; desc = lib.math.si('J', def.capacity) }; { title = 'Discharge Rate', affinity = 'info'; desc = lib.math.si('W', def.dischargeRate) }; { title = 'Charge Efficiency', affinity = 'info'; desc = string.format('%s%%', (1-def.leak) * 100) }; { title = 'Size', affinity = 'info'; desc = lib.math.si('m', def.fab.size.print) }; }; }; _starlit = { event = { create = function(st, how) --[[if not how.gift then -- cheap hack to make starting batteries fully charged E.battery.setCharge(st, 0) end]] E.battery.update(st) end; }; fab = def.fab; dynamo = { vtable = E.dynamo.kind.battery; }; battery = def; }; }) end) -- to use the power functions, consider the following situation. you have -- a high-tier battery charger that can draw 100kW. (for simplicity, assume -- it supports only one battery). if you install a low-tier battery, and -- the charging callback is called every five seconds, you might use -- a `recharge` call that looks like -- -- starlit.mod.electronics.battery.recharge(bat, 5 * 100*1e4, 5) -- -- this would offer the battery 500kJ over five seconds. the battery will -- determine how much power it can actually make use of in 5 five seconds, -- and then return that amount. -- -- always remember to save the battery back to its inventory slot after -- modifying its ItemStack with one of these functions! -- battery types -- supercapacitor: low capacity, no degrade, high dischargeRate, no leak -- chemical: high capacity, high degrade, mid dischargeRate, low leak -- battery tiers -- makeshift: cheap, weak, low quality -- imperial ("da red wunz go fasta"): powerful, low quality -- commune ("snooty sophisticates"): limited power, high quality, expensive -- usukwinya ("value engineering"): high power, mid quality, affordable -- eluthrai ("uncompromising"): high power, high quality, wildly expensive -- firstborn ("god-tier"): exceptional local batteryTiers = { makeshift = { name = 'Makeshift'; capacity = .5, decay = 3, leak = 2, dischargeRate = 1, fab = starlit.type.fab { metal = {copper=10}; }; desc = "Every attosecond this electrical abomination doesn't explode in your face is but the unearned grace of the Wild Gods."; complexity = 1; sw = {rarity = 1}; }; imperial = { name = 'Imperial'; capacity = 2, decay = 2, leak = 2, dischargeRate = 2; fab = starlit.type.fab { metal = {copper=15, iron = 20}; size = { print = 0.1 }; }; desc = "The Empire's native technology is a lumbering titan: bulky, inefficient, unreliable, ugly, and awesomely powerful. Their batteries are no exception, with raw capacity and throughput that exceed even Usukinwya designs."; drm = 1; complexity = 2; sw = {rarity = 2}; }; commune = { name = 'Commune'; capacity = 1, decay = .5, leak = .2, dischargeRate = 1; fab = starlit.type.fab { metal = {vanadium=50, steel=10}; size = { print = 0.05 }; }; desc = "The Commune's proprietary battery designs prioritize reliability, compactness, and maintenance concerns above raw throughput, with an elegance of engineering and design that would make a Su'ikuri cry."; complexity = 5; sw = {rarity = 3}; }; usukwinya = { name = 'Usukwinya'; capacity = 2, decay = 1, leak = 1, dischargeRate = 1.5, fab = starlit.type.fab { metal = {vanadium=30, argon=10}; size = { print = 0.07 }; }; desc = "A race of consummate value engineers, the Usukwinya have spent thousands of years refining their tech to be as cheap to build as possible, without compromising much on quality. The Tradebirds drive an infamously hard bargain, but their batteries are more than worth their meagre cost."; drm = 2; sw = {rarity = 10}; complexity = 15; }; eluthrai = { name = 'Eluthrai'; capacity = 3, decay = .4, leak = .1, dischargeRate = 1.5, fab = starlit.type.fab { metal = {beryllium=20, platinum=20, technetium = 1, cinderstone = 10 }; size = { print = 0.03 }; }; desc = "The uncompromising Eluthrai are never satisfied until every quantifiable characteristic of their tech is maximally optimised down to the picoscale. Their batteries are some of the best in the Reach, and unquestionably the most expensive -- especially for those lesser races trying to copy the designs without the benefit of the sublime autofabricator ecosystem of the Eluthrai themselves."; complexity = 200; sw = {rarity = 0}; -- you think you're gonna buy eluthran schematics on SuperDiscountNanoWare.space?? }; firstborn = { name = 'Firstborn'; capacity = 5, decay = 0.1, leak = 0, dischargeRate = 3; fab = starlit.type.fab { metal = {neodymium=20, xenon=150, technetium=5, sunsteel = 10 }; crystal = {astrite = 1}; size = { print = 0.05 }; }; desc = "Firstborn engineering seamlessly merges psionic effects with a mastery of the physical universe unattained by even the greatest of the living Starsouls. Their batteries reach levels of performance that strongly imply Quantum Gravity Theory -- and several major holy books -- need to be rewritten. From the ground up."; complexity = 1000; sw = {rarity = 0}; -- lol no }; } local batterySizes = { small = {name = 'Small', capacity = .5, dischargeRate = .5, complexity = 1, matMult = .5, fab = starlit.type.fab {size={print=0.1}}}; mid = { capacity = 1, dischargeRate = 1, complexity = 1, matMult = 1, fab = starlit.type.fab {size={print=0.3}}}; large = {name = 'Large', capacity = 2, dischargeRate = 1.5, complexity = 1, matMult = 1.5, fab = starlit.type.fab {size={print=0.5}}}; huge = {name = 'Huge', capacity = 3, dischargeRate = 2, complexity = 1, matMult = 2, fab = starlit.type.fab {size={print=0.8}}}; } local batteryTypes = { supercapacitor = { name = 'Supercapacitor'; desc = 'Room-temperature superconductors make for very reliable, high-dischargeRate, but low-capacity batteries.'; fab = starlit.type.fab { metal = { enodium = 5 }; size = {print=0.8}; }; sw = { cost = { cycles = 5e9; -- 5 bil cycles ram = 10e9; -- 10GB }; pgmSize = 2e9; -- 2GB rarity = 5; }; capacity = 50e3, dischargeRate = 1000; leak = 0, decay = 1e-6; complexity = 3; }; chemical = { name = 'Chemical'; desc = ''; fab = starlit.type.fab { element = { lithium = 3}; metal = {iron = 5}; size = {print=1.0}; }; sw = { cost = { cycles = 1e9; -- 1 bil cycles ram = 2e9; -- 2GB }; pgmSize = 512e6; -- 512MB rarity = 2; }; capacity = 200e3, dischargeRate = 200; leak = 0.2, decay = 1e-2; complexity = 1; }; carbon = { name = 'Carbon'; desc = 'Carbon nanotubes form the basis of many important metamaterials, chief among them power-polymer.'; capacity = 1; fab = starlit.type.fab { element = { carbon = 40 }; size = {print=0.5}; }; sw = { cost = { cycles = 50e9; -- 50 bil cycles ram = 64e9; -- 64GB }; pgmSize = 1e9; -- 1GB rarity = 10; }; capacity = 100e3, dischargeRate = 500; leak = 0.1, decay = 1e-3; complexity = 10; }; hybrid = { name = 'Hybrid'; desc = ''; capacity = 1; fab = starlit.type.fab { element = { lithium = 3; }; metal = { iron = 5; }; size = {print=1.5}; }; sw = { cost = { cycles = 65e9; -- 65 bil cycles ram = 96e9; -- 96GB }; pgmSize = 5e9; -- 5GB rarity = 15; }; capacity = 300e3, dischargeRate = 350; leak = 0.3, decay = 1e-5; complexity = 30; }; } local function elemath(dest, src, mult) dest = dest or {} for k,v in pairs(src) do if not dest[k] then dest[k] = 0 end dest[k] = dest[k] + v*mult end return dest end for bTypeName, bType in pairs(batteryTypes) do for bTierName, bTier in pairs(batteryTiers) do for bSizeName, bSize in pairs(batterySizes) do -- elemath(elementCost, bType.fab.element or {}, bSize.matMult) -- elemath(elementCost, bTier.fab.element or {}, bSize.matMult) -- elemath(metalCost, bType.fab.metal or {}, bSize.matMult) -- elemath(metalCost, bTier.fab.metal or {}, bSize.matMult) local fab = bType.fab + bTier.fab + bSize.fab + starlit.type.fab { element = {copper = 10, silicon = 5}; } local baseID = string.format('battery_%s_%s_%s', bTypeName, bTierName, bSizeName) local id = 'starlit_electronics:'..baseID local name = string.format('%s %s Battery', bTier.name, bType.name) if bSize.name then name = bSize.name .. ' ' .. name end local function batStat(s) if s == 'size' then --return bType.fab[s] * (bTier.fab[s] or 1) * (bSize.fab[s] or 1) return fab.size and fab.size.print or 1 else return bType[s] * (bTier[s] or 1) * (bSize[s] or 1) end end local swID = 'starlit_electronics:schematic_'..baseID fab.reverseEngineer = { complexity = bTier.complexity * bSize.complexity * bType.complexity; sw = swID; } fab.flag = {print=true} starlit.item.battery.link(id, { name = name; desc = table.concat({ bType.desc or ''; bTier.desc or ''; bSize.desc or ''; }, ' '); fab = fab; capacity = batStat 'capacity'; dischargeRate = batStat 'dischargeRate'; leak = batStat 'leak'; decay = batStat 'decay'; }) local rare if bType.sw.rarity == 0 or bTier.sw.rarity == 0 then -- rarity is measured such that the player has a 1/r -- chance of finding a given item, or if r=0, no chance -- whatsoever (the sw must be obtained e.g. by reverse- -- engineering alien tech) rare = 0 else rare = bType.sw.rarity + bTier.sw.rarity end starlit.item.sw.link(swID, { kind = 'schematic'; name = name .. ' Schematic'; output = id; size = bType.sw.pgmSize; cost = bType.sw.cost; rarity = rare; }) E.schematicGroupLink('starlit_electronics:battery', swID) end end end ----------- -- chips -- ----------- E.sw = {} function E.sw.findSchematicFor(item) local id = ItemStack(item):get_name() print(id) local fm = minetest.registered_items[id]._starlit if not (fm and fm.fab and fm.fab.reverseEngineer) then return nil end local id = fm.fab.reverseEngineer.sw return id, starlit.item.sw.db[id] end E.chip = { file = {} } do local T,G = lib.marshal.t, lib.marshal.g -- love too reinvent unions from first principles E.chip.data = G.struct { label = T.str; uuid = T.u64; files = G.array(16, G.class(G.struct { kind = G.enum { 'sw'; -- a piece of installed software 'note'; -- a user-readable text file 'research'; -- saved RE progress 'genome'; -- for use with plant biosequencer? 'blob'; -- opaque binary blob, so 3d-pty mods can use the -- file mechanism to store arbirary data. }; drm = T.u8; -- inhibit copying name = T.str; body = T.text; }, function(file) -- enc local b = E.chip.file[file.kind].enc(file.body) return { kind = file.kind; drm = file.drm; name = file.name; body = b; } end, function(file) -- dec local f, ns = E.chip.file[file.kind].dec(file.body) file.body = f return file, ns end)); bootSlot = T.u8; -- indexes into files; 0 = no bootloader } E.chip.file.sw = G.struct { pgmId = T.str; conf = G.array(16, G.struct { key = T.str, value = T.str; }); } E.chip.file.note = G.struct { author = T.str; entries = G.array(16, G.struct { title = T.str; body = T.str; }); } E.chip.file.research = G.struct { itemId = T.str; progress = T.clamp; } E.chip.file.blob = G.struct { kind = T.str; -- MT ID that identifies a blob file type belonging to an external mod size = T.u8; -- this must be manually reported since we don't know how to evaluate it data = T.text; } function E.chip.fileSize(file) -- boy howdy if file.kind == 'blob' then return file.body.size elseif file.kind == 'note' then local sz = 0x10 + #file.body.author for _, e in pairs(file.body.entries) do sz = sz + #e.title + #e.body + 0x10 -- header overhead end return sz elseif file.kind == 'research' then local re = assert(minetest.registered_items[file.body.itemId]._starlit.fab.reverseEngineer) return starlit.item.sw.db[re.sw].size * file.body.progress elseif file.kind == 'sw' then return starlit.item.sw.db[file.body.pgmId].size elseif file.kind == 'genome' then return 0 -- TODO end end local metaKey = 'starlit_electronics:chip' function E.chip.read(chip) local m = chip:get_meta() local blob = m:get_string(metaKey) if blob and blob ~= '' then return E.chip.data.dec(lib.str.meta_dearmor(blob)) else -- prepare to format the chip return { label = ''; bootSlot = 0; uuid = math.floor(math.random(0,2^32)); files = {}; } end end function E.chip.write(chip, data) local m = chip:get_meta() m:set_string(metaKey, lib.str.meta_armor(E.chip.data.enc(data))) E.chip.update(chip) end function E.chip.fileOpen(chip, inode, fn) local c = E.chip.read(chip) if fn(c.files[inode]) then E.chip.write(chip, c) return true end return false end function E.chip.fileWrite(chip, inode, file) local c = E.chip.read(chip) c.files[inode] = file E.chip.write(chip, c) end function E.chip.usedSpace(chip, d) d = d or E.chip.read(chip) local sz = 0 for _, f in pairs(d.files) do sz = sz + E.chip.fileSize(f) end return sz end function E.chip.freeSpace(chip, d) local used = E.chip.usedSpace(chip,d) local max = assert(chip:get_definition()._starlit.chip.flash) return max - used end function E.chip.install(chip, file) -- remember to write out the itemstack after using this function! local d = E.chip.read(chip) if E.chip.freeSpace(chip, d) - E.chip.fileSize(file) >= 0 then table.insert(d.files, file) E.chip.write(chip, d) return true else return false end end end function E.chip.files(ch) local m = ch:get_meta() if not m:contains 'starlit_electronics:chip' then return nil end local data = E.chip.read(ch) local f = 0 return function() f = f + 1 return data.files[f], f end end function E.chip.describe(ch, defOnly) local def, data if defOnly then def, data = ch, {} else def = ch:get_definition() local m = ch:get_meta() if m:contains 'starlit_electronics:chip' then data = E.chip.read(ch) else data = {} defOnly = true end def = assert(def._starlit.chip) end local props = { {title = 'Clock Rate', affinity = 'info'; desc = lib.math.si('Hz', def.clockRate)}; {title = 'RAM', affinity = 'info'; desc = lib.math.si('B', def.ram)}; } if not defOnly then table.insert(props, { title = 'Free Storage', affinity = 'info'; desc = lib.math.si('B', E.chip.freeSpace(ch, data)) .. ' / ' .. lib.math.si('B', def.flash); }) local swAffMap = { schematic = 'schematic'; suitPower = 'ability'; driver = 'driver'; } for i, e in ipairs(data.files) do local aff = 'neutral' local name = e.name local disabled = false if e.kind == 'sw' then for _,cf in pairs(e.body.conf) do if cf.key == 'disable' and cf.value == 'yes' then disabled = true break end end local sw = starlit.item.sw.db[e.body.pgmId] aff = swAffMap[sw.kind] or 'good' if name == '' then name = sw.name end end name = name or '<???>' table.insert(props, disabled and { title = name; affinity = aff; desc = '<off>'; } or { --title = name; affinity = aff; desc = name; }) end else table.insert(props, { title = 'Flash Storage', affinity = 'info'; desc = lib.math.si('B', def.flash); }) end return starlit.ui.tooltip { title = data.label and data.label~='' and string.format('<%s>', data.label) or def.name; color = lib.color(.6,.6,.6); desc = def.desc; props = props; }; end function E.chip.update(chip) chip:get_meta():set_string('description', E.chip.describe(chip)) end starlit.item.chip.foreach('starlit_electronics:chip-gen', {}, function(id, def) minetest.register_craftitem(id, { short_description = def.name; description = E.chip.describe(def, true); inventory_image = def.img or 'starlit-item-chip.png'; groups = {chip = 1}; _starlit = { fab = def.fab; chip = def; }; }) end) -- in case other mods want to define their own tiers E.chip.tiers = lib.registry.mk 'starlit_electronics:chipTiers' E.chip.tiers.meld { -- GP chips tiny = {name = 'Tiny Chip', clockRate = 512e3, flash = 4096, ram = 1024, powerEfficiency = 1e9, size = 1}; small = {name = 'Small Chip', clockRate = 128e6, flash = 512e6, ram = 512e6, powerEfficiency = 1e8, size = 3}; med = {name = 'Chip', clockRate = 1e9, flash = 4e9, ram = 4e9, powerEfficiency = 1e7, size = 6}; large = {name = 'Large Chip', clockRate = 2e9, flash = 8e9, ram = 8e9, powerEfficiency = 1e6, size = 8}; -- specialized chips compute = {name = 'Compute Chip', clockRate = 4e9, flash = 24e6, ram = 64e9, powerEfficiency = 1e8, size = 4}; data = {name = 'Data Chip', clockRate = 128e3, flash = 2e12, ram = 32e3, powerEfficiency = 1e5, size = 4}; lp = {name = 'Low-Power Chip', clockRate = 128e6, flash = 64e6, ram = 1e9, powerEfficiency = 1e10, size = 4}; carbon = {name = 'Carbon Chip', clockRate = 64e6, flash = 32e6, ram = 2e6, powerEfficiency = 2e9, size = 2, circ='carbon'}; } E.chip.tiers.foreach('starlit_electronics:genChips', {}, function(id, t) id = t.id or string.format('%s:chip_%s', minetest.get_current_modname(), id) local circMat = t.circ or 'silicon'; starlit.item.chip.link(id, { name = t.name; clockRate = t.clockRate; flash = t.flash; ram = t.ram; powerEfficiency = t.powerEfficiency; -- cycles per joule fab = { flag = { silicompile = true; }; time = { silicompile = t.size * 24*60; }; cost = { energy = 50e3 + t.size * 15e2; }; element = { [circMat] = 50 * t.size; copper = 30; gold = 15; }; }; }) end) function E.chip.findBest(test, ...) local chip, bestFitness for id, c in pairs(starlit.item.chip.db) do local fit, fitness = test(c, ...) if fit and (bestFitness == nil or fitness > bestFitness) then chip, bestFitness = id, fitness end end return chip, starlit.item.chip.db[chip], bestFitness end function E.chip.findForStorage(sz) return E.chip.findBest(function(c) return c.flash >= sz, -math.abs(c.flash - sz) end) end function E.chip.sumCompute(chips) local c = { cycles = 0; ram = 0; flashFree = 0; powerEfficiency = 0; } local n = 0 for _, e in pairs(chips) do n = n + 1 if not e:is_empty() then local ch = e:get_definition()._starlit.chip c.cycles = c.cycles + ch.clockRate c.ram = c.ram + ch.clockRate c.flashFree = c.flashFree + E.chip.freeSpace(e) c.powerEfficiency = c.powerEfficiency + ch.powerEfficiency end end if n > 0 then c.powerEfficiency = c.powerEfficiency / n end return c end E.chip.fileHandle = lib.class { __name = 'starlit_electronics:chip.fileHandle'; construct = function(chip, inode) -- stack, int --> fd return { chip = chip, inode = inode } end; __index = { read = function(self) local dat = E.chip.read(self.chip) return dat.files[self.inode] end; write = function(self,data) -- print('writing', self.chip, self.inode) return E.chip.fileWrite(self.chip, self.inode, data) end; erase = function(self) local dat = E.chip.read(self.chip) table.remove(dat.files, self.inode) E.chip.write(self.chip, dat) self.inode = nil end; open = function(self,fn) return E.chip.fileOpen(self.chip, self.inode, fn) end; }; } function E.chip.usableSoftware(chips,pgm) local comp = E.chip.sumCompute(chips) local r = {} local unusable = {} local sw if pgm then if type(pgm) == 'string' then pgm = {starlit.item.sw.db[pgm]} end sw = pgm else sw = {} for i, e in ipairs(chips) do if (not e:is_empty()) and minetest.get_item_group(e:get_name(), 'chip') ~= 0 then for fl, inode in E.chip.files(e) do if fl.kind == 'sw' then local s = starlit.item.sw.db[fl.body.pgmId] table.insert(sw, { sw = s, chip = e, chipSlot = i; file = fl, inode = inode; }) end end end end end for _, s in pairs(sw) do if s.sw.cost.ram <= comp.ram then table.insert(r, { sw = s.sw; chip = s.chip, chipSlot = s.chipSlot; file = s.file; fd = E.chip.fileHandle(s.chip, s.inode); speed = s.sw.cost.cycles / comp.cycles; powerCost = s.sw.cost.cycles / comp.powerEfficiency; comp = comp; }) else table.insert(unusable, { sw = s.sw; chip = s.chip; ramNeeded = s.sw.cost.ram - comp.ram; }) end end return r, unusable end starlit.include 'sw' |
Added mods/starlit-electronics/mod.conf version [c7d10b71e3].
> > > > |
1 2 3 4 |
name = starlit_electronics title = starlit electronics description = basic electronic components and logic depends = starlit |
Added mods/starlit-electronics/sw.lua version [288b4b5859].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
-- [ʞ] sw.lua -- ~ lexi hale <lexi@hale.su> -- 🄯 EUPL v1.2 -- ? ------------------------------- -- basic suit nano abilities -- ------------------------------- local function shredder(prop) local function getItemsForFab(fab) local elt if fab then elt = fab:elementalize() else elt = {} end local items = {} if elt.element then for k,v in pairs(elt.element) do local st = ItemStack { name = starlit.world.material.element.db[k].form.element; count = v; } table.insert(items, st) end end return items end return function(user, ctx) local function cleanup() user.action.prog.shred = nil if user.action.sfx.shred then minetest.sound_fade(user.action.sfx.shred, 1, 0) user.action.sfx.shred = nil end if user.action.fx.shred then user.action.fx.shred.abort() end end if user.action.tgt.type ~= 'node' then return end local what = user.action.tgt.under if what == nil or user.entity:get_pos():distance(what) > prop.range then cleanup() return false end local shredTime = 1.0 local soundPitch = 1.0 -- TODO local pdraw = prop.powerDraw or 0 local node = minetest.get_node(what) local nd = minetest.registered_nodes[node.name] local elt, fab, vary if nd._starlit then fab = nd._starlit.recover or nd._starlit.fab vary = nd._starlit.recover_vary end if fab then if fab.flag then if fab.flag.unshreddable then cleanup() return false -- TODO error beep end end shredTime = fab.time and fab.time.shred or shredTime -- FIXME if fab.cost and fab.cost.shredPower then pdraw = pdraw * fab.cost.shredPower end end local maxW = user:getSuit():maxPowerUse() if maxW < pdraw then shredTime = shredTime * (pdraw/maxW) pdraw = maxW end if ctx.how.state == 'prog' then local pdx = pdraw * ctx.how.delta local p = user:suitDrawCurrent(pdx, ctx.how.delta, {kind='nano',label='Shredder'}, pdx) if p < pdx then cleanup() return false elseif not user.action.prog.shred then cleanup() -- kill danglers -- begin user.action.prog.shred = 0 user.action.sfx.shred = minetest.sound_play('starlit-nano-shred', { object = user.entity; max_hear_distance = prop.range*2; loop = true; pitch = soundPitch; }) user.action.fx.shred = starlit.fx.nano.shred(user, what, prop, shredTime, node) else user.action.prog.shred = user.action.prog.shred + ctx.how.delta or 0 end --print('shred progress: ', user.action.prog.shred) if user.action.prog.shred >= shredTime then if minetest.dig_node(what) then --print('shred complete') user:suitSound 'starlit-success' if fab then local vf = fab if vary then local rng = (starlit.world.seedbank+0xa891f62)[minetest.hash_node_position(what)] vf = vf + vary(rng, {}) end local items = getItemsForFab(vf) for i, it in ipairs(items) do user:give(it) end end else user:suitSound 'starlit-error' end cleanup() end elseif ctx.how.state == 'halt' then cleanup() end return true end end starlit.item.sw.link('starlit_electronics:shred', { name = 'NanoShred'; kind = 'suitPower', powerKind = 'active'; desc = 'An open-source program used in its various forks and iterations all across human-inhabited space and beyond. Rumored to contain fragments of code stolen from the nanoware of the Greater Races by an elusive infoterrorist.'; size = 500e3; cost = { cycles = 100e6; ram = 500e6; }; run = shredder{range=2, powerDraw=200}; }) starlit.item.sw.link('starlit_electronics:compile_commune', { name = 'Compile Matter'; kind = 'suitPower', powerKind = 'direct'; desc = "A basic suit matter compiler program, rather slow but ruthlessly optimized for power- and memory-efficiency by some of the Commune's most fanatic coders."; size = 700e3; cost = { cycles = 300e6; ram = 2e9; }; ui = 'starlit:compile-matter-component'; run = function(user, ctx) end; }) starlit.item.sw.link('starlit_electronics:compile_block_commune', { name = 'Compile Block'; kind = 'suitPower', powerKind = 'active'; desc = "An advanced suit matter compiler program, capable of printing complete devices and structure parts directly into the world."; size = 5e6; cost = { cycles = 700e6; ram = 4e9; }; ui = 'starlit:compile-matter-block'; run = function(user, ctx) end; }) do local J = starlit.store.compilerJob starlit.item.sw.link('starlit_electronics:driver_compiler_commune', { name = 'Matter Compiler'; kind = 'driver'; desc = "A driver for a standalone matter compiler, suitable for building larger components than your suit alone can handle."; size = 850e3; cost = { cycles = 400e6; ram = 2e9; }; ui = 'starlit:device-compile-matter-component'; run = function(user, ctx) end; bgProc = function(user, ctx, interval, runState) if runState.flags.compiled == true then return false end -- only so many nanides to go around runState.flags.compiled = true local time = minetest.get_gametime() local cyclesLeft = ctx.comp.cycles * interval for id, e in ipairs(ctx.file.body.conf) do if e.key == 'job' then local t = J.dec(e.value) local remove = false local r = starlit.item.sw.db[t.schematic] if not r then -- bad schematic remove = true else local ccost = ctx.sw.cost.cycles + r.cost.cycles local tcost = ccost / cyclesLeft t.progress = t.progress + (1/tcost)*interval cyclesLeft = cyclesLeft - ccost*interval if t.progress >= 1 then -- complete remove = true local i = starlit.item.mk(r.output, { how = 'print'; user = user; -- for suit compiler = { node = ctx.compiler; -- for device sw = ctx.sw; install = ctx.fd; }; schematic = r; }) ctx.giveItem(i) end end if remove then table.remove(ctx.file.body.conf, id) else e.value = J.enc(t) end if not cyclesLeft > 0 then break end end end ctx.saveConf() end; }) end local function pasv_heal(effect, energy, lvl, pgmId) return function(user, ctx, interval, runState) if runState.flags.healed == true then return false end -- competing nanosurgical programs?? VERY bad idea runState.flags.healed = true local amt, f = user:effectiveStat 'health' local st = user:getSuit():powerState() if (st == 'on' and f < lvl) or (st == 'powerSave' and f < math.min(lvl,0.25)) then local maxPower = energy*interval local p = user:suitDrawCurrent(maxPower, interval, { id = 'heal'; src = 'suitPower'; pgmId = pgmId; healAmount = effect; }) if p > 0 then local heal = (p/maxPower) * ctx.speed * effect*interval --user:statDelta('health', math.max(1, heal)) starlit.fx.nano.heal(user, {{player=user.entity}}, heal, 1) return true end end return false -- program did not run end; end starlit.item.sw.link('starlit_electronics:nanomed', { name = 'NanoMed'; kind = 'suitPower', powerKind = 'passive'; desc = 'Repair of the body is a Commune specialty, and their environment suits all come equipped with highly sophisticated nanomedicine suites, able to repair even the most grievous of wounds given sufficient energy input and time.'; size = 2e9; cost = { cycles = 400e6; ram = 3e9; }; run = pasv_heal(2, 20, 1); }) starlit.item.sw.link('starlit_electronics:autodoc_deluxe', { name = 'AutoDoc Deluxe'; kind = 'suitPower', powerKind = 'passive'; desc = "A flagship offering of the Excellence Unyielding nanoware division, AutoDoc Deluxe has been the top-rated nanocare package in the Celestial Shores Province for six centuries and counting. Every chip includes our comprehensive database of illnesses, prosyn schematics, and organ repair techniques, with free over-the-ether updates guaranteed for ten solariads from date of purchase! When professional medical care just isn't an option, 9/10 doctors recommend Excellence Unyielding AutoDoc Deluxe! The remaining doctor was bribed by our competitors."; size = 1e9; cost = { cycles = 700e6; ram = 1e9; }; run = pasv_heal(4, 50, .7); }) |
Added mods/starlit-material/elements.lua version [5740080b38].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 |
local lib = starlit.mod.lib local W = starlit.world local M = W.material M.element.meld { hydrogen = { name = 'hydrogen', sym = 'H', n = 1; gas = true; color = lib.color(1,0.8,.3); }; beryllium = { name = 'Beryllium', sym = 'Be', n = 4; metal = true; -- rare emerald-stuff color = lib.color(0.2,1,0.2); }; oxygen = { name = 'oxygen', sym = 'O', n = 8; gas = true; color = lib.color(.2,1,.2); }; carbon = { name = 'carbon', sym = 'C', n = 6; color = lib.color(.7,.2,.1); }; silicon = { name = 'silicon', sym = 'Si', n = 14; metal = true; -- can be forged into an ingot color = lib.color(.6,.6,.4); }; potassium = { name = 'potassium', sym = 'K', n = 19; -- potassium is technically a metal but it's so soft -- it can be easily nanoworked without high temps, so -- ingots make no sense color = lib.color(1,.8,0.1); }; calcium = { name = 'calcium', sym = 'Ca', n = 20; metal = true; color = lib.color(1,1,0.7); }; aluminum = { name = 'aluminum', sym = 'Al', n = 13; metal = true; color = lib.color(0.9,.95,1); }; iron = { name = 'iron', sym = 'Fe', n = 26; metal = true; color = lib.color(.3,.3,.3); }; copper = { name = 'copper', sym = 'Cu', n = 29; metal = true; color = lib.color(.8,.4,.1); }; lithium = { name = 'lithium', sym = 'Li', n = 3; -- i think lithium is considered a metal but we don't mark it as -- one here because making a 'lithium ingot' is insane (even possible?) color = lib.color(1,0.8,.3); }; titanium = { name = 'titanium', sym = 'Ti', n = 22; metal = true; color = lib.color(.7,.7,.7); }; vanadium = { name = 'vanadium', sym = 'V', n = 23; metal = true; color = lib.color(.3,0.5,.3); }; xenon = { name = 'xenon', sym = 'Xe', n = 54; gas = true; color = lib.color(.5,.1,1); }; argon = { name = 'argon', sym = 'Ar', n = 18; gas = true; color = lib.color(0,0.1,.9); }; osmium = { name = 'osmium', sym = 'Os', n = 76; metal = true; color = lib.color(.8,.1,1); }; iridium = { name = 'iridium', sym = 'Ir', n = 77; metal = true; color = lib.color(.8,0,.5); }; technetium = { name = 'technetium', sym = 'Tc', n = 43; desc = 'Prized by the higher Powers for subtle interactions that elude mere human scholars, technetium is of particular use in nuclear nanobatteries.'; metal = true; color = lib.color(.2,0.2,1); }; uranium = { name = 'uranium', sym = 'U', n = 92; desc = 'A weak but relatively plentiful nuclear fuel.'; metal = true; color = lib.color(.2,.7,0); }; thorium = { name = 'thorium', sym = 'Th', n = 90; desc = 'A frighteningly powerful nuclear fuel.'; metal = true; color = lib.color(.7,.3,.1); }; silver = { name = 'silver', sym = 'Ag', n = 47; metal = true; color = lib.color(.7,.7,.8); }; gold = { name = 'gold', sym = 'Au', n = 79; metal = true; color = lib.color(1,.8,0); }; } |
Added mods/starlit-material/init.lua version [fffa847df5].
> > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
local lib = starlit.mod.lib local M = { canisterSizes = lib.registry.mk 'starlit_material:canister-size'; } M.canisterSizes.foreach('starlit_material:canister_link', {}, function(id, sz) starlit.item.canister.link(minetest.get_current_modname() .. ':canister_' .. id, { name = sz.name; slots = sz.slots; vol = 0.1; -- too big for suit? desc = sz.desc; }) end) M.canisterSizes.meld { tiny = {name = 'Tiny Canister', slots = 1, vol = 0.05}; small = {name = 'Small Canister', slots = 3, vol = 0.2}; mid = {name = 'Canister', slots = 5, vol = 0.5}; large = {name = 'Large Canister', slots = 10, vol = 1.0}; storage = {name = 'Storage Canister', slots = 50, vol = 5.0}; } starlit.include 'elements' |
Added mods/starlit-material/mod.conf version [e33ae98ab9].
> > > > |
1 2 3 4 |
name = starlit_material title = starlit materials description = defines the raw materials and alloys used in printing depends = starlit |
Added mods/starlit-scenario/init.lua version [bf764e9a62].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 |
local lib = starlit.mod.lib local scenario = starlit.world.scenario local function makeChip(label, schem, sw) local E = starlit.mod.electronics local files = {} local sz = 0 for _, e in ipairs(schem) do local p = E.sw.findSchematicFor(e[1]) if p then local file = { kind = 'sw', name = '', drm = e[2]; body = {pgmId = p}; } table.insert(files, file) sz = sz + E.chip.fileSize(file) end end for _, e in ipairs(sw) do local file = { kind = 'sw', name = '', drm = e[2]; body = {pgmId = e[1]}; } table.insert(files, file) sz = sz + E.chip.fileSize(file) end local chip = ItemStack(assert(E.chip.findBest(function(c) return c.flash >= sz, c.ram + c.clockRate end))) local r = E.chip.read(chip) r.label = label r.files = files E.chip.write(chip, r) return chip end local chipLibrary = { compendium = makeChip('The Gentleman Adventurer\'s Compleat Wilderness Compendium', { {'starlit_electronics:battery_chemical_imperial_small', 0}; }, { {'starlit_electronics:shred', 0}; --{'starlit_electronics:compile_empire', 0}; {'starlit_electronics:autodoc_deluxe', 1}; --{'starlit_electronics:driver_compiler_empire', 0}; }); survivalware = makeChip('Emergency Survivalware', { {'starlit_electronics:battery_chemical_commune_small', 0}; }, { {'starlit_electronics:shred', 0}; {'starlit_electronics:compile_commune', 0}; {'starlit_electronics:nanomed', 0}; {'starlit_electronics:driver_compiler_commune', 0}; }); misfortune = makeChip("Sold1er0fMisf0rtune TOP Schematic Crackz REPACK", { {'starlit_electronics:battery_chemical_usukwinya_mid', 0}; {'starlit_electronics:battery_hybrid_imperial_small', 0}; -- ammunition }, {}); } local battery = function(name) local s = ItemStack(name) starlit.mod.electronics.battery.setChargeF(s, 1.0) return s end table.insert(scenario, { id = 'starlit_scenario:imperialExpat'; name = 'Imperial Expat'; desc = "Hoping to escape a miserable life deep in the grinding gears of the capitalist machine for the bracing freedom of the frontier, you sought entry as a colonist to the new Commune world of Thousand Petal. Fate -- which is to say, terrorists -- intervened, and you wound up stranded on Farthest Shadow with little more than the nanosuit on your back, ship blown to tatters and your soul thoroughly mauled by the explosion of a twisted alien artifact -- which SOMEONE neglected to inform you your ride would be carrying.\nAt least you got some nifty psionic powers out of this whole clusterfuck. Hopefully they're safe to use."; species = 'human'; speciesVariant = 'female'; soul = { externalChannel = true; -- able to touch other souls in the spiritual realm physicalChannel = true; -- able to extend influence into physical realm damage = 1; }; social = { empire = 'workingClass'; commune = 'metic'; }; startingItems = { suit = ItemStack('starlit_suit:suit_survival_commune'); suitBatteries = {battery 'starlit_electronics:battery_carbon_commune_small'}; suitChips = { chipLibrary.survivalware; -- you didn't notice it earlier, but your Commune environment suit -- came with this chip already plugged in. it's apparently true -- what they say: the Commune is always prepared for everything. -- E V E R Y T H I N G. }; suitGuns = {}; suitAmmo = {}; suitCans = { ItemStack('starlit_material:canister_small'); }; carry = { chipLibrary.compendium; -- you bought this on a whim before you left the Empire, and -- just happened to still have it on your person when everything -- went straight to the Wild Gods' privy }; }; }) table.insert(scenario, { id = 'starlit_scenario:gentlemanAdventurer'; -- Othar Tryggvasson, name = 'Gentleman Adventurer'; desc = "Tired of the same-old-same-old, sick of your idiot contemporaries, exasperated with the shallow soul-rotting luxury of life as landless lordling, and earnestly eager to enrage your father, you resolved to see the Reach in all her splendor. Deftly evading the usual tourist traps, you finagled your way into the confidence of the Commune ambassador with a few modest infusions of Father's money -- now *that* should pop his monocle -- and secured yourself a seat on a ride to their brand-new colony at Thousand Petal. How exciting -- a genuine frontier outing!"; species = 'human'; speciesVariant = 'male'; soul = { externalChannel = true; physicalChannel = true; damage = 1; }; social = { empire = 'aristo'; }; startingItems = { suit = 'starlit_suit:suit_survival_imperial'; suitBatteries = {battery 'starlit_electronics:battery_supercapacitor_imperial_mid'}; suitChips = { chipLibrary.compendium; -- Mother, bless her soul, simply insisted on buying you this as a parting -- gift. "it's dangerous out there for a young man," she proclaimed as -- if she had profound firsthand experience of the matter. mindful of the -- husband she endures, you suffered to humor her, and made a big show of -- installing it your brand-new nanosuit before you fled the family seat. }; suitGuns = {}; suitAmmo = {}; suitCans = { ItemStack('starlit_material:canister_mid'); }; carry = {}; }; }) table.insert(scenario, { -- you start out with strong combat abilities but weak engineering, -- and will have to scavenge wrecks to find basic crafting gear id = 'starlit_scenario:terroristTagalong'; name = 'Terrorist Tagalong'; desc = "It turns out there's a *reason* Crown jobs pay so well."; species = 'human'; speciesVariant = 'female'; social = { empire = 'lowlife'; commune = 'mostWanted'; underworldConnections = true; }; soul = { externalChannel = true; physicalChannel = true; damage = 2; -- closer to the blast }; startingItems = { suit = 'starlit_suit:suit_combat_imperial'; suitBatteries = { ItemStack('starlit_electronics:battery_supercapacitor_imperial_small'); ItemStack('starlit_electronics:battery_chemical_imperial_large'); }; suitGuns = {}; suitAmmo = {}; carry = {}; }; suitChips = {chipLibrary.misfortune}; }) table.insert(scenario, { id = 'starlit_scenario:tradebirdBodyguard'; name = 'Tradebird Bodyguard'; desc = "You've never understood why astropaths of all people *insist* on bodyguards. This one could probably make hash of a good-sized human batallion, if her feathers were sufficiently ruffled. Perhaps it's a status thing. Whatever the case, it's easy money.\nAt least, it was supposed to be.'"; species = 'usukwinya'; speciesVariant = 'male'; soul = { damage = 0; -- Inyukiriku and her entourage fled the ship when she sensed something serious was about to go down. lucky: the humans only survived because their souls were closed off to the Physical. less luckily, the explosion knocked your escape pod off course and the damn astropath was too busy saving her own skin to come after you externalChannel = true; -- usukwinya are already psionic physicalChannel = true; -- usukwinya are Starlit }; startingItems = { suit = 'starlit_suit:suit_combat_usukwinya'; suitBatteries = { ItemStack('starlit_electronics:battery_hybrid_usukwinya_mid'); }; suitGuns = {}; suitChips = {}; suitAmmo = {}; carry = {}; }; }) |
Added mods/starlit-scenario/mod.conf version [8a1ae19f59].
> > > > > |
1 2 3 4 5 |
name = starlit_scenario title = starlit scenarios description = built-in scenarios for Starsoul depends = starlit, starlit_suit, starlit_electronics, starlit_building, starlit_material # be sure to add any mods from which you list new starting items! |
Added mods/starlit-secrets/init.lua version [0a228a51be].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
---------------------------------------------------- ------------- CONTROLLED INFORMATION -------------- ---------------------------------------------------- -- THE INFORMATION CONTAINED IN THIS DOCUMENT IS -- -- SUBJECT TO RESTRAINT OF TRANSMISSION PER THE -- -- TERMS OF THE COMMUNE CHARTER INFOSECURITY -- -- PROVISION. IF YOU ARE NOT AUTHORIZED UNDER THE -- -- AEGIS OF THE APPROPRITE CONTROLLING AUTHORITY, -- -- CLOSE THIS DOCUMENT IMMEDIATELY AND REPORT THE -- -- SECURITY BREACH TO YOUR DESIGNATED INFORMATION -- -- HYGIENE OVERSEER OR FACE CORRECTIVE DISCIPLINE -- ---------------------------------------------------- local lib = starlit.mod.lib local sec = {} starlit.mod.secrets = sec sec.index = lib.registry.mk 'starlit_secrets:secret' --[==[ a secret is a piece of information that is made available for review once certain conditions are met. despite the name, it doesn't necessarily have to be secret -- it could include e.g. journal entries about a character's background. a secret is defined in the following manner: { title = the string that appears in the UI stages = { { prereqs = { {kind = 'fact', id = 'starlit:terroristEmployer'} {kind = 'item', id = 'starlit_electronic:firstbornDoomBong'} {kind = 'background', id = 'starlit:terroristTagalong'} } body = { 'the firstborn smonked hella weed'; }; -- body can also be a function(user,secret) } } } TODO would it be useful to impl horn clauses and a general fact database? is that level of flexibility meaningful? or are simply flags better a secret can be a single piece of information predicated on a fact, in which case the secret and fact should share the same ID. the ID should be as non-indicative as possible to avoid spoilers for devs of unrelated code. a secret can also be manually unlocked e.g. by using an item ]==]-- function sec.prereqCheck(user, pr) end |
Added mods/starlit-secrets/mod.conf version [74f79378c7].
> > > > |
1 2 3 4 |
name = starlit_secrets title = starlit secrets description = TS//NOFORN depends = starlit |
Added mods/starlit-suit/init.lua version [078f484c1f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 |
local lib = starlit.mod.lib local fab = starlit.type.fab local facDescs = { commune = { survival = { suit = 'A light, simple, bare-bones environment suit that will provide heating, cooling, and nanide support to a stranded cosmonaut'; cc = 'The Survival Suit uses compact thermoelectrics to keep the wearer perfectly comfortable in extremes of heat or cold. It makes up for its heavy power usage with effective insulation that substantially reduces any need for climate control.'; }; engineer = { suit = 'A lightweight environment suit designed for indoor work, the Commune\'s Engineer Suit boasts advanced nanotech capable of constructing objects in place.'; cc = 'The Engineer Suit is designed for indoor work. Consequently, it features only a low-power thermoelectric cooler meant to keep its wearer comfortable during strenuous work.'; }; combat = { suit = 'A military-grade suit with the latest Commune technology. Designed for maximum force multiplication, the suit has dual weapon hardpoints and supports a gargantuan power reserve. Its nanotech systems are specialized for tearing through obstacles, but can also be used to manufacturer ammunition in a pinch.'; cc = 'This Combat Suit uses electrothermal cooling to keep an active soldier comfortable and effective, as well as conventional heating coils to enable operation in hostile atmospheres.'; }; }; } starlit.world.tier.foreach('starlit:suit-gen', {}, function(tid, t) local function hasTech(tech) return starlit.world.tier.tech(tid, tech) end if not hasTech 'suit' then return end -- TODO tier customization -- local function fabsum(f) return starlit.world.tier.fabsum(tid, 'suit') end local function fabReq(sz, days) local tierMatBase = ( (fabsum 'electric' * 4) + (fabsum 'basis' + fabsum 'suit') * sz ) local b = tierMatBase + fab { -- universal suit requirements time = { print = 60*60*24 * days }; size = { printBay = sz }; } b.flag = lib.tbl.set('print'); return b end local function facDesc(s, t) local default = 'A protective nanosuit' -- FIXME if not facDescs[tid] then return default end if not facDescs[tid][s] then return default end if not facDescs[tid][s][t] then return default end return facDescs[tid][s][t] end starlit.item.suit.link('starlit_suit:suit_survival_' .. tid, { name = t.name .. ' Survival Suit'; desc = facDesc('survival','suit'); fab = fabReq(1, 2.2) + fab { }; tex = { plate = { id = 'starlit-suit-survival-plate'; tint = lib.color {hue = 210, sat = .5, lum = .5}; }; lining = { id = 'starlit-suit-survival-lining'; tint = lib.color {hue = 180, sat = .2, lum = .7}; }; }; tints = {'suit_plate', 'suit_lining'}; temp = { desc = facDesc('survival','cc'); maxHeat = 0.7; -- can produce a half-degree Δ per second maxCool = 0.5; heatPower = 50; -- 50W coolPower = 50/t.efficiency; insulation = 0.5; -- prevent half of heat loss }; protection = { rad = 0.7; -- blocks 70% of ionizing radiation }; slots = { canisters = 1; batteries = math.ceil(math.max(1, t.power/2)); chips = 3; guns = 0; ammo = 0; }; nano = { compileSpeed = 0.1 * t.efficiency; shredSpeed = 0.1 * t.power; fabSizeLimit = 0.6; -- 60cm }; }) starlit.item.suit.link('starlit_suit:suit_engineer_' .. tid, { name = t.name .. ' Engineer Suit'; desc = facDesc('engineer','suit'); tex = { plate = { id = 'starlit-suit-survival-plate'; tint = lib.color {hue = 0, sat = .5, lum = .7}; }; }; tints = {'suit_plate', 'suit_lining'}; fab = fabReq(.8, 7) + fab { }; temp = { desc = facDesc('engineer','cc'); maxHeat = 0; maxCool = 0.2; heatPower = 0; coolPower = 10 / t.efficiency; insulation = 0.1; -- no lining }; slots = { canisters = 2; batteries = 2; chips = 6; guns = 0; ammo = 0; }; compat = { maxBatterySize = 0.10 * t.power; -- 10cm }; protection = { rad = 0.1; -- blocks 10% of ionizing radiation }; nano = { compileSpeed = 1 * t.efficiency; shredSpeed = 0.7 * t.power; fabSizeLimit = 1.5; -- 1.5m (enables node compilation) }; }) if hasTech 'suitCombat' then starlit.item.suit.link('starlit_suit:suit_combat_' .. tid, { name = t.name .. ' Combat Suit'; desc = facDesc('combat','suit'); fab = fabReq(1.5, 14) + fab { metal = {iridium = 1e3}; }; tex = { plate = { id = 'starlit-suit-survival-plate'; tint = lib.color {hue = 0, sat = 0, lum = 0}; }; lining = { id = 'starlit-suit-survival-lining'; tint = lib.color {hue = 180, sat = .5, lum = .3}; }; }; tints = {'suit_plate', 'suit_lining'}; slots = { canisters = 1; batteries = math.ceil(math.max(3, 8*(t.power/2))); chips = 5; guns = 2; ammo = 1; }; compat = { maxBatterySize = 0.10 * t.power; -- 10cm }; temp = { desc = facDesc('combat','cc'); maxHeat = 0.3; maxCool = 0.6; heatPower = 20 / t.efficiency; coolPower = 40 / t.efficiency; insulation = 0.2; }; protection = { rad = 0.9; -- blocks 90% of ionizing radiation }; nano = { compileSpeed = 0.05; shredSpeed = 2 * t.power; fabSizeLimit = 0.3; -- 30cm }; }) end end) |
Added mods/starlit-suit/mod.conf version [785651d288].
> > > > |
1 2 3 4 |
name = starlit_suit title = starlit environment suit description = defines the environment suits available in starlit depends = starlit, starlit_electronics |
Added mods/starlit/container.lua version [38768ee087].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 |
-- a container item defines a 'container' structure listing its -- inventories and their properties. a container object is created -- in order to interact with a container local lib = starlit.mod.lib starlit.item.container = lib.class { __name = 'starlit:container'; construct = function(stack, inv, def) local T,G = lib.marshal.t, lib.marshal.g local cdef = stack:get_definition()._starlit.container; local sd = {} for k,v in pairs(cdef.list) do sd[k] = { key = v.key; type = T.inventoryList; } end return { stack = stack, inv = inv, pdef = def, cdef = cdef; store = lib.marshal.metaStore(sd)(stack); } end; __index = { slot = function(self, id) return string.format("%s_%s", self.pdef.pfx, id) end; clear = function(self) -- initialize or empty the metadata self:update(function() for k,v in pairs(self.cdef.list) do if v.sz > 0 then self.store.write(k, {}) end end end) end; list = function(self, k) return self.store.read(k) end; read = function(self) local lst = {} for k,v in pairs(self.cdef.list) do if v.sz > 0 then lst[k] = self:list(k) end end return lst end; pull = function(self) -- align the inventories with the metadata for k,v in pairs(self.cdef.list) do if v.sz > 0 then local stacks = self:list(k) local sid = self:slot(k) self.inv:set_size(sid, v.sz) self.inv:set_list(sid, stacks) end end end; update = function(self, fn) local old = ItemStack(self.stack) if fn then fn() end if self.cdef.handle then self.cdef.handle(self.stack, old) end end; push = function(self) -- align the metadata with the inventories self:update(function() for k,v in pairs(self.cdef.list) do if v.sz > 0 then local sid = self:slot(k) local lst = self.inv:get_list(sid) self.store.write(k, lst) end end end) end; drop = function(self) -- remove the inventories from the node/entity for k,v in pairs(self.cdef.list) do local sid = self:slot(k) self.inv:set_size(sid, 0) end end; slotAccepts = function(self, lst, slot, stack) end; }; } function starlit.item.container.dropPrefix(inv, pfx) local lists = inv:get_lists() for k,v in pairs(lists) do if #k > #pfx then if string.sub(k, 1, #pfx + 1) == pfx .. '_' then inv:set_size(k, 0) end end end end |
Added mods/starlit/effect.lua version [eed7790b6a].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
-- ported from sorcery/spell.lua, hence the lingering refs to "magic" -- -- this file is used to track active effects, for the purposes of metamagic -- like disjunction. a "effect" is a table consisting of several properties: -- a "disjoin" function that, if present, is called when the effect is -- abnormally interrupted, a "terminate" function that calls when the effect -- completes, a "duration" property specifying how long the effect lasts in -- seconds, and a "timeline" table that maps floats to functions called at -- specific points during the function's activity. it can also have a -- 'delay' property that specifies how long to wait until the effect sequence -- starts; the effect is however still vulnerable to disjunction during this -- period. there can also be a sounds table that maps timepoints to sounds -- the same way timeline does. each value should be a table of form {sound, -- where}. the `where` field may contain one of 'pos', 'caster', 'subjects', or -- a vector specifying a position in the world, and indicate where the sound -- should be played. by default 'caster' and 'subjects' sounds will be attached -- to the objects they reference; 'attach=false' can be added to prevent this. -- by default sounds will be faded out quickly when disjunction occurs; this -- can be controlled by the fade parameter. -- -- effects can have various other properties, for instance 'disjunction', which -- when true prevents other effects from being cast in its radius while it is -- still in effect. disjunction is absolute; there is no way to overwhelm it. -- -- the effect also needs at least one of "anchor", "subjects", or "caster". -- * an anchor is a position that, in combination with 'range', specifies the area -- where a effect is in effect; this is used for determining whether it -- is affected by a disjunction that incorporates part of that position -- * subjects is an array of individuals affected by the effect. when -- disjunction is cast on one of them, they will be removed from the -- table. each entry should have at least a 'player' field; they can -- also contain any other data useful to the effect. if a subject has -- a 'disjoin' field it must be a function called when they are removed -- from the list of effect targets. -- * caster is the individual who cast the effect, if any. a disjunction -- against their person will totally disrupt the effect. local log = starlit.logger 'effect' local lib = starlit.mod.lib -- FIXME saving object refs is iffy, find a better alternative starlit.effect = { active = {} } local get_effect_positions = function(effect) local effectpos if effect.anchor then effectpos = {effect.anchor} elseif effect.attach then if effect.attach == 'caster' then effectpos = {effect.caster:get_pos()} elseif effect.attach == 'subjects' or effect.attach == 'both' then if effect.attach == 'both' then effectpos = {effect.caster:get_pos()} else effectpos = {} end for _,s in pairs(effect.subjects) do effectpos[#effectpos+1] = s.player:get_pos() end else effectpos = {effect.attach:get_pos()} end else assert(false) end return effectpos end local ineffectrange = function(effect,pos,range) local effectpos = get_effect_positions(effect) for _,p in pairs(effectpos) do if vector.equals(pos,p) or (range and lib.math.vdcomp(range, pos,p)<=1) or (effect.range and lib.math.vdcomp(effect.range,p,pos)<=1) then return true end end return false end starlit.effect.probe = function(pos,range) -- this should be called before any effects are performed. -- other mods can overlay their own functions to e.g. protect areas -- from effects local result = {} -- first we need to check if any active injunctions are in effect -- injunctions are registered as effects with a 'disjunction = true' -- property for id,effect in pairs(starlit.effect.active) do if not (effect.disjunction and (effect.anchor or effect.attach)) then goto skip end if ineffectrange(effect,pos,range) then result.disjunction = true break end ::skip::end -- at some point we might also check to see if certain anti-effect -- blocks are nearby or suchlike. there could also be regions where -- perhaps certain kinds of effect are unusually empowered or weak return result end starlit.effect.disjoin = function(d) local effects,targets = {},{} if d.effect then effects = {{v=d.effect}} elseif d.target then targets = {d.target} elseif d.pos then -- find effects anchored here and people in range for id,effect in pairs(starlit.effect.active) do if not effect.anchor then goto skip end -- this intentionally excludes attached effects if ineffectrange(effect,d.pos,d.range) then effects[#effects+1] = {v=effect,i=id} end ::skip::end local ppl = minetest.get_objects_inside_radius(d.pos,d.range) if #targets == 0 then targets = ppl else for _,p in pairs(ppl) do targets[#targets+1] = p end end end -- iterate over targets to remove from any effect's influence for _,t in pairs(targets) do for id,effect in pairs(starlit.effect.active) do if effect.caster == t then effects[#effects+1] = {v=effect,i=id} else for si, sub in pairs(effect.subjects) do if sub.player == t then if sub.disjoin then sub:disjoin(effect) end effect.release_subject(si) break end end end end end -- effects to disjoin entirely for _,s in pairs(effects) do local effect = s.v if effect.disjoin then effect:disjoin() end effect.abort() if s.i then starlit.effect.active[s.i] = nil else for k,v in pairs(starlit.effect.active) do if v == effect then starlit.effect.active[k] = nil break end end end end end starlit.effect.ensorcelled = function(player,effect) if type(player) == 'string' then player = minetest.get_player_by_name(player) end for _,s in pairs(starlit.effect.active) do if effect and (s.name ~= effect) then goto skip end for _,sub in pairs(s.subjects) do if sub.player == player then return s end end ::skip::end return false end starlit.effect.each = function(player,effect) local idx = 0 return function() repeat idx = idx + 1 local sp = starlit.effect.active[idx] if sp == nil then return nil end if effect == nil or sp.name == effect then for _,sub in pairs(sp.subjects) do if sub.player == player then return sp end end end until idx >= #starlit.effect.active end end -- when a new effect is created, we analyze it and make the appropriate calls -- to minetest.after to queue up the events. each job returned needs to be -- saved in 'jobs' so they can be canceled if the effect is disjoined. no polling -- necessary :D starlit.effect.cast = function(proto) local s = table.copy(proto) s.jobs = s.jobs or {} s.vfx = s.vfx or {} s.sfx = s.sfx or {} s.impacts = s.impacts or {} s.subjects = s.subjects or {} s.delay = s.delay or 0 s.visual = function(subj, def) s.vfx[#s.vfx + 1] = { handle = minetest.add_particlespawner(def); subject = subj; } end s.visual_caster = function(def) -- convenience function local d = table.copy(def) d.attached = s.caster s.visual(nil, d) end s.visual_subjects = function(def) for _,sub in pairs(s.subjects) do local d = table.copy(def) d.attached = sub.player s.visual(sub, d) end end s.affect = function(i) local etbl = {} for _,sub in pairs(s.subjects) do -- local eff = late.new_effect(sub.player, i) -- starlit will not be using late local rec = { effect = eff; subject = sub; } s.impacts[#s.impacts+1] = rec etbl[#etbl+1] = rec end return etbl end s.abort = function() for _,j in ipairs(s.jobs) do j:cancel() end for _,v in ipairs(s.vfx) do minetest.delete_particlespawner(v.handle) end for _,i in ipairs(s.sfx) do s.silence(i) end for _,i in ipairs(s.impacts) do i.effect:stop() end end s.release_subject = function(si) local t = s.subjects[si] for _,f in pairs(s.sfx) do if f.subject == t then s.silence(f) end end for _,f in pairs(s.impacts) do if f.subject == t then f.effect:stop() end end for _,f in pairs(s.vfx) do if f.subject == t then minetest.delete_particlespawner(f.handle) end end s.subjects[si] = nil end local interpret_timespec = function(when) if when == nil then return 0 end local t if type(when) == 'number' then t = s.duration * when else t = (s.duration * (when.whence or 0)) + (when.secs or 0) end if t then return math.min(s.duration,math.max(0,t)) end log.err('invalid timespec ' .. dump(when)) return 0 end s.queue = function(when,fn) local elapsed = s.starttime and minetest.get_server_uptime() - s.starttime or 0 local timepast = interpret_timespec(when) if not timepast then timepast = 0 end local timeleft = s.duration - timepast local howlong = (s.delay + timepast) - elapsed if howlong < 0 then log.err('cannot time-travel! queue() called with `when` specifying timepoint that has already passed') howlong = 0 end s.jobs[#s.jobs+1] = minetest.after(howlong, function() -- this is somewhat awkward. since we're using a non-polling approach, we -- need to find a way to account for a caster or subject walking into an -- existing antimagic field, or someone with an existing antimagic aura -- walking into range of the anchor. so every time a effect effect would -- take place, we first check to see if it's in range of something nasty if not s.disjunction and -- avoid self-disjunction ((s.caster and starlit.effect.probe(s.caster:get_pos()).disjunction) or (s.anchor and starlit.effect.probe(s.anchor,s.range).disjunction)) then starlit.effect.disjoin{effect=s} else if not s.disjunction then for _,sub in pairs(s.subjects) do local sp = sub.player:get_pos() if starlit.effect.probe(sp).disjunction then starlit.effect.disjoin{pos=sp} end end end -- effect still exists and we've removed any subjects who have been -- affected by a disjunction effect, it's now time to actually perform -- the queued-up action fn(s,timepast,timeleft) end end) end s.play_now = function(spec) local specs, stbl = {}, {} local addobj = function(obj,sub) if spec.attach == false then specs[#specs+1] = { spec = { pos = obj:get_pos() }; obj = obj, subject = sub; } else specs[#specs+1] = { spec = { object = obj }; obj = obj, subject = sub; } end end if spec.where == 'caster' then addobj(s.caster) elseif spec.where == 'subjects' then for _,sub in pairs(s.subjects) do addobj(sub.player,sub) end elseif spec.where == 'pos' then specs[#specs+1] = { spec = {pos = s.anchor} } else specs[#specs+1] = { spec = {pos = spec.where} } end for _,sp in pairs(specs) do sp.spec.gain = sp.spec.gain or spec.gain local so = { handle = minetest.sound_play(spec.sound, sp.spec, spec.ephemeral); ctl = spec; -- object = sp.obj; subject = sp.subject; } stbl[#stbl+1] = so s.sfx[#s.sfx+1] = so end return stbl end s.play = function(when,spec) s.queue(when, function() local snds = s.play_now(spec) if spec.stop then s.queue(spec.stop, function() for _,snd in pairs(snds) do s.silence(snd) end end) end end) end s.silence = function(sound) if sound.ctl.fade == 0 then minetest.sound_stop(sound.handle) else minetest.sound_fade(sound.handle,sound.ctl.fade or 1,0) end end local startqueued, termqueued = false, false local myid = #starlit.effect.active+1 s.cancel = function() s.abort() starlit.effect.active[myid] = nil end local perform_disjunction_calls = function() local positions = get_effect_positions(s) for _,p in pairs(positions) do starlit.effect.disjoin{pos = p, range = s.range} end end if s.timeline then for when_raw,what in pairs(s.timeline) do local when = interpret_timespec(when_raw) if s.delay == 0 and when == 0 then startqueued = true if s.disjunction then perform_disjunction_calls() end what(s,0,s.duration) elseif when_raw == 1 or when >= s.duration then -- avoid race conditions if not termqueued then termqueued = true s.queue(1,function(s,...) what(s,...) if s.terminate then s:terminate() end starlit.effect.active[myid] = nil end) else log.warn('multiple final timeline events not possible, ignoring') end elseif when == 0 and s.disjunction then startqueued = true s.queue(when_raw,function(...) perform_disjunction_calls() what(...) end) else s.queue(when_raw,what) end end end if s.intervals then for _,int in pairs(s.intervals) do local timeleft = s.duration - interpret_timespec(int.after) local iteration, itercount = 0, timeleft / int.period local function iterate(lastreturn) iteration = iteration + 1 local nr = int.fn { effect = s; iteration = iteration; iterationcount = itercount; timeleft = timeleft; timeelapsed = s.duration - timeleft; lastreturn = lastreturn; } if nr ~= false and iteration < itercount then s.jobs[#s.jobs+1] = minetest.after(int.period, function() iterate(nr) end) end end if int.after then s.queue(int.after, iterate) else s.queue({whence=0, secs=s.period}, iterate) end end end if s.disjunction and not startqueued then if s.delay == 0 then perform_disjunction_calls() else s.queue(0, function() perform_disjunction_calls() end) end end if s.sounds then for when,what in pairs(s.sounds) do s.play(when,what) end end starlit.effect.active[myid] = s if not termqueued then s.jobs[#s.jobs+1] = minetest.after(s.delay + s.duration, function() if s.terminate then s:terminate() end starlit.effect.active[myid] = nil end) end s.starttime = minetest.get_server_uptime() return s end minetest.register_on_dieplayer(function(player) starlit.effect.disjoin{target=player} end) |
Added mods/starlit/element.lua version [db8d1e2ad1].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
local lib = starlit.mod.lib local W = starlit.world local M = W.material M.element.foreach('starlit:sort', {}, function(id, m) if m.metal then M.metal.link(id, { name = m.name; composition = starlit.type.fab{element = {[id] = 1}}; color = m.color; -- n.b. this is a RATIO: it will be appropriately multiplied -- for the object in question; e.g a normal chunk will be -- 100 $element, an ingot will be 1000 $element }) elseif m.gas then M.gas.link(id, { name = m.name; composition = starlit.type.fab{element = {[id] = 1}}; }) elseif m.liquid then M.liquid.link(id, { name = m.name; composition = starlit.type.fab{element = {[id] = 1}}; }) end end) local F = string.format local function mkEltIndicator(composition) local indicator = '' local idx = 0 local ccount = 0 for _ in pairs(composition) do ccount = ccount + 1 end local indsz,indpad = 28,4 local ofs = math.min(11, (indsz-indpad)/ccount) for id, amt in pairs(composition) do idx = idx + 1 indicator = indicator .. F( ':%s,3=starlit-element-%s.png', (indsz-indpad) - (idx*ofs), id ) end indicator = lib.image(indicator) return function(s) return string.format('(%s^[resize:%sx%s)^[combine:%sx%s%s', s, indsz, indsz, indsz, indsz, indicator); end end M.element.foreach('starlit:gen-forms', {}, function(id, m) local eltID = F('%s:element_%s', minetest.get_current_modname(), id) local eltName = F('Elemental %s', lib.str.capitalize(m.name)) local tt = function(t, d, g) return starlit.ui.tooltip { title = t, desc = d; color = lib.color(0.1,0.2,0.1); props = { {title = 'Mass', desc = lib.math.si('g', g), affinity='info'} } } end local comp = {[id] = 1} local iblit = mkEltIndicator(comp) m.form = m.form or {} m.form.element = eltID local powder = F('starlit-element-%s-powder.png', id); minetest.register_craftitem(eltID, { short_description = eltName; description = tt(eltName, F('Elemental %s kept in suspension by a nanide storage system, ready to be worked by a cold matter compiler', m.name), 1); inventory_image = iblit(powder); wield_image = powder; stack_max = 1000; -- 1kg groups = {element = 1, powder = 1, specialInventory = 1}; _starlit = { mass = 1; material = { kind = 'element'; element = id; }; fab = starlit.type.fab { element = comp; }; }; }); end) M.metal.foreach('starlit:gen-forms', {}, function(id, m) local baseID = F('%s:metal_%s_', minetest.get_current_modname(), id) local brickID, ingotID = baseID .. 'brick', baseID .. 'ingot' local brickName, ingotName = F('%s Brick', lib.str.capitalize(m.name)), F('%s Ingot', lib.str.capitalize(m.name)) m.form = m.form or {} m.form.brick = brickID m.form.ingot = ingotID local tt = function(t, d, g) return starlit.ui.tooltip { title = t, desc = d; color = lib.color(0.1,0.1,0.1); props = { {title = 'Mass', desc = lib.math.si('g', g), affinity='info'} } } end local mcomp = m.composition:elementalize().element local function comp(n) local t = {} for id, amt in pairs(mcomp) do t[id] = amt * n end return t end local iblit = mkEltIndicator(mcomp) local function img(s) return iblit(s:colorize(m.color):render()) end minetest.register_craftitem(brickID, { short_description = brickName; description = tt(brickName, F('A solid brick of %s, ready to be worked by a matter compiler', m.name), 100); inventory_image = img(lib.image 'starlit-item-brick.png'); wield_image = lib.image 'starlit-item-brick.png':colorize(m.color):render(); stack_max = 10; groups = {metal = 1, ingot = 1}; _starlit = { mass = 100; material = { kind = 'metal'; metal = id; }; fab = starlit.type.fab { flag = {smelt= true}; element = comp(1e2); }; }; }); minetest.register_craftitem(ingotID, { short_description = ingotName; description = tt(ingotName, F('A solid ingot of %s, ready to be worked by a large matter compiler', m.name), 1e3); inventory_image = img(lib.image('starlit-item-ingot.png')); wield_image = lib.image 'starlit-item-ingot.png':colorize(m.color):render(); groups = {metal = 1, ingot = 1}; stack_max = 5; _starlit = { mass = 1e3; material = { kind = 'metal'; metal = id; }; fab = starlit.type.fab { flag = {smelt= true}; element = comp(1e3); }; }; }); end) local function canisterDesc(stack, def) def = def or stack:get_definition()._starlit.canister local props = { {title = 'Charge Slots', affinity = 'info', desc = tostring(def.slots)}; }; if stack then local inv = starlit.item.container(stack) for i,e in ipairs(inv:list 'elem') do local comp = e:get_definition()._starlit.fab table.insert(props, { title = comp:formula(); desc = lib.math.si('g', e:get_count()); affinity = 'good'; }) end -- TODO list masses end return starlit.ui.tooltip { title = def.name, desc = def.desc or 'A canister that can store a charge of elemental powder, gas, or liquid'; color = lib.color(0.2,0.1,0.1); props = props; }; end starlit.item.canister = lib.registry.mk 'starlit:canister'; starlit.item.canister.foreach('starlit:item-gen', {}, function(id, c) minetest.register_craftitem(id, { short_description = c.name; description = canisterDesc(nil, c); inventory_image = c.image or 'starlit-item-element-canister.png'; groups = {canister = 1}; stack_max = 1; _starlit = { canister = c; container = { handle = function(stack, oldstack) stack:get_meta():set_string('description', canisterDesc(stack)) return stack end; list = { elem = { key = 'starlit:canister_elem'; accept = 'powder'; sz = c.slots; }; }; }; }; }) end) |
Added mods/starlit/fab.lua version [9968a2e2d9].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
-- [ʞ] fab.lua -- ~ lexi hale <lexi@hale.su> -- 🄯 EUPL1.2 -- ? fabrication spec class -- a type.fab supports two operators: -- -- + used for compounding recipes. that is, -- a+b = compose a new spec from the spec parts a and b. -- this is used e.g. for creating tier-based -- fabspecs. -- -- * used for determining quantities. that is, -- f*x = spec to make x instances of f -- -- new fab fields must be defined in starlit.type.fab.opClass. -- this maps a name to fn(a,b,n) -> quant, where a is the first -- argument, b is a compounding amount, and n is a quantity of -- items to produce. fields that are unnamed will be underwritten local function fQuant(a,b,n) return ((a or 0)+(b or 0))*n end local function fFac (a,b,n) if a == nil and b == nil then return nil end local f if a == nil or b == nil then f = a or b else f = (a or 1)*(b or 1) end return f*n end local function fReq (a,b,n) return a or b end local function fFlag (a,b,n) return a and b end local function fSize (a,b,n) return math.max(a,b) end local opClass = { -- fabrication eligibility will be determined by which kinds -- of input a particular fabricator can introduce. e.g. a -- printer with a but no cache can only print items whose -- recipe only names elements as ingredients -- ingredients element = fQuant; -- (g) gas = fQuant; -- () liquid = fQuant; -- (l) crystal = fQuant; -- (g) item = fQuant; -- n metal = fQuant; -- (g) metalIngot = fQuant; -- (g) -- factors cost = fFac; -- units vary time = fFac; -- (s) -- print: base printing time size = fSize; -- printBay: size of the printer bay necessary to produce the item req = fReq; flag = fFlag; -- means that can be used to produce the item & misc flags -- print: allow production with a printer -- smelt: allow production with a smelter -- all else defaults to underwrite } local F = string.format local strClass = { element = function(x, n) local el = starlit.world.material.element[x] return lib.math.si('g', n) .. ' ' .. (el.sym or el.name) end; metal = function(x, n) local met = starlit.world.material.metal[x] return lib.math.si('g', n) .. ' ' .. met.name end; liquid = function(x, n) local liq = starlit.world.material.liquid[x] return lib.math.si('L', n) .. ' ' .. liq.name end; gas = function(x, n) local gas = starlit.world.material.gas[x] return lib.math.si('g', n) .. ' ' .. gas.name end; item = function(x, n) local i = minetest.registered_items[x] return tostring(n) .. 'x ' .. i.short_description end; } local order = { 'element', 'metal', 'liquid', 'gas', 'item' } local lib = starlit.mod.lib local fab fab = lib.class { __name = 'starlit:fab'; opClass = opClass; strClass = strClass; order = order; construct = function(q) return q end; __index = { elementalize = function(self) local e = fab {element = self.element or {}} for _, kind in pairs {'metal', 'gas', 'liquid'} do for m,mass in pairs(self[kind] or {}) do local mc = starlit.world.material[kind][m].composition e = e + mc:elementalize()*mass end end return e end; elementSeq = function(self) local el = {} local em = self.element local s = 0 local eldb = starlit.world.material.element.db for k in pairs(em) do table.insert(el, k) s=s+eldb[k].n end table.sort(el, function(a,b) return eldb[a].n > eldb[b].n end) return el, em, s end; formula = function(self) print('make formula', dump(self)) local ts,f=0 if self.element then f = {} local el, em, s = self:elementSeq() local eldb = starlit.world.material.element.db for i, e in ipairs(el) do local sym, n = eldb[e].sym, em[e] if n > 0 then table.insert(f, string.format("%s%s", sym, n>1 and lib.str.nIdx(n) or '')) end end f = table.concat(f) ts = ts + s end local sub = {} for _, w in pairs {'metal', 'gas', 'liquid'} do if self[w] then local mdb = starlit.world.material[w].db for k, amt in pairs(self[w]) do local mf, s = mdb[k].composition:formula() if amt > 0 then table.insert(sub, { f = string.format("(%s)%s",mf, lib.str.nIdx(amt)); s = s; }) end ts = ts + s*amt end end end table.sort(sub, function(a,b) return a.s > b.s end) local fml = {} for i, v in ipairs(sub) do fml[i] = v.f end if f then table.insert(fml, f) end fml = table.concat(fml, ' + ') return fml, ts end; }; __tostring = function(self) local t = {} for i,o in ipairs(order) do if self[o] then for mat,amt in pairs(self[o]) do if amt > 0 then table.insert(t, strClass[o](mat, amt)) end end end end return table.concat(t, ", ") end; __add = function(a,b) local new = fab {} for cat, vals in pairs(a) do new[cat] = lib.tbl.copy(vals) end for cat, vals in pairs(b) do if not new[cat] then new[cat] = lib.tbl.copy(vals) else local f = opClass[cat] for k,v in pairs(vals) do local n = f(new[cat][k], v, 1) new[cat][k] = n > 0 and n or nil end end end return new end; __mul = function(x,n) local new = fab {} for cat, vals in pairs(x) do new[cat] = {} local f = opClass[cat] for k,v in pairs(vals) do local num = f(v,nil,n) new[cat][k] = num > 0 and num or nil end end return new end; __div = function(x,n) return x * (1/n) end; } starlit.type.fab = fab |
Added mods/starlit/fx/nano.lua version [85f63bf07b].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 |
local lib = starlit.mod.lib local E = starlit.effect local N = {} starlit.fx.nano = N local nanopool= { { name = 'starlit-fx-nano-spark-small.png'; scale_tween = {0,.5, style = 'pulse', rep = 3}; }; { name = 'starlit-fx-nano-spark-small.png'; scale_tween = {0,1, style = 'pulse', rep = 2}; }; { name = 'starlit-fx-nano-spark-big.png'; scale_tween = {0,1, style = 'pulse'}; }; } function N.heal(user, targets, amt, dur) local amthealed = {} local f = E.cast { caster = user.entity; subjects = targets; duration = dur; intervals = { { after = 0; period = 4; fn = function(c) for i,v in pairs(c.effect.subjects) do local u = starlit.activeUsers[v.player:get_player_name()] if u then local heal = math.max(amt/4, 1) amthealed[u] = amthealed[u] or 0 if amthealed[u] < amt then amthealed[u] = amthealed[u] + heal u:statDelta('health', heal) end end end end; } } } local casterIsTarget = false for _, sub in pairs(f.subjects) do if sub.player == user.entity then casterIsTarget = true end f.visual(sub, { amount = 50; time = dur; glow = 14; jitter = 0.01; attached = user.entity; vel = { min = -0.1, max = 0.1; }; pos = { min = vector.new(0,0.2,0); max = vector.new(0,1.2,0); }; radius = { min = 0.2; max = 0.6; bias = -1; }; exptime = {min=0.5,max=2}; attract = { kind = 'line'; strength = {min = 0.5, max = 2}; origin = 0; direction = vector.new(0,1,0); origin_attached = sub.player; direction_attached = sub.player; }; texpool = nanopool; }) end if not casterIsTarget then -- f.visual_caster { } end f.play(0.3, { where = 'subjects'; sound = 'starlit-nano-heal'; ephemeral = true; spec = {gain = 0.3}; }) return f end function N.shred(user, pos, prop, time, node) local f = E.cast { caster = user.entity; subjects = {}; duration = time; } local sp,sv = user:lookupSpecies() local eh = sv.eyeHeight or sp.eyeHeight f.visual_caster { amount = 200 * time; pos = vector.new(0.12,eh - 0.1,0); radius = 0.2; time = time - (time/3); glow = 14; jitter = 0.1; size = {min = 0.2, max = 0.5}; exptime = {min=0.5,max=1}; vel_tween = { 0; { min = -0.4, max = 0.4; }; style = 'pulse', rep = time * 2; }; attract = { kind = 'point'; origin = pos; radius = 0.5; strength = {min=.3,max=2}; }; texpool = nanopool; }; f.queue(0.05, function(s, timepast, timeleft) f.visual(nil, { amount = timeleft * 40; time = timeleft; pos = pos; size_tween = { 0, {min = 0.5, max = 2}; }; vel = { min = vector.new(-1.2,0.5,-1.2); max = vector.new(1.2,3.5,1.2); }; acc = vector.new(0,-starlit.world.planet.gravity,0); node = node; }) end); f.queue(0.9, function(s, timepast, timeleft) f.visual(nil, { amount = 200; time = timeleft; pos = pos; size = {min = 0.1, max = 0.3}; vel = { min = vector.new(-2,0.5,-2); max = vector.new(2,4,2); }; acc = vector.new(0,-starlit.world.planet.gravity,0); node = node; }) end); f.queue(0.3, function(s, timepast, timeleft) local function v(fn) local def = { amount = timeleft * 100; pos = pos; time = timeleft; radius = 0.5; jitter = {min = 0.0, max = 0.2}; size = {min = 0.2, max = 0.5}; exptime = {min = 0.5, max = 1}; attract = { kind = 'point'; strength = {min=0.3, max = 1}; origin = vector.new(0,eh-0.1,0); radius = 0.5; origin_attached = user.entity; }; } fn(def) f.visual(nil, def) end v(function(t) t.texpool = nanopool t.glow = 14 end) v(function(t) t.node = node t.amount = timeleft * 20 t.size = {min = 0.1, max = 0.3}; end) end) return f end |
Added mods/starlit/init.lua version [1f09b99871].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
-- [ʞ] starlit/init.lua -- ~ lexi hale <lexi@hale.su> -- ? basic setup, game rules, terrain -- © EUPL v1.2 local T = minetest.get_translator 'starlit' -- TODO enforce latest engine version local mod = { -- subordinate mods register here lib = vtlib; -- vtlib should be accessed as starlit.mod.lib by starlit modules for the sake of proper encapsulation. vtlib should simply be a provider, not a hardcoded dependency } local lib = mod.lib starlit = { ident = minetest.get_current_modname(); mod = mod; translator = T; constant = { light = { --minetest units dim = 3; lamp = 7; bright = 10; brightest = 14; -- only sun and growlights }; heat = { -- celsius freezing = 0; safe = 4; overheat = 32; boiling = 100; }; rad = { }; }; activeUsers = { -- map of username -> user object }; activeUI = { -- map of username -> UI context }; liveUI = { -- cached subset of activeUI containing those UIs needing live updates }; interface = lib.registry.mk 'starlit:interface'; item = { }; region = { radiator = { store = AreaStore(); emitters = {} }; }; -- standardized effects fx = {}; type = {}; world = { defaultScenario = 'starlit_scenario:imperialExpat'; seedbank = lib.math.seedbank(minetest.get_mapgen_setting 'seed'); mineral = lib.registry.mk 'starlit:mineral'; material = { -- raw materials element = lib.registry.mk 'starlit:element'; -- elements are automatically sorted into the following categories -- if they match. however, it's possible to have a metal/gas/liquid -- that *isn't* a pure element, so these need separate registries -- for alloys and mixtures like steel and water metal = lib.registry.mk 'starlit:metal'; gas = lib.registry.mk 'starlit:gas'; liquid = lib.registry.mk 'starlit:liquid'; }; ecology = { plants = lib.registry.mk 'starlit:plants'; trees = lib.registry.mk 'starlit:trees'; biomes = lib.registry.mk 'starlit:biome'; }; climate = {}; scenario = {}; planet = { gravity = 7.44; orbit = 189; -- 1 year is 189 days revolve = 20; -- 1 day is 20 irl minutes }; fact = lib.registry.mk 'starlit:fact'; time = { calendar = { empire = { name = 'Imperial Regnal Calendar'; year = function(t, long) local reigns = { -- if anyone actually makes it to his Honor & Glory Unfailing Persigan I i will be -- exceptionally flattered {4, 'Emperor', 'Atavarka', 'the Bold'}; -- died at war {9, 'Emperor', 'Vatikserka', 'the Unconquered'}; -- died at war {22, 'Emperor', 'Rusifend', 'the Wise'}; -- poisoned at diplomacy {61, 'Empress', 'Tafseshendi', 'the Great'}; -- died of an 'insurrection of the innards' after a celebrated reign {291, 'Emperor', 'Treptebaska', 'the Unwise'}; -- murdered by his wife in short order {292, 'Empress', 'Vilintalti', 'the Impious'}; -- removed by the praetorian elite {298, 'Emperor', 'Radavan', 'the Reckless'}; -- died at war {316, 'Emperor', 'Suldibrand', 'the Forsaken of Men'}; -- fucked around. found out. {320, 'Emperor', 'Persigan', 'the Deathless'}; } local year, r = math.floor(t / 414) for i=1, #reigns do if reigns[i+1][1] < year then r = reigns[i+1] end end local reignBegin, title, name, epithet = lib.tbl.unpack(r) local ry = 1 + (year - reignBegin) return long and string.format('Year %s of the Reign of HH&GU %s %s %s', ry, title, name, epithet) or string.format('Y. %s %s', name, ry) end; time = function(t, long) local bellsInDay, candleSpansInBell = 5, 7 local bell = bellsInDay*t local cspan = (bellsInDay*candleSpansInBell*t) % candleSpansInBell return string.format(long and 'Bell %s, Candlespan %s' or '%sb %sc', math.floor(bell), math.floor(cspan)) end; }; commune = { name = 'People\'s Calendar'; date = function(t, long) local year = math.floor(t / 256) + 314 return string.format(long and 'Foundation %s' or 'F:%s', year) end; time = function(t, long) local hoursInDay, minutesInHour = 16, 16 local hour = hoursInDay*t local min = (hoursInDay*minutesInHour*t) % minutesInHour local dawn = 0.24*hoursInDay local noon = 0.5*hoursInDay local dusk = 0.76*hoursInDay local midnight = 1.0*hoursInDay local tl, str if hour < dawn then tl = dawn - hour str = long and 'dawn' or 'D' elseif hour < noon then tl = noon - hour str = long and 'noon' or 'N' elseif hour < dusk then tl = dusk - hour str = long and 'dusk' or 'd' elseif hour < midnight then tl = midnight - hour str = long and 'midnight' or 'M' end return long and string.format('%s hours, %s minutes to %s', math.floor(tl), math.floor(minutesInHour - min), str) or string.format('%s.%sH.%sM', str, math.floor(tl), math.floor(minutesInHour - min)) end; }; }; }; }; jobs = {}; } starlit.cfgDir = minetest.get_worldpath() .. '/' .. starlit.ident local logger = function(module) local function argjoin(arg, nxt, ...) if arg and not nxt then return tostring(arg) end if not arg then return "(nil)" end return tostring(arg) .. ' ' .. argjoin(nxt, ...) end local lg = {} local setup = function(fn, lvl) lvl = lvl or fn local function emit(...) local call = (fn == 'fatal') and error or function(str) minetest.log(lvl, str) end if module then call(string.format('[%s :: %s] %s',starlit.ident,module,argjoin(...))) else call(string.format('[%s] %s',starlit.ident,argjoin(...))) end end lg[fn ] = function(...) emit(...) end lg[fn .. 'f'] = function(...) emit(string.format(...)) end -- convenience fn end setup('info') setup('warn','warning') setup('err','error') setup('act','action') setup('fatal') return lg end starlit.logger = logger local log = logger() function starlit.evaluate(name, ...) local path = minetest.get_modpath(minetest.get_current_modname()) local filename = string.format('%s/%s', path, name) log.info('loading', filename) local chunk, err = loadfile(filename, filename) if not chunk then error(err) end return chunk(...) end function starlit.include(name, ...) -- semantic variant used for loading modules return starlit.evaluate(name..'.lua', ...) end minetest.register_lbm { label = 'build radiator index'; name = 'starlit:loadradiatorboxes'; nodenames = {'group:radiator'}; run_at_every_load = true; action = function(pos, node, dt) local R = starlit.region local phash = minetest.hash_node_position(pos) if R.radiator.sources[phash] then return end -- already loaded local def = minetest.registered_nodes[node.name] local cl = def._starlit.radiator local min,max = cl.maxEffectArea(pos) local id = R.radiator.store:insert_area(min,max, minetest.pos_to_string(pos)) R.radiator.sources[phash] = id end; -- NOTE: temp emitter nodes are responsible for decaching themselves in their on_destruct cb } function starlit.startJob(id, interval, job) local lastRun local function start() starlit.jobs[id] = minetest.after(interval, function() local t = minetest.get_gametime() local d = lastRun and t - lastRun or nil lastRun = t local continue = job(d, interval) if continue == true or continue == nil then start() elseif continue ~= false then interval = continue start() end end) end start() end starlit.include 'stats' starlit.include 'world' starlit.include 'fab' starlit.include 'tiers' starlit.include 'species' starlit.include 'store' starlit.include 'ui' starlit.include 'item' starlit.include 'container' starlit.include 'user' starlit.include 'effect' starlit.include 'fx/nano' starlit.include 'element' starlit.include 'terrain' starlit.include 'interfaces' starlit.include 'suit' minetest.settings:set('movement_gravity', starlit.world.planet.gravity) -- ??? seriously??? --------------- -- callbacks -- --------------- -- here we connect our types up to the minetest API local function userCB(fn) return function(luser, ...) local name = luser:get_player_name() local user = starlit.activeUsers[name] return fn(user, ...) end end minetest.register_on_joinplayer(function(luser, lastLogin) -- TODO check that necessary CSMs are installed local user = starlit.type.user(luser) if lastLogin == nil then user:onSignup() end user:onJoin() starlit.activeUsers[user.name] = user end) minetest.register_on_leaveplayer(function(luser) starlit.activeUsers[luser:get_player_name()]:onPart() end) minetest.register_on_player_receive_fields(function(luser, formid, fields) local name = luser:get_player_name() local user = starlit.activeUsers[name] if not user then return false end if formid == '' then -- main menu return starlit.ui.userMenuDispatch(user,fields) end local ui = starlit.interface.db[formid] local state = starlit.activeUI[name] or {} if formid == '__builtin:help_cmds' or formid == '__builtin:help_privs' then return false end assert(state.form == formid) -- sanity check user:onRespond(ui, state, fields) if fields.quit then starlit.activeUI[name] = nil end return true end) minetest.register_on_respawnplayer(userCB(function(user) return user:onRespawn() end)) minetest.register_on_dieplayer(userCB(function(user, reason) return user:onDie(reason) end)) minetest.register_on_punchnode(function(pos,node,puncher,point) local user = starlit.activeUsers[puncher:get_player_name()] local oldTgt = user.action.tgt user.action.tgt = point if bit.band(user.action.bits, 0x80)==0 then user.action.bits = bit.bor(user.action.bits, 0x80) --user:trigger('primary', {state = 'init'}) else user:trigger('retarget', {oldTgt = oldTgt}) end end) local function pointChanged(a,b) return a.type ~= b.type or a.type == 'node' and vector.new(a.under) ~= vector.new(b.under) or a.type == 'object' and a.ref ~= b.ref end local function triggerPower(_, luser, point) local user = starlit.activeUsers[luser:get_player_name()] local oldTgt = user.action.tgt user.action.tgt = point if bit.band(user.action.bits, 0x100)==0 then user.action.bits = bit.bor(user.action.bits, 0x100) --return user:trigger('secondary', {state = 'prog', delta = 0}) elseif pointChanged(oldTgt, point) then user:trigger('retarget', {oldTgt = oldTgt}) end end -- sigh core.noneitemdef_default.on_place = function(...) if not triggerPower(...) then minetest.item_place(...) end end core.noneitemdef_default.on_use = function(...) triggerPower(...) end core.noneitemdef_default.on_secondary_use = function(...) triggerPower(...) end minetest.register_on_player_inventory_action(function(luser, act, inv, p) local name = luser:get_player_name() local user = starlit.activeUsers[name] -- allow UIs to update on UI changes local state = starlit.activeUI[name] if state then local ui = starlit.interface.db[state.form] ui:cb('onMoveItem', user, act, inv, p) end end) minetest.register_on_player_hpchange(function(luser, delta, cause) local user = starlit.activeUsers[luser:get_player_name()] if cause.type == 'fall' then delta = user:damageModifier('bluntForceTrauma', (delta * 50)) -- justification: a short fall can do around -- five points of damage, which is nearly 50% -- of the default hp_max. since we crank up -- hp by a factor of 50~40, damage should be -- cranked by similarly end return delta end, true) function minetest.handle_node_drops(pos, drops, digger) local function jitter(pos) local function r(x) return x+math.random(-0.2, 0.2) end return vector.new( r(pos.x), r(pos.y), r(pos.z) ) end for i, it in ipairs(drops) do minetest.add_item(jitter(pos), it) end end -- TODO timer iterates live UI |
Added mods/starlit/interfaces.lua version [1cb802f20f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 440 441 |
local lib = starlit.mod.lib function starlit.ui.setupForUser(user) local function cmode(mode) if user.actMode == mode then return {hue = 150, sat = 0, lum = .3} end end user.entity:set_inventory_formspec(starlit.ui.build { kind = 'vert', mode = 'sw'; padding = .5, spacing = 0.1; {kind = 'hztl'; {kind = 'contact', w=1.5,h=1.5, id = 'mode_nano', img='starlit-ui-icon-nano.png', close=true, color = cmode'nano'}; {kind = 'contact', w=1.5,h=1.5, id = 'mode_weapon', img='starlit-ui-icon-weapon.png', close=true, color = cmode'weapon'}; {kind = 'contact', w=1.5,h=1.5, id = 'mode_psi', img='starlit-ui-icon-psi.png', close=true, color = cmode'psi'}; }; {kind = 'hztl'; {kind = 'contact', w=1.5,h=1.5, id = 'open_elements', img='starlit-ui-icon-element.png'}; {kind = 'contact', w=1.5,h=1.5, id = 'open_suit', img='starlit-item-suit.png^[hsl:200:-.7:0'}; {kind = 'contact', w=1.5,h=1.5, id = 'open_psi', img='starlit-ui-icon-psi-cfg.png'}; {kind = 'contact', w=1.5,h=1.5, id = 'open_body', img='starlit-ui-icon-self.png'}; }; {kind = 'list'; target = 'current_player', inv = 'main'; w = 6, h = 1, spacing = 0.1; }; }) end function starlit.ui.userMenuDispatch(user, fields) local function setSuitMode(mode) if user.actMode == mode then user:actModeSet 'off' else user:actModeSet(mode) end end local modes = { nano = true, psi = false, weapon = true } for e,s in pairs(modes) do if fields['mode_' .. e] then if s and (user:naked() or user:getSuit():powerState() == 'off') then user:suitSound 'starlit-error' else setSuitMode(e) end return true end end if fields.open_elements then user:openUI('starlit:user-menu', 'compiler') return true elseif fields.open_psi then user:openUI('starlit:user-menu', 'psi') return true elseif fields.open_suit then if not user:naked() then user:openUI('starlit:user-menu', 'suit') end return true elseif fields.open_body then user:openUI('starlit:user-menu', 'body') end return false end local function listWrap(n, max) local h = math.ceil(n / max) local w = math.min(max, n) return w, h end local function wrapMenu(w, h, rh, max, l) local root = {kind = 'vert', w=w, h=h} local bar local function flush() if bar and bar[1] then table.insert(root, bar) end bar = {kind = 'hztl'} end flush() for _, i in ipairs(l) do local bw = w/max if i.cfg then w = w - rh end table.insert(bar, { kind = 'button', close = i.close; color = i.color; fg = i.fg; label = i.label; icon = i.img; id = i.id; w = bw, h = rh; }) if i.cfg then table.insert(bar, { kind = 'button'; color = i.color; fg = i.fg; label = "CFG"; icon = i.img; id = i.id .. '_cfg'; w = rh, h = rh; }) end if bar[max] then flush() end end flush() return root end local function abilityMenu(a) -- select primary/secondary abilities or activate ritual abilities local p = {kind = 'vert'} for _, o in ipairs(a.order) do local m = a.menu[o] table.insert(p, {kind='label', text=m.label, w=a.w, h = .5}) table.insert(p, wrapMenu(a.w, a.h, 1.2, 2, m.opts)) end return p end local function pptrMatch(a,b) if a == nil or b == nil then return false end return a.chipID == b.chipID and a.pgmIndex == b.pgmIndex end starlit.interface.install(starlit.type.ui { id = 'starlit:user-menu'; pages = { compiler = { setupState = function(state, user) -- nanotech/suit software menu local chips = user.entity:get_inventory():get_list 'starlit_suit_chips' -- FIXME need better subinv api local sw = starlit.mod.electronics.chip.usableSoftware(chips) state.suitSW = {} local dedup = {} for i, r in ipairs(sw) do if r.sw.kind == 'suitPower' then if not dedup[r.sw] then dedup[r.sw] = true table.insert(state.suitSW, r) end end end end; handle = function(state, user, act) if user:getSuit():powerState() == 'off' then return false end local pgm, cfg for k in next, act do local id, mode = k:match('^suit_pgm_([0-9]+)_(.*)$') if id then id = tonumber(id) if state.suitSW[id] then pgm = state.suitSW[id] cfg = mode == '_cfg' break end end end if not pgm then return false end -- HAX -- kind=active programs must be assigned to a command slot -- kind=direct programs must open their UI -- kind=passive programs must toggle on and off if pgm.sw.powerKind == 'active' then if cfg then user:openUI(pgm.sw.ui, 'index', { context = 'suit'; program = pgm; }) return false end local ptr = {chipID = starlit.mod.electronics.chip.read(pgm.chip).uuid, pgmIndex = pgm.fd.inode} local pnan = user.power.nano if pnan.primary == nil then pnan.primary = ptr elseif pptrMatch(ptr, pnan.primary) then pnan.primary = nil elseif pptrMatch(ptr, pnan.secondary) then pnan.secondary = nil else pnan.secondary = ptr end user:suitSound 'starlit-configure' elseif pgm.sw.powerKind == 'direct' then local ctx = { context = 'suit'; program = pgm; } if pgm.sw.ui then user:openUI(pgm.sw.ui, 'index', ctx) return false else pgm.sw.run(user, ctx) end elseif pgm.sw.powerKind == 'passive' then if cfg then user:openUI(pgm.sw.ui, 'index', { context = 'suit'; program = pgm; }) return false end local addDisableRec = true for i, e in ipairs(pgm.file.body.conf) do if e.key == 'disable' and e.value == 'yes' then addDisableRec = false table.remove(pgm.file.body.conf, i) break elseif e.key == 'disable' and e.value == 'no' then e.value = 'yes' addDisableRec = false break end end if addDisableRec then table.insert(pgm.file.body.conf, {key='disable',value='yes'}) end -- update the chip *wince* pgm.fd:write(pgm.file) user.entity:get_inventory():set_stack('starlit_suit_chips', pgm.chipSlot, pgm.chip) user:reconfigureSuit() user:suitSound('starlit-configure') end return true, true end; render = function(state, user) local suit = user:getSuit() local swm if user:getSuit():powerState() ~= 'off' then swm = { w = 8, h = 3; order = {'active','ritual','pasv'}; menu = { active = { label = 'Nanoware'; opts = {}; }; ritual = { label = 'Programs'; opts = {}; }; pasv = { label = 'Passive'; opts = {}; }; }; } for id, r in pairs(state.suitSW) do local color = {hue=300,sat=0,lum=0} local fg = nil local close = nil local tbl, cfg if r.sw.powerKind == 'active' then tbl = swm.menu.active.opts if r.sw.ui then cfg = true end local pnan = user.power.nano if pnan then local ptr = {chipID = starlit.mod.electronics.chip.read(r.chip).uuid, pgmIndex = r.fd.inode} if pptrMatch(ptr, pnan.primary) then color.lum = 1 elseif pptrMatch(ptr, pnan.secondary) then color.lum = 0.8 end end elseif r.sw.powerKind == 'direct' then tbl = swm.menu.ritual.opts if not r.sw.ui then close = true end elseif r.sw.powerKind == 'passive' then tbl = swm.menu.pasv.opts if r.sw.ui then cfg = true end for i, e in ipairs(r.file.body.conf) do if e.key == 'disable' and e.value == 'yes' then color.lum = -.2 fg = lib.color {hue=color.hue,sat=0.7,lum=0.7} break end end end if tbl then table.insert(tbl, { color = color, fg = fg; label = r.sw.label or r.sw.name; id = string.format('suit_pgm_%s_', id); cfg = cfg, close = close; }) end end end local menu = { kind = 'vert', mode = 'sw', padding = 0.5 } if swm then table.insert(menu, abilityMenu(swm)) end local inv = user.entity:get_inventory() local cans = inv:get_list 'starlit_suit_canisters' if cans and next(cans) then for i, st in ipairs(cans) do local id = string.format('starlit_canister_%u_elem', i) local esz = inv:get_size(id) if esz > 0 then local eltW, eltH = listWrap(esz, 5) table.insert(menu, {kind = 'hztl', {kind = 'img', desc='Elements', img = 'starlit-ui-icon-element.png', w=1,h=1}; {kind = 'list', target = 'current_player', inv = id, listContent = 'element', w = eltW, h = eltH, spacing = 0.1}; }) end end end if #menu == 0 then table.insert(menu, { kind = 'img'; img = 'starlit-ui-alert.png'; w=2, h=2; }) menu.padding = 1; end return starlit.ui.build(menu) end; }; compilerListRecipes = { }; psi = { render = function(state, user) return starlit.ui.build { kind = 'vert', mode = 'sw'; padding = 0.5; } end; }; body = { render = function(state, user) local barh = .75 local tb = { kind = 'vert', mode = 'sw'; padding = 0.5, {kind = 'hztl', padding = 0.25; {kind = 'label', text = 'Name', w = 2, h = barh}; {kind = 'label', text = user.persona.name, w = 4, h = barh}}; } local statBars = {'hunger', 'thirst', 'fatigue', 'morale'} for idx, id in ipairs(statBars) do local s = starlit.world.stats[id] local amt, sv = user:effectiveStat(id) local min, max = starlit.world.species.statRange(user.persona.species, user.persona.speciesVariant, id) local st = string.format('%s / %s', s.desc(amt, true), s.desc(max)) table.insert(tb, {kind = 'hztl', padding = 0.25; {kind = 'label', w=2, h=barh, text = s.name}; {kind = 'hbar', w=4, h=barh, fac = sv, text = st, color=s.color}; }) end local abilities = { {id = 'abl_sprint', label = 'Sprint', img = 'starlit-ui-icon-ability-sprint.png'}; } table.insert(tb, wrapMenu(6.25,4, 1,2, abilities)) return starlit.ui.build(tb) end; }; suit = { render = function(state, user) local suit = user:getSuit() local suitDef = suit:def() local chipW, chipH = listWrap(suitDef.slots.chips, 5) local batW, batH = listWrap(suitDef.slots.batteries, 5) local canW, canH = listWrap(suitDef.slots.canisters, 5) local suitMode = suit:powerState() local function modeColor(mode) if mode == suitMode then return {hue = 180, sat = 0, lum = .5} end end return starlit.ui.build { kind = 'vert', mode = 'sw'; padding = 0.5, spacing = 0.1; {kind = 'hztl', {kind = 'img', desc='Batteries', img = 'starlit-item-battery.png', w=1,h=1}; {kind = 'list', target = 'current_player', inv = 'starlit_suit_bat', listContent = 'power', w = batW, h = batH, spacing = 0.1}; }; {kind = 'hztl', {kind = 'img', desc='Chips', img = 'starlit-item-chip.png', w=1,h=1}; {kind = 'list', target = 'current_player', inv = 'starlit_suit_chips', listContent = 'chip', w = chipW, h = chipH, spacing = 0.1}; }; {kind = 'hztl', {kind = 'img', desc='Canisters', img = 'starlit-item-element-canister.png', w=1,h=1}; {kind = 'list', target = 'current_player', inv = 'starlit_suit_canisters', listContent = nil, w = canW, h = canH, spacing = 0.1}; }; {kind = 'hztl'; {kind = 'img', w=1,h=1, item = suit.item:get_name(), desc = suit.item:get_definition().short_description}; {kind = 'button', w=1.5,h=1, id = 'powerMode_off', label = 'Off'; color=modeColor'off'}; {kind = 'button', w=2.5,h=1, id = 'powerMode_save', label = 'Power Save'; color=modeColor'powerSave'}; {kind = 'button', w=1.5,h=1, id = 'powerMode_on', label = 'On'; color=modeColor'on'}; }; {kind = 'list', target = 'current_player', inv = 'main', w = 6, h = 1, spacing = 0.1}; } end; handle = function(state, user, q) local suitMode if q.powerMode_off then suitMode = 'off' elseif q.powerMode_save then suitMode = 'powerSave' elseif q.powerMode_on then suitMode = 'on' end if suitMode then user:suitPowerStateSet(suitMode) return true end end; }; }; }) starlit.interface.install(starlit.type.ui { id = 'starlit:compile-matter-component'; pages = { index = { setupState = function(state, user, ctx) if ctx.context == 'suit' then end state.pgm = ctx.program end; render = function(state, user) return starlit.ui.build { kind = 'vert', padding = 0.5; w = 5, h = 5, mode = 'sw'; {kind = 'label', w = 4, h = 1, text = 'hello'}; } end; }; }; }) |
Added mods/starlit/item.lua version [aa837e16cf].
> > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
local lib = starlit.mod.lib local I = starlit.item function I.mk(item, context) local st = ItemStack(item) local md = st:get_definition()._starlit local ctx = context or {} if md and md.event then md.event.create(st, ctx) end if context.how == 'print' then if context.schematic and context.schematic.setup then context.schematic.setup(st, ctx) end end return st end |
Added mods/starlit/mod.conf version [e7817791d1].
> > > > |
1 2 3 4 |
name = starlit author = velartrill description = world logic and UI depends = vtlib |
Added mods/starlit/species.lua version [3944fdb227].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
local lib = starlit.mod.lib local paramTypes do local T,G = lib.marshal.t, lib.marshal.g paramTypes = { tone = G.struct { hue = T.angle; sat = T.clamp; lum = T.clamp; }; str = T.str; num = T.decimal; } end -- constants local animationFrameRate = 60 local species = { human = { name = 'Human'; desc = 'The weeds of the galactic flowerbed. Humans are one of the Lesser Races, excluded from the ranks of the Greatest Races by souls that lack, in normal circumstances, external psionic channels. Their mastery of the universe cut unexpectedly short, forever locked out of FTL travel, short-lived without augments, and alternately pitied or scorned by the lowest of the low, humans flourish nonetheless due to a capacity for adaptation unmatched among the Thinking Few, terrifyingly rapid reproductive cycles -- and a keen facility for bribery. While the lack of human psions remains a sensitive topic, humans (unlike the bitter and emotional Kruthandi) are practical enough to hire the talent they cannot possess, and have even built a small number of symbiotic civilizations with the more indulging of the Powers. In a galaxy where nearly all sophont life is specialized to a fault, humans have found the unique niche of occupying no particular niche.'; scale = 1.0; params = { {'eyeColor', 'Eye Color', 'tone', {hue=327, sat=0, lum=0}}; {'hairColor', 'Hair Color', 'tone', {hue=100, sat=0, lum=0}}; {'skinTone', 'Skin Tone', 'tone', {hue= 0, sat=0, lum=0}}; }; tempRange = { comfort = {18.3, 23.8}; -- needed for full stamina regen survivable = {5, 33}; -- anything below/above will cause progressively more damage }; variants = { female = { name = 'Human Female'; mesh = 'starlit-body-female.x'; eyeHeight = 1.4; texture = function(t, adorn) local skin = lib.image 'starlit-body-skin.png' : shift(t.skinTone) local eye = lib.image 'starlit-body-eye.png' : shift(t.eyeColor) local hair = lib.image 'starlit-body-hair.png' : shift(t.hairColor) local invis = lib.image '[fill:1x1:0,0:#00000000' local plate = adorn.suit and adorn.suit.plate or invis local lining = adorn.suit and adorn.suit.lining or invis return {lining, plate, skin, skin, eye, hair} end; stats = { psiRegen = 1.3; psiPower = 1.2; psi = 1.2; hunger = .8; -- women have smaller stomachs thirst = .8; staminaRegen = 1.0; morale = 0.8; -- you are not She-Bear Grylls }; traits = { health = 400; lungCapacity = .6; irradiation = 0.8; -- you are smaller, so it takes less rads to kill ya sturdiness = 0; -- women are more fragile and thus susceptible to blunt force trauma metabolism = 1800; --Cal painTolerance = 0.4; }; }; male = { name = 'Human Male'; eyeHeight = 1.6; stats = { psiRegen = 1.0; psiPower = 1.0; psi = 1.0; hunger = 1.0; staminaRegen = .7; -- men are strong but have inferior endurance }; traits = { health = 500; painTolerance = 1.0; lungCapacity = 1.0; sturdiness = 0.3; metabolism = 2200; --Cal }; }; }; traits = {}; }; } starlit.world.species = { index = species; paramTypes = paramTypes; } function starlit.world.species.mkDefaultParamsTable(pSpecies, pVariant) local sp = species[pSpecies] local var = sp.variants[pVariant] local vpd = var.defaults or {} local tbl = {} for _, p in pairs(sp.params) do local name, desc, ty, dflt = lib.tbl.unpack(p) tbl[name] = vpd[name] or dflt end return tbl end function starlit.world.species.mkPersonaFor(pSpecies, pVariant) return { species = pSpecies; speciesVariant = pVariant; bodyParams = starlit.world.species.paramsFromTable(pSpecies, starlit.world.species.mkDefaultParamsTable(pSpecies, pVariant) ); statDeltas = {}; } end local function spLookup(pSpecies, pVariant) local sp = species[pSpecies] local var = sp.variants[pVariant or next(sp.variants)] return sp, var end starlit.world.species.lookup = spLookup function starlit.world.species.statRange(pSpecies, pVariant, pStat) local sp,spv = spLookup(pSpecies, pVariant) local min, max, base if pStat == 'health' then min,max = 0, spv.traits.health elseif pStat == 'breath' then min,max = 0, 65535 else local spfac = spv.stats[pStat] local basis = starlit.world.stats[pStat] min,max = basis.min, basis.max if spfac then min = min * spfac max = max * spfac end base = basis.base if base == true then base = max elseif base == false then base = min end end return min, max, base end -- set the necessary properties and create a persona for a newspawned entity function starlit.world.species.birth(pSpecies, pVariant, entity, circumstances) circumstances = circumstances or {} local sp,var = spLookup(pSpecies, pVariant) local function pct(st, p) local min, max = starlit.world.species.statRange(pSpecies, pVariant, st) local delta = max - min return min + delta*p end local ps = starlit.world.species.mkPersonaFor(pSpecies,pVariant) local startingHP = pct('health', 1.0) if circumstances.injured then startingHP = pct('health', circumstances.injured) end if circumstances.psiCharged then ps.statDeltas.psi = pct('psi', circumstances.psiCharged) end ps.statDeltas.warmth = 20 -- don't instantly start dying of frostbite entity:set_properties{hp_max = var.traits.health or sp.traits.health} entity:set_hp(startingHP, 'initial hp') return ps end function starlit.world.species.paramsFromTable(pSpecies, tbl) local lst = {} local sp = species[pSpecies] for i, par in pairs(sp.params) do local name,desc,ty,dflt = lib.tbl.unpack(par) if tbl[name] then table.insert(lst, {id=name, value=paramTypes[ty].enc(tbl[name])}) end end return lst end function starlit.world.species.paramsToTable(pSpecies, lst) local tymap = {} local sp = species[pSpecies] for i, par in pairs(sp.params) do local name,desc,ty,dflt = lib.tbl.unpack(par) tymap[name] = paramTypes[ty] end local tbl = {} for _, e in pairs(lst) do tbl[e.id] = tymap[e.id].dec(e.value) end return tbl end for speciesName, sp in pairs(species) do for varName, var in pairs(sp.variants) do if var.mesh then var.animations = starlit.evaluate(string.format('models/%s.nla', var.mesh)).skel.action end end end function starlit.world.species.updateTextures(ent, persona, adornment) local s,v = spLookup(persona.species, persona.speciesVariant) local paramTable = starlit.world.species.paramsToTable(persona.species, persona.bodyParams) local texs = {} for i, t in ipairs(v.texture(paramTable, adornment)) do texs[i] = t:render() end ent:set_properties { textures = texs } end function starlit.world.species.setupEntity(ent, persona) local s,v = spLookup(persona.species, persona.speciesVariant) local _, maxHealth = starlit.world.species.statRange(persona.species, persona.speciesVariant, 'health') ent:set_properties { visual = 'mesh'; mesh = v.mesh; stepheight = .51; eye_height = v.eyeHeight; collisionbox = { -- FIXME -0.3, 0.0, -0.3; 0.3, 1.5, 0.3; }; visual_size = vector.new(10,10,10) * s.scale; hp_max = maxHealth; } local function P(v) if v then return {x=v[1],y=v[2]} end return {x=0,y=0} end ent:set_local_animation( P(v.animations.idle), P(v.animations.run), P(v.animations.work), P(v.animations.runWork), animationFrameRate) end |
Added mods/starlit/stats.lua version [c766e87490].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
local lib = starlit.mod.lib local function U(unit, prec, fixed) if fixed then return function(amt, excludeUnit) if excludeUnit then return tostring(amt/prec) end return string.format("%s %s", amt/prec, unit) end else return function(amt, excludeUnit) if excludeUnit then return tostring(amt/prec) end return lib.math.si(unit, amt/prec) end end end local function C(h, s, l) return lib.color {hue = h, sat = s or 1, lum = l or .7} end starlit.world.stats = { psi = {min = 0, max = 500, base = 0, desc = U('ψ', 10), color = C(320), name = 'Numina'}; -- numina is measured in daψ warmth = {min = -1000, max = 1000, base = 0, desc = U('°C', 10, true), color = C(5), name = 'Warmth'}; -- warmth in measured in °C×10 fatigue = {min = 0, max = 76 * 60, base = 0, desc = U('hr', 60, true), color = C(288,.3,.5), name = 'Fatigue'}; -- fatigue is measured in minutes one needs to sleep to cure it stamina = {min = 0, max = 20 * 100, base = true, desc = U('m', 100), color = C(88), name = 'Stamina'}; -- stamina is measured in how many 10th-nodes (== cm) one can sprint hunger = {min = 0, max = 20000, base = 0, desc = U('Cal', 1), color = C(43,.5,.4), name = 'Hunger'}; -- hunger is measured in calories one must consume to cure it thirst = {min = 0, max = 1600, base = 0, desc = U('l', 100), color = C(217, .25,.4), name = 'Thirst'}; -- thirst is measured in centiliters of H²O required to cure it morale = {min = 0, max = 24 * 60 * 10, base = true, desc = U('hr', 60, true), color = C(0,0,.8), name = 'Morale'}; -- morale is measured in minutes. e.g. at base rate morale degrades by -- 60 points every hour. morale can last up to 10 days irradiation = {min = 0, max = 20000, base = 0, desc = U('Gy', 1000), color = C(141,1,.5), name = 'Irradiation'}; -- irrad is measured is milligreys -- 1Gy counters natural healing -- ~3Gy counters basic nanomedicine -- 5Gy causes death within two weeks without nanomedicine -- radiation speeds up psi regen -- morale drain doubles with each 2Gy illness = {min = 0, max = 1000, base = 0, desc = U('%', 10, true), color = C(71,.4,.25), name = 'Illness'}; -- as illness increases, maximum stamina and health gain a corresponding limit -- illness is increased by certain conditions, and decreases on its own as your -- body heals when those conditions wear off. some drugs can lower accumulated illness -- but illness-causing conditions require specific cures -- illness also causes thirst and fatigue to increase proportionately } |
Added mods/starlit/store.lua version [73c6f814ce].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
-- [ʞ] store.lua -- ~ lexi hale <lexi@hale.su> -- © EUPLv1.2 -- ? defines serialization datatypes that don't belong to -- any individual class local lib = starlit.mod.lib local T,G = lib.marshal.t, lib.marshal.g starlit.store = {} -- the serialization equivalent of .type ------------- -- persona -- ------------- ----------------------------------------------- -- a Persona is a structure that defines the nature of -- -- an (N)PC and how it interacts with the Starsoul-managed -- -- portion of the game world -- things like name, species, -- -- stat values, physical characteristics, and so forth -- local statStructFields = {} for k,v in pairs(starlit.world.stats) do statStructFields[k] = v.srzType or ( (v.base == true or v.base > 0) and T.s16 or T.u16 ) end starlit.store.compilerJob = G.struct { schematic = T.str; progress = T.clamp; } starlit.store.persona = G.struct { name = T.str; species = T.str; speciesVariant = T.str; background = T.str; bodyParams = G.array(8, G.struct {id = T.str, value = T.str}); --variant statDeltas = G.struct(statStructFields); facts = G.array(32, G.array(8, T.str)); -- facts stores information the player has discovered and narrative choices -- she has made. -- parametric facts are encoded as horn clauses -- non-parametric facts are encoded as {'fact-mod:fact-id'} } starlit.store.suitMeta = lib.marshal.metaStore { batteries = {key = 'starlit:suit_slots_bat', type = T.inventoryList}; chips = {key = 'starlit:suit_slots_chips', type = T.inventoryList}; elements = {key = 'starlit:suit_slots_elem', type = T.inventoryList}; guns = {key = 'starlit:suit_slots_gun', type = T.inventoryList}; ammo = {key = 'starlit:suit_slots_ammo', type = T.inventoryList}; } |
Added mods/starlit/suit.lua version [7112e6c94b].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
local lib = starlit.mod.lib local suitStore = starlit.store.suitMeta starlit.item.suit = lib.registry.mk 'starlit:suits'; -- note that this cannot be persisted as a reference to a particular suit in the world local function suitContainer(stack, inv) return starlit.item.container(stack, inv, { pfx = 'starlit_suit' }) end starlit.type.suit = lib.class { name = 'starlit:suit'; construct = function(stack) return { item = stack; inv = suitStore(stack); } end; __index = { powerState = function(self) local s = self.item if not s then return nil end local m = s:get_meta():get_int('starlit:power_mode') if m == 1 then return 'on' elseif m == 2 then return 'powerSave' else return 'off' end end; powerStateSet = function(self, state) local s = self.item if not s then return nil end local m if state == 'on' then m = 1 -- TODO check power level elseif state == 'powerSave' then m = 2 else m = 0 end if self:powerLeft() <= 0 then m = 0 end s:get_meta():set_int('starlit:power_mode', m) end; powerLeft = function(self) local batteries = self.inv.read 'batteries' local power = 0 for idx, slot in pairs(batteries) do power = power + starlit.mod.electronics.dynamo.totalPower(slot) end return power end; powerCapacity = function(self) local batteries = self.inv.read 'batteries' local power = 0 for idx, slot in pairs(batteries) do power = power + starlit.mod.electronics.dynamo.initialPower(slot) end return power end; maxPowerUse = function(self) local batteries = self.inv.read 'batteries' local w = 0 for idx, slot in pairs(batteries) do w = w + starlit.mod.electronics.dynamo.dischargeRate(slot) end return w end; onReconfigure = function(self, inv) -- apply any changes to item metadata and export any subinventories -- to the provided invref, as they may have changed local sc = starlit.item.container(self.item, inv, {pfx = 'starlit_suit'}) sc:push() self:pullCanisters(inv) end; onItemMove = function(self, user, list, act, what) -- called when the suit inventory is changed if act == 'put' then if list == 'starlit_suit_bat' then user:suitSound('starlit-suit-battery-in') elseif list == 'starlit_suit_chips' then user:suitSound('starlit-suit-chip-in') elseif list == 'starlit_suit_canisters' then user:suitSound('starlit-insert-snap') end elseif act == 'take' then if list == 'starlit_suit_bat' then user:suitSound('starlit-insert-snap') elseif list == 'starlit_suit_chips' then --user:suitSound('starlit-suit-chip-out') elseif list == 'starlit_suit_canisters' then user:suitSound('starlit-insert-snap') end end end; def = function(self) return self.item:get_definition()._starlit.suit end; pullCanisters = function(self, inv) starlit.item.container.dropPrefix(inv, 'starlit_canister') self:forCanisters(inv, function(sc) sc:pull() end) end; pushCanisters = function(self, inv, st, i) self:forCanisters(inv, function(sc) sc:push() return true end) end; forCanisters = function(self, inv, fn) local cans = inv:get_list 'starlit_suit_canisters' if cans and next(cans) then for i, st in ipairs(cans) do if not st:is_empty() then local pfx = 'starlit_canister_' .. tostring(i) local sc = starlit.item.container(st, inv, {pfx = pfx}) if fn(sc, st, i, pfx) then inv:set_stack('starlit_suit_canisters', i, st) end end end end end; establishInventories = function(self, obj) local inv = obj:get_inventory() local ct = suitContainer(self.item, inv) ct:pull() self:pullCanisters(inv) --[[ local def = self:def() local sst = suitStore(self.item) local function readList(listName, prop) inv:set_size(listName, def.slots[prop]) if def.slots[prop] > 0 then local lst = sst.read(prop) inv:set_list(listName, lst) end end readList('starlit_suit_chips', 'chips') readList('starlit_suit_bat', 'batteries') readList('starlit_suit_guns', 'guns') readList('starlit_suit_elem', 'elements') readList('starlit_suit_ammo', 'ammo') ]] end; }; } -- TODO find a better place for this! starlit.type.suit.purgeInventories = function(obj) local inv = obj:get_inventory() starlit.item.container.dropPrefix(inv, 'starlit_suit') starlit.item.container.dropPrefix(inv, 'starlit_canister') --[[inv:set_size('starlit_suit_bat', 0) inv:set_size('starlit_suit_guns', 0) inv:set_size('starlit_suit_chips', 0) inv:set_size('starlit_suit_ammo', 0) inv:set_size('starlit_suit_elem', 0) ]] end starlit.item.suit.foreach('starlit:suit-gen', {}, function(id, def) local icon = lib.image(def.img or 'starlit-item-suit.png') local iconColor = def.iconColor if not iconColor then iconColor = (def.tex and def.tex.plate and def.tex.plate.tint) or def.defaultColor iconColor = iconColor:to_hsl() iconColor.lum = 0 end if iconColor then icon = icon:shift(iconColor) end if not def.adorn then function def.adorn(a, item, persona) local function imageFor(pfx) return lib.image(string.format("%s-%s-%s.png", pfx, persona.species, persona.speciesVariant)) end if not def.tex then return end a.suit = {} for name, t in pairs(def.tex) do local img = imageFor(t.id) local color local cstr = item:get_meta():get_string('starlit:tint_suit_' .. name) if cstr and cstr ~= '' then color = lib.color.unmarshal(cstr) elseif t.tint then color = t.tint or def.defaultColor end if color then local hsl = color:to_hsl() local adjusted = { hue = hsl.hue; sat = hsl.sat * 2 - 1; lum = hsl.lum * 2 - 1; } img = img:shift(adjusted) end a.suit[name] = img end end end minetest.register_tool(id, { short_description = def.name; description = starlit.ui.tooltip { title = def.name; desc = def.desc; color = lib.color(.1, .7, 1); }; groups = { suit = 1; inv = 1; -- has inventories batteryPowered = 1; -- has a battery inv programmable = 1; -- has a chip inv }; on_use = function(st, luser, pointed) local user = starlit.activeUsers[luser:get_player_name()] if not user then return end -- have mercy on users who've lost their suits and wound -- up naked and dying of exposure if user:naked() then local ss = st:take_item(1) user:setSuit(starlit.type.suit(ss)) user:suitSound('starlit-suit-don') return st end end; inventory_image = icon:render(); _starlit = { container = { workbench = { order = {'batteries','chips','guns','ammo'} }; list = { bat = { key = 'starlit:suit_slots_bat'; accept = 'dynamo'; sz = def.slots.batteries; }; chips = { key = 'starlit:suit_slots_chips'; accept = 'chip'; sz = def.slots.chips; }; canisters = { key = 'starlit:suit_slots_canisters'; accept = 'canister'; sz = def.slots.canisters; }; guns = { key = 'starlit:suit_slots_gun'; accept = 'weapon'; workbench = { label = 'Weapon'; icon = 'starlit-ui-icon-gun'; color = lib.color(1,0,0); }; sz = def.slots.guns; }; ammo = { key = 'starlit:suit_slots_ammo'; accept = 'ammo'; workbench = { label = 'Ammunition'; color = lib.color(1,.5,0); easySlots = true; -- all slots accessible on the go }; sz = def.slots.ammo; }; }; }; event = { create = function(st,how) local s = suitStore(st) -- make sure there's a defined powerstate starlit.type.suit(st):powerStateSet 'off' suitContainer(st):clear() --[[ populate meta tables s.write('batteries', {}) s.write('guns', {}) s.write('ammo', {}) s.write('elements', {}) s.write('chips', {})]] end; }; suit = def; }; }); end) local slotProps = { starlit_cfg = { itemClass = 'inv'; }; starlit_suit_bat = { suitSlot = true; powerLock = true; itemClass = 'dynamo'; }; starlit_suit_chips = { suitSlot = true; powerLock = true; itemClass = 'chip'; }; starlit_suit_guns = { suitSlot = true; maintenanceNode = ''; itemClass = 'suitWeapon'; }; starlit_suit_ammo = { suitSlot = true; maintenanceNode = ''; itemClass = 'suitAmmo'; }; starlit_suit_canisters = { suitSlot = true; itemClass = 'canister'; }; } minetest.register_allow_player_inventory_action(function(luser, act, inv, p) local user = starlit.activeUsers[luser:get_player_name()] local function grp(i,g) return minetest.get_item_group(i:get_name(), g) ~= 0 end local function checkBaseRestrictions(list) local restrictions = slotProps[list] if not restrictions then return nil, true end if restrictions.suitSlot then if user:naked() then return restrictions, false end end if restrictions.powerLock then if user:getSuit():powerState() ~= 'off' then return restrictions, false end end return restrictions, true end local function itemFits(item, list) local rst, ok = checkBaseRestrictions(list) if not ok then return false end if rst == nil then return true end if rst.itemClass and not grp(item, rst.itemClass) then return false end if rst.maintenanceNode then return false end -- FIXME figure out best way to identify when the player is using a maintenance node if grp(item, 'specialInventory') then if grp(item, 'powder') and list ~= 'starlit_suit_elem' then return false end -- FIXME handle containers if grp(item, 'psi') and list ~= 'starlit_psi' then return false end end return true end local function itemCanLeave(item, list) local rst, ok = checkBaseRestrictions(list) if not ok then return false end if rst == nil then return true end if minetest.get_item_group(item:get_name(), 'specialInventory') then end if rst.maintenanceNode then return false end return true end if act == 'move' then local item = inv:get_stack(p.from_list, p.from_index) if not (itemFits(item, p.to_list) and itemCanLeave(item, p.from_list)) then return 0 end elseif act == 'put' then if not itemFits(p.stack, p.listname) then return 0 end elseif act == 'take' then if not itemCanLeave(p.stack, p.listname) then return 0 end end return true end) minetest.register_on_player_inventory_action(function(luser, act, inv, p) local user = starlit.activeUsers[luser:get_player_name()] local function slotChange(slot,a,item) local s = slotProps[slot] if slot == 'starlit_suit' then user:updateSuit() if user:naked() then starlit.type.suit.purgeInventories(user.entity) user.power.nano = {} end elseif s and s.suitSlot then local s = user:getSuit() s:onItemMove(user, slot, a, item) s:onReconfigure(user.entity:get_inventory()) user:setSuit(s) else return end user:updateHUD() end if act == 'put' or act == 'take' then local item = p.stack slotChange(p.listname, act, item) elseif act == 'move' then local item = inv:get_stack(p.to_list, p.to_index) slotChange(p.from_list, 'take', item) slotChange(p.to_list, 'put', item) end end) local suitInterval = 2.0 starlit.startJob('starlit:suit-software', suitInterval, function(delta) local runState = { pgmsRun = {}; flags = {}; } for id, u in pairs(starlit.activeUsers) do if not u:naked() then local reconfSuit = false local inv = u.entity:get_inventory() local chips = inv:get_list('starlit_suit_chips') local suitprog = starlit.mod.electronics.chip.usableSoftware(chips) for _, prop in pairs(suitprog) do local s = prop.sw if s.kind == 'suitPower' and (s.powerKind == 'passive' or s.bgProc) and (not runState.pgmsRun[s]) then local conf = prop.file.body.conf local enabled = true for _, e in ipairs(conf) do if e.key == 'disable' and e.value == 'yes' then enabled = false break end end local fn if s.powerKind == 'passive' then fn = s.run else fn = s.bgProc end function prop.saveConf(cfg) cfg = cfg or conf prop.fd:write(cfg) inv:set_stack('starlit_suit_chips', prop.chipSlot, prop.fd.chip) reconfSuit = true end function prop.giveItem(st) u:thrustUpon(st) end if enabled and fn(u, prop, suitInterval, runState) then runState.pgmsRun[s] = true end end end if reconfSuit then u:reconfigureSuit() end end end end) |
Added mods/starlit/terrain.lua version [5a8b3b76d0].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
local T = starlit.translator local lib = starlit.mod.lib starlit.terrain = {} local soilSounds = {} local grassSounds = {} minetest.register_node('starlit:soil', { description = T 'Soil'; tiles = {'default_dirt.png'}; groups = {dirt = 1}; drop = ''; sounds = soilSounds; _starlit = { onDestroy = function() end; kind = 'block'; elements = {}; }; }) minetest.register_node('starlit:sand', { description = T 'Sand'; tiles = {'default_sand.png'}; groups = {dirt = 1}; drop = ''; sounds = soilSounds; _starlit = { kind = 'block'; fab = starlit.type.fab { element = { silicon = 25 } }; }; }) minetest.register_craftitem('starlit:soil_clump', { short_description = T 'Soil'; description = starlit.ui.tooltip { title = T 'Soil'; desc = 'A handful of nutrient-packed soil, suitable for growing plants'; color = lib.color(0.3,0.2,0.1); }; inventory_image = 'starlit-item-soil.png'; groups = {soil = 1}; _starlit = { fab = starlit.type.fab { element = { carbon = 12 / 4 } }; }; }) function starlit.terrain.createGrass(def) local function grassfst(i) local nextNode = def.name if i >= 0 then nextNode = nextNode .. '_walk_' .. tostring(i) end return { onWalk = function(pos) minetest.set_node_at(pos, def.name .. '_walk_2'); end; onDecay = function(pos,delta) minetest.set_node_at(pos, nextNode); end; onDestroy = function(pos) end; fab = def.fab; recover = def.recover; recover_vary = def.recover_vary; }; end local drop = { max_items = 4; items = { { items = {'starlit:soil'}, rarity = 2; tool_groups = { 'shovel', 'trowel' }; }; }; } minetest.register_node(def.name, { description = T 'Greengraze'; tiles = { def.img .. '.png'; 'default_dirt.png'; { name = 'default_dirt.png^' .. def.img ..'_side.png'; tileable_vertical = false; }; }; groups = {grass = 1, sub_walk = 1}; drop = ''; sounds = grassSounds; _starlit = grassfst(2); }) for i=2,0,-1 do local opacity = tostring((i/2.0) * 255) minetest.register_node(def.name, { description = def.desc; tiles = { def.img .. '.png^(default_footprint.png^[opacity:'..opacity..')'; 'default_dirt.png'; { name = 'default_dirt.png^' .. def.img ..'_side.png'; tileable_vertical = false; }; }; groups = {grass = 1, sub_walk = 1, sub_decay = 5}; drop = ''; _starlit = grassfst(i-1); sounds = grassSounds; }) end end starlit.terrain.createGrass { name = 'starlit:greengraze'; desc = T 'Greengraze'; img = 'default_grass'; fab = starlit.type.fab { element = { carbon = 12; }; time = { shred = 2.5; }; }; } for _, w in pairs {false,true} do minetest.register_node('starlit:liquid_water' .. (w and '_flowing' or ''), { description = T 'Water'; drawtype = 'liquid'; waving = 3; tiles = { { name = "default_water_source_animated.png"; backface_culling = false; animation = { type = "vertical_frames"; aspect_w = 16; aspect_h = 16; length = 2.0; }; }; { name = "default_water_source_animated.png"; backface_culling = true; animation = { type = "vertical_frames"; aspect_w = 16; aspect_h = 16; length = 2.0; }; }; }; use_texture_alpha = 'blend'; paramtype = 'light'; walkable = false, pointable = "blocking", diggable = false, buildable_to = true; is_ground_content = false; drop = ''; drowning = 1; liquidtype = w and 'flowing' or 'source'; liquid_alternative_flowing = 'starlit:liquid_water_flowing'; liquid_alternative_source = 'starlit:liquid_water'; liquid_viscosity = 1; liquid_renewable = true; liquid_range = 2; drowning = 40; post_effect_color = {a=103, r=10, g=40, b=70}; groups = {water = 3, liquid = 3}; }); end starlit.world.mineral.foreach('starlit:mineral_generate', {}, function(name,m) local node = string.format('starlit:mineral_%s', name) local grp = {mineral = 1} minetest.register_node(node, { description = m.desc; tiles = m.tiles or (m.tone and { string.format('default_stone.png^[colorizehsl:%s:%s:%s', m.tone.hue, m.tone.sat, m.tone.lum) }) or {'default_stone.png'}; groups = grp; drop = m.rocks or ''; _starlit = { kind = 'block'; elements = m.elements; fab = m.fab; recover = m.recover; recover_vary = m.recover_vary; }; }) if not m.excludeOre then local seed = 0 grp.ore = 1 for i = 1, #m.name do seed = seed*50 + string.byte(name, i) end minetest.register_ore { ore = node; ore_type = m.dist.kind; wherein = {m.dist.among}; clust_scarcity = m.dist.rare; y_max = m.dist.height[1], y_min = m.dist.height[2]; noise_params = m.dist.noise or { offset = 28; scale = 16; spread = vector.new(128,128,128); seed = seed; octaves = 1; }; } end end) starlit.world.mineral.link('feldspar', { desc = T 'Feldspar'; excludeOre = true; recover = starlit.type.fab { time = { shred = 3; }; cost = { shredPower = 3; }; }; recover_vary = function(rng, ctx) -- print('vary!', rng:int(), rng:int(0,10)) return starlit.type.fab { element = { aluminum = rng:int(0,4); potassium = rng:int(0,2); calcium = rng:int(0,2); } }; end; }) -- map generation minetest.register_alias('mapgen_stone', 'starlit:mineral_feldspar') minetest.register_alias('mapgen_water_source', 'starlit:liquid_water') minetest.register_alias('mapgen_river_water_source', 'starlit:liquid_water') |
Added mods/starlit/tiers.lua version [513c94b946].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
local lib = starlit.mod.lib starlit.world.tier = lib.registry.mk 'starlit:tier' local T = starlit.world.tier local fab = starlit.type.fab function starlit.world.tier.fabsum(name, ty) local dest = fab {} local t = starlit.world.tier.db[name] assert(t, 'reference to nonexisting tier '..name) if t.super then dest = dest+starlit.world.tier.fabsum(t.super, ty)*(t.cost or 1) end if t.fabclasses and t.fabclasses[ty] then dest = dest + t.fabclasses[ty] end return dest end function starlit.world.tier.tech(name, tech) local t = starlit.world.tier.db[name] if t.techs and t.techs[tech] ~= nil then return t.techs[tech] end if t.super then return starlit.world.tier.tech(t.super, tech) end return false end T.meld { base = { fabclass = { electric = fab {metal={copper = 10}}; suit = fab {element={carbon = 1e3}}; psi = fab {metal={numinium = 1}}; bio = fab {element={carbon = 1}}; }; }; -- properties that apply to all tiers ------------------ -- tier classes -- ------------------ lesser = { name = 'Lesser', adj = 'Lesser'; super = 'base'; fabclasses = { basis = fab { metal = {aluminum=4}; }; }; }; greater = { name = 'Greater', adj = 'Greater'; super = 'base'; fabclasses = { basis = fab { metal = {vanadium=2}; }; }; }; starlit = { name = 'Starsoul', adj = 'Starsoul'; super = 'base'; fabclasses = { basis = fab { metal = {osmiridium=1}; }; }; }; forevanished = { name = 'Forevanished One', adj = 'Forevanished'; super = 'base'; fabclasses = { basis = fab { metal = {elusium=1}; }; }; }; ------------------ -- Lesser Races -- ------------------ makeshift = { -- regular trash name = 'Makeshift', adj = 'Makeshift'; super = 'lesser'; techs = {tool = true, prim = true, electric = true}; power = 0.5; efficiency = 0.3; reliability = 0.2; cost = 0.3; fabclasses = { -- characteristic materials basis = fab { -- fallback metal = {iron=3}; }; }; }; imperial = { --powerful trash name = 'Imperial', adj = 'Imperial'; super = 'lesser'; techs = {tool = true, electric = true, electronic = true, suit = true, combatSuit = true, weapon = true, hover='ion'}; power = 2.0; efficiency = 0.5; reliability = 0.5; cost = 1.0; fabclasses = { basis = fab { metal = {steel=2}; }; }; }; commune = { --reliability name = 'Commune', adj = 'Commune'; super = 'lesser'; techs = {tool = true, electric = true, electronic = true, suit = true, combatSuit = true, weapon = true, gravitic = true, hover='grav'}; power = 1.0; efficiency = 2.0; reliability = 3.0; cost = 1.5; fabclasses = { basis = fab { metal = {titanium=1}; time = {print = 1.2}; -- commune stuff is intricate }; }; }; ------------------- -- Greater Races -- ------------------- ---------------- -- Starsouled -- ---------------- suIkuri = { --super-tier name = 'Su\'ikuri', adj = "Su'ikuruk"; super = 'starlit'; techs = {psi = true, prim = true, bioSuit = true, psiSuit = true}; power = 1.5; efficiency = 1.0; reliability = 3.0; cost = 2.0; fabclasses = { psi = fab { metal = {numinium = 2.0}; crystal = {beryllium = 1.0}; }; bio = fab { crystal = {beryllium = 1.0}; }; }; }; usukwinya = { --value for 'money'; no weapons; no hovertech (they are birds) -- NOTA BENE: the ususkwinya *do* have weapons of their own; however, -- they are extremely restricted and never made available except to a -- very select number of that species. consequently, usuk players -- of a certain scenario may have usuk starting weapons, but these must -- be manually encoded to avoid injecting them into the overall crafting -- /loot system. because there are so few of these weapons in existence, -- all so tightly controlled, the odds of the weapons or plans winding -- up on Farthest Shadow are basically zero unless you bring them yourself name = 'Usukwinya', adj = 'Usuk'; super = 'starlit'; techs = lib.tbl.set('tool', 'electric', 'electronic', 'suit', 'gravitic'); power = 2.0; efficiency = 2.0; reliability = 2.0; cost = 0.5; fabclasses = { basis = fab { crystal = {aluminum = 5}; -- ruby }; }; }; eluthrai = { --super-tier name = 'Eluthrai', adj = 'Eluthran'; super = 'starlit'; techs = {tool = true, electric = true, electronic = true, weapon = true, gravitic = true, gravweapon = true, suit = true, combatSuit = true, hover = 'grav'}; power = 4.0; efficiency = 4.0; reliability = 4.0; cost = 4.0; fabclasses = { basis = fab { crystal = {carbon = 5}; -- diamond }; special = fab { metal = {technetium=1, cinderstone=1} }; }; }; ----------------------- -- Forevanished Ones -- ----------------------- firstborn = { --god-tier name = 'Firstborn', adj = 'Firstborn'; super = 'forevanished'; techs = {tool = true, electric = true, electronic = true, suit = true, psi = true, combatSuit = true, weapon = true, gravitic = true, gravweapon = true}; power = 10.0; efficiency = 5.0; reliability = 3.0; cost = 10.0; fabclasses = { basis = fab { metal = {technetium=2, neodymium=3, sunsteel=1}; crystal = {astrite=1}; }; }; }; forevanisher = { --godslayer-tier name = 'Forevanisher', adj = 'Forevanisher'; super = 'forevanished'; techs = {tool = true, electric = true, electronic = true, suit = true, psi = true, combatSuit = true, weapon = true, gravitic = true, gravweapon = true}; power = 20.0; efficiency = 1.0; reliability = 2.0; cost = 100.0; fabclasses = { basis = fab { metal = {}; crystal = {}; }; }; }; } |
Added mods/starlit/ui.lua version [7085b387cd].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
local lib = starlit.mod.lib starlit.ui = {} starlit.type.ui = lib.class { name = 'starlit:ui'; __index = { action = function(self, user, state, fields) local pg = self.pages[state.page or 'index'] if not pg then return end if pg.handle then local redraw, reset = pg.handle(state, user, fields) if reset then pg.setupState(state,user) end if redraw then self:show(user) end end if fields.quit then self:cb('onClose', user) end end; cb = function(self, name, user, ...) local state = self:begin(user) if self[name] then self[name](state, user, ...) end local pcb = self.pages[state.page][name] if pcb then pcb(state, user, ...) end end; begin = function(self, user, page, ...) local state = starlit.activeUI[user.name] if state and state.form ~= self.id then state = nil starlit.activeUI[user.name] = nil end local created = state == nil if not state then state = { page = page or 'index'; form = self.id; } starlit.activeUI[user.name] = state self:cb('setupState', user, ...) elseif page ~= nil and state.page ~= page then state.page = page local psetup = self.pages[state.page].setupState if psetup then psetup(state,user, ...) end end return state, created end; render = function(self, state, user) return self.pages[state.page].render(state, user) end; show = function(self, user) local state = self:begin(user) minetest.show_formspec(user.name, self.id,self:render(state, user)) end; open = function(self, user, page, ...) user:suitSound 'starlit-nav' self:begin(user, page, ...) self:show(user) end; close = function(self, user) local state = starlit.activeUI[user.name] if state and state.form == self.id then self:cb('onClose', user) starlit.activeUI[user.name] = nil minetest.close_formspec(user.name, self.id) end end; }; construct = function(p) if not p.id then error('UI missing id') end p.pages = p.pages or {} return p end; } function starlit.interface.install(ui) starlit.interface.link(ui.id, ui) end function starlit.ui.build(def, parent) local clr = def.color if clr and lib.color.id(clr) then clr = clr:to_hsl_o() end local state = { x = (def.x or 0); y = (def.y or 0); w = def.w or 0, h = def.h or 0; fixed = def.fixed or false; spacing = def.spacing or 0; padding = def.padding or 0; align = def.align or (parent and parent.align) or 'left'; lines = {}; mode = def.mode or (parent and parent.mode or nil); -- hw or sw gen = (parent and parent.gen or 0) + 1; fg = def.fg or (parent and parent.fg); color = clr or (parent and parent.color) or { hue = 260, sat = 0, lum = 0 }; } local lines = state.lines local cmod = string.format('^[hsl:%s:%s:%s', state.color.hue, state.color.sat*0xff, state.color.lum*0xff) local E = minetest.formspec_escape if state.padding/2 > state.x then state.x = state.padding/2 end if state.padding/2 > state.y then state.y = state.padding/2 end local function btnColorDef(sel) local function climg(state,img) local selstr if sel == nil then selstr = string.format( 'button%s,' .. 'button_exit%s,' .. 'image_button%s,' .. 'item_image_button%s', state, state, state, state) else selstr = E(sel) .. state end return string.format('%s[%s;' .. 'bgimg=%s;' .. 'bgimg_middle=16;' .. 'content_offset=0,0' .. ']', sel and 'style' or 'style_type', selstr, E(img..'^[resize:48x48'..cmod)) end return climg('', 'starlit-ui-button-sw.png') .. climg(':hovered', 'starlit-ui-button-sw-hover.png') .. climg(':pressed', 'starlit-ui-button-sw-press.png') end local function widget(...) table.insert(lines, string.format(...)) end if def.kind == 'vert' then for _, w in ipairs(def) do local src, st = starlit.ui.build(w, state) widget('container[%s,%s]%scontainer_end[]', state.x, state.y, src) state.y=state.y + state.spacing + st.h state.w = math.max(state.w, st.w) end state.w = state.w + state.padding state.h = state.y + state.padding/2 elseif def.kind == 'hztl' then for _, w in ipairs(def) do local src, st = starlit.ui.build(w, state) widget('container[%s,%s]%scontainer_end[]', state.x, state.y, src) -- TODO alignments state.x=state.x + state.spacing + st.w state.h = math.max(state.h, st.h) end state.h = state.h + state.padding state.w = state.x + state.padding/2 elseif def.kind == 'list' then local slotTypes = { plain = {hue = 200, sat = -.1, lum = 0}; element = {hue = 20, sat = -.3, lum = 0}; chip = {hue = 0, sat = -1, lum = 0}; psi = {hue = 300, sat = 0, lum = 0}; power = {hue = 50, sat = 0, lum = .2}; } local img if state.mode == 'hw' then img = lib.image('starlit-ui-slot-physical.png'); else img = lib.image('starlit-ui-slot.png'):shift(slotTypes[def.listContent or 'plain']); end local spac = state.spacing widget('style_type[list;spacing=%s,%s]',spac,spac) assert(def.w and def.h, 'ui-lists require a fixed size') for lx = 0, def.w-1 do for ly = 0, def.h-1 do local ox, oy = state.x + lx*(1+spac), state.y + ly*(1+spac) table.insert(lines, string.format('image[%s,%s;1.1,1.1;%s]', ox-0.05,oy-0.05, img:render())) end end table.insert(lines, string.format('listcolors[#00000000;#ffffff10]')) -- FIXME table.insert(lines, string.format('list[%s;%s;%s,%s;%s,%s;%s]', E(def.target), E(def.inv), state.x, state.y, def.w, def.h, def.idx)) local sm = 1 state.w = def.w * sm + (spac * (def.w - 1)) state.h = def.h * sm + (spac * (def.h - 1)) elseif def.kind == 'contact' then if def.color then table.insert(lines, btnColorDef(def.id)) end widget('image_button%s[%s,%s;%s,%s;%s;%s;%s]', def.close and '_exit' or '', state.x, state.y, def.w, def.h, E(def.img), E(def.id), E(def.label or '')) elseif def.kind == 'button' then if def.color then table.insert(lines, btnColorDef(def.id)) end local label = E(def.label or '') if state.fg then label = lib.color(state.fg):fmt(label) end widget('button%s[%s,%s;%s,%s;%s;%s]', def.close and '_exit' or '', state.x, state.y, def.w, def.h, E(def.id), label) elseif def.kind == 'img' then widget('%s[%s,%s;%s,%s;%s]', def.item and 'item_image' or 'image', state.x, state.y, def.w, def.h, E(def.item or def.img)) elseif def.kind == 'label' then local txt = E(def.text) if state.fg then txt = lib.color(state.fg):fmt(txt) end widget('label[%s,%s;%s]', state.x, state.y + def.h*.5, txt) elseif def.kind == 'text' then -- TODO paragraph formatter widget('hypertext[%s,%s;%s,%s;%s;%s]', state.x, state.y, def.w, def.h, E(def.id), E(def.text)) elseif def.kind == 'hbar' or def.kind == 'vbar' then -- TODO fancy image bars local cl = lib.color(state.color) local fg = state.fg or cl:readable(.8,1) local wfac, hfac = 1,1 local clamp = math.min(math.max(def.fac, 0), 1) if def.kind == 'hbar' then wfac = wfac * clamp else hfac = hfac * clamp end local x,y, w,h = state.x, state.y, def.w, def.h widget('box[%s,%s;%s,%s;%s]', x,y, w,h, cl:brighten(0.2):hex()) widget('box[%s,%s;%s,%s;%s]', x, y + (h*(1-hfac)), w * wfac, h * hfac, cl:hex()) if def.text then widget('hypertext[%s,%s;%s,%s;;%s]', state.x, state.y, def.w, def.h, string.format('<global halign=center valign=middle color=%s>%s', fg:hex(), E(def.text))) end end if def.desc then widget('tooltip[%s,%s;%s,%s;%s]', state.x, state.y, def.w, def.h, E(def.desc)) end local originX = (parent and parent.x or 0) local originY = (parent and parent.y or 0) local l = table.concat(lines) -- if state.fixed and (state.w < state.x or state.h < state.y) then -- l = string.format('scroll_container[%s,%s;%s,%s;scroll_%s;%s]%sscroll_container_end[]', -- (parent and parent.x or 0), (parent and parent.y or 0), -- state.w, state.h, state.gen, -- (state.x > state.w) and 'horizontal' or 'vertical', l) -- end if def.mode or def.container then if def.mode then l = string.format('background9[%s,%s;%s,%s;%s;false;64]', originX, originY, state.w, state.h, E(string.format('starlit-ui-bg-%s.png%s^[resize:128x128', (def.mode == 'sw') and 'digital' or 'panel', cmod))) .. l end if parent == nil or state.color ~= parent.color then l = btnColorDef() .. l end end if not parent then return string.format('formspec_version[6]size[%s,%s]%s', state.w, state.h, l), state else return l, state end end starlit.ui.tooltip = lib.ui.tooltipper { colors = { -- generic notes neutral = lib.color(.5,.5,.5); good = lib.color(.2,1,.2); bad = lib.color(1,.2,.2); info = lib.color(.4,.4,1); -- chip notes schemaic = lib.color(.2,.7,1); ability = lib.color(.7,.2,1); driver = lib.color(1,.7,.2); }; } |
Added mods/starlit/user.lua version [aee7410825].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 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 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 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 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 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 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 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 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 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 |
-- [ʞ] user.lua -- ~ lexi hale <lexi@hale.su> -- © EUPL v1.2 -- ? defines the starlit.type.user class, which is -- the main interface between the game world and the -- client. it provides for initial signup and join, -- managing the HUD, skinning the player model, -- effecting weather changes, etc. local lib = starlit.mod.lib local function hudAdjustBacklight(img) local night = math.abs(minetest.get_timeofday() - .5) * 2 local opacity = night*0.8 return img:fade(opacity) end local userStore = lib.marshal.metaStore { persona = { key = 'starlit:persona'; type = starlit.store.persona; }; } local suitStore = starlit.store.suitMeta starlit.type.user = lib.class { name = 'starlit:user'; construct = function(ident) local name, luser if type(ident) == 'string' then name = ident luser = minetest.get_player_by_name(name) else luser = ident name = luser:get_player_name() end return { entity = luser; name = name; hud = { elt = {}; }; tree = {}; action = { bits = 0; -- for control deltas prog = {}; -- for recording action progress on a node; reset on refocus tgt = {type='nothing'}; sfx = {}; fx = {}; }; actMode = 'off'; power = { nano = {primary = nil, secondary = nil}; 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' 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 base if abs then local min, max min, max, base = self:statRange(stat) if d == true then d = max elseif d == false then d = min end end if stat == 'health' then self.entity:set_hp(abs and d or (self.entity:get_hp() + d), cause) elseif stat == 'breath' then self.entity:set_breath(abs and d or (self.entity:get_breath() + d)) else if abs then dt[stat] = d - base else dt[stat] = dt[stat] + d 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) local s,v = self:lookupSpecies() return v.traits[trait] or s.traits[trait] or 0 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) if stat == 'health' then val = self.entity:get_hp() elseif stat == 'breath' then val = self.entity:get_breath() 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; alignment = def.align; position = def.pos; offset = def.ofs; z_index = def.z; } if def.update then img.update = function() def.update(user, function(prop, val) user:hud_change(img.id, prop, val) end, def) end end return img end; attachMeter = function(self, def) local luser = self.entity local m = {} local w = def.size or 80 local szf = w / 80 local h = szf * 260 m.meter = luser:hud_add { type = 'image'; scale = {x = szf, y = szf}; alignment = def.align; position = def.pos; offset = def.ofs; z_index = def.z or 0; } local cx = def.ofs.x + (w/2)*def.align.x local cy = def.ofs.y + (h/2)*def.align.y local oy = cy + h/2 - 42 -- this is so fucking fragile holy fuck m.readout = luser:hud_add { type = 'text'; scale = {x = w, y = h}; size = szf; style = 4; position = def.pos; alignment = {x=0,0}; offset = {x = cx, y = oy}; z_index = (def.z or 0)+1; number = 0xffffff; } m.destroy = function() luser:hud_remove(m.meter) luser:hud_remove(m.readout) end m.update = function() local v,txt,color,txtcolor = def.measure(luser,def) v = math.max(0, math.min(1, v)) local n = math.floor(v*16) + 1 local img = hudAdjustBacklight(lib.image('starlit-ui-meter.png')) :colorize(color or def.color) if def.flipX then img = img:transform 'FX' end img = img:render() img = img .. '^[verticalframe:17:' .. tostring(17 - n) luser:hud_change(m.meter, 'text', img) if txt then luser:hud_change(m.readout, 'text', txt) end if txtcolor then luser:hud_change(m.readout, 'number', txtcolor:hex()) end end return m end; attachTextBox = function(self, def) local luser = self.entity local box = {} box.id = luser:hud_add { type = 'text'; text = ''; alignment = def.align; number = def.color and def.color:int24() or 0xFFffFF; scale = def.bound; size = {x = def.size, y=0}; style = def.style; position = def.pos; offset = def.ofs; } box.update = function() local text, color = def.text(self, box, def) luser:hud_change(box.id, 'text', text) if color then luser:hud_change(box.id, 'number', color:int24()) end end return box end; attachStatBar = function(self, def) local luser = self.entity local bar = {} local img = lib.image 'starlit-ui-bar.png' local colorized = img if type(def.color) ~= 'function' then colorized = colorized:shift(def.color) end bar.id = luser:hud_add { type = 'statbar'; position = def.pos; offset = def.ofs; name = def.name; text = colorized:render(); text2 = img:tint{hue=0, sat=-1, lum = -0.5}:fade(0.5):render(); number = def.size; item = def.size; direction = def.dir; alignment = def.align; size = {x=4,y=24}; } bar.update = function() local sv, sf = def.stat(self, bar, def) luser:hud_change(bar.id, 'number', def.size * sf) if type(def.color) == 'function' then local clr = def.color(sv, luser, sv, sf) luser:hud_change(bar.id, 'text', img:tint(clr):render()) end end return bar, {x=3 * def.size, y=16} -- x*2??? what end; createHUD = function(self) local function basicStat(statName) return function(user, bar) return self:effectiveStat(statName) end end local function batteryLookup(user) local max = user:suitPowerCapacity() if max == 0 then return 0, 0 end local ch = user:suitCharge() return (ch/max)*100, ch/max end local function C(h,s,l) return {hue=h,sat=s,lum=l} end local hbofs = (1+self.entity:hud_get_hotbar_itemcount()) * 25 local bpad = 8 self.hud.elt.health = self:attachStatBar { name = 'health', stat = basicStat 'health'; color = C(340,0,.3), size = 100; pos = {x=0.5, y=1}, ofs = {x = -hbofs, y=-48 - bpad}; dir = 1; align = {x=-1, y=-1}; } self.hud.elt.stamina = self:attachStatBar { name = 'stamina', stat = basicStat 'stamina'; color = C(60,0,.2), size = 100; pos = {x=0.5, y=1}, ofs = {x = -hbofs, y=-24 - bpad}; dir = 1; align = {x=-1, y=-1}; } self.hud.elt.bat = self:attachStatBar { name = 'battery', stat = batteryLookup; color = C(190,0,.2), size = 100; pos = {x=0.5, y=1}, ofs = {x = hbofs - 4, y=-48 - bpad}; dir = 0; align = {x=1, y=-1}; } self.hud.elt.psi = self:attachStatBar { name = 'psi', stat = basicStat 'psi'; color = C(320,0,.2), size = 100; pos = {x=0.5, y=1}, ofs = {x = hbofs - 4, y=-24 - bpad}; dir = 0; align = {x=1, y=-1}; } self.hud.elt.time = self:attachTextBox { name = 'time'; align = {x=0, y=1}; pos = {x=0.5, y=1}; ofs = {x=0,y=-95}; text = function(user) local cal = starlit.world.time.calendar[user.pref.calendar] return cal.time(minetest.get_timeofday()) end; } self.hud.elt.temp = self:attachMeter { name = 'temp'; align = {x=1, y=-1}; pos = {x=0, y=1}; ofs = {x=20, y=-20}; measure = function(user) local warm = self:effectiveStat 'warmth' local n, color if warm < 0 then n = math.min(100, -warm) color = lib.color(0.1,0.3,1):lerp(lib.color(0.7, 1, 1), math.min(1, n/50)) else n = math.min(100, warm) color = lib.color(0.1,0.3,1):lerp(lib.color(1, 0, 0), math.min(1, n/50)) end local txt = string.format("%s°", math.floor(warm)) return (n/50), txt, color end; } self.hud.elt.geiger = self:attachMeter { name = 'geiger'; align = {x=-1, y=-1}; pos = {x=1, y=1}; ofs = {x=-20, y=-20}; flipX = true; measure = function(user) local hot = self:effectiveStat 'irradiation' local color = self:uiColor():lerp(lib.color(0.3, 1, 0), math.min(1, hot/5)) local txt = string.format("%sGy", math.floor(hot)) return (hot/5), txt, color end; } self.hud.elt.crosshair = self:attachImage { name = 'crosshair '; tex = ''; pos = {x=.5, y=.5}; scale = {x=1,y=1}; ofs = {x=0, y=0}; align = {x=0, y=0}; update = function(user, set) local imgs = { off = ''; nano = 'starlit-ui-crosshair-nano.png'; psi = 'starlit-ui-crosshair-psi.png'; weapon = 'starlit-ui-crosshair-weapon.png'; } set('text', imgs[self.actMode] or imgs.off) end; }; local hudCenterBG = lib.image 'starlit-ui-hud-bg.png':colorize(self:uiColor()) self.hud.elt.bg = self:attachImage { name = 'hudBg'; tex = hudCenterBG:render(); pos = {x=.5, y=1}; scale = {x=1,y=1}; ofs = {x=0, y=0}; align = {x=0, y=-1}; z = -1; update = function(user, set) set('text', hudAdjustBacklight(hudCenterBG):render()) end; }; 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'; weapon = 'starlit-mode-weapon'; } local sfx = self.actMode and sfxt[self.actMode] or sfxt.off self:suitSound(sfx) end end; actModeSet = function(self, mode, silent) if not mode then mode = 'off' end 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('starlit_suit', 1) -- your environment suit (change at wardrobe) inv:set_size('starlit_cfg', 1) -- the item you're reconfiguring / container you're accessing local scenario for _, e in pairs(starlit.world.scenario) do if e.id == starlit.world.defaultScenario then scenario = e break end end assert(scenario) self.persona = starlit.world.species.birth(scenario.species, scenario.speciesVariant, self.entity) self.persona.name = self.entity:get_player_name() -- a reasonable default self.persona.background = starlit.world.defaultScenario self:pushPersona() local gifts = scenario.startingItems local inv = self.entity:get_inventory() inv:set_stack('starlit_suit', 1, starlit.item.mk(gifts.suit, self, {gift=true})) self:getSuit():establishInventories(self.entity) local function giveGifts(name, list) if inv:get_size(name) > 0 then for i, e in ipairs(list) do inv:add_item(name, starlit.item.mk(e, self, {gift=true})) end end end giveGifts('starlit_suit_bat', gifts.suitBatteries) giveGifts('starlit_suit_chips', gifts.suitChips) giveGifts('starlit_suit_guns', gifts.suitGuns) giveGifts('starlit_suit_ammo', gifts.suitAmmo) 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 startPoint 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 if cx > 10000 then break end -- avoid infiniloop in pathological conditions until temp > -2 self.entity:set_pos(startPoint) meta:set_string('starlit_spawn', startPoint:to_string()) end; onDie = function(self, reason) local inv = self.entity:get_inventory() local where = self.entity:get_pos() local function dropInv(lst) local l = inv:get_list(lst) for i, o in ipairs(l) do if o and not o:is_empty() then minetest.item_drop(o, self.entity, where) end end inv:set_list(lst, {}) end dropInv 'main' dropInv 'starlit_suit' self:statDelta('psi', 0, 'death', true) self:statDelta('hunger', 0, 'death', true) self:statDelta('thirst', 0, 'death', true) self:statDelta('fatigue', 0, 'death', true) self:statDelta('stamina', 0, 'death', true) self:updateSuit() end; onRespawn = function(self) local meta = self.entity:get_meta() self.entity:set_pos(vector.from_string(meta:get_string'starlit_spawn')) self:updateSuit() return true end; onJoin = function(self) local me = self.entity local meta = me:get_meta() self:pullPersona() -- formspec_version and real_coordinates are apparently just -- completely ignored here me:set_formspec_prepend [[ bgcolor[#00000000;true] style_type[button,button_exit,image_button,item_image_button;border=false] style_type[button;bgimg=starlit-ui-button-hw.png;bgimg_middle=8;content_offset=0,-2] style_type[button:hovered;bgimg=starlit-ui-button-hw-hover.png;bgimg_middle=8] style_type[button:pressed;bgimg=starlit-ui-button-hw-press.png;bgimg_middle=8;content_offset=0,1] ]] local hotbarSlots = me:get_inventory():get_size 'main'; -- local slotTex = 'starlit-ui-slot.png' -- local hbimg = string.format('[combine:%sx128', 128 * hotbarSlots) -- for i = 0, hotbarSlots-1 do -- hbimg = hbimg .. string.format(':%s,0=%s', 128 * i, slotTex) -- end --me:hud_set_hotbar_image(lib.image(hbimg):colorize(self:uiColor()):fade(.36):render()) -- me:hud_set_hotbar_selected_image(lib.image(slotTex):colorize(self:uiColor()):render()) me:hud_set_hotbar_image('[fill:1x24:0,0:' .. self:uiColor():fade(.1):hex()) me:hud_set_hotbar_selected_image( '[fill:1x24,0,0:' .. self:uiColor():fade(.4):hex() .. '^[fill:1x1:0,23:#ffFFffff' ) me:hud_set_hotbar_itemcount(hotbarSlots) me:hud_set_flags { hotbar = true; healthbar = false; breathbar = false; basic_debug = false; crosshair = false; } -- disable builtin crafting local inv = me:get_inventory() inv:set_size('craftpreview', 0) inv:set_size('craftresult', 0) inv:set_size('craft', 0) me:set_stars { day_opacity = 0.7; } me:set_sky { sky_color = { day_sky = '#a7c2cd', day_horizon = '#ddeeff'; dawn_sky = '#003964', dawn_horizon = '#87ebff'; night_sky = '#000000', night_horizon = '#000E29'; fog_sun_tint = '#72e4ff'; fog_moon_tint = '#2983d0'; fog_tint_type = 'custom'; }; fog = { -- not respected?? -- TODO make this seasonal & vary with weather fog_distance = 40; fog_start = 0.3; }; } me:set_sun { texture = 'starlit-sun.png'; sunrise = 'sunrisebg.png^[hsl:180:1:.7'; tonemap = 'sun_tonemap.png^[hsl:180:1:.7'; scale = 0.8; } me:set_lighting { shadows = { intensity = .5; }; exposure = { luminance_max = 3.0; speed_dark_bright = 0.5; speed_bright_dark = 1.0; }; volumetric_light = { strength = 0.3; }; } 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; suitPowerStateSet = function(self, state, silent) -- necessary to enable reacting to power state changes -- e.g. to play sound effects, display warnings local os self:forSuit(function(s) os=s:powerState() s:powerStateSet(state) end) if state == 'off' then if self.actMode == 'nano' or self.actMode == 'weapon' then self:actModeSet('off', silent) end end if not silent and os ~= state then local sfx if state == 'off' then sfx = 'starlit-power-down' elseif os == 'off' then 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 starlit.world.species.updateTextures(self.entity, self.persona, adornment) end; updateSuit = function(self) self:updateBody() local inv = self.entity:get_inventory() local sst = suitStore(self:suitStack()) if self:naked() then starlit.type.suit.purgeInventories(self.entity) if self.actMode == 'nano' or self.actMode == 'weapon' then self:actModeSet 'off' end else local suit = self:getSuit() suit:establishInventories(self.entity) if self:suitCharge() <= 0 then self:suitPowerStateSet 'off' end end self:updateHUD() end; reconfigureSuit = function(self) -- and here's where things get ugly -- you can't have an inventory inside another item. to hack around this, -- we use the player as the location of the suit inventories, and whenever -- there's a change in the content of these inventories, this function is -- called to serialize those inventories out to the suit stack if self:naked() then return end local suit = self:getSuit() suit:onReconfigure(self.entity:get_inventory()) self:setSuit(suit) -- reconfiguring the suit can affect player abilities: e.g. removing -- / inserting a chip with a minimap program end; getSuit = function(self) local st = self:suitStack() if st:is_empty() then return nil end return starlit.type.suit(st) end; setSuit = function(self, suit) self.entity:get_inventory():set_stack('starlit_suit', 1, suit.item) end; changeSuit = function(self, ...) self:setSuit(...) self:updateSuit() end; forSuit = function(self, fn) local s = self:getSuit() if fn(s) ~= false then self:setSuit(s) end end; suitPowerCapacity = function(self) -- TODO optimize if self:naked() then return 0 end return self:getSuit():powerCapacity() end; suitCharge = function(self) -- TODO optimize if self:naked() then return 0 end return self:getSuit():powerLeft() end; suitDrawCurrent = function(self, power, time, whatFor, min) if self:naked() then return 0,0 end local inv = self.entity:get_inventory() local bl = inv:get_list('starlit_suit_bat') local supply = 0 local wasteHeat = 0 --TODO handle internally for slot, ps in ipairs(bl) do if not ps:is_empty() then local p, h = starlit.mod.electronics.dynamo.drawCurrent(ps, power - supply, time) supply = supply + p wasteHeat = wasteHeat + h if power-supply <= 0 then break end end end if min and supply < min then return 0,0 end inv:set_list('starlit_suit_bat', bl) self:reconfigureSuit() if whatFor then -- 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) --print('trigger', which, dump(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 = {} elseif wld and not wld:is_empty() then local wdef = wld:get_definition() if wdef._starlit and wdef._starlit.tool then p = {tool = wdef._starlit.tool} end elseif self.actMode ~= 'off' then p = self.power[self.actMode][which] end if p == nil then return false end local ctx, run = { how = how; } if p.chipID then local inv = self.entity:get_inventory() local chips = inv:get_list 'starlit_suit_chips' for chSlot, ch in pairs(chips) do if ch and not ch:is_empty() then local d = starlit.mod.electronics.chip.read(ch) if d.uuid == p.chipID then local pgm = assert(d.files[p.pgmIndex], 'file missing for ability') ctx.file = starlit.mod.electronics.chip.fileHandle(ch, p.pgmIndex) ctx.saveChip = function() inv:set_slot('starlit_suit_chips', chSlot, ch) 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 if is 'powder' then if self:naked() then return item end local cans = inv:get_list 'starlit_suit_canisters' if cans and next(cans) then for i, st in ipairs(cans) do local lst = string.format('starlit_canister_%u_elem', i) item = inv:add_item(lst, item) if item:is_empty() then break end end end self:forSuit(function(x) x:pushCanisters(inv) end) end return item else return inv:add_item('main', item) end end; thrustUpon = function(self, item) local r = self:give(st) if not r:is_empty() then return minetest.add_item(self.entity:get_pos(), r) end end; }; } local biointerval = 3.0 starlit.startJob('starlit:bio', biointerval, function(delta) for id, u in pairs(starlit.activeUsers) do end end) local cbit = { up = 0x001; down = 0x002; left = 0x004; right= 0x008; jump = 0x010; manv = 0x020; snk = 0x040; dig = 0x080; put = 0x100; zoom = 0x200; } -- this is the painful part minetest.register_globalstep(function(delta) local doNothing,mustInit,mustHalt = 0,1,2 for id, user in pairs(starlit.activeUsers) do local ent = user.entity local bits = ent:get_player_control_bits() local function what(b) if bit.band(bits, b) ~= 0 and bit.band(user.action.bits, b) == 0 then return mustInit elseif bit.band(bits, b) == 0 and bit.band(user.action.bits, b) ~= 0 then 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) |
Added mods/starlit/world.lua version [830720f731].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 |
local lib = starlit.mod.lib local world = starlit.world function world.date() local days = minetest.get_day_count() local year = math.floor(days / world.planet.orbit); local day = days % world.planet.orbit; return { year = year, day = day; season = day / world.planet.orbit; } end local lerp = lib.math.lerp local function gradient(grad, pos) local n = #grad if n == 1 then return grad[1] end local op = pos*(n-1) local idx = math.floor(op) local t = op-idx return lerp(t, grad[1 + idx], grad[2 + idx]) end local altitudeCooling = 10 / 100 -- this function provides the basis for temperature calculation, -- which is performed by adding this value to the ambient temperature, -- determined by querying nearby group:heatSource items in accordance -- with the inverse-square law function world.climate.eval(pos, tod, season) local data = minetest.get_biome_data(pos) local biome = world.ecology.biomes.db[minetest.get_biome_name(data.biome)] local heat, humid = data.heat, data.humidity tod = tod or minetest.get_timeofday() heat = lerp(math.abs(tod - 0.5)*2, heat, heat + biome.nightTempDelta) local td = world.date() heat = heat + gradient(biome.seasonalTemp, season or td.season) if pos.y > 0 then heat = heat - pos.y*altitudeCooling end return { surfaceTemp = heat; waterTemp = heat + biome.waterTempDelta; surfaceHumid = humid; } end local vdsq = lib.math.vdsq function world.climate.temp(pos) --> irradiance at pos in W local cl = world.climate.eval(pos) local radCenters = starlit.region.radiator.store:get_areas_for_pos(pos, false, true) local irradiance = 0 for _,e in pairs(radCenters) do local rpos = minetest.string_to_pos(e.data) local rdef = assert(minetest.registered_nodes[assert(minetest.get_node(rpos)).name]) local rc = rdef._starlit.radiator local r_max = rc.radius(rpos) local dist_sq = vdsq(rpos,pos) if dist_sq <= r_max^2 then -- cheap bad way -- if minetest.line_of_sight(rpos,pos) then -- -- expensive way local obstruct = 0 local ray = Raycast(rpos, pos, true, true) for p in ray do if p.type == 'node' then obstruct = obstruct + 1 end end if obstruct < 4 then local power, customFalloff = rc.radiate(rpos, pos) -- okay this isn't the real inverse square law but i -- couldn't figure out a better way to simplify the -- model without checking an ENORMOUS number of nodes -- maybe someone else who isn't completely -- mathtarded can do better. if not customFalloff then power = power * (1 - (dist_sq / ((r_max+1)^2))) end power = power * (1 - (obstruct/5)) irradiance = irradiance + power end end end return irradiance + cl.surfaceTemp end world.ecology.biomes.foreach('starlit:biome-gen', {}, function(id, b) b.def.name = id minetest.register_biome(b.def) end) world.ecology.biomes.link('starlit:steppe', { nightTempDelta = -30; waterTempDelta = 0; -- W Sp Su Au W seasonalTemp = {-50, -10, 5, 5, -20, -50}; def = { node_top = 'starlit:greengraze', depth_top = 1; node_filler = 'starlit:soil', depth_filler = 4; node_riverbed = 'starlit:sand', depth_riverbed = 4; y_min = 0; y_max = 512; heat_point = 10; humidity_point = 30; }; }) world.ecology.biomes.link('starlit:ocean', { nightTempDelta = -35; waterTempDelta = 5; seasonalTemp = {0}; -- no seasonal variance def = { y_max = 3; y_min = -512; heat_point = 15; humidity_point = 50; node_top = 'starlit:sand', depth_top = 1; node_filler = 'starlit:sand', depth_filler = 3; }; }) local toward = lib.math.toward local hfinterval = 1.5 starlit.startJob('starlit:heatflow', hfinterval, function(delta) -- our base thermal conductivity (κ) is measured in °C/°C/s. say the -- player is in -30°C weather, and has an internal temperature of -- 10°C. then: -- κ = .1°C/C/s (which is apparently 100mHz) -- Tₚ = 10°C -- Tₑ = -30°C -- d = Tₑ − Tₚ = -40°C -- ΔT = κ×d = -.4°C/s -- our final change in temperature is computed as tΔC where t is time local kappa = .05 for name,user in pairs(starlit.activeUsers) do local tr = user:species().tempRange local t = starlit.world.climate.temp(user.entity:get_pos()) local insul = 0 local naked = user:naked() local suitDef if not naked then suitDef = user:suitStack():get_definition() insul = suitDef._starlit.suit.temp.insulation end local warm = user:effectiveStat 'warmth' local tSafeMin, tSafeMax = tr.survivable[1], tr.survivable[2] local tComfMin, tComfMax = tr.comfort[1], tr.comfort[2] local tDelta = (kappa * (1-insul)) * (t - warm) * hfinterval local tgt = warm + tDelta -- old logic: we move the user towards the exterior temperature, modulated -- by her suit insulation. --local tgt = toward(warm, t, hfinterval * thermalConductivity * (1 - insul)) if not naked then local suit = user:getSuit() local suitPower = suit:powerState() local suitPowerLeft = suit:powerLeft() if suitPower ~= 'off' then local coilPower = 1.0 local st = suitDef._starlit.suit.temp if suitPower == 'powerSave' and (tgt >= tSafeMin and tgt <= tSafeMax) then coilPower = 0.5 end if tgt < tComfMin and st.maxHeat > 0 then local availPower = user:suitDrawCurrent(st.heatPower*coilPower, hfinterval) tgt = tgt + (availPower / st.heatPower) * st.maxHeat * coilPower * hfinterval end if tgt > tComfMax and st.maxCool > 0 then local availPower = user:suitDrawCurrent(st.coolPower*coilPower, hfinterval) tgt = tgt - (availPower / st.coolPower) * st.maxCool * coilPower * hfinterval end end end user:statDelta('warmth', tgt - warm) -- dopey but w/e warm = tgt -- for the sake of readable code if warm < tSafeMin or warm > tSafeMax then local dv if warm < tSafeMin then dv = math.abs(warm - tSafeMin) else dv = math.abs(warm - tSafeMax) end -- for every degree of difference you suffer 2 points of damage/s local dmg = math.ceil(dv * 2) user:statDelta('health', -dmg) end end end) |
Deleted mods/starsoul-building/init.lua version [a42549c83d].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
local lib = starsoul.mod.lib local B = {} starsoul.mod.building = B B.path = {} -- this maps stage IDs to tables of the following form --[[ { part = { ['starsoul_building:pipe'] = 'myMod:stage3'; }; tool = { ['starsoul:scredriver'] = 'myMod:otherThing_stage1'; ['starsoul:saw'] = function(node, tool) minetest.replace_node(node, {name='myMod:stage1'}) minetest.drop_item(node, 'starsoul_building:pipe') end; ['myMod:laserWrench'] = { allow = function(node, tool) ... end; handle = function(node, tool) ... end; }; }; } ]] -- it should only be written by special accessor functions! B.stage = lib.registry.mk 'starsoul_building:stage' -- a stage consists of a list of pieces and maps from possible materials -- / tool usages to succeeding stages in the build tree. note that all -- used pieces must be defined before a stage is defined currently, due -- to a lack of cross-registry dependency mechanisms. i will hopefully -- improve vtlib to handle this condition eventually. --[[ starsoul.mod.building.stage.link(id, { pieces = { 'starsoul_building:foundation'; 'starsoul_building:insulation'; -- offset ofsFac {'starsoul_building:pipe', vector.new(-.5, 0, 0), 0};-- {'starsoul_building:pipe', vector.new(-.5, 0, 0), 1}; 'starsoul_building:insulation'; 'starsoul_building:panel'; }; }) ]] B.piece = lib.registry.mk 'starsoul_building:piece' -- a piece is used to produce stage definitions, by means of appending -- nodeboxes with appropriate offsets. it also lists the recoverable -- materials which can be obtained by destroying a stage containing -- this piece using nano. part IDs should correspond with piece IDs -- where possible --[[ starsoul.mod.building.piece.link(id, { tex = 'myMod_part.png'; height = 0.1; -- used for auto-offset fab = { element = {iron=10}; }; shape = { type = "fixed"; fixed = { ... }; }; }) ]] B.part = lib.registry.mk 'starsoul_building:part' -- a part is implemented as a special craftitem with the proper callbacks -- to index the registries and place/replace noes by reference to the -- build tree. --[[ starsoul.mod.building.part.link(id, { name = ''; -- display name desc = ''; -- display desc img = ''; -- display image }) ]] B.stage.foreach('starsoul:stageGen', {}, function(id, e) local box = {type = 'fixed', fixed = {}} local tex = {} local ofs = vector.new(0,0,0) for idx, p in ipairs(e.pieces) do local ho, pieceID, pos if type(p) == 'string' then pieceID, pos, ho = p, vector.zero(), 1.0 else pieceID, pos, ho = pc[1],pc[2],pc[3] end local pc = B.piece.db[pieceID] pos = pos + ofs if ho ~= 0.0 then ofs = vector.offset(ofs, 0, pc.height) end local sh = lib.node.boxwarped(pc.shape, function(b) -- { -x, -y, -z; -- +x, +y, +z } b[1] = b[1] + ofs.x b[4] = b[4] + ofs.x b[2] = b[2] + ofs.y b[5] = b[5] + ofs.y b[3] = b[3] + ofs.z b[6] = b[6] + ofs.z end) table.insert(box, sh) if type(pc.tex) == 'string' then table.insert(tex, pc.tex) else for i,t in ipairs(pc.tex) do table.insert(tex, t or '') end end end minetest.register_node(id, { description = 'Construction'; drawtype = 'nodebox'; paramtype = 'light'; paramtype2 = e.stateful or 'none'; textures = tex; node_box = box; group = { stage = 1 }; _starsoul = { stage = id; }; }) end) function B.pathLink(from, kind, what, to) if not B.path[from] then B.path[from] = {part={}, tool={}} end local k = B.path[from][kind] assert(k[what] == nil) k[what] = to end function B.pathFind(from, kind, what) if not B.path[from] then return nil end return B.path[from][kind][what] end |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul-building/mod.conf version [41dba78d03].
1 2 3 |
name = starsoul_building depends = starsoul_electronics, starsoul description = implements construction elements |
< < < |
Deleted mods/starsoul-electronics/init.lua version [4ca9d32d80].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 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 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 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 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 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 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 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 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 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 880 881 882 883 884 885 886 887 888 889 890 891 892 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 |
local lib = starsoul.mod.lib local E = {} starsoul.mod.electronics = E --------------------- -- item registries -- --------------------- -- a dynamo is any item that produces power and can be slotted into a power -- source slot. this includes batteries, but also things like radiothermal -- dynamos. starsoul.item.dynamo = lib.registry.mk 'starsoul_electronics:dynamo' -- batteries hold a charge of power (measured in kJ). how much they can hold -- (and how much power they can discharge?) depends on their quality starsoul.item.battery = lib.registry.mk 'starsoul_electronics:battery' -- a battery has the properties: -- class -- |- capacity (J ): amount of energy the battery can hold -- |- dischargeRate (W ): rate at which battery can supply power/be charged -- |- decay (J/J): rate at which the battery capacity degrades while -- discharging. decay=0 batteries require no maintenance; -- decay=1 batteries are effectively disposable -- |- leak (fac): charging inefficiency. depends on the energy storage -- technology. when N J are drawn from a power source, -- only (N*leak) J actually make it into the battery. -- leak=0 is a supercapacitor, leak=1 is /dev/null -- |- size (m): each suit has a limit to how big of a battery it can take -- instance -- |- degrade (mJ): how much the battery has degraded. instance max charge is -- | determined by $capacity - @degrade -- |- %wear ÷ 2¹⁶ : used as a factor to determine battery charge -- chips are standardized data storage hardware that can contain a certain amount -- of software. in addition to their flash storage, they also provide a given amount -- of working memory and processor power. processor power speeds up operations like -- crafting, while programs require a certain amount of memory. -- chips have a variable number of program slots and a single bootloader slot -- starsoul.item.chip = lib.registry.mk 'starsoul_electronics:chip' -- software is of one of the following types: -- schematic: program for your matter compiler that enables crafting a given item. -- output: the result -- driver: inserted into a Core to control attached hardware -- suitPower: provides suit functionality like nanoshredding or healing -- passive powers are iterated on suit application/configuration and upon fst-tick -- cost: what the software needs to run. some fields are fab-specific -- energy: for fab, total energy cost of process in joules -- for suitPassive, added suit power consumption in watts starsoul.item.sw = lib.registry.mk 'starsoul_electronics:sw' -- chip = lib.color(0, 0, .3); E.schematicGroups = lib.registry.mk 'starsoul_electronics:schematicGroups' E.schematicGroupMembers = {} E.schematicGroups.foreach('starsoul_electronics:ensure-memlist', {}, function(id,g) E.schematicGroupMembers[id] = {} end) function E.schematicGroupLink(group, item) table.insert(E.schematicGroupMembers[group], item) end E.schematicGroups.link('starsoul_electronics:chip', { title = 'Chip', icon = 'starsoul-item-chip.png'; description = 'Standardized data storage and compute modules'; }) E.schematicGroups.link('starsoul_electronics:battery', { title = 'Battery', icon = 'starsoul-item-battery.png'; description = 'Portable power storage cells are essential to all aspects of survival'; }) E.schematicGroups.link('starsoul_electronics:decayCell', { title = 'Decay Cell', icon = 'starsoul-item-decaycell.png'; description = "Radioisotope generators can pack much more power into a smaller amount of space than conventional batteries, but they can't be recharged, dump power and heat whether they're in use or not, and their power yield drops towards zero over their usable lifetime."; }) ------------------------- -- batteries & dynamos -- ------------------------- E.battery = {} local function accessor(ty, fn) return function(stack, ...) local function fail() error(string.format('object %q is not a %s', stack:get_name(), ty)) end if not stack or stack:is_empty() then fail() end if minetest.get_item_group(stack:get_name(), ty) == 0 then fail() end return fn(stack, stack:get_definition()._starsoul[ty], stack:get_meta(), ...) end end -- return a wear level that won't destroy the item -- local function safeWear(fac) return math.min(math.max(fac,0),1) * 0xFFFE end -- local function safeWearToFac(w) return w/0xFFFE end E.battery.update = accessor('battery', function(stack, batClass, meta) -- local cap = E.battery.capacity(stack) local charge = meta:get_float 'starsoul_electronics:battery_charge' meta:set_string('count_meta', string.format('%s%%', math.floor(charge * 100))) meta:set_int('count_alignment', bit.lshift(3, 2) + 2) end) -- E.battery.capacity(bat) --> charge (J) E.battery.capacity = accessor('battery', function(stack, batClass, meta) local dmg = meta:get_int 'starsoul_electronics:battery_degrade' -- µJ/μW local dmg_J = dmg / 1000 return (batClass.capacity - dmg_J) end) -- E.battery.charge(bat) --> charge (J) E.battery.charge = accessor('battery', function(stack, batClass, meta) local fac = meta:get_float 'starsoul_electronics:battery_charge' -- local fac = 1 - safeWearToFac(stack:get_wear()) return E.battery.capacity(stack) * fac end) -- E.battery.dischargeRate(bat) --> dischargeRate (W) E.battery.dischargeRate = accessor('battery', function(stack, batClass, meta) local dmg = meta:get_int 'starsoul_electronics:battery_degrade' -- µJ/μW local dmg_W = dmg / 1000 return batClass.dischargeRate - dmg_W end); -- E.battery.drawCurrent(bat, power, time, test) --> supply (J), wasteHeat (J) -- bat = battery stack -- power J = joules of energy user wishes to consume -- time s = the amount of time available for this transaction -- supply J = how much power was actually provided in $time seconds -- wasteHeat J = how heat is generated in the process -- test = if true, the battery is not actually modified E.battery.drawCurrent = accessor('battery', function(s, bc, m, power, time, test) local ch = E.battery.charge(s) local maxPower = math.min(E.battery.dischargeRate(s)*time, power, ch) ch = ch - maxPower if not test then local degrade = m:get_int 'starsoul_electronics:battery_degrade' or 0 degrade = degrade + maxPower * bc.decay -- for each joule of power drawn, capacity degrades by `decay` J -- this should ordinarily be on the order of mJ or smaller m:set_int('starsoul_electronics:battery_degrade', degrade) -- s:set_wear(safeWear(1 - (ch / E.battery.capacity(s)))) m:set_float('starsoul_electronics:battery_charge', ch / E.battery.capacity(s)) E.battery.update(s) end return maxPower, 0 -- FIXME specify waste heat end) -- E.battery.recharge(bat, power, time) --> draw (J) -- bat = battery stack -- power J = joules of energy user wishes to charge the battery with -- time s = the amount of time available for this transaction -- draw J = how much power was actually drawn in $time seconds E.battery.recharge = accessor('battery', function(s, bc, m, power, time) local ch = E.battery.charge(s) local cap = E.battery.capacity(s) local maxPower = math.min(E.battery.dischargeRate(s)*time, power) local total = math.min(ch + maxPower, cap) -- s:set_wear(safeWear(1 - (total/cap))) m:set_float('starsoul_electronics:battery_charge', total/cap) E.battery.update(s) return maxPower, 0 -- FIXME end) E.battery.setCharge = accessor('battery', function(s, bc, m, newPower) local cap = E.battery.capacity(s) local power = math.min(cap, newPower) -- s:set_wear(safeWear(1 - (power/cap))) m:set_float('starsoul_electronics:battery_charge', power/cap) E.battery.update(s) end) E.battery.setChargeF = accessor('battery', function(s, bc, m, newPowerF) local power = math.min(1.0, newPowerF) m:set_float('starsoul_electronics:battery_charge', power) E.battery.update(s) end) E.dynamo = { kind = {} } E.dynamo.drawCurrent = accessor('dynamo', function(s,c,m, power, time, test) return c.vtable.drawCurrent(s, power, time, test) end) E.dynamo.totalPower = accessor('dynamo', function(s,c,m) return c.vtable.totalPower(s) end) E.dynamo.dischargeRate = accessor('dynamo', function(s,c,m) return c.vtable.dischargeRate (s) end) E.dynamo.initialPower = accessor('dynamo', function(s,c,m) return c.vtable.initialPower(s) end) E.dynamo.wasteHeat = accessor('dynamo', function(s,c,m) return c.vtable.wasteHeat(s) end) -- baseline waste heat, produced whether or not power is being drawn. for batteries this is 0, but for -- radiothermal generators it may be high E.dynamo.kind.battery = { drawCurrent = E.battery.drawCurrent; totalPower = E.battery.charge; initialPower = E.battery.capacity; dischargeRate = E.battery.dischargeRate; wasteHeat = function() return 0 end; }; starsoul.item.battery.foreach('starsoul_electronics:battery-gen', {}, function(id, def) minetest.register_tool(id, { short_description = def.name; groups = { battery = 1; dynamo = 1; electronic = 1; }; inventory_image = def.img or 'starsoul-item-battery.png'; description = starsoul.ui.tooltip { title = def.name; desc = def.desc; color = lib.color(0,.2,1); props = { { title = 'Optimal Capacity', affinity = 'info'; desc = lib.math.si('J', def.capacity) }; { title = 'Discharge Rate', affinity = 'info'; desc = lib.math.si('W', def.dischargeRate) }; { title = 'Charge Efficiency', affinity = 'info'; desc = string.format('%s%%', (1-def.leak) * 100) }; { title = 'Size', affinity = 'info'; desc = lib.math.si('m', def.fab.size.print) }; }; }; _starsoul = { event = { create = function(st, how) --[[if not how.gift then -- cheap hack to make starting batteries fully charged E.battery.setCharge(st, 0) end]] E.battery.update(st) end; }; fab = def.fab; dynamo = { vtable = E.dynamo.kind.battery; }; battery = def; }; }) end) -- to use the power functions, consider the following situation. you have -- a high-tier battery charger that can draw 100kW. (for simplicity, assume -- it supports only one battery). if you install a low-tier battery, and -- the charging callback is called every five seconds, you might use -- a `recharge` call that looks like -- -- starsoul.mod.electronics.battery.recharge(bat, 5 * 100*1e4, 5) -- -- this would offer the battery 500kJ over five seconds. the battery will -- determine how much power it can actually make use of in 5 five seconds, -- and then return that amount. -- -- always remember to save the battery back to its inventory slot after -- modifying its ItemStack with one of these functions! -- battery types -- supercapacitor: low capacity, no degrade, high dischargeRate, no leak -- chemical: high capacity, high degrade, mid dischargeRate, low leak -- battery tiers -- makeshift: cheap, weak, low quality -- imperial ("da red wunz go fasta"): powerful, low quality -- commune ("snooty sophisticates"): limited power, high quality, expensive -- usukwinya ("value engineering"): high power, mid quality, affordable -- eluthrai ("uncompromising"): high power, high quality, wildly expensive -- firstborn ("god-tier"): exceptional local batteryTiers = { makeshift = { name = 'Makeshift'; capacity = .5, decay = 3, leak = 2, dischargeRate = 1, fab = starsoul.type.fab { metal = {copper=10}; }; desc = "Every attosecond this electrical abomination doesn't explode in your face is but the unearned grace of the Wild Gods."; complexity = 1; sw = {rarity = 1}; }; imperial = { name = 'Imperial'; capacity = 2, decay = 2, leak = 2, dischargeRate = 2; fab = starsoul.type.fab { metal = {copper=15, iron = 20}; size = { print = 0.1 }; }; desc = "The Empire's native technology is a lumbering titan: bulky, inefficient, unreliable, ugly, and awesomely powerful. Their batteries are no exception, with raw capacity and throughput that exceed even Usukinwya designs."; drm = 1; complexity = 2; sw = {rarity = 2}; }; commune = { name = 'Commune'; capacity = 1, decay = .5, leak = .2, dischargeRate = 1; fab = starsoul.type.fab { metal = {vanadium=50, steel=10}; size = { print = 0.05 }; }; desc = "The Commune's proprietary battery designs prioritize reliability, compactness, and maintenance concerns above raw throughput, with an elegance of engineering and design that would make a Su'ikuri cry."; complexity = 5; sw = {rarity = 3}; }; usukwinya = { name = 'Usukwinya'; capacity = 2, decay = 1, leak = 1, dischargeRate = 1.5, fab = starsoul.type.fab { metal = {vanadium=30, argon=10}; size = { print = 0.07 }; }; desc = "A race of consummate value engineers, the Usukwinya have spent thousands of years refining their tech to be as cheap to build as possible, without compromising much on quality. The Tradebirds drive an infamously hard bargain, but their batteries are more than worth their meagre cost."; drm = 2; sw = {rarity = 10}; complexity = 15; }; eluthrai = { name = 'Eluthrai'; capacity = 3, decay = .4, leak = .1, dischargeRate = 1.5, fab = starsoul.type.fab { metal = {beryllium=20, platinum=20, technetium = 1, cinderstone = 10 }; size = { print = 0.03 }; }; desc = "The uncompromising Eluthrai are never satisfied until every quantifiable characteristic of their tech is maximally optimised down to the picoscale. Their batteries are some of the best in the Reach, and unquestionably the most expensive -- especially for those lesser races trying to copy the designs without the benefit of the sublime autofabricator ecosystem of the Eluthrai themselves."; complexity = 200; sw = {rarity = 0}; -- you think you're gonna buy eluthran schematics on SuperDiscountNanoWare.space?? }; firstborn = { name = 'Firstborn'; capacity = 5, decay = 0.1, leak = 0, dischargeRate = 3; fab = starsoul.type.fab { metal = {neodymium=20, xenon=150, technetium=5, sunsteel = 10 }; crystal = {astrite = 1}; size = { print = 0.05 }; }; desc = "Firstborn engineering seamlessly merges psionic effects with a mastery of the physical universe unattained by even the greatest of the living Starsouls. Their batteries reach levels of performance that strongly imply Quantum Gravity Theory -- and several major holy books -- need to be rewritten. From the ground up."; complexity = 1000; sw = {rarity = 0}; -- lol no }; } local batterySizes = { small = {name = 'Small', capacity = .5, dischargeRate = .5, complexity = 1, matMult = .5, fab = starsoul.type.fab {size={print=0.1}}}; mid = { capacity = 1, dischargeRate = 1, complexity = 1, matMult = 1, fab = starsoul.type.fab {size={print=0.3}}}; large = {name = 'Large', capacity = 2, dischargeRate = 1.5, complexity = 1, matMult = 1.5, fab = starsoul.type.fab {size={print=0.5}}}; huge = {name = 'Huge', capacity = 3, dischargeRate = 2, complexity = 1, matMult = 2, fab = starsoul.type.fab {size={print=0.8}}}; } local batteryTypes = { supercapacitor = { name = 'Supercapacitor'; desc = 'Room-temperature superconductors make for very reliable, high-dischargeRate, but low-capacity batteries.'; fab = starsoul.type.fab { metal = { enodium = 5 }; size = {print=0.8}; }; sw = { cost = { cycles = 5e9; -- 5 bil cycles ram = 10e9; -- 10GB }; pgmSize = 2e9; -- 2GB rarity = 5; }; capacity = 50e3, dischargeRate = 1000; leak = 0, decay = 1e-6; complexity = 3; }; chemical = { name = 'Chemical'; desc = ''; fab = starsoul.type.fab { element = { lithium = 3}; metal = {iron = 5}; size = {print=1.0}; }; sw = { cost = { cycles = 1e9; -- 1 bil cycles ram = 2e9; -- 2GB }; pgmSize = 512e6; -- 512MB rarity = 2; }; capacity = 200e3, dischargeRate = 200; leak = 0.2, decay = 1e-2; complexity = 1; }; carbon = { name = 'Carbon'; desc = 'Carbon nanotubes form the basis of many important metamaterials, chief among them power-polymer.'; capacity = 1; fab = starsoul.type.fab { element = { carbon = 40 }; size = {print=0.5}; }; sw = { cost = { cycles = 50e9; -- 50 bil cycles ram = 64e9; -- 64GB }; pgmSize = 1e9; -- 1GB rarity = 10; }; capacity = 100e3, dischargeRate = 500; leak = 0.1, decay = 1e-3; complexity = 10; }; hybrid = { name = 'Hybrid'; desc = ''; capacity = 1; fab = starsoul.type.fab { element = { lithium = 3; }; metal = { iron = 5; }; size = {print=1.5}; }; sw = { cost = { cycles = 65e9; -- 65 bil cycles ram = 96e9; -- 96GB }; pgmSize = 5e9; -- 5GB rarity = 15; }; capacity = 300e3, dischargeRate = 350; leak = 0.3, decay = 1e-5; complexity = 30; }; } local function elemath(dest, src, mult) dest = dest or {} for k,v in pairs(src) do if not dest[k] then dest[k] = 0 end dest[k] = dest[k] + v*mult end return dest end for bTypeName, bType in pairs(batteryTypes) do for bTierName, bTier in pairs(batteryTiers) do for bSizeName, bSize in pairs(batterySizes) do -- elemath(elementCost, bType.fab.element or {}, bSize.matMult) -- elemath(elementCost, bTier.fab.element or {}, bSize.matMult) -- elemath(metalCost, bType.fab.metal or {}, bSize.matMult) -- elemath(metalCost, bTier.fab.metal or {}, bSize.matMult) local fab = bType.fab + bTier.fab + bSize.fab + starsoul.type.fab { element = {copper = 10, silicon = 5}; } local baseID = string.format('battery_%s_%s_%s', bTypeName, bTierName, bSizeName) local id = 'starsoul_electronics:'..baseID local name = string.format('%s %s Battery', bTier.name, bType.name) if bSize.name then name = bSize.name .. ' ' .. name end local function batStat(s) if s == 'size' then --return bType.fab[s] * (bTier.fab[s] or 1) * (bSize.fab[s] or 1) return fab.size and fab.size.print or 1 else return bType[s] * (bTier[s] or 1) * (bSize[s] or 1) end end local swID = 'starsoul_electronics:schematic_'..baseID fab.reverseEngineer = { complexity = bTier.complexity * bSize.complexity * bType.complexity; sw = swID; } fab.flag = {print=true} starsoul.item.battery.link(id, { name = name; desc = table.concat({ bType.desc or ''; bTier.desc or ''; bSize.desc or ''; }, ' '); fab = fab; capacity = batStat 'capacity'; dischargeRate = batStat 'dischargeRate'; leak = batStat 'leak'; decay = batStat 'decay'; }) local rare if bType.sw.rarity == 0 or bTier.sw.rarity == 0 then -- rarity is measured such that the player has a 1/r -- chance of finding a given item, or if r=0, no chance -- whatsoever (the sw must be obtained e.g. by reverse- -- engineering alien tech) rare = 0 else rare = bType.sw.rarity + bTier.sw.rarity end starsoul.item.sw.link(swID, { kind = 'schematic'; name = name .. ' Schematic'; output = id; size = bType.sw.pgmSize; cost = bType.sw.cost; rarity = rare; }) E.schematicGroupLink('starsoul_electronics:battery', swID) end end end ----------- -- chips -- ----------- E.sw = {} function E.sw.findSchematicFor(item) local id = ItemStack(item):get_name() print(id) local fm = minetest.registered_items[id]._starsoul if not (fm and fm.fab and fm.fab.reverseEngineer) then return nil end local id = fm.fab.reverseEngineer.sw return id, starsoul.item.sw.db[id] end E.chip = { file = {} } do local T,G = lib.marshal.t, lib.marshal.g -- love too reinvent unions from first principles E.chip.data = G.struct { label = T.str; uuid = T.u64; files = G.array(16, G.class(G.struct { kind = G.enum { 'sw'; -- a piece of installed software 'note'; -- a user-readable text file 'research'; -- saved RE progress 'genome'; -- for use with plant biosequencer? 'blob'; -- opaque binary blob, so 3d-pty mods can use the -- file mechanism to store arbirary data. }; drm = T.u8; -- inhibit copying name = T.str; body = T.text; }, function(file) -- enc local b = E.chip.file[file.kind].enc(file.body) return { kind = file.kind; drm = file.drm; name = file.name; body = b; } end, function(file) -- dec local f, ns = E.chip.file[file.kind].dec(file.body) file.body = f return file, ns end)); bootSlot = T.u8; -- indexes into files; 0 = no bootloader } E.chip.file.sw = G.struct { pgmId = T.str; conf = G.array(16, G.struct { key = T.str, value = T.str; }); } E.chip.file.note = G.struct { author = T.str; entries = G.array(16, G.struct { title = T.str; body = T.str; }); } E.chip.file.research = G.struct { itemId = T.str; progress = T.clamp; } E.chip.file.blob = G.struct { kind = T.str; -- MT ID that identifies a blob file type belonging to an external mod size = T.u8; -- this must be manually reported since we don't know how to evaluate it data = T.text; } function E.chip.fileSize(file) -- boy howdy if file.kind == 'blob' then return file.body.size elseif file.kind == 'note' then local sz = 0x10 + #file.body.author for _, e in pairs(file.body.entries) do sz = sz + #e.title + #e.body + 0x10 -- header overhead end return sz elseif file.kind == 'research' then local re = assert(minetest.registered_items[file.body.itemId]._starsoul.fab.reverseEngineer) return starsoul.item.sw.db[re.sw].size * file.body.progress elseif file.kind == 'sw' then return starsoul.item.sw.db[file.body.pgmId].size elseif file.kind == 'genome' then return 0 -- TODO end end local metaKey = 'starsoul_electronics:chip' function E.chip.read(chip) local m = chip:get_meta() local blob = m:get_string(metaKey) if blob and blob ~= '' then return E.chip.data.dec(lib.str.meta_dearmor(blob)) else -- prepare to format the chip return { label = ''; bootSlot = 0; uuid = math.floor(math.random(0,2^32)); files = {}; } end end function E.chip.write(chip, data) local m = chip:get_meta() m:set_string(metaKey, lib.str.meta_armor(E.chip.data.enc(data))) E.chip.update(chip) end function E.chip.fileOpen(chip, inode, fn) local c = E.chip.read(chip) if fn(c.files[inode]) then E.chip.write(chip, c) return true end return false end function E.chip.fileWrite(chip, inode, file) local c = E.chip.read(chip) c.files[inode] = file E.chip.write(chip, c) end function E.chip.usedSpace(chip, d) d = d or E.chip.read(chip) local sz = 0 for _, f in pairs(d.files) do sz = sz + E.chip.fileSize(f) end return sz end function E.chip.freeSpace(chip, d) local used = E.chip.usedSpace(chip,d) local max = assert(chip:get_definition()._starsoul.chip.flash) return max - used end function E.chip.install(chip, file) -- remember to write out the itemstack after using this function! local d = E.chip.read(chip) if E.chip.freeSpace(chip, d) - E.chip.fileSize(file) >= 0 then table.insert(d.files, file) E.chip.write(chip, d) return true else return false end end end function E.chip.files(ch) local m = ch:get_meta() if not m:contains 'starsoul_electronics:chip' then return nil end local data = E.chip.read(ch) local f = 0 return function() f = f + 1 return data.files[f], f end end function E.chip.describe(ch, defOnly) local def, data if defOnly then def, data = ch, {} else def = ch:get_definition() local m = ch:get_meta() if m:contains 'starsoul_electronics:chip' then data = E.chip.read(ch) else data = {} defOnly = true end def = assert(def._starsoul.chip) end local props = { {title = 'Clock Rate', affinity = 'info'; desc = lib.math.si('Hz', def.clockRate)}; {title = 'RAM', affinity = 'info'; desc = lib.math.si('B', def.ram)}; } if not defOnly then table.insert(props, { title = 'Free Storage', affinity = 'info'; desc = lib.math.si('B', E.chip.freeSpace(ch, data)) .. ' / ' .. lib.math.si('B', def.flash); }) local swAffMap = { schematic = 'schematic'; suitPower = 'ability'; driver = 'driver'; } for i, e in ipairs(data.files) do local aff = 'neutral' local name = e.name local disabled = false if e.kind == 'sw' then for _,cf in pairs(e.body.conf) do if cf.key == 'disable' and cf.value == 'yes' then disabled = true break end end local sw = starsoul.item.sw.db[e.body.pgmId] aff = swAffMap[sw.kind] or 'good' if name == '' then name = sw.name end end name = name or '<???>' table.insert(props, disabled and { title = name; affinity = aff; desc = '<off>'; } or { --title = name; affinity = aff; desc = name; }) end else table.insert(props, { title = 'Flash Storage', affinity = 'info'; desc = lib.math.si('B', def.flash); }) end return starsoul.ui.tooltip { title = data.label and data.label~='' and string.format('<%s>', data.label) or def.name; color = lib.color(.6,.6,.6); desc = def.desc; props = props; }; end function E.chip.update(chip) chip:get_meta():set_string('description', E.chip.describe(chip)) end starsoul.item.chip.foreach('starsoul_electronics:chip-gen', {}, function(id, def) minetest.register_craftitem(id, { short_description = def.name; description = E.chip.describe(def, true); inventory_image = def.img or 'starsoul-item-chip.png'; groups = {chip = 1}; _starsoul = { fab = def.fab; chip = def; }; }) end) -- in case other mods want to define their own tiers E.chip.tiers = lib.registry.mk 'starsoul_electronics:chipTiers' E.chip.tiers.meld { -- GP chips tiny = {name = 'Tiny Chip', clockRate = 512e3, flash = 4096, ram = 1024, powerEfficiency = 1e9, size = 1}; small = {name = 'Small Chip', clockRate = 128e6, flash = 512e6, ram = 512e6, powerEfficiency = 1e8, size = 3}; med = {name = 'Chip', clockRate = 1e9, flash = 4e9, ram = 4e9, powerEfficiency = 1e7, size = 6}; large = {name = 'Large Chip', clockRate = 2e9, flash = 8e9, ram = 8e9, powerEfficiency = 1e6, size = 8}; -- specialized chips compute = {name = 'Compute Chip', clockRate = 4e9, flash = 24e6, ram = 64e9, powerEfficiency = 1e8, size = 4}; data = {name = 'Data Chip', clockRate = 128e3, flash = 2e12, ram = 32e3, powerEfficiency = 1e5, size = 4}; lp = {name = 'Low-Power Chip', clockRate = 128e6, flash = 64e6, ram = 1e9, powerEfficiency = 1e10, size = 4}; carbon = {name = 'Carbon Chip', clockRate = 64e6, flash = 32e6, ram = 2e6, powerEfficiency = 2e9, size = 2, circ='carbon'}; } E.chip.tiers.foreach('starsoul_electronics:genChips', {}, function(id, t) id = t.id or string.format('%s:chip_%s', minetest.get_current_modname(), id) local circMat = t.circ or 'silicon'; starsoul.item.chip.link(id, { name = t.name; clockRate = t.clockRate; flash = t.flash; ram = t.ram; powerEfficiency = t.powerEfficiency; -- cycles per joule fab = { flag = { silicompile = true; }; time = { silicompile = t.size * 24*60; }; cost = { energy = 50e3 + t.size * 15e2; }; element = { [circMat] = 50 * t.size; copper = 30; gold = 15; }; }; }) end) function E.chip.findBest(test, ...) local chip, bestFitness for id, c in pairs(starsoul.item.chip.db) do local fit, fitness = test(c, ...) if fit and (bestFitness == nil or fitness > bestFitness) then chip, bestFitness = id, fitness end end return chip, starsoul.item.chip.db[chip], bestFitness end function E.chip.findForStorage(sz) return E.chip.findBest(function(c) return c.flash >= sz, -math.abs(c.flash - sz) end) end function E.chip.sumCompute(chips) local c = { cycles = 0; ram = 0; flashFree = 0; powerEfficiency = 0; } local n = 0 for _, e in pairs(chips) do n = n + 1 if not e:is_empty() then local ch = e:get_definition()._starsoul.chip c.cycles = c.cycles + ch.clockRate c.ram = c.ram + ch.clockRate c.flashFree = c.flashFree + E.chip.freeSpace(e) c.powerEfficiency = c.powerEfficiency + ch.powerEfficiency end end if n > 0 then c.powerEfficiency = c.powerEfficiency / n end return c end E.chip.fileHandle = lib.class { __name = 'starsoul_electronics:chip.fileHandle'; construct = function(chip, inode) -- stack, int --> fd return { chip = chip, inode = inode } end; __index = { read = function(self) local dat = E.chip.read(self.chip) return dat.files[self.inode] end; write = function(self,data) -- print('writing', self.chip, self.inode) return E.chip.fileWrite(self.chip, self.inode, data) end; erase = function(self) local dat = E.chip.read(self.chip) table.remove(dat.files, self.inode) E.chip.write(self.chip, dat) self.inode = nil end; open = function(self,fn) return E.chip.fileOpen(self.chip, self.inode, fn) end; }; } function E.chip.usableSoftware(chips,pgm) local comp = E.chip.sumCompute(chips) local r = {} local unusable = {} local sw if pgm then if type(pgm) == 'string' then pgm = {starsoul.item.sw.db[pgm]} end sw = pgm else sw = {} for i, e in ipairs(chips) do if (not e:is_empty()) and minetest.get_item_group(e:get_name(), 'chip') ~= 0 then for fl, inode in E.chip.files(e) do if fl.kind == 'sw' then local s = starsoul.item.sw.db[fl.body.pgmId] table.insert(sw, { sw = s, chip = e, chipSlot = i; file = fl, inode = inode; }) end end end end end for _, s in pairs(sw) do if s.sw.cost.ram <= comp.ram then table.insert(r, { sw = s.sw; chip = s.chip, chipSlot = s.chipSlot; file = s.file; fd = E.chip.fileHandle(s.chip, s.inode); speed = s.sw.cost.cycles / comp.cycles; powerCost = s.sw.cost.cycles / comp.powerEfficiency; comp = comp; }) else table.insert(unusable, { sw = s.sw; chip = s.chip; ramNeeded = s.sw.cost.ram - comp.ram; }) end end return r, unusable end starsoul.include 'sw' |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul-electronics/mod.conf version [8bb64074c2].
1 2 3 |
name = starsoul_electronics description = basic electronic components and logic depends = starsoul |
< < < |
Deleted mods/starsoul-electronics/sw.lua version [3b2a0cfbed].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
-- [ʞ] sw.lua -- ~ lexi hale <lexi@hale.su> -- 🄯 EUPL v1.2 -- ? ------------------------------- -- basic suit nano abilities -- ------------------------------- local function shredder(prop) local function getItemsForFab(fab) local elt if fab then elt = fab:elementalize() else elt = {} end local items = {} if elt.element then for k,v in pairs(elt.element) do local st = ItemStack { name = starsoul.world.material.element.db[k].form.element; count = v; } table.insert(items, st) end end return items end return function(user, ctx) local function cleanup() user.action.prog.shred = nil if user.action.sfx.shred then minetest.sound_fade(user.action.sfx.shred, 1, 0) user.action.sfx.shred = nil end if user.action.fx.shred then user.action.fx.shred.abort() end end if user.action.tgt.type ~= 'node' then return end local what = user.action.tgt.under if what == nil or user.entity:get_pos():distance(what) > prop.range then cleanup() return false end local shredTime = 1.0 local soundPitch = 1.0 -- TODO local pdraw = prop.powerDraw or 0 local node = minetest.get_node(what) local nd = minetest.registered_nodes[node.name] local elt, fab, vary if nd._starsoul then fab = nd._starsoul.recover or nd._starsoul.fab vary = nd._starsoul.recover_vary end if fab then if fab.flag then if fab.flag.unshreddable then cleanup() return false -- TODO error beep end end shredTime = fab.time and fab.time.shred or shredTime -- FIXME if fab.cost and fab.cost.shredPower then pdraw = pdraw * fab.cost.shredPower end end local maxW = user:getSuit():maxPowerUse() if maxW < pdraw then shredTime = shredTime * (pdraw/maxW) pdraw = maxW end if ctx.how.state == 'prog' then local pdx = pdraw * ctx.how.delta local p = user:suitDrawCurrent(pdx, ctx.how.delta, {kind='nano',label='Shredder'}, pdx) if p < pdx then cleanup() return false elseif not user.action.prog.shred then cleanup() -- kill danglers -- begin user.action.prog.shred = 0 user.action.sfx.shred = minetest.sound_play('starsoul-nano-shred', { object = user.entity; max_hear_distance = prop.range*2; loop = true; pitch = soundPitch; }) user.action.fx.shred = starsoul.fx.nano.shred(user, what, prop, shredTime, node) else user.action.prog.shred = user.action.prog.shred + ctx.how.delta or 0 end --print('shred progress: ', user.action.prog.shred) if user.action.prog.shred >= shredTime then if minetest.dig_node(what) then --print('shred complete') user:suitSound 'starsoul-success' if fab then local vf = fab if vary then local rng = (starsoul.world.seedbank+0xa891f62)[minetest.hash_node_position(what)] vf = vf + vary(rng, {}) end local items = getItemsForFab(vf) for i, it in ipairs(items) do user:give(it) end end else user:suitSound 'starsoul-error' end cleanup() end elseif ctx.how.state == 'halt' then cleanup() end return true end end starsoul.item.sw.link('starsoul_electronics:shred', { name = 'NanoShred'; kind = 'suitPower', powerKind = 'active'; desc = 'An open-source program used in its various forks and iterations all across human-inhabited space and beyond. Rumored to contain fragments of code stolen from the nanoware of the Greater Races by an elusive infoterrorist.'; size = 500e3; cost = { cycles = 100e6; ram = 500e6; }; run = shredder{range=2, powerDraw=200}; }) starsoul.item.sw.link('starsoul_electronics:compile_commune', { name = 'Compile Matter'; kind = 'suitPower', powerKind = 'direct'; desc = "A basic suit matter compiler program, rather slow but ruthlessly optimized for power- and memory-efficiency by some of the Commune's most fanatic coders."; size = 700e3; cost = { cycles = 300e6; ram = 2e9; }; ui = 'starsoul:compile-matter-component'; run = function(user, ctx) end; }) starsoul.item.sw.link('starsoul_electronics:compile_block_commune', { name = 'Compile Block'; kind = 'suitPower', powerKind = 'active'; desc = "An advanced suit matter compiler program, capable of printing complete devices and structure parts directly into the world."; size = 5e6; cost = { cycles = 700e6; ram = 4e9; }; ui = 'starsoul:compile-matter-block'; run = function(user, ctx) end; }) do local J = starsoul.store.compilerJob starsoul.item.sw.link('starsoul_electronics:driver_compiler_commune', { name = 'Matter Compiler'; kind = 'driver'; desc = "A driver for a standalone matter compiler, suitable for building larger components than your suit alone can handle."; size = 850e3; cost = { cycles = 400e6; ram = 2e9; }; ui = 'starsoul:device-compile-matter-component'; run = function(user, ctx) end; bgProc = function(user, ctx, interval, runState) if runState.flags.compiled == true then return false end -- only so many nanides to go around runState.flags.compiled = true local time = minetest.get_gametime() local cyclesLeft = ctx.comp.cycles * interval for id, e in ipairs(ctx.file.body.conf) do if e.key == 'job' then local t = J.dec(e.value) local remove = false local r = starsoul.item.sw.db[t.schematic] if not r then -- bad schematic remove = true else local ccost = ctx.sw.cost.cycles + r.cost.cycles local tcost = ccost / cyclesLeft t.progress = t.progress + (1/tcost)*interval cyclesLeft = cyclesLeft - ccost*interval if t.progress >= 1 then -- complete remove = true local i = starsoul.item.mk(r.output, { how = 'print'; user = user; -- for suit compiler = { node = ctx.compiler; -- for device sw = ctx.sw; install = ctx.fd; }; schematic = r; }) ctx.giveItem(i) end end if remove then table.remove(ctx.file.body.conf, id) else e.value = J.enc(t) end if not cyclesLeft > 0 then break end end end ctx.saveConf() end; }) end local function pasv_heal(effect, energy, lvl, pgmId) return function(user, ctx, interval, runState) if runState.flags.healed == true then return false end -- competing nanosurgical programs?? VERY bad idea runState.flags.healed = true local amt, f = user:effectiveStat 'health' local st = user:getSuit():powerState() if (st == 'on' and f < lvl) or (st == 'powerSave' and f < math.min(lvl,0.25)) then local maxPower = energy*interval local p = user:suitDrawCurrent(maxPower, interval, { id = 'heal'; src = 'suitPower'; pgmId = pgmId; healAmount = effect; }) if p > 0 then local heal = (p/maxPower) * ctx.speed * effect*interval --user:statDelta('health', math.max(1, heal)) starsoul.fx.nano.heal(user, {{player=user.entity}}, heal, 1) return true end end return false -- program did not run end; end starsoul.item.sw.link('starsoul_electronics:nanomed', { name = 'NanoMed'; kind = 'suitPower', powerKind = 'passive'; desc = 'Repair of the body is a Commune specialty, and their environment suits all come equipped with highly sophisticated nanomedicine suites, able to repair even the most grievous of wounds given sufficient energy input and time.'; size = 2e9; cost = { cycles = 400e6; ram = 3e9; }; run = pasv_heal(2, 20, 1); }) starsoul.item.sw.link('starsoul_electronics:autodoc_deluxe', { name = 'AutoDoc Deluxe'; kind = 'suitPower', powerKind = 'passive'; desc = "A flagship offering of the Excellence Unyielding nanoware division, AutoDoc Deluxe has been the top-rated nanocare package in the Celestial Shores Province for six centuries and counting. Every chip includes our comprehensive database of illnesses, prosyn schematics, and organ repair techniques, with free over-the-ether updates guaranteed for ten solariads from date of purchase! When professional medical care just isn't an option, 9/10 doctors recommend Excellence Unyielding AutoDoc Deluxe! The remaining doctor was bribed by our competitors."; size = 1e9; cost = { cycles = 700e6; ram = 1e9; }; run = pasv_heal(4, 50, .7); }) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul-material/elements.lua version [8655f86d81].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 |
local lib = starsoul.mod.lib local W = starsoul.world local M = W.material M.element.meld { hydrogen = { name = 'hydrogen', sym = 'H', n = 1; gas = true; color = lib.color(1,0.8,.3); }; beryllium = { name = 'Beryllium', sym = 'Be', n = 4; metal = true; -- rare emerald-stuff color = lib.color(0.2,1,0.2); }; oxygen = { name = 'oxygen', sym = 'O', n = 8; gas = true; color = lib.color(.2,1,.2); }; carbon = { name = 'carbon', sym = 'C', n = 6; color = lib.color(.7,.2,.1); }; silicon = { name = 'silicon', sym = 'Si', n = 14; metal = true; -- can be forged into an ingot color = lib.color(.6,.6,.4); }; potassium = { name = 'potassium', sym = 'K', n = 19; -- potassium is technically a metal but it's so soft -- it can be easily nanoworked without high temps, so -- ingots make no sense color = lib.color(1,.8,0.1); }; calcium = { name = 'calcium', sym = 'Ca', n = 20; metal = true; color = lib.color(1,1,0.7); }; aluminum = { name = 'aluminum', sym = 'Al', n = 13; metal = true; color = lib.color(0.9,.95,1); }; iron = { name = 'iron', sym = 'Fe', n = 26; metal = true; color = lib.color(.3,.3,.3); }; copper = { name = 'copper', sym = 'Cu', n = 29; metal = true; color = lib.color(.8,.4,.1); }; lithium = { name = 'lithium', sym = 'Li', n = 3; -- i think lithium is considered a metal but we don't mark it as -- one here because making a 'lithium ingot' is insane (even possible?) color = lib.color(1,0.8,.3); }; titanium = { name = 'titanium', sym = 'Ti', n = 22; metal = true; color = lib.color(.7,.7,.7); }; vanadium = { name = 'vanadium', sym = 'V', n = 23; metal = true; color = lib.color(.3,0.5,.3); }; xenon = { name = 'xenon', sym = 'Xe', n = 54; gas = true; color = lib.color(.5,.1,1); }; argon = { name = 'argon', sym = 'Ar', n = 18; gas = true; color = lib.color(0,0.1,.9); }; osmium = { name = 'osmium', sym = 'Os', n = 76; metal = true; color = lib.color(.8,.1,1); }; iridium = { name = 'iridium', sym = 'Ir', n = 77; metal = true; color = lib.color(.8,0,.5); }; technetium = { name = 'technetium', sym = 'Tc', n = 43; desc = 'Prized by the higher Powers for subtle interactions that elude mere human scholars, technetium is of particular use in nuclear nanobatteries.'; metal = true; color = lib.color(.2,0.2,1); }; uranium = { name = 'uranium', sym = 'U', n = 92; desc = 'A weak but relatively plentiful nuclear fuel.'; metal = true; color = lib.color(.2,.7,0); }; thorium = { name = 'thorium', sym = 'Th', n = 90; desc = 'A frighteningly powerful nuclear fuel.'; metal = true; color = lib.color(.7,.3,.1); }; silver = { name = 'silver', sym = 'Ag', n = 47; metal = true; color = lib.color(.7,.7,.8); }; gold = { name = 'gold', sym = 'Au', n = 79; metal = true; color = lib.color(1,.8,0); }; } |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul-material/init.lua version [4473abdf0e].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
local lib = starsoul.mod.lib local M = { canisterSizes = lib.registry.mk 'starsoul_material:canister-size'; } M.canisterSizes.foreach('starsoul_material:canister_link', {}, function(id, sz) starsoul.item.canister.link(minetest.get_current_modname() .. ':canister_' .. id, { name = sz.name; slots = sz.slots; vol = 0.1; -- too big for suit? desc = sz.desc; }) end) M.canisterSizes.meld { tiny = {name = 'Tiny Canister', slots = 1, vol = 0.05}; small = {name = 'Small Canister', slots = 3, vol = 0.2}; mid = {name = 'Canister', slots = 5, vol = 0.5}; large = {name = 'Large Canister', slots = 10, vol = 1.0}; storage = {name = 'Storage Canister', slots = 50, vol = 5.0}; } starsoul.include 'elements' |
< < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul-material/mod.conf version [4c6f9cbdac].
1 2 3 |
name = starsoul_material description = defines the raw materials and alloys used in printing depends = starsoul |
< < < |
Deleted mods/starsoul-scenario/init.lua version [d3b56b60b6].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 |
local lib = starsoul.mod.lib local scenario = starsoul.world.scenario local function makeChip(label, schem, sw) local E = starsoul.mod.electronics local files = {} local sz = 0 for _, e in ipairs(schem) do local p = E.sw.findSchematicFor(e[1]) if p then local file = { kind = 'sw', name = '', drm = e[2]; body = {pgmId = p}; } table.insert(files, file) sz = sz + E.chip.fileSize(file) end end for _, e in ipairs(sw) do local file = { kind = 'sw', name = '', drm = e[2]; body = {pgmId = e[1]}; } table.insert(files, file) sz = sz + E.chip.fileSize(file) end local chip = ItemStack(assert(E.chip.findBest(function(c) return c.flash >= sz, c.ram + c.clockRate end))) local r = E.chip.read(chip) r.label = label r.files = files E.chip.write(chip, r) return chip end local chipLibrary = { compendium = makeChip('The Gentleman Adventurer\'s Compleat Wilderness Compendium', { {'starsoul_electronics:battery_chemical_imperial_small', 0}; }, { {'starsoul_electronics:shred', 0}; --{'starsoul_electronics:compile_empire', 0}; {'starsoul_electronics:autodoc_deluxe', 1}; --{'starsoul_electronics:driver_compiler_empire', 0}; }); survivalware = makeChip('Emergency Survivalware', { {'starsoul_electronics:battery_chemical_commune_small', 0}; }, { {'starsoul_electronics:shred', 0}; {'starsoul_electronics:compile_commune', 0}; {'starsoul_electronics:nanomed', 0}; {'starsoul_electronics:driver_compiler_commune', 0}; }); misfortune = makeChip("Sold1er0fMisf0rtune TOP Schematic Crackz REPACK", { {'starsoul_electronics:battery_chemical_usukwinya_mid', 0}; {'starsoul_electronics:battery_hybrid_imperial_small', 0}; -- ammunition }, {}); } local battery = function(name) local s = ItemStack(name) starsoul.mod.electronics.battery.setChargeF(s, 1.0) return s end table.insert(scenario, { id = 'starsoul_scenario:imperialExpat'; name = 'Imperial Expat'; desc = "Hoping to escape a miserable life deep in the grinding gears of the capitalist machine for the bracing freedom of the frontier, you sought entry as a colonist to the new Commune world of Thousand Petal. Fate -- which is to say, terrorists -- intervened, and you wound up stranded on Farthest Shadow with little more than the nanosuit on your back, ship blown to tatters and your soul thoroughly mauled by the explosion of a twisted alien artifact -- which SOMEONE neglected to inform you your ride would be carrying.\nAt least you got some nifty psionic powers out of this whole clusterfuck. Hopefully they're safe to use."; species = 'human'; speciesVariant = 'female'; soul = { externalChannel = true; -- able to touch other souls in the spiritual realm physicalChannel = true; -- able to extend influence into physical realm damage = 1; }; social = { empire = 'workingClass'; commune = 'metic'; }; startingItems = { suit = ItemStack('starsoul_suit:suit_survival_commune'); suitBatteries = {battery 'starsoul_electronics:battery_carbon_commune_small'}; suitChips = { chipLibrary.survivalware; -- you didn't notice it earlier, but your Commune environment suit -- came with this chip already plugged in. it's apparently true -- what they say: the Commune is always prepared for everything. -- E V E R Y T H I N G. }; suitGuns = {}; suitAmmo = {}; suitCans = { ItemStack('starsoul_material:canister_small'); }; carry = { chipLibrary.compendium; -- you bought this on a whim before you left the Empire, and -- just happened to still have it on your person when everything -- went straight to the Wild Gods' privy }; }; }) table.insert(scenario, { id = 'starsoul_scenario:gentlemanAdventurer'; -- Othar Tryggvasson, name = 'Gentleman Adventurer'; desc = "Tired of the same-old-same-old, sick of your idiot contemporaries, exasperated with the shallow soul-rotting luxury of life as landless lordling, and earnestly eager to enrage your father, you resolved to see the Reach in all her splendor. Deftly evading the usual tourist traps, you finagled your way into the confidence of the Commune ambassador with a few modest infusions of Father's money -- now *that* should pop his monocle -- and secured yourself a seat on a ride to their brand-new colony at Thousand Petal. How exciting -- a genuine frontier outing!"; species = 'human'; speciesVariant = 'male'; soul = { externalChannel = true; physicalChannel = true; damage = 1; }; social = { empire = 'lord'; }; startingItems = { suit = 'starsoul_suit:suit_survival_imperial'; suitBatteries = {battery 'starsoul_electronics:battery_supercapacitor_imperial_mid'}; suitChips = { chipLibrary.compendium; -- Mother, bless her soul, simply insisted on buying you this as a parting -- gift. "it's dangerous out there for a young man," she proclaimed as -- if she had profound firsthand experience of the matter. mindful of the -- husband she endures, you suffered to humor her, and made a big show of -- installing it your brand-new nanosuit before you fled the family seat. }; suitGuns = {}; suitAmmo = {}; suitCans = { ItemStack('starsoul_material:canister_mid'); }; carry = {}; }; }) table.insert(scenario, { -- you start out with strong combat abilities but weak engineering, -- and will have to scavenge wrecks to find basic crafting gear id = 'starsoul_scenario:terroristTagalong'; name = 'Terrorist Tagalong'; desc = "It turns out there's a *reason* Crown jobs pay so well."; species = 'human'; speciesVariant = 'female'; social = { empire = 'lowlife'; commune = 'mostWanted'; underworldConnections = true; }; soul = { externalChannel = true; physicalChannel = true; damage = 2; -- closer to the blast }; startingItems = { suit = 'starsoul_suit:suit_combat_imperial'; suitBatteries = { ItemStack('starsoul_electronics:battery_supercapacitor_imperial_small'); ItemStack('starsoul_electronics:battery_chemical_imperial_large'); }; suitGuns = {}; suitAmmo = {}; carry = {}; }; suitChips = {chipLibrary.misfortune}; }) table.insert(scenario, { id = 'starsoul_scenario:tradebirdBodyguard'; name = 'Tradebird Bodyguard'; desc = "You've never understood why astropaths of all people *insist* on bodyguards. This one could probably make hash of a good-sized human batallion, if her feathers were sufficiently ruffled. Perhaps it's a status thing. Whatever the case, it's easy money.\nAt least, it was supposed to be.'"; species = 'usukwinya'; speciesVariant = 'male'; soul = { damage = 0; -- Inyukiriku and her entourage fled the ship when she sensed something serious was about to go down. lucky: the humans only survived because their souls were closed off to the Physical. less luckily, the explosion knocked your escape pod off course and the damn astropath was too busy saving her own skin to come after you externalChannel = true; -- usukwinya are already psionic physicalChannel = true; -- usukwinya are Starsouls }; startingItems = { suit = 'starsoul_suit:suit_combat_usukwinya'; suitBatteries = { ItemStack('starsoul_electronics:battery_hybrid_usukwinya_mid'); }; suitGuns = {}; suitChips = {}; suitAmmo = {}; carry = {}; }; }) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul-scenario/mod.conf version [cb3b4db72f].
1 2 3 4 |
name = starsoul_scenario description = built-in scenarios for Starsoul depends = starsoul, starsoul_suit, starsoul_electronics, starsoul_building, starsoul_material # be sure to add any mods from which you list new starting items! |
< < < < |
Deleted mods/starsoul-secrets/init.lua version [f98f7f7b59].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
---------------------------------------------------- ------------- CONTROLLED INFORMATION -------------- ---------------------------------------------------- -- THE INFORMATION CONTAINED IN THIS DOCUMENT IS -- -- SUBJECT TO RESTRAINT OF TRANSMISSION PER THE -- -- TERMS OF THE COMMUNE CHARTER INFOSECURITY -- -- PROVISION. IF YOU ARE NOT AUTHORIZED UNDER THE -- -- AEGIS OF THE APPROPRITE CONTROLLING AUTHORITY, -- -- CLOSE THIS DOCUMENT IMMEDIATELY AND REPORT THE -- -- SECURITY BREACH TO YOUR DESIGNATED INFORMATION -- -- HYGIENE OVERSEER OR FACE CORRECTIVE DISCIPLINE -- ---------------------------------------------------- local lib = starsoul.mod.lib local sec = {} starsoul.mod.secrets = sec sec.index = lib.registry.mk 'starsoul_secrets:secret' --[==[ a secret is a piece of information that is made available for review once certain conditions are met. despite the name, it doesn't necessarily have to be secret -- it could include e.g. journal entries about a character's background. a secret is defined in the following manner: { title = the string that appears in the UI stages = { { prereqs = { {kind = 'fact', id = 'starsoul:terroristEmployer'} {kind = 'item', id = 'starsoul_electronic:firstbornDoomBong'} {kind = 'background', id = 'starsoul:terroristTagalong'} } body = { 'the firstborn smonked hella weed'; }; -- body can also be a function(user,secret) } } } TODO would it be useful to impl horn clauses and a general fact database? is that level of flexibility meaningful? or are simply flags better a secret can be a single piece of information predicated on a fact, in which case the secret and fact should share the same ID. the ID should be as non-indicative as possible to avoid spoilers for devs of unrelated code. a secret can also be manually unlocked e.g. by using an item ]==]-- function sec.prereqCheck(user, pr) end |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul-secrets/mod.conf version [26c2999a4f].
1 2 3 4 |
name = starsoul_secrets title = starsoul secrets description = TS//NOFORN depends = starsoul |
< < < < |
Deleted mods/starsoul-suit/init.lua version [d4fabd1162].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 |
local lib = starsoul.mod.lib local fab = starsoul.type.fab local facDescs = { commune = { survival = { suit = 'A light, simple, bare-bones environment suit that will provide heating, cooling, and nanide support to a stranded cosmonaut'; cc = 'The Survival Suit uses compact thermoelectrics to keep the wearer perfectly comfortable in extremes of heat or cold. It makes up for its heavy power usage with effective insulation that substantially reduces any need for climate control.'; }; engineer = { suit = 'A lightweight environment suit designed for indoor work, the Commune\'s Engineer Suit boasts advanced nanotech capable of constructing objects in place.'; cc = 'The Engineer Suit is designed for indoor work. Consequently, it features only a low-power thermoelectric cooler meant to keep its wearer comfortable during strenuous work.'; }; combat = { suit = 'A military-grade suit with the latest Commune technology. Designed for maximum force multiplication, the suit has dual weapon hardpoints and supports a gargantuan power reserve. Its nanotech systems are specialized for tearing through obstacles, but can also be used to manufacturer ammunition in a pinch.'; cc = 'This Combat Suit uses electrothermal cooling to keep an active soldier comfortable and effective, as well as conventional heating coils to enable operation in hostile atmospheres.'; }; }; } starsoul.world.tier.foreach('starsoul:suit-gen', {}, function(tid, t) local function hasTech(tech) return starsoul.world.tier.tech(tid, tech) end if not hasTech 'suit' then return end -- TODO tier customization -- local function fabsum(f) return starsoul.world.tier.fabsum(tid, 'suit') end local function fabReq(sz, days) local tierMatBase = ( (fabsum 'electric' * 4) + (fabsum 'basis' + fabsum 'suit') * sz ) local b = tierMatBase + fab { -- universal suit requirements time = { print = 60*60*24 * days }; size = { printBay = sz }; } b.flag = lib.tbl.set('print'); return b end local function facDesc(s, t) local default = 'A protective nanosuit' -- FIXME if not facDescs[tid] then return default end if not facDescs[tid][s] then return default end if not facDescs[tid][s][t] then return default end return facDescs[tid][s][t] end starsoul.item.suit.link('starsoul_suit:suit_survival_' .. tid, { name = t.name .. ' Survival Suit'; desc = facDesc('survival','suit'); fab = fabReq(1, 2.2) + fab { }; tex = { plate = { id = 'starsoul-suit-survival-plate'; tint = lib.color {hue = 210, sat = .5, lum = .5}; }; lining = { id = 'starsoul-suit-survival-lining'; tint = lib.color {hue = 180, sat = .2, lum = .7}; }; }; tints = {'suit_plate', 'suit_lining'}; temp = { desc = facDesc('survival','cc'); maxHeat = 0.7; -- can produce a half-degree Δ per second maxCool = 0.5; heatPower = 50; -- 50W coolPower = 50/t.efficiency; insulation = 0.5; -- prevent half of heat loss }; protection = { rad = 0.7; -- blocks 70% of ionizing radiation }; slots = { canisters = 1; batteries = math.ceil(math.max(1, t.power/2)); chips = 3; guns = 0; ammo = 0; }; nano = { compileSpeed = 0.1 * t.efficiency; shredSpeed = 0.1 * t.power; fabSizeLimit = 0.6; -- 60cm }; }) starsoul.item.suit.link('starsoul_suit:suit_engineer_' .. tid, { name = t.name .. ' Engineer Suit'; desc = facDesc('engineer','suit'); tex = { plate = { id = 'starsoul-suit-survival-plate'; tint = lib.color {hue = 0, sat = .5, lum = .7}; }; }; tints = {'suit_plate', 'suit_lining'}; fab = fabReq(.8, 7) + fab { }; temp = { desc = facDesc('engineer','cc'); maxHeat = 0; maxCool = 0.2; heatPower = 0; coolPower = 10 / t.efficiency; insulation = 0.1; -- no lining }; slots = { canisters = 2; batteries = 2; chips = 6; guns = 0; ammo = 0; }; compat = { maxBatterySize = 0.10 * t.power; -- 10cm }; protection = { rad = 0.1; -- blocks 10% of ionizing radiation }; nano = { compileSpeed = 1 * t.efficiency; shredSpeed = 0.7 * t.power; fabSizeLimit = 1.5; -- 1.5m (enables node compilation) }; }) if hasTech 'suitCombat' then starsoul.item.suit.link('starsoul_suit:suit_combat_' .. tid, { name = t.name .. ' Combat Suit'; desc = facDesc('combat','suit'); fab = fabReq(1.5, 14) + fab { metal = {iridium = 1e3}; }; tex = { plate = { id = 'starsoul-suit-survival-plate'; tint = lib.color {hue = 0, sat = 0, lum = 0}; }; lining = { id = 'starsoul-suit-survival-lining'; tint = lib.color {hue = 180, sat = .5, lum = .3}; }; }; tints = {'suit_plate', 'suit_lining'}; slots = { canisters = 1; batteries = math.ceil(math.max(3, 8*(t.power/2))); chips = 5; guns = 2; ammo = 1; }; compat = { maxBatterySize = 0.10 * t.power; -- 10cm }; temp = { desc = facDesc('combat','cc'); maxHeat = 0.3; maxCool = 0.6; heatPower = 20 / t.efficiency; coolPower = 40 / t.efficiency; insulation = 0.2; }; protection = { rad = 0.9; -- blocks 90% of ionizing radiation }; nano = { compileSpeed = 0.05; shredSpeed = 2 * t.power; fabSizeLimit = 0.3; -- 30cm }; }) end end) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul-suit/mod.conf version [dbff224e42].
1 2 3 |
name = starsoul_suit description = defines the environment suits available in starsoul depends = starsoul, starsoul_electronics |
< < < |
Deleted mods/starsoul/container.lua version [72968ffd62].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 |
-- a container item defines a 'container' structure listing its -- inventories and their properties. a container object is created -- in order to interact with a container local lib = starsoul.mod.lib starsoul.item.container = lib.class { __name = 'starsoul:container'; construct = function(stack, inv, def) local T,G = lib.marshal.t, lib.marshal.g local cdef = stack:get_definition()._starsoul.container; local sd = {} for k,v in pairs(cdef.list) do sd[k] = { key = v.key; type = T.inventoryList; } end return { stack = stack, inv = inv, pdef = def, cdef = cdef; store = lib.marshal.metaStore(sd)(stack); } end; __index = { slot = function(self, id) return string.format("%s_%s", self.pdef.pfx, id) end; clear = function(self) -- initialize or empty the metadata self:update(function() for k,v in pairs(self.cdef.list) do if v.sz > 0 then self.store.write(k, {}) end end end) end; list = function(self, k) return self.store.read(k) end; read = function(self) local lst = {} for k,v in pairs(self.cdef.list) do if v.sz > 0 then lst[k] = self:list(k) end end return lst end; pull = function(self) -- align the inventories with the metadata for k,v in pairs(self.cdef.list) do if v.sz > 0 then local stacks = self:list(k) local sid = self:slot(k) self.inv:set_size(sid, v.sz) self.inv:set_list(sid, stacks) end end end; update = function(self, fn) local old = ItemStack(self.stack) if fn then fn() end if self.cdef.handle then self.cdef.handle(self.stack, old) end end; push = function(self) -- align the metadata with the inventories self:update(function() for k,v in pairs(self.cdef.list) do if v.sz > 0 then local sid = self:slot(k) local lst = self.inv:get_list(sid) self.store.write(k, lst) end end end) end; drop = function(self) -- remove the inventories from the node/entity for k,v in pairs(self.cdef.list) do local sid = self:slot(k) self.inv:set_size(sid, 0) end end; slotAccepts = function(self, lst, slot, stack) end; }; } function starsoul.item.container.dropPrefix(inv, pfx) local lists = inv:get_lists() for k,v in pairs(lists) do if #k > #pfx then if string.sub(k, 1, #pfx + 1) == pfx .. '_' then inv:set_size(k, 0) end end end end |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/effect.lua version [2bec98314f].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
-- ported from sorcery/spell.lua, hence the lingering refs to "magic" -- -- this file is used to track active effects, for the purposes of metamagic -- like disjunction. a "effect" is a table consisting of several properties: -- a "disjoin" function that, if present, is called when the effect is -- abnormally interrupted, a "terminate" function that calls when the effect -- completes, a "duration" property specifying how long the effect lasts in -- seconds, and a "timeline" table that maps floats to functions called at -- specific points during the function's activity. it can also have a -- 'delay' property that specifies how long to wait until the effect sequence -- starts; the effect is however still vulnerable to disjunction during this -- period. there can also be a sounds table that maps timepoints to sounds -- the same way timeline does. each value should be a table of form {sound, -- where}. the `where` field may contain one of 'pos', 'caster', 'subjects', or -- a vector specifying a position in the world, and indicate where the sound -- should be played. by default 'caster' and 'subjects' sounds will be attached -- to the objects they reference; 'attach=false' can be added to prevent this. -- by default sounds will be faded out quickly when disjunction occurs; this -- can be controlled by the fade parameter. -- -- effects can have various other properties, for instance 'disjunction', which -- when true prevents other effects from being cast in its radius while it is -- still in effect. disjunction is absolute; there is no way to overwhelm it. -- -- the effect also needs at least one of "anchor", "subjects", or "caster". -- * an anchor is a position that, in combination with 'range', specifies the area -- where a effect is in effect; this is used for determining whether it -- is affected by a disjunction that incorporates part of that position -- * subjects is an array of individuals affected by the effect. when -- disjunction is cast on one of them, they will be removed from the -- table. each entry should have at least a 'player' field; they can -- also contain any other data useful to the effect. if a subject has -- a 'disjoin' field it must be a function called when they are removed -- from the list of effect targets. -- * caster is the individual who cast the effect, if any. a disjunction -- against their person will totally disrupt the effect. local log = starsoul.logger 'effect' local lib = starsoul.mod.lib -- FIXME saving object refs is iffy, find a better alternative starsoul.effect = { active = {} } local get_effect_positions = function(effect) local effectpos if effect.anchor then effectpos = {effect.anchor} elseif effect.attach then if effect.attach == 'caster' then effectpos = {effect.caster:get_pos()} elseif effect.attach == 'subjects' or effect.attach == 'both' then if effect.attach == 'both' then effectpos = {effect.caster:get_pos()} else effectpos = {} end for _,s in pairs(effect.subjects) do effectpos[#effectpos+1] = s.player:get_pos() end else effectpos = {effect.attach:get_pos()} end else assert(false) end return effectpos end local ineffectrange = function(effect,pos,range) local effectpos = get_effect_positions(effect) for _,p in pairs(effectpos) do if vector.equals(pos,p) or (range and lib.math.vdcomp(range, pos,p)<=1) or (effect.range and lib.math.vdcomp(effect.range,p,pos)<=1) then return true end end return false end starsoul.effect.probe = function(pos,range) -- this should be called before any effects are performed. -- other mods can overlay their own functions to e.g. protect areas -- from effects local result = {} -- first we need to check if any active injunctions are in effect -- injunctions are registered as effects with a 'disjunction = true' -- property for id,effect in pairs(starsoul.effect.active) do if not (effect.disjunction and (effect.anchor or effect.attach)) then goto skip end if ineffectrange(effect,pos,range) then result.disjunction = true break end ::skip::end -- at some point we might also check to see if certain anti-effect -- blocks are nearby or suchlike. there could also be regions where -- perhaps certain kinds of effect are unusually empowered or weak return result end starsoul.effect.disjoin = function(d) local effects,targets = {},{} if d.effect then effects = {{v=d.effect}} elseif d.target then targets = {d.target} elseif d.pos then -- find effects anchored here and people in range for id,effect in pairs(starsoul.effect.active) do if not effect.anchor then goto skip end -- this intentionally excludes attached effects if ineffectrange(effect,d.pos,d.range) then effects[#effects+1] = {v=effect,i=id} end ::skip::end local ppl = minetest.get_objects_inside_radius(d.pos,d.range) if #targets == 0 then targets = ppl else for _,p in pairs(ppl) do targets[#targets+1] = p end end end -- iterate over targets to remove from any effect's influence for _,t in pairs(targets) do for id,effect in pairs(starsoul.effect.active) do if effect.caster == t then effects[#effects+1] = {v=effect,i=id} else for si, sub in pairs(effect.subjects) do if sub.player == t then if sub.disjoin then sub:disjoin(effect) end effect.release_subject(si) break end end end end end -- effects to disjoin entirely for _,s in pairs(effects) do local effect = s.v if effect.disjoin then effect:disjoin() end effect.abort() if s.i then starsoul.effect.active[s.i] = nil else for k,v in pairs(starsoul.effect.active) do if v == effect then starsoul.effect.active[k] = nil break end end end end end starsoul.effect.ensorcelled = function(player,effect) if type(player) == 'string' then player = minetest.get_player_by_name(player) end for _,s in pairs(starsoul.effect.active) do if effect and (s.name ~= effect) then goto skip end for _,sub in pairs(s.subjects) do if sub.player == player then return s end end ::skip::end return false end starsoul.effect.each = function(player,effect) local idx = 0 return function() repeat idx = idx + 1 local sp = starsoul.effect.active[idx] if sp == nil then return nil end if effect == nil or sp.name == effect then for _,sub in pairs(sp.subjects) do if sub.player == player then return sp end end end until idx >= #starsoul.effect.active end end -- when a new effect is created, we analyze it and make the appropriate calls -- to minetest.after to queue up the events. each job returned needs to be -- saved in 'jobs' so they can be canceled if the effect is disjoined. no polling -- necessary :D starsoul.effect.cast = function(proto) local s = table.copy(proto) s.jobs = s.jobs or {} s.vfx = s.vfx or {} s.sfx = s.sfx or {} s.impacts = s.impacts or {} s.subjects = s.subjects or {} s.delay = s.delay or 0 s.visual = function(subj, def) s.vfx[#s.vfx + 1] = { handle = minetest.add_particlespawner(def); subject = subj; } end s.visual_caster = function(def) -- convenience function local d = table.copy(def) d.attached = s.caster s.visual(nil, d) end s.visual_subjects = function(def) for _,sub in pairs(s.subjects) do local d = table.copy(def) d.attached = sub.player s.visual(sub, d) end end s.affect = function(i) local etbl = {} for _,sub in pairs(s.subjects) do -- local eff = late.new_effect(sub.player, i) -- starsoul will not be using late local rec = { effect = eff; subject = sub; } s.impacts[#s.impacts+1] = rec etbl[#etbl+1] = rec end return etbl end s.abort = function() for _,j in ipairs(s.jobs) do j:cancel() end for _,v in ipairs(s.vfx) do minetest.delete_particlespawner(v.handle) end for _,i in ipairs(s.sfx) do s.silence(i) end for _,i in ipairs(s.impacts) do i.effect:stop() end end s.release_subject = function(si) local t = s.subjects[si] for _,f in pairs(s.sfx) do if f.subject == t then s.silence(f) end end for _,f in pairs(s.impacts) do if f.subject == t then f.effect:stop() end end for _,f in pairs(s.vfx) do if f.subject == t then minetest.delete_particlespawner(f.handle) end end s.subjects[si] = nil end local interpret_timespec = function(when) if when == nil then return 0 end local t if type(when) == 'number' then t = s.duration * when else t = (s.duration * (when.whence or 0)) + (when.secs or 0) end if t then return math.min(s.duration,math.max(0,t)) end log.err('invalid timespec ' .. dump(when)) return 0 end s.queue = function(when,fn) local elapsed = s.starttime and minetest.get_server_uptime() - s.starttime or 0 local timepast = interpret_timespec(when) if not timepast then timepast = 0 end local timeleft = s.duration - timepast local howlong = (s.delay + timepast) - elapsed if howlong < 0 then log.err('cannot time-travel! queue() called with `when` specifying timepoint that has already passed') howlong = 0 end s.jobs[#s.jobs+1] = minetest.after(howlong, function() -- this is somewhat awkward. since we're using a non-polling approach, we -- need to find a way to account for a caster or subject walking into an -- existing antimagic field, or someone with an existing antimagic aura -- walking into range of the anchor. so every time a effect effect would -- take place, we first check to see if it's in range of something nasty if not s.disjunction and -- avoid self-disjunction ((s.caster and starsoul.effect.probe(s.caster:get_pos()).disjunction) or (s.anchor and starsoul.effect.probe(s.anchor,s.range).disjunction)) then starsoul.effect.disjoin{effect=s} else if not s.disjunction then for _,sub in pairs(s.subjects) do local sp = sub.player:get_pos() if starsoul.effect.probe(sp).disjunction then starsoul.effect.disjoin{pos=sp} end end end -- effect still exists and we've removed any subjects who have been -- affected by a disjunction effect, it's now time to actually perform -- the queued-up action fn(s,timepast,timeleft) end end) end s.play_now = function(spec) local specs, stbl = {}, {} local addobj = function(obj,sub) if spec.attach == false then specs[#specs+1] = { spec = { pos = obj:get_pos() }; obj = obj, subject = sub; } else specs[#specs+1] = { spec = { object = obj }; obj = obj, subject = sub; } end end if spec.where == 'caster' then addobj(s.caster) elseif spec.where == 'subjects' then for _,sub in pairs(s.subjects) do addobj(sub.player,sub) end elseif spec.where == 'pos' then specs[#specs+1] = { spec = {pos = s.anchor} } else specs[#specs+1] = { spec = {pos = spec.where} } end for _,sp in pairs(specs) do sp.spec.gain = sp.spec.gain or spec.gain local so = { handle = minetest.sound_play(spec.sound, sp.spec, spec.ephemeral); ctl = spec; -- object = sp.obj; subject = sp.subject; } stbl[#stbl+1] = so s.sfx[#s.sfx+1] = so end return stbl end s.play = function(when,spec) s.queue(when, function() local snds = s.play_now(spec) if spec.stop then s.queue(spec.stop, function() for _,snd in pairs(snds) do s.silence(snd) end end) end end) end s.silence = function(sound) if sound.ctl.fade == 0 then minetest.sound_stop(sound.handle) else minetest.sound_fade(sound.handle,sound.ctl.fade or 1,0) end end local startqueued, termqueued = false, false local myid = #starsoul.effect.active+1 s.cancel = function() s.abort() starsoul.effect.active[myid] = nil end local perform_disjunction_calls = function() local positions = get_effect_positions(s) for _,p in pairs(positions) do starsoul.effect.disjoin{pos = p, range = s.range} end end if s.timeline then for when_raw,what in pairs(s.timeline) do local when = interpret_timespec(when_raw) if s.delay == 0 and when == 0 then startqueued = true if s.disjunction then perform_disjunction_calls() end what(s,0,s.duration) elseif when_raw == 1 or when >= s.duration then -- avoid race conditions if not termqueued then termqueued = true s.queue(1,function(s,...) what(s,...) if s.terminate then s:terminate() end starsoul.effect.active[myid] = nil end) else log.warn('multiple final timeline events not possible, ignoring') end elseif when == 0 and s.disjunction then startqueued = true s.queue(when_raw,function(...) perform_disjunction_calls() what(...) end) else s.queue(when_raw,what) end end end if s.intervals then for _,int in pairs(s.intervals) do local timeleft = s.duration - interpret_timespec(int.after) local iteration, itercount = 0, timeleft / int.period local function iterate(lastreturn) iteration = iteration + 1 local nr = int.fn { effect = s; iteration = iteration; iterationcount = itercount; timeleft = timeleft; timeelapsed = s.duration - timeleft; lastreturn = lastreturn; } if nr ~= false and iteration < itercount then s.jobs[#s.jobs+1] = minetest.after(int.period, function() iterate(nr) end) end end if int.after then s.queue(int.after, iterate) else s.queue({whence=0, secs=s.period}, iterate) end end end if s.disjunction and not startqueued then if s.delay == 0 then perform_disjunction_calls() else s.queue(0, function() perform_disjunction_calls() end) end end if s.sounds then for when,what in pairs(s.sounds) do s.play(when,what) end end starsoul.effect.active[myid] = s if not termqueued then s.jobs[#s.jobs+1] = minetest.after(s.delay + s.duration, function() if s.terminate then s:terminate() end starsoul.effect.active[myid] = nil end) end s.starttime = minetest.get_server_uptime() return s end minetest.register_on_dieplayer(function(player) starsoul.effect.disjoin{target=player} end) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/element.lua version [52033ae372].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
local lib = starsoul.mod.lib local W = starsoul.world local M = W.material M.element.foreach('starsoul:sort', {}, function(id, m) if m.metal then M.metal.link(id, { name = m.name; composition = starsoul.type.fab{element = {[id] = 1}}; color = m.color; -- n.b. this is a RATIO: it will be appropriately multiplied -- for the object in question; e.g a normal chunk will be -- 100 $element, an ingot will be 1000 $element }) elseif m.gas then M.gas.link(id, { name = m.name; composition = starsoul.type.fab{element = {[id] = 1}}; }) elseif m.liquid then M.liquid.link(id, { name = m.name; composition = starsoul.type.fab{element = {[id] = 1}}; }) end end) local F = string.format local function mkEltIndicator(composition) local indicator = '' local idx = 0 local ccount = 0 for _ in pairs(composition) do ccount = ccount + 1 end local indsz,indpad = 28,4 local ofs = math.min(11, (indsz-indpad)/ccount) for id, amt in pairs(composition) do idx = idx + 1 indicator = indicator .. F( ':%s,3=starsoul-element-%s.png', (indsz-indpad) - (idx*ofs), id ) end indicator = lib.image(indicator) return function(s) return string.format('(%s^[resize:%sx%s)^[combine:%sx%s%s', s, indsz, indsz, indsz, indsz, indicator); end end M.element.foreach('starsoul:gen-forms', {}, function(id, m) local eltID = F('%s:element_%s', minetest.get_current_modname(), id) local eltName = F('Elemental %s', lib.str.capitalize(m.name)) local tt = function(t, d, g) return starsoul.ui.tooltip { title = t, desc = d; color = lib.color(0.1,0.2,0.1); props = { {title = 'Mass', desc = lib.math.si('g', g), affinity='info'} } } end local comp = {[id] = 1} local iblit = mkEltIndicator(comp) m.form = m.form or {} m.form.element = eltID local powder = F('starsoul-element-%s-powder.png', id); minetest.register_craftitem(eltID, { short_description = eltName; description = tt(eltName, F('Elemental %s kept in suspension by a nanide storage system, ready to be worked by a cold matter compiler', m.name), 1); inventory_image = iblit(powder); wield_image = powder; stack_max = 1000; -- 1kg groups = {element = 1, powder = 1, specialInventory = 1}; _starsoul = { mass = 1; material = { kind = 'element'; element = id; }; fab = starsoul.type.fab { element = comp; }; }; }); end) M.metal.foreach('starsoul:gen-forms', {}, function(id, m) local baseID = F('%s:metal_%s_', minetest.get_current_modname(), id) local brickID, ingotID = baseID .. 'brick', baseID .. 'ingot' local brickName, ingotName = F('%s Brick', lib.str.capitalize(m.name)), F('%s Ingot', lib.str.capitalize(m.name)) m.form = m.form or {} m.form.brick = brickID m.form.ingot = ingotID local tt = function(t, d, g) return starsoul.ui.tooltip { title = t, desc = d; color = lib.color(0.1,0.1,0.1); props = { {title = 'Mass', desc = lib.math.si('g', g), affinity='info'} } } end local mcomp = m.composition:elementalize().element local function comp(n) local t = {} for id, amt in pairs(mcomp) do t[id] = amt * n end return t end local iblit = mkEltIndicator(mcomp) local function img(s) return iblit(s:colorize(m.color):render()) end minetest.register_craftitem(brickID, { short_description = brickName; description = tt(brickName, F('A solid brick of %s, ready to be worked by a matter compiler', m.name), 100); inventory_image = img(lib.image 'starsoul-item-brick.png'); wield_image = lib.image 'starsoul-item-brick.png':colorize(m.color):render(); stack_max = 10; groups = {metal = 1, ingot = 1}; _starsoul = { mass = 100; material = { kind = 'metal'; metal = id; }; fab = starsoul.type.fab { flag = {smelt= true}; element = comp(1e2); }; }; }); minetest.register_craftitem(ingotID, { short_description = ingotName; description = tt(ingotName, F('A solid ingot of %s, ready to be worked by a large matter compiler', m.name), 1e3); inventory_image = img(lib.image('starsoul-item-ingot.png')); wield_image = lib.image 'starsoul-item-ingot.png':colorize(m.color):render(); groups = {metal = 1, ingot = 1}; stack_max = 5; _starsoul = { mass = 1e3; material = { kind = 'metal'; metal = id; }; fab = starsoul.type.fab { flag = {smelt= true}; element = comp(1e3); }; }; }); end) local function canisterDesc(stack, def) def = def or stack:get_definition()._starsoul.canister local props = { {title = 'Charge Slots', affinity = 'info', desc = tostring(def.slots)}; }; if stack then local inv = starsoul.item.container(stack) for i,e in ipairs(inv:list 'elem') do local comp = e:get_definition()._starsoul.fab table.insert(props, { title = comp:formula(); desc = lib.math.si('g', e:get_count()); affinity = 'good'; }) end -- TODO list masses end return starsoul.ui.tooltip { title = def.name, desc = def.desc or 'A canister that can store a charge of elemental powder, gas, or liquid'; color = lib.color(0.2,0.1,0.1); props = props; }; end starsoul.item.canister = lib.registry.mk 'starsoul:canister'; starsoul.item.canister.foreach('starsoul:item-gen', {}, function(id, c) minetest.register_craftitem(id, { short_description = c.name; description = canisterDesc(nil, c); inventory_image = c.image or 'starsoul-item-element-canister.png'; groups = {canister = 1}; stack_max = 1; _starsoul = { canister = c; container = { handle = function(stack, oldstack) stack:get_meta():set_string('description', canisterDesc(stack)) return stack end; list = { elem = { key = 'starsoul:canister_elem'; accept = 'powder'; sz = c.slots; }; }; }; }; }) end) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/fab.lua version [d8c093fcd3].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
-- [ʞ] fab.lua -- ~ lexi hale <lexi@hale.su> -- 🄯 EUPL1.2 -- ? fabrication spec class -- a type.fab supports two operators: -- -- + used for compounding recipes. that is, -- a+b = compose a new spec from the spec parts a and b. -- this is used e.g. for creating tier-based -- fabspecs. -- -- * used for determining quantities. that is, -- f*x = spec to make x instances of f -- -- new fab fields must be defined in starsoul.type.fab.opClass. -- this maps a name to fn(a,b,n) -> quant, where a is the first -- argument, b is a compounding amount, and n is a quantity of -- items to produce. fields that are unnamed will be underwritten local function fQuant(a,b,n) return ((a or 0)+(b or 0))*n end local function fFac (a,b,n) if a == nil and b == nil then return nil end local f if a == nil or b == nil then f = a or b else f = (a or 1)*(b or 1) end return f*n end local function fReq (a,b,n) return a or b end local function fFlag (a,b,n) return a and b end local function fSize (a,b,n) return math.max(a,b) end local opClass = { -- fabrication eligibility will be determined by which kinds -- of input a particular fabricator can introduce. e.g. a -- printer with a but no cache can only print items whose -- recipe only names elements as ingredients -- ingredients element = fQuant; -- (g) gas = fQuant; -- () liquid = fQuant; -- (l) crystal = fQuant; -- (g) item = fQuant; -- n metal = fQuant; -- (g) metalIngot = fQuant; -- (g) -- factors cost = fFac; -- units vary time = fFac; -- (s) -- print: base printing time size = fSize; -- printBay: size of the printer bay necessary to produce the item req = fReq; flag = fFlag; -- means that can be used to produce the item & misc flags -- print: allow production with a printer -- smelt: allow production with a smelter -- all else defaults to underwrite } local F = string.format local strClass = { element = function(x, n) local el = starsoul.world.material.element[x] return lib.math.si('g', n) .. ' ' .. (el.sym or el.name) end; metal = function(x, n) local met = starsoul.world.material.metal[x] return lib.math.si('g', n) .. ' ' .. met.name end; liquid = function(x, n) local liq = starsoul.world.material.liquid[x] return lib.math.si('L', n) .. ' ' .. liq.name end; gas = function(x, n) local gas = starsoul.world.material.gas[x] return lib.math.si('g', n) .. ' ' .. gas.name end; item = function(x, n) local i = minetest.registered_items[x] return tostring(n) .. 'x ' .. i.short_description end; } local order = { 'element', 'metal', 'liquid', 'gas', 'item' } local lib = starsoul.mod.lib local fab fab = lib.class { __name = 'starsoul:fab'; opClass = opClass; strClass = strClass; order = order; construct = function(q) return q end; __index = { elementalize = function(self) local e = fab {element = self.element or {}} for _, kind in pairs {'metal', 'gas', 'liquid'} do for m,mass in pairs(self[kind] or {}) do local mc = starsoul.world.material[kind][m].composition e = e + mc:elementalize()*mass end end return e end; elementSeq = function(self) local el = {} local em = self.element local s = 0 local eldb = starsoul.world.material.element.db for k in pairs(em) do table.insert(el, k) s=s+eldb[k].n end table.sort(el, function(a,b) return eldb[a].n > eldb[b].n end) return el, em, s end; formula = function(self) print('make formula', dump(self)) local ts,f=0 if self.element then f = {} local el, em, s = self:elementSeq() local eldb = starsoul.world.material.element.db for i, e in ipairs(el) do local sym, n = eldb[e].sym, em[e] if n > 0 then table.insert(f, string.format("%s%s", sym, n>1 and lib.str.nIdx(n) or '')) end end f = table.concat(f) ts = ts + s end local sub = {} for _, w in pairs {'metal', 'gas', 'liquid'} do if self[w] then local mdb = starsoul.world.material[w].db for k, amt in pairs(self[w]) do local mf, s = mdb[k].composition:formula() if amt > 0 then table.insert(sub, { f = string.format("(%s)%s",mf, lib.str.nIdx(amt)); s = s; }) end ts = ts + s*amt end end end table.sort(sub, function(a,b) return a.s > b.s end) local fml = {} for i, v in ipairs(sub) do fml[i] = v.f end if f then table.insert(fml, f) end fml = table.concat(fml, ' + ') return fml, ts end; }; __tostring = function(self) local t = {} for i,o in ipairs(order) do if self[o] then for mat,amt in pairs(self[o]) do if amt > 0 then table.insert(t, strClass[o](mat, amt)) end end end end return table.concat(t, ", ") end; __add = function(a,b) local new = fab {} for cat, vals in pairs(a) do new[cat] = lib.tbl.copy(vals) end for cat, vals in pairs(b) do if not new[cat] then new[cat] = lib.tbl.copy(vals) else local f = opClass[cat] for k,v in pairs(vals) do local n = f(new[cat][k], v, 1) new[cat][k] = n > 0 and n or nil end end end return new end; __mul = function(x,n) local new = fab {} for cat, vals in pairs(x) do new[cat] = {} local f = opClass[cat] for k,v in pairs(vals) do local num = f(v,nil,n) new[cat][k] = num > 0 and num or nil end end return new end; __div = function(x,n) return x * (1/n) end; } starsoul.type.fab = fab |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/fx/nano.lua version [4c580d7de8].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 |
local lib = starsoul.mod.lib local E = starsoul.effect local N = {} starsoul.fx.nano = N local nanopool= { { name = 'starsoul-fx-nano-spark-small.png'; scale_tween = {0,.5, style = 'pulse', rep = 3}; }; { name = 'starsoul-fx-nano-spark-small.png'; scale_tween = {0,1, style = 'pulse', rep = 2}; }; { name = 'starsoul-fx-nano-spark-big.png'; scale_tween = {0,1, style = 'pulse'}; }; } function N.heal(user, targets, amt, dur) local amthealed = {} local f = E.cast { caster = user.entity; subjects = targets; duration = dur; intervals = { { after = 0; period = 4; fn = function(c) for i,v in pairs(c.effect.subjects) do local u = starsoul.activeUsers[v.player:get_player_name()] if u then local heal = math.max(amt/4, 1) amthealed[u] = amthealed[u] or 0 if amthealed[u] < amt then amthealed[u] = amthealed[u] + heal u:statDelta('health', heal) end end end end; } } } local casterIsTarget = false for _, sub in pairs(f.subjects) do if sub.player == user.entity then casterIsTarget = true end f.visual(sub, { amount = 50; time = dur; glow = 14; jitter = 0.01; attached = user.entity; vel = { min = -0.1, max = 0.1; }; pos = { min = vector.new(0,0.2,0); max = vector.new(0,1.2,0); }; radius = { min = 0.2; max = 0.6; bias = -1; }; exptime = {min=0.5,max=2}; attract = { kind = 'line'; strength = {min = 0.5, max = 2}; origin = 0; direction = vector.new(0,1,0); origin_attached = sub.player; direction_attached = sub.player; }; texpool = nanopool; }) end if not casterIsTarget then -- f.visual_caster { } end f.play(0.3, { where = 'subjects'; sound = 'starsoul-nano-heal'; ephemeral = true; spec = {gain = 0.3}; }) return f end function N.shred(user, pos, prop, time, node) local f = E.cast { caster = user.entity; subjects = {}; duration = time; } local sp,sv = user:lookupSpecies() local eh = sv.eyeHeight or sp.eyeHeight f.visual_caster { amount = 200 * time; pos = vector.new(0.12,eh - 0.1,0); radius = 0.2; time = time - (time/3); glow = 14; jitter = 0.1; size = {min = 0.2, max = 0.5}; exptime = {min=0.5,max=1}; vel_tween = { 0; { min = -0.4, max = 0.4; }; style = 'pulse', rep = time * 2; }; attract = { kind = 'point'; origin = pos; radius = 0.5; strength = {min=.3,max=2}; }; texpool = nanopool; }; f.queue(0.05, function(s, timepast, timeleft) f.visual(nil, { amount = timeleft * 40; time = timeleft; pos = pos; size_tween = { 0, {min = 0.5, max = 2}; }; vel = { min = vector.new(-1.2,0.5,-1.2); max = vector.new(1.2,3.5,1.2); }; acc = vector.new(0,-starsoul.world.planet.gravity,0); node = node; }) end); f.queue(0.9, function(s, timepast, timeleft) f.visual(nil, { amount = 200; time = timeleft; pos = pos; size = {min = 0.1, max = 0.3}; vel = { min = vector.new(-2,0.5,-2); max = vector.new(2,4,2); }; acc = vector.new(0,-starsoul.world.planet.gravity,0); node = node; }) end); f.queue(0.3, function(s, timepast, timeleft) local function v(fn) local def = { amount = timeleft * 100; pos = pos; time = timeleft; radius = 0.5; jitter = {min = 0.0, max = 0.2}; size = {min = 0.2, max = 0.5}; exptime = {min = 0.5, max = 1}; attract = { kind = 'point'; strength = {min=0.3, max = 1}; origin = vector.new(0,eh-0.1,0); radius = 0.5; origin_attached = user.entity; }; } fn(def) f.visual(nil, def) end v(function(t) t.texpool = nanopool t.glow = 14 end) v(function(t) t.node = node t.amount = timeleft * 20 t.size = {min = 0.1, max = 0.3}; end) end) return f end |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/init.lua version [aca0a214d9].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
-- [ʞ] starsoul/init.lua -- ~ lexi hale <lexi@hale.su> -- ? basic setup, game rules, terrain -- © EUPL v1.2 local T = minetest.get_translator 'starsoul' -- TODO enforce latest engine version local mod = { -- subordinate mods register here lib = vtlib; -- vtlib should be accessed as starsoul.mod.lib by starsoul modules for the sake of proper encapsulation. vtlib should simply be a provider, not a hardcoded dependency } local lib = mod.lib starsoul = { ident = minetest.get_current_modname(); mod = mod; translator = T; constant = { light = { --minetest units dim = 3; lamp = 7; bright = 10; brightest = 14; -- only sun and growlights }; heat = { -- celsius freezing = 0; safe = 4; overheat = 32; boiling = 100; }; rad = { }; }; activeUsers = { -- map of username -> user object }; activeUI = { -- map of username -> UI context }; liveUI = { -- cached subset of activeUI containing those UIs needing live updates }; interface = lib.registry.mk 'starsoul:interface'; item = { }; region = { radiator = { store = AreaStore(); emitters = {} }; }; -- standardized effects fx = {}; type = {}; world = { defaultScenario = 'starsoul_scenario:imperialExpat'; seedbank = lib.math.seedbank(minetest.get_mapgen_setting 'seed'); mineral = lib.registry.mk 'starsoul:mineral'; material = { -- raw materials element = lib.registry.mk 'starsoul:element'; -- elements are automatically sorted into the following categories -- if they match. however, it's possible to have a metal/gas/liquid -- that *isn't* a pure element, so these need separate registries -- for alloys and mixtures like steel and water metal = lib.registry.mk 'starsoul:metal'; gas = lib.registry.mk 'starsoul:gas'; liquid = lib.registry.mk 'starsoul:liquid'; }; ecology = { plants = lib.registry.mk 'starsoul:plants'; trees = lib.registry.mk 'starsoul:trees'; biomes = lib.registry.mk 'starsoul:biome'; }; climate = {}; scenario = {}; planet = { gravity = 7.44; orbit = 189; -- 1 year is 189 days revolve = 20; -- 1 day is 20 irl minutes }; fact = lib.registry.mk 'starsoul:fact'; time = { calendar = { empire = { name = 'Imperial Regnal Calendar'; year = function(t, long) local reigns = { -- if anyone actually makes it to his Honor & Glory Unfailing Persigan I i will be -- exceptionally flattered {4, 'Emperor', 'Atavarka', 'the Bold'}; -- died at war {9, 'Emperor', 'Vatikserka', 'the Unconquered'}; -- died at war {22, 'Emperor', 'Rusifend', 'the Wise'}; -- poisoned at diplomacy {61, 'Empress', 'Tafseshendi', 'the Great'}; -- died of an 'insurrection of the innards' after a celebrated reign {291, 'Emperor', 'Treptebaska', 'the Unwise'}; -- murdered by his wife in short order {292, 'Empress', 'Vilintalti', 'the Impious'}; -- removed by the praetorian elite {298, 'Emperor', 'Radavan', 'the Reckless'}; -- died at war {316, 'Emperor', 'Suldibrand', 'the Forsaken of Men'}; -- fucked around. found out. {320, 'Emperor', 'Persigan', 'the Deathless'}; } local year, r = math.floor(t / 414) for i=1, #reigns do if reigns[i+1][1] < year then r = reigns[i+1] end end local reignBegin, title, name, epithet = lib.tbl.unpack(r) local ry = 1 + (year - reignBegin) return long and string.format('Year %s of the Reign of HH&GU %s %s %s', ry, title, name, epithet) or string.format('Y. %s %s', name, ry) end; time = function(t, long) local bellsInDay, candleSpansInBell = 5, 7 local bell = bellsInDay*t local cspan = (bellsInDay*candleSpansInBell*t) % candleSpansInBell return string.format(long and 'Bell %s, Candlespan %s' or '%sb %sc', math.floor(bell), math.floor(cspan)) end; }; commune = { name = 'People\'s Calendar'; date = function(t, long) local year = math.floor(t / 256) + 314 return string.format(long and 'Foundation %s' or 'F:%s', year) end; time = function(t, long) local hoursInDay, minutesInHour = 16, 16 local hour = hoursInDay*t local min = (hoursInDay*minutesInHour*t) % minutesInHour local dawn = 0.24*hoursInDay local noon = 0.5*hoursInDay local dusk = 0.76*hoursInDay local midnight = 1.0*hoursInDay local tl, str if hour < dawn then tl = dawn - hour str = long and 'dawn' or 'D' elseif hour < noon then tl = noon - hour str = long and 'noon' or 'N' elseif hour < dusk then tl = dusk - hour str = long and 'dusk' or 'd' elseif hour < midnight then tl = midnight - hour str = long and 'midnight' or 'M' end return long and string.format('%s hours, %s minutes to %s', math.floor(tl), math.floor(minutesInHour - min), str) or string.format('%s.%sH.%sM', str, math.floor(tl), math.floor(minutesInHour - min)) end; }; }; }; }; jobs = {}; } starsoul.cfgDir = minetest.get_worldpath() .. '/' .. starsoul.ident local logger = function(module) local function argjoin(arg, nxt, ...) if arg and not nxt then return tostring(arg) end if not arg then return "(nil)" end return tostring(arg) .. ' ' .. argjoin(nxt, ...) end local lg = {} local setup = function(fn, lvl) lvl = lvl or fn local function emit(...) local call = (fn == 'fatal') and error or function(str) minetest.log(lvl, str) end if module then call(string.format('[%s :: %s] %s',starsoul.ident,module,argjoin(...))) else call(string.format('[%s] %s',starsoul.ident,argjoin(...))) end end lg[fn ] = function(...) emit(...) end lg[fn .. 'f'] = function(...) emit(string.format(...)) end -- convenience fn end setup('info') setup('warn','warning') setup('err','error') setup('act','action') setup('fatal') return lg end starsoul.logger = logger local log = logger() function starsoul.evaluate(name, ...) local path = minetest.get_modpath(minetest.get_current_modname()) local filename = string.format('%s/%s', path, name) log.info('loading', filename) local chunk, err = loadfile(filename, filename) if not chunk then error(err) end return chunk(...) end function starsoul.include(name, ...) -- semantic variant used for loading modules return starsoul.evaluate(name..'.lua', ...) end minetest.register_lbm { label = 'build radiator index'; name = 'starsoul:loadradiatorboxes'; nodenames = {'group:radiator'}; run_at_every_load = true; action = function(pos, node, dt) local R = starsoul.region local phash = minetest.hash_node_position(pos) if R.radiator.sources[phash] then return end -- already loaded local def = minetest.registered_nodes[node.name] local cl = def._starsoul.radiator local min,max = cl.maxEffectArea(pos) local id = R.radiator.store:insert_area(min,max, minetest.pos_to_string(pos)) R.radiator.sources[phash] = id end; -- NOTE: temp emitter nodes are responsible for decaching themselves in their on_destruct cb } function starsoul.startJob(id, interval, job) local lastRun local function start() starsoul.jobs[id] = minetest.after(interval, function() local t = minetest.get_gametime() local d = lastRun and t - lastRun or nil lastRun = t local continue = job(d, interval) if continue == true or continue == nil then start() elseif continue ~= false then interval = continue start() end end) end start() end starsoul.include 'stats' starsoul.include 'world' starsoul.include 'fab' starsoul.include 'tiers' starsoul.include 'species' starsoul.include 'store' starsoul.include 'ui' starsoul.include 'item' starsoul.include 'container' starsoul.include 'user' starsoul.include 'effect' starsoul.include 'fx/nano' starsoul.include 'element' starsoul.include 'terrain' starsoul.include 'interfaces' starsoul.include 'suit' minetest.settings:set('movement_gravity', starsoul.world.planet.gravity) -- ??? seriously??? --------------- -- callbacks -- --------------- -- here we connect our types up to the minetest API local function userCB(fn) return function(luser, ...) local name = luser:get_player_name() local user = starsoul.activeUsers[name] return fn(user, ...) end end minetest.register_on_joinplayer(function(luser, lastLogin) -- TODO check that necessary CSMs are installed local user = starsoul.type.user(luser) if lastLogin == nil then user:onSignup() end user:onJoin() starsoul.activeUsers[user.name] = user end) minetest.register_on_leaveplayer(function(luser) starsoul.activeUsers[luser:get_player_name()]:onPart() end) minetest.register_on_player_receive_fields(function(luser, formid, fields) local name = luser:get_player_name() local user = starsoul.activeUsers[name] if not user then return false end if formid == '' then -- main menu return starsoul.ui.userMenuDispatch(user,fields) end local ui = starsoul.interface.db[formid] local state = starsoul.activeUI[name] or {} if formid == '__builtin:help_cmds' or formid == '__builtin:help_privs' then return false end assert(state.form == formid) -- sanity check user:onRespond(ui, state, fields) if fields.quit then starsoul.activeUI[name] = nil end return true end) minetest.register_on_respawnplayer(userCB(function(user) return user:onRespawn() end)) minetest.register_on_dieplayer(userCB(function(user, reason) return user:onDie(reason) end)) minetest.register_on_punchnode(function(pos,node,puncher,point) local user = starsoul.activeUsers[puncher:get_player_name()] local oldTgt = user.action.tgt user.action.tgt = point if bit.band(user.action.bits, 0x80)==0 then user.action.bits = bit.bor(user.action.bits, 0x80) --user:trigger('primary', {state = 'init'}) else user:trigger('retarget', {oldTgt = oldTgt}) end end) local function pointChanged(a,b) return a.type ~= b.type or a.type == 'node' and vector.new(a.under) ~= vector.new(b.under) or a.type == 'object' and a.ref ~= b.ref end local function triggerPower(_, luser, point) local user = starsoul.activeUsers[luser:get_player_name()] local oldTgt = user.action.tgt user.action.tgt = point if bit.band(user.action.bits, 0x100)==0 then user.action.bits = bit.bor(user.action.bits, 0x100) --return user:trigger('secondary', {state = 'prog', delta = 0}) elseif pointChanged(oldTgt, point) then user:trigger('retarget', {oldTgt = oldTgt}) end end -- sigh core.noneitemdef_default.on_place = function(...) if not triggerPower(...) then minetest.item_place(...) end end core.noneitemdef_default.on_use = function(...) triggerPower(...) end core.noneitemdef_default.on_secondary_use = function(...) triggerPower(...) end minetest.register_on_player_inventory_action(function(luser, act, inv, p) local name = luser:get_player_name() local user = starsoul.activeUsers[name] -- allow UIs to update on UI changes local state = starsoul.activeUI[name] if state then local ui = starsoul.interface.db[state.form] ui:cb('onMoveItem', user, act, inv, p) end end) minetest.register_on_player_hpchange(function(luser, delta, cause) local user = starsoul.activeUsers[luser:get_player_name()] if cause.type == 'fall' then delta = user:damageModifier('bluntForceTrauma', (delta * 50)) -- justification: a short fall can do around -- five points of damage, which is nearly 50% -- of the default hp_max. since we crank up -- hp by a factor of 50~40, damage should be -- cranked by similarly end return delta end, true) function minetest.handle_node_drops(pos, drops, digger) local function jitter(pos) local function r(x) return x+math.random(-0.2, 0.2) end return vector.new( r(pos.x), r(pos.y), r(pos.z) ) end for i, it in ipairs(drops) do minetest.add_item(jitter(pos), it) end end -- TODO timer iterates live UI |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/interfaces.lua version [b552296eeb].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 440 441 |
local lib = starsoul.mod.lib function starsoul.ui.setupForUser(user) local function cmode(mode) if user.actMode == mode then return {hue = 150, sat = 0, lum = .3} end end user.entity:set_inventory_formspec(starsoul.ui.build { kind = 'vert', mode = 'sw'; padding = .5, spacing = 0.1; {kind = 'hztl'; {kind = 'contact', w=1.5,h=1.5, id = 'mode_nano', img='starsoul-ui-icon-nano.png', close=true, color = cmode'nano'}; {kind = 'contact', w=1.5,h=1.5, id = 'mode_weapon', img='starsoul-ui-icon-weapon.png', close=true, color = cmode'weapon'}; {kind = 'contact', w=1.5,h=1.5, id = 'mode_psi', img='starsoul-ui-icon-psi.png', close=true, color = cmode'psi'}; }; {kind = 'hztl'; {kind = 'contact', w=1.5,h=1.5, id = 'open_elements', img='starsoul-ui-icon-element.png'}; {kind = 'contact', w=1.5,h=1.5, id = 'open_suit', img='starsoul-item-suit.png^[hsl:200:-.7:0'}; {kind = 'contact', w=1.5,h=1.5, id = 'open_psi', img='starsoul-ui-icon-psi-cfg.png'}; {kind = 'contact', w=1.5,h=1.5, id = 'open_body', img='starsoul-ui-icon-self.png'}; }; {kind = 'list'; target = 'current_player', inv = 'main'; w = 6, h = 1, spacing = 0.1; }; }) end function starsoul.ui.userMenuDispatch(user, fields) local function setSuitMode(mode) if user.actMode == mode then user:actModeSet 'off' else user:actModeSet(mode) end end local modes = { nano = true, psi = false, weapon = true } for e,s in pairs(modes) do if fields['mode_' .. e] then if s and (user:naked() or user:getSuit():powerState() == 'off') then user:suitSound 'starsoul-error' else setSuitMode(e) end return true end end if fields.open_elements then user:openUI('starsoul:user-menu', 'compiler') return true elseif fields.open_psi then user:openUI('starsoul:user-menu', 'psi') return true elseif fields.open_suit then if not user:naked() then user:openUI('starsoul:user-menu', 'suit') end return true elseif fields.open_body then user:openUI('starsoul:user-menu', 'body') end return false end local function listWrap(n, max) local h = math.ceil(n / max) local w = math.min(max, n) return w, h end local function wrapMenu(w, h, rh, max, l) local root = {kind = 'vert', w=w, h=h} local bar local function flush() if bar and bar[1] then table.insert(root, bar) end bar = {kind = 'hztl'} end flush() for _, i in ipairs(l) do local bw = w/max if i.cfg then w = w - rh end table.insert(bar, { kind = 'button', close = i.close; color = i.color; fg = i.fg; label = i.label; icon = i.img; id = i.id; w = bw, h = rh; }) if i.cfg then table.insert(bar, { kind = 'button'; color = i.color; fg = i.fg; label = "CFG"; icon = i.img; id = i.id .. '_cfg'; w = rh, h = rh; }) end if bar[max] then flush() end end flush() return root end local function abilityMenu(a) -- select primary/secondary abilities or activate ritual abilities local p = {kind = 'vert'} for _, o in ipairs(a.order) do local m = a.menu[o] table.insert(p, {kind='label', text=m.label, w=a.w, h = .5}) table.insert(p, wrapMenu(a.w, a.h, 1.2, 2, m.opts)) end return p end local function pptrMatch(a,b) if a == nil or b == nil then return false end return a.chipID == b.chipID and a.pgmIndex == b.pgmIndex end starsoul.interface.install(starsoul.type.ui { id = 'starsoul:user-menu'; pages = { compiler = { setupState = function(state, user) -- nanotech/suit software menu local chips = user.entity:get_inventory():get_list 'starsoul_suit_chips' -- FIXME need better subinv api local sw = starsoul.mod.electronics.chip.usableSoftware(chips) state.suitSW = {} local dedup = {} for i, r in ipairs(sw) do if r.sw.kind == 'suitPower' then if not dedup[r.sw] then dedup[r.sw] = true table.insert(state.suitSW, r) end end end end; handle = function(state, user, act) if user:getSuit():powerState() == 'off' then return false end local pgm, cfg for k in next, act do local id, mode = k:match('^suit_pgm_([0-9]+)_(.*)$') if id then id = tonumber(id) if state.suitSW[id] then pgm = state.suitSW[id] cfg = mode == '_cfg' break end end end if not pgm then return false end -- HAX -- kind=active programs must be assigned to a command slot -- kind=direct programs must open their UI -- kind=passive programs must toggle on and off if pgm.sw.powerKind == 'active' then if cfg then user:openUI(pgm.sw.ui, 'index', { context = 'suit'; program = pgm; }) return false end local ptr = {chipID = starsoul.mod.electronics.chip.read(pgm.chip).uuid, pgmIndex = pgm.fd.inode} local pnan = user.power.nano if pnan.primary == nil then pnan.primary = ptr elseif pptrMatch(ptr, pnan.primary) then pnan.primary = nil elseif pptrMatch(ptr, pnan.secondary) then pnan.secondary = nil else pnan.secondary = ptr end user:suitSound 'starsoul-configure' elseif pgm.sw.powerKind == 'direct' then local ctx = { context = 'suit'; program = pgm; } if pgm.sw.ui then user:openUI(pgm.sw.ui, 'index', ctx) return false else pgm.sw.run(user, ctx) end elseif pgm.sw.powerKind == 'passive' then if cfg then user:openUI(pgm.sw.ui, 'index', { context = 'suit'; program = pgm; }) return false end local addDisableRec = true for i, e in ipairs(pgm.file.body.conf) do if e.key == 'disable' and e.value == 'yes' then addDisableRec = false table.remove(pgm.file.body.conf, i) break elseif e.key == 'disable' and e.value == 'no' then e.value = 'yes' addDisableRec = false break end end if addDisableRec then table.insert(pgm.file.body.conf, {key='disable',value='yes'}) end -- update the chip *wince* pgm.fd:write(pgm.file) user.entity:get_inventory():set_stack('starsoul_suit_chips', pgm.chipSlot, pgm.chip) user:reconfigureSuit() user:suitSound('starsoul-configure') end return true, true end; render = function(state, user) local suit = user:getSuit() local swm if user:getSuit():powerState() ~= 'off' then swm = { w = 8, h = 3; order = {'active','ritual','pasv'}; menu = { active = { label = 'Nanoware'; opts = {}; }; ritual = { label = 'Programs'; opts = {}; }; pasv = { label = 'Passive'; opts = {}; }; }; } for id, r in pairs(state.suitSW) do local color = {hue=300,sat=0,lum=0} local fg = nil local close = nil local tbl, cfg if r.sw.powerKind == 'active' then tbl = swm.menu.active.opts if r.sw.ui then cfg = true end local pnan = user.power.nano if pnan then local ptr = {chipID = starsoul.mod.electronics.chip.read(r.chip).uuid, pgmIndex = r.fd.inode} if pptrMatch(ptr, pnan.primary) then color.lum = 1 elseif pptrMatch(ptr, pnan.secondary) then color.lum = 0.8 end end elseif r.sw.powerKind == 'direct' then tbl = swm.menu.ritual.opts if not r.sw.ui then close = true end elseif r.sw.powerKind == 'passive' then tbl = swm.menu.pasv.opts if r.sw.ui then cfg = true end for i, e in ipairs(r.file.body.conf) do if e.key == 'disable' and e.value == 'yes' then color.lum = -.2 fg = lib.color {hue=color.hue,sat=0.7,lum=0.7} break end end end if tbl then table.insert(tbl, { color = color, fg = fg; label = r.sw.label or r.sw.name; id = string.format('suit_pgm_%s_', id); cfg = cfg, close = close; }) end end end local menu = { kind = 'vert', mode = 'sw', padding = 0.5 } if swm then table.insert(menu, abilityMenu(swm)) end local inv = user.entity:get_inventory() local cans = inv:get_list 'starsoul_suit_canisters' if cans and next(cans) then for i, st in ipairs(cans) do local id = string.format('starsoul_canister_%u_elem', i) local esz = inv:get_size(id) if esz > 0 then local eltW, eltH = listWrap(esz, 5) table.insert(menu, {kind = 'hztl', {kind = 'img', desc='Elements', img = 'starsoul-ui-icon-element.png', w=1,h=1}; {kind = 'list', target = 'current_player', inv = id, listContent = 'element', w = eltW, h = eltH, spacing = 0.1}; }) end end end if #menu == 0 then table.insert(menu, { kind = 'img'; img = 'starsoul-ui-alert.png'; w=2, h=2; }) menu.padding = 1; end return starsoul.ui.build(menu) end; }; compilerListRecipes = { }; psi = { render = function(state, user) return starsoul.ui.build { kind = 'vert', mode = 'sw'; padding = 0.5; } end; }; body = { render = function(state, user) local barh = .75 local tb = { kind = 'vert', mode = 'sw'; padding = 0.5, {kind = 'hztl', padding = 0.25; {kind = 'label', text = 'Name', w = 2, h = barh}; {kind = 'label', text = user.persona.name, w = 4, h = barh}}; } local statBars = {'hunger', 'thirst', 'fatigue', 'morale'} for idx, id in ipairs(statBars) do local s = starsoul.world.stats[id] local amt, sv = user:effectiveStat(id) local min, max = starsoul.world.species.statRange(user.persona.species, user.persona.speciesVariant, id) local st = string.format('%s / %s', s.desc(amt, true), s.desc(max)) table.insert(tb, {kind = 'hztl', padding = 0.25; {kind = 'label', w=2, h=barh, text = s.name}; {kind = 'hbar', w=4, h=barh, fac = sv, text = st, color=s.color}; }) end local abilities = { {id = 'abl_sprint', label = 'Sprint', img = 'starsoul-ui-icon-ability-sprint.png'}; } table.insert(tb, wrapMenu(6.25,4, 1,2, abilities)) return starsoul.ui.build(tb) end; }; suit = { render = function(state, user) local suit = user:getSuit() local suitDef = suit:def() local chipW, chipH = listWrap(suitDef.slots.chips, 5) local batW, batH = listWrap(suitDef.slots.batteries, 5) local canW, canH = listWrap(suitDef.slots.canisters, 5) local suitMode = suit:powerState() local function modeColor(mode) if mode == suitMode then return {hue = 180, sat = 0, lum = .5} end end return starsoul.ui.build { kind = 'vert', mode = 'sw'; padding = 0.5, spacing = 0.1; {kind = 'hztl', {kind = 'img', desc='Batteries', img = 'starsoul-item-battery.png', w=1,h=1}; {kind = 'list', target = 'current_player', inv = 'starsoul_suit_bat', listContent = 'power', w = batW, h = batH, spacing = 0.1}; }; {kind = 'hztl', {kind = 'img', desc='Chips', img = 'starsoul-item-chip.png', w=1,h=1}; {kind = 'list', target = 'current_player', inv = 'starsoul_suit_chips', listContent = 'chip', w = chipW, h = chipH, spacing = 0.1}; }; {kind = 'hztl', {kind = 'img', desc='Canisters', img = 'starsoul-item-element-canister.png', w=1,h=1}; {kind = 'list', target = 'current_player', inv = 'starsoul_suit_canisters', listContent = nil, w = canW, h = canH, spacing = 0.1}; }; {kind = 'hztl'; {kind = 'img', w=1,h=1, item = suit.item:get_name(), desc = suit.item:get_definition().short_description}; {kind = 'button', w=1.5,h=1, id = 'powerMode_off', label = 'Off'; color=modeColor'off'}; {kind = 'button', w=2.5,h=1, id = 'powerMode_save', label = 'Power Save'; color=modeColor'powerSave'}; {kind = 'button', w=1.5,h=1, id = 'powerMode_on', label = 'On'; color=modeColor'on'}; }; {kind = 'list', target = 'current_player', inv = 'main', w = 6, h = 1, spacing = 0.1}; } end; handle = function(state, user, q) local suitMode if q.powerMode_off then suitMode = 'off' elseif q.powerMode_save then suitMode = 'powerSave' elseif q.powerMode_on then suitMode = 'on' end if suitMode then user:suitPowerStateSet(suitMode) return true end end; }; }; }) starsoul.interface.install(starsoul.type.ui { id = 'starsoul:compile-matter-component'; pages = { index = { setupState = function(state, user, ctx) if ctx.context == 'suit' then end state.pgm = ctx.program end; render = function(state, user) return starsoul.ui.build { kind = 'vert', padding = 0.5; w = 5, h = 5, mode = 'sw'; {kind = 'label', w = 4, h = 1, text = 'hello'}; } end; }; }; }) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/item.lua version [597f09c699].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
local lib = starsoul.mod.lib local I = starsoul.item function I.mk(item, context) local st = ItemStack(item) local md = st:get_definition()._starsoul local ctx = context or {} if md and md.event then md.event.create(st, ctx) end if context.how == 'print' then if context.schematic and context.schematic.setup then context.schematic.setup(st, ctx) end end return st end |
< < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/mod.conf version [7e3c22e1be].
1 2 3 4 |
name = starsoul author = velartrill description = world logic and UI depends = vtlib |
< < < < |
Deleted mods/starsoul/species.lua version [9c138bbb33].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
local lib = starsoul.mod.lib local paramTypes do local T,G = lib.marshal.t, lib.marshal.g paramTypes = { tone = G.struct { hue = T.angle; sat = T.clamp; lum = T.clamp; }; str = T.str; num = T.decimal; } end -- constants local animationFrameRate = 60 local species = { human = { name = 'Human'; desc = 'The weeds of the galactic flowerbed. Humans are one of the Lesser Races, excluded from the ranks of the Greatest Races by souls that lack, in normal circumstances, external psionic channels. Their mastery of the universe cut unexpectedly short, forever locked out of FTL travel, short-lived without augments, and alternately pitied or scorned by the lowest of the low, humans flourish nonetheless due to a capacity for adaptation unmatched among the Thinking Few, terrifyingly rapid reproductive cycles -- and a keen facility for bribery. While the lack of human psions remains a sensitive topic, humans (unlike the bitter and emotional Kruthandi) are practical enough to hire the talent they cannot possess, and have even built a small number of symbiotic civilizations with the more indulging of the Powers. In a galaxy where nearly all sophont life is specialized to a fault, humans have found the unique niche of occupying no particular niche.'; scale = 1.0; params = { {'eyeColor', 'Eye Color', 'tone', {hue=327, sat=0, lum=0}}; {'hairColor', 'Hair Color', 'tone', {hue=100, sat=0, lum=0}}; {'skinTone', 'Skin Tone', 'tone', {hue= 0, sat=0, lum=0}}; }; tempRange = { comfort = {18.3, 23.8}; -- needed for full stamina regen survivable = {5, 33}; -- anything below/above will cause progressively more damage }; variants = { female = { name = 'Human Female'; mesh = 'starsoul-body-female.x'; eyeHeight = 1.4; texture = function(t, adorn) local skin = lib.image 'starsoul-body-skin.png' : shift(t.skinTone) local eye = lib.image 'starsoul-body-eye.png' : shift(t.eyeColor) local hair = lib.image 'starsoul-body-hair.png' : shift(t.hairColor) local invis = lib.image '[fill:1x1:0,0:#00000000' local plate = adorn.suit and adorn.suit.plate or invis local lining = adorn.suit and adorn.suit.lining or invis return {lining, plate, skin, skin, eye, hair} end; stats = { psiRegen = 1.3; psiPower = 1.2; psi = 1.2; hunger = .8; -- women have smaller stomachs thirst = .8; staminaRegen = 1.0; morale = 0.8; -- you are not She-Bear Grylls }; traits = { health = 400; lungCapacity = .6; irradiation = 0.8; -- you are smaller, so it takes less rads to kill ya sturdiness = 0; -- women are more fragile and thus susceptible to blunt force trauma metabolism = 1800; --Cal painTolerance = 0.4; }; }; male = { name = 'Human Male'; eyeHeight = 1.6; stats = { psiRegen = 1.0; psiPower = 1.0; psi = 1.0; hunger = 1.0; staminaRegen = .7; -- men are strong but have inferior endurance }; traits = { health = 500; painTolerance = 1.0; lungCapacity = 1.0; sturdiness = 0.3; metabolism = 2200; --Cal }; }; }; traits = {}; }; } starsoul.world.species = { index = species; paramTypes = paramTypes; } function starsoul.world.species.mkDefaultParamsTable(pSpecies, pVariant) local sp = species[pSpecies] local var = sp.variants[pVariant] local vpd = var.defaults or {} local tbl = {} for _, p in pairs(sp.params) do local name, desc, ty, dflt = lib.tbl.unpack(p) tbl[name] = vpd[name] or dflt end return tbl end function starsoul.world.species.mkPersonaFor(pSpecies, pVariant) return { species = pSpecies; speciesVariant = pVariant; bodyParams = starsoul.world.species.paramsFromTable(pSpecies, starsoul.world.species.mkDefaultParamsTable(pSpecies, pVariant) ); statDeltas = {}; } end local function spLookup(pSpecies, pVariant) local sp = species[pSpecies] local var = sp.variants[pVariant or next(sp.variants)] return sp, var end starsoul.world.species.lookup = spLookup function starsoul.world.species.statRange(pSpecies, pVariant, pStat) local sp,spv = spLookup(pSpecies, pVariant) local min, max, base if pStat == 'health' then min,max = 0, spv.traits.health elseif pStat == 'breath' then min,max = 0, 65535 else local spfac = spv.stats[pStat] local basis = starsoul.world.stats[pStat] min,max = basis.min, basis.max if spfac then min = min * spfac max = max * spfac end base = basis.base if base == true then base = max elseif base == false then base = min end end return min, max, base end -- set the necessary properties and create a persona for a newspawned entity function starsoul.world.species.birth(pSpecies, pVariant, entity, circumstances) circumstances = circumstances or {} local sp,var = spLookup(pSpecies, pVariant) local function pct(st, p) local min, max = starsoul.world.species.statRange(pSpecies, pVariant, st) local delta = max - min return min + delta*p end local ps = starsoul.world.species.mkPersonaFor(pSpecies,pVariant) local startingHP = pct('health', 1.0) if circumstances.injured then startingHP = pct('health', circumstances.injured) end if circumstances.psiCharged then ps.statDeltas.psi = pct('psi', circumstances.psiCharged) end ps.statDeltas.warmth = 20 -- don't instantly start dying of frostbite entity:set_properties{hp_max = var.traits.health or sp.traits.health} entity:set_hp(startingHP, 'initial hp') return ps end function starsoul.world.species.paramsFromTable(pSpecies, tbl) local lst = {} local sp = species[pSpecies] for i, par in pairs(sp.params) do local name,desc,ty,dflt = lib.tbl.unpack(par) if tbl[name] then table.insert(lst, {id=name, value=paramTypes[ty].enc(tbl[name])}) end end return lst end function starsoul.world.species.paramsToTable(pSpecies, lst) local tymap = {} local sp = species[pSpecies] for i, par in pairs(sp.params) do local name,desc,ty,dflt = lib.tbl.unpack(par) tymap[name] = paramTypes[ty] end local tbl = {} for _, e in pairs(lst) do tbl[e.id] = tymap[e.id].dec(e.value) end return tbl end for speciesName, sp in pairs(species) do for varName, var in pairs(sp.variants) do if var.mesh then var.animations = starsoul.evaluate(string.format('models/%s.nla', var.mesh)).skel.action end end end function starsoul.world.species.updateTextures(ent, persona, adornment) local s,v = spLookup(persona.species, persona.speciesVariant) local paramTable = starsoul.world.species.paramsToTable(persona.species, persona.bodyParams) local texs = {} for i, t in ipairs(v.texture(paramTable, adornment)) do texs[i] = t:render() end ent:set_properties { textures = texs } end function starsoul.world.species.setupEntity(ent, persona) local s,v = spLookup(persona.species, persona.speciesVariant) local _, maxHealth = starsoul.world.species.statRange(persona.species, persona.speciesVariant, 'health') ent:set_properties { visual = 'mesh'; mesh = v.mesh; stepheight = .51; eye_height = v.eyeHeight; collisionbox = { -- FIXME -0.3, 0.0, -0.3; 0.3, 1.5, 0.3; }; visual_size = vector.new(10,10,10) * s.scale; hp_max = maxHealth; } local function P(v) if v then return {x=v[1],y=v[2]} end return {x=0,y=0} end ent:set_local_animation( P(v.animations.idle), P(v.animations.run), P(v.animations.work), P(v.animations.runWork), animationFrameRate) end |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/stats.lua version [39aabe1578].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
local lib = starsoul.mod.lib local function U(unit, prec, fixed) if fixed then return function(amt, excludeUnit) if excludeUnit then return tostring(amt/prec) end return string.format("%s %s", amt/prec, unit) end else return function(amt, excludeUnit) if excludeUnit then return tostring(amt/prec) end return lib.math.si(unit, amt/prec) end end end local function C(h, s, l) return lib.color {hue = h, sat = s or 1, lum = l or .7} end starsoul.world.stats = { psi = {min = 0, max = 500, base = 0, desc = U('ψ', 10), color = C(320), name = 'Numina'}; -- numina is measured in daψ warmth = {min = -1000, max = 1000, base = 0, desc = U('°C', 10, true), color = C(5), name = 'Warmth'}; -- warmth in measured in °C×10 fatigue = {min = 0, max = 76 * 60, base = 0, desc = U('hr', 60, true), color = C(288,.3,.5), name = 'Fatigue'}; -- fatigue is measured in minutes one needs to sleep to cure it stamina = {min = 0, max = 20 * 100, base = true, desc = U('m', 100), color = C(88), name = 'Stamina'}; -- stamina is measured in how many 10th-nodes (== cm) one can sprint hunger = {min = 0, max = 20000, base = 0, desc = U('Cal', 1), color = C(43,.5,.4), name = 'Hunger'}; -- hunger is measured in calories one must consume to cure it thirst = {min = 0, max = 1600, base = 0, desc = U('l', 100), color = C(217, .25,.4), name = 'Thirst'}; -- thirst is measured in centiliters of H²O required to cure it morale = {min = 0, max = 24 * 60 * 10, base = true, desc = U('hr', 60, true), color = C(0,0,.8), name = 'Morale'}; -- morale is measured in minutes. e.g. at base rate morale degrades by -- 60 points every hour. morale can last up to 10 days irradiation = {min = 0, max = 20000, base = 0, desc = U('Gy', 1000), color = C(141,1,.5), name = 'Irradiation'}; -- irrad is measured is milligreys -- 1Gy counters natural healing -- ~3Gy counters basic nanomedicine -- 5Gy causes death within two weeks without nanomedicine -- radiation speeds up psi regen -- morale drain doubles with each 2Gy illness = {min = 0, max = 1000, base = 0, desc = U('%', 10, true), color = C(71,.4,.25), name = 'Illness'}; -- as illness increases, maximum stamina and health gain a corresponding limit -- illness is increased by certain conditions, and decreases on its own as your -- body heals when those conditions wear off. some drugs can lower accumulated illness -- but illness-causing conditions require specific cures -- illness also causes thirst and fatigue to increase proportionately } |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/store.lua version [efa19f35ab].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
-- [ʞ] store.lua -- ~ lexi hale <lexi@hale.su> -- © EUPLv1.2 -- ? defines serialization datatypes that don't belong to -- any individual class local lib = starsoul.mod.lib local T,G = lib.marshal.t, lib.marshal.g starsoul.store = {} -- the serialization equivalent of .type ------------- -- persona -- ------------- ----------------------------------------------- -- a Persona is a structure that defines the nature of -- -- an (N)PC and how it interacts with the Starsoul-managed -- -- portion of the game world -- things like name, species, -- -- stat values, physical characteristics, and so forth -- local statStructFields = {} for k,v in pairs(starsoul.world.stats) do statStructFields[k] = v.srzType or ( (v.base == true or v.base > 0) and T.s16 or T.u16 ) end starsoul.store.compilerJob = G.struct { schematic = T.str; progress = T.clamp; } starsoul.store.persona = G.struct { name = T.str; species = T.str; speciesVariant = T.str; background = T.str; bodyParams = G.array(8, G.struct {id = T.str, value = T.str}); --variant statDeltas = G.struct(statStructFields); facts = G.array(32, G.array(8, T.str)); -- facts stores information the player has discovered and narrative choices -- she has made. -- parametric facts are encoded as horn clauses -- non-parametric facts are encoded as {'fact-mod:fact-id'} } starsoul.store.suitMeta = lib.marshal.metaStore { batteries = {key = 'starsoul:suit_slots_bat', type = T.inventoryList}; chips = {key = 'starsoul:suit_slots_chips', type = T.inventoryList}; elements = {key = 'starsoul:suit_slots_elem', type = T.inventoryList}; guns = {key = 'starsoul:suit_slots_gun', type = T.inventoryList}; ammo = {key = 'starsoul:suit_slots_ammo', type = T.inventoryList}; } |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/suit.lua version [5d51eaa4b3].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
local lib = starsoul.mod.lib local suitStore = starsoul.store.suitMeta starsoul.item.suit = lib.registry.mk 'starsoul:suits'; -- note that this cannot be persisted as a reference to a particular suit in the world local function suitContainer(stack, inv) return starsoul.item.container(stack, inv, { pfx = 'starsoul_suit' }) end starsoul.type.suit = lib.class { name = 'starsoul:suit'; construct = function(stack) return { item = stack; inv = suitStore(stack); } end; __index = { powerState = function(self) local s = self.item if not s then return nil end local m = s:get_meta():get_int('starsoul:power_mode') if m == 1 then return 'on' elseif m == 2 then return 'powerSave' else return 'off' end end; powerStateSet = function(self, state) local s = self.item if not s then return nil end local m if state == 'on' then m = 1 -- TODO check power level elseif state == 'powerSave' then m = 2 else m = 0 end if self:powerLeft() <= 0 then m = 0 end s:get_meta():set_int('starsoul:power_mode', m) end; powerLeft = function(self) local batteries = self.inv.read 'batteries' local power = 0 for idx, slot in pairs(batteries) do power = power + starsoul.mod.electronics.dynamo.totalPower(slot) end return power end; powerCapacity = function(self) local batteries = self.inv.read 'batteries' local power = 0 for idx, slot in pairs(batteries) do power = power + starsoul.mod.electronics.dynamo.initialPower(slot) end return power end; maxPowerUse = function(self) local batteries = self.inv.read 'batteries' local w = 0 for idx, slot in pairs(batteries) do w = w + starsoul.mod.electronics.dynamo.dischargeRate(slot) end return w end; onReconfigure = function(self, inv) -- apply any changes to item metadata and export any subinventories -- to the provided invref, as they may have changed local sc = starsoul.item.container(self.item, inv, {pfx = 'starsoul_suit'}) sc:push() self:pullCanisters(inv) end; onItemMove = function(self, user, list, act, what) -- called when the suit inventory is changed if act == 'put' then if list == 'starsoul_suit_bat' then user:suitSound('starsoul-suit-battery-in') elseif list == 'starsoul_suit_chips' then user:suitSound('starsoul-suit-chip-in') elseif list == 'starsoul_suit_canisters' then user:suitSound('starsoul-insert-snap') end elseif act == 'take' then if list == 'starsoul_suit_bat' then user:suitSound('starsoul-insert-snap') elseif list == 'starsoul_suit_chips' then --user:suitSound('starsoul-suit-chip-out') elseif list == 'starsoul_suit_canisters' then user:suitSound('starsoul-insert-snap') end end end; def = function(self) return self.item:get_definition()._starsoul.suit end; pullCanisters = function(self, inv) starsoul.item.container.dropPrefix(inv, 'starsoul_canister') self:forCanisters(inv, function(sc) sc:pull() end) end; pushCanisters = function(self, inv, st, i) self:forCanisters(inv, function(sc) sc:push() return true end) end; forCanisters = function(self, inv, fn) local cans = inv:get_list 'starsoul_suit_canisters' if cans and next(cans) then for i, st in ipairs(cans) do if not st:is_empty() then local pfx = 'starsoul_canister_' .. tostring(i) local sc = starsoul.item.container(st, inv, {pfx = pfx}) if fn(sc, st, i, pfx) then inv:set_stack('starsoul_suit_canisters', i, st) end end end end end; establishInventories = function(self, obj) local inv = obj:get_inventory() local ct = suitContainer(self.item, inv) ct:pull() self:pullCanisters(inv) --[[ local def = self:def() local sst = suitStore(self.item) local function readList(listName, prop) inv:set_size(listName, def.slots[prop]) if def.slots[prop] > 0 then local lst = sst.read(prop) inv:set_list(listName, lst) end end readList('starsoul_suit_chips', 'chips') readList('starsoul_suit_bat', 'batteries') readList('starsoul_suit_guns', 'guns') readList('starsoul_suit_elem', 'elements') readList('starsoul_suit_ammo', 'ammo') ]] end; }; } -- TODO find a better place for this! starsoul.type.suit.purgeInventories = function(obj) local inv = obj:get_inventory() starsoul.item.container.dropPrefix(inv, 'starsoul_suit') starsoul.item.container.dropPrefix(inv, 'starsoul_canister') --[[inv:set_size('starsoul_suit_bat', 0) inv:set_size('starsoul_suit_guns', 0) inv:set_size('starsoul_suit_chips', 0) inv:set_size('starsoul_suit_ammo', 0) inv:set_size('starsoul_suit_elem', 0) ]] end starsoul.item.suit.foreach('starsoul:suit-gen', {}, function(id, def) local icon = lib.image(def.img or 'starsoul-item-suit.png') local iconColor = def.iconColor if not iconColor then iconColor = (def.tex and def.tex.plate and def.tex.plate.tint) or def.defaultColor iconColor = iconColor:to_hsl() iconColor.lum = 0 end if iconColor then icon = icon:shift(iconColor) end if not def.adorn then function def.adorn(a, item, persona) local function imageFor(pfx) return lib.image(string.format("%s-%s-%s.png", pfx, persona.species, persona.speciesVariant)) end if not def.tex then return end a.suit = {} for name, t in pairs(def.tex) do local img = imageFor(t.id) local color local cstr = item:get_meta():get_string('starsoul:tint_suit_' .. name) if cstr and cstr ~= '' then color = lib.color.unmarshal(cstr) elseif t.tint then color = t.tint or def.defaultColor end if color then local hsl = color:to_hsl() local adjusted = { hue = hsl.hue; sat = hsl.sat * 2 - 1; lum = hsl.lum * 2 - 1; } img = img:shift(adjusted) end a.suit[name] = img end end end minetest.register_tool(id, { short_description = def.name; description = starsoul.ui.tooltip { title = def.name; desc = def.desc; color = lib.color(.1, .7, 1); }; groups = { suit = 1; inv = 1; -- has inventories batteryPowered = 1; -- has a battery inv programmable = 1; -- has a chip inv }; on_use = function(st, luser, pointed) local user = starsoul.activeUsers[luser:get_player_name()] if not user then return end -- have mercy on users who've lost their suits and wound -- up naked and dying of exposure if user:naked() then local ss = st:take_item(1) user:setSuit(starsoul.type.suit(ss)) user:suitSound('starsoul-suit-don') return st end end; inventory_image = icon:render(); _starsoul = { container = { workbench = { order = {'batteries','chips','guns','ammo'} }; list = { bat = { key = 'starsoul:suit_slots_bat'; accept = 'dynamo'; sz = def.slots.batteries; }; chips = { key = 'starsoul:suit_slots_chips'; accept = 'chip'; sz = def.slots.chips; }; canisters = { key = 'starsoul:suit_slots_canisters'; accept = 'canister'; sz = def.slots.canisters; }; guns = { key = 'starsoul:suit_slots_gun'; accept = 'weapon'; workbench = { label = 'Weapon'; icon = 'starsoul-ui-icon-gun'; color = lib.color(1,0,0); }; sz = def.slots.guns; }; ammo = { key = 'starsoul:suit_slots_ammo'; accept = 'ammo'; workbench = { label = 'Ammunition'; color = lib.color(1,.5,0); easySlots = true; -- all slots accessible on the go }; sz = def.slots.ammo; }; }; }; event = { create = function(st,how) local s = suitStore(st) -- make sure there's a defined powerstate starsoul.type.suit(st):powerStateSet 'off' suitContainer(st):clear() --[[ populate meta tables s.write('batteries', {}) s.write('guns', {}) s.write('ammo', {}) s.write('elements', {}) s.write('chips', {})]] end; }; suit = def; }; }); end) local slotProps = { starsoul_cfg = { itemClass = 'inv'; }; starsoul_suit_bat = { suitSlot = true; powerLock = true; itemClass = 'dynamo'; }; starsoul_suit_chips = { suitSlot = true; powerLock = true; itemClass = 'chip'; }; starsoul_suit_guns = { suitSlot = true; maintenanceNode = ''; itemClass = 'suitWeapon'; }; starsoul_suit_ammo = { suitSlot = true; maintenanceNode = ''; itemClass = 'suitAmmo'; }; starsoul_suit_canisters = { suitSlot = true; itemClass = 'canister'; }; } minetest.register_allow_player_inventory_action(function(luser, act, inv, p) local user = starsoul.activeUsers[luser:get_player_name()] local function grp(i,g) return minetest.get_item_group(i:get_name(), g) ~= 0 end local function checkBaseRestrictions(list) local restrictions = slotProps[list] if not restrictions then return nil, true end if restrictions.suitSlot then if user:naked() then return restrictions, false end end if restrictions.powerLock then if user:getSuit():powerState() ~= 'off' then return restrictions, false end end return restrictions, true end local function itemFits(item, list) local rst, ok = checkBaseRestrictions(list) if not ok then return false end if rst == nil then return true end if rst.itemClass and not grp(item, rst.itemClass) then return false end if rst.maintenanceNode then return false end -- FIXME figure out best way to identify when the player is using a maintenance node if grp(item, 'specialInventory') then if grp(item, 'powder') and list ~= 'starsoul_suit_elem' then return false end -- FIXME handle containers if grp(item, 'psi') and list ~= 'starsoul_psi' then return false end end return true end local function itemCanLeave(item, list) local rst, ok = checkBaseRestrictions(list) if not ok then return false end if rst == nil then return true end if minetest.get_item_group(item:get_name(), 'specialInventory') then end if rst.maintenanceNode then return false end return true end if act == 'move' then local item = inv:get_stack(p.from_list, p.from_index) if not (itemFits(item, p.to_list) and itemCanLeave(item, p.from_list)) then return 0 end elseif act == 'put' then if not itemFits(p.stack, p.listname) then return 0 end elseif act == 'take' then if not itemCanLeave(p.stack, p.listname) then return 0 end end return true end) minetest.register_on_player_inventory_action(function(luser, act, inv, p) local user = starsoul.activeUsers[luser:get_player_name()] local function slotChange(slot,a,item) local s = slotProps[slot] if slot == 'starsoul_suit' then user:updateSuit() if user:naked() then starsoul.type.suit.purgeInventories(user.entity) user.power.nano = {} end elseif s and s.suitSlot then local s = user:getSuit() s:onItemMove(user, slot, a, item) s:onReconfigure(user.entity:get_inventory()) user:setSuit(s) else return end user:updateHUD() end if act == 'put' or act == 'take' then local item = p.stack slotChange(p.listname, act, item) elseif act == 'move' then local item = inv:get_stack(p.to_list, p.to_index) slotChange(p.from_list, 'take', item) slotChange(p.to_list, 'put', item) end end) local suitInterval = 2.0 starsoul.startJob('starsoul:suit-software', suitInterval, function(delta) local runState = { pgmsRun = {}; flags = {}; } for id, u in pairs(starsoul.activeUsers) do if not u:naked() then local reconfSuit = false local inv = u.entity:get_inventory() local chips = inv:get_list('starsoul_suit_chips') local suitprog = starsoul.mod.electronics.chip.usableSoftware(chips) for _, prop in pairs(suitprog) do local s = prop.sw if s.kind == 'suitPower' and (s.powerKind == 'passive' or s.bgProc) and (not runState.pgmsRun[s]) then local conf = prop.file.body.conf local enabled = true for _, e in ipairs(conf) do if e.key == 'disable' and e.value == 'yes' then enabled = false break end end local fn if s.powerKind == 'passive' then fn = s.run else fn = s.bgProc end function prop.saveConf(cfg) cfg = cfg or conf prop.fd:write(cfg) inv:set_stack('starsoul_suit_chips', prop.chipSlot, prop.fd.chip) reconfSuit = true end function prop.giveItem(st) u:thrustUpon(st) end if enabled and fn(u, prop, suitInterval, runState) then runState.pgmsRun[s] = true end end end if reconfSuit then u:reconfigureSuit() end end end end) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/terrain.lua version [faca82716c].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
local T = starsoul.translator local lib = starsoul.mod.lib starsoul.terrain = {} local soilSounds = {} local grassSounds = {} minetest.register_node('starsoul:soil', { description = T 'Soil'; tiles = {'default_dirt.png'}; groups = {dirt = 1}; drop = ''; sounds = soilSounds; _starsoul = { onDestroy = function() end; kind = 'block'; elements = {}; }; }) minetest.register_node('starsoul:sand', { description = T 'Sand'; tiles = {'default_sand.png'}; groups = {dirt = 1}; drop = ''; sounds = soilSounds; _starsoul = { kind = 'block'; fab = starsoul.type.fab { element = { silicon = 25 } }; }; }) minetest.register_craftitem('starsoul:soil_clump', { short_description = T 'Soil'; description = starsoul.ui.tooltip { title = T 'Soil'; desc = 'A handful of nutrient-packed soil, suitable for growing plants'; color = lib.color(0.3,0.2,0.1); }; inventory_image = 'starsoul-item-soil.png'; groups = {soil = 1}; _starsoul = { fab = starsoul.type.fab { element = { carbon = 12 / 4 } }; }; }) function starsoul.terrain.createGrass(def) local function grassfst(i) local nextNode = def.name if i >= 0 then nextNode = nextNode .. '_walk_' .. tostring(i) end return { onWalk = function(pos) minetest.set_node_at(pos, def.name .. '_walk_2'); end; onDecay = function(pos,delta) minetest.set_node_at(pos, nextNode); end; onDestroy = function(pos) end; fab = def.fab; recover = def.recover; recover_vary = def.recover_vary; }; end local drop = { max_items = 4; items = { { items = {'starsoul:soil'}, rarity = 2; tool_groups = { 'shovel', 'trowel' }; }; }; } minetest.register_node(def.name, { description = T 'Greengraze'; tiles = { def.img .. '.png'; 'default_dirt.png'; { name = 'default_dirt.png^' .. def.img ..'_side.png'; tileable_vertical = false; }; }; groups = {grass = 1, sub_walk = 1}; drop = ''; sounds = grassSounds; _starsoul = grassfst(2); }) for i=2,0,-1 do local opacity = tostring((i/2.0) * 255) minetest.register_node(def.name, { description = def.desc; tiles = { def.img .. '.png^(default_footprint.png^[opacity:'..opacity..')'; 'default_dirt.png'; { name = 'default_dirt.png^' .. def.img ..'_side.png'; tileable_vertical = false; }; }; groups = {grass = 1, sub_walk = 1, sub_decay = 5}; drop = ''; _starsoul = grassfst(i-1); sounds = grassSounds; }) end end starsoul.terrain.createGrass { name = 'starsoul:greengraze'; desc = T 'Greengraze'; img = 'default_grass'; fab = starsoul.type.fab { element = { carbon = 12; }; time = { shred = 2.5; }; }; } for _, w in pairs {false,true} do minetest.register_node('starsoul:liquid_water' .. (w and '_flowing' or ''), { description = T 'Water'; drawtype = 'liquid'; waving = 3; tiles = { { name = "default_water_source_animated.png"; backface_culling = false; animation = { type = "vertical_frames"; aspect_w = 16; aspect_h = 16; length = 2.0; }; }; { name = "default_water_source_animated.png"; backface_culling = true; animation = { type = "vertical_frames"; aspect_w = 16; aspect_h = 16; length = 2.0; }; }; }; use_texture_alpha = 'blend'; paramtype = 'light'; walkable = false, pointable = false, diggable = false, buildable_to = true; is_ground_content = false; drop = ''; drowning = 1; liquidtype = w and 'flowing' or 'source'; liquid_alternative_flowing = 'starsoul:liquid_water_flowing'; liquid_alternative_source = 'starsoul:liquid_water'; liquid_viscosity = 1; liquid_renewable = true; liquid_range = 2; drowning = 40; post_effect_color = {a=103, r=10, g=40, b=70}; groups = {water = 3, liquid = 3}; }); end starsoul.world.mineral.foreach('starsoul:mineral_generate', {}, function(name,m) local node = string.format('starsoul:mineral_%s', name) local grp = {mineral = 1} minetest.register_node(node, { description = m.desc; tiles = m.tiles or (m.tone and { string.format('default_stone.png^[colorizehsl:%s:%s:%s', m.tone.hue, m.tone.sat, m.tone.lum) }) or {'default_stone.png'}; groups = grp; drop = m.rocks or ''; _starsoul = { kind = 'block'; elements = m.elements; fab = m.fab; recover = m.recover; recover_vary = m.recover_vary; }; }) if not m.excludeOre then local seed = 0 grp.ore = 1 for i = 1, #m.name do seed = seed*50 + string.byte(name, i) end minetest.register_ore { ore = node; ore_type = m.dist.kind; wherein = {m.dist.among}; clust_scarcity = m.dist.rare; y_max = m.dist.height[1], y_min = m.dist.height[2]; noise_params = m.dist.noise or { offset = 28; scale = 16; spread = vector.new(128,128,128); seed = seed; octaves = 1; }; } end end) starsoul.world.mineral.link('feldspar', { desc = T 'Feldspar'; excludeOre = true; recover = starsoul.type.fab { time = { shred = 3; }; cost = { shredPower = 3; }; }; recover_vary = function(rng, ctx) -- print('vary!', rng:int(), rng:int(0,10)) return starsoul.type.fab { element = { aluminum = rng:int(0,4); potassium = rng:int(0,2); calcium = rng:int(0,2); } }; end; }) -- map generation minetest.register_alias('mapgen_stone', 'starsoul:mineral_feldspar') minetest.register_alias('mapgen_water_source', 'starsoul:liquid_water') minetest.register_alias('mapgen_river_water_source', 'starsoul:liquid_water') |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/tiers.lua version [97d9d01bfc].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
local lib = starsoul.mod.lib starsoul.world.tier = lib.registry.mk 'starsoul:tier' local T = starsoul.world.tier local fab = starsoul.type.fab function starsoul.world.tier.fabsum(name, ty) local dest = fab {} local t = starsoul.world.tier.db[name] assert(t, 'reference to nonexisting tier '..name) if t.super then dest = dest+starsoul.world.tier.fabsum(t.super, ty)*(t.cost or 1) end if t.fabclasses and t.fabclasses[ty] then dest = dest + t.fabclasses[ty] end return dest end function starsoul.world.tier.tech(name, tech) local t = starsoul.world.tier.db[name] if t.techs and t.techs[tech] ~= nil then return t.techs[tech] end if t.super then return starsoul.world.tier.tech(t.super, tech) end return false end T.meld { base = { fabclass = { electric = fab {metal={copper = 10}}; suit = fab {element={carbon = 1e3}}; psi = fab {metal={numinium = 1}}; bio = fab {element={carbon = 1}}; }; }; -- properties that apply to all tiers ------------------ -- tier classes -- ------------------ lesser = { name = 'Lesser', adj = 'Lesser'; super = 'base'; fabclasses = { basis = fab { metal = {aluminum=4}; }; }; }; greater = { name = 'Greater', adj = 'Greater'; super = 'base'; fabclasses = { basis = fab { metal = {vanadium=2}; }; }; }; starsoul = { name = 'Starsoul', adj = 'Starsoul'; super = 'base'; fabclasses = { basis = fab { metal = {osmiridium=1}; }; }; }; forevanished = { name = 'Forevanished One', adj = 'Forevanished'; super = 'base'; fabclasses = { basis = fab { metal = {elusium=1}; }; }; }; ------------------ -- Lesser Races -- ------------------ makeshift = { -- regular trash name = 'Makeshift', adj = 'Makeshift'; super = 'lesser'; techs = {tool = true, prim = true, electric = true}; power = 0.5; efficiency = 0.3; reliability = 0.2; cost = 0.3; fabclasses = { -- characteristic materials basis = fab { -- fallback metal = {iron=3}; }; }; }; imperial = { --powerful trash name = 'Imperial', adj = 'Imperial'; super = 'lesser'; techs = {tool = true, electric = true, electronic = true, suit = true, combatSuit = true, weapon = true, hover='ion'}; power = 2.0; efficiency = 0.5; reliability = 0.5; cost = 1.0; fabclasses = { basis = fab { metal = {steel=2}; }; }; }; commune = { --reliability name = 'Commune', adj = 'Commune'; super = 'lesser'; techs = {tool = true, electric = true, electronic = true, suit = true, combatSuit = true, weapon = true, gravitic = true, hover='grav'}; power = 1.0; efficiency = 2.0; reliability = 3.0; cost = 1.5; fabclasses = { basis = fab { metal = {titanium=1}; time = {print = 1.2}; -- commune stuff is intricate }; }; }; ------------------- -- Greater Races -- ------------------- ---------------- -- Starsouled -- ---------------- suIkuri = { --super-tier name = 'Su\'ikuri', adj = "Su'ikuruk"; super = 'starsoul'; techs = {psi = true, prim = true, bioSuit = true, psiSuit = true}; power = 1.5; efficiency = 1.0; reliability = 3.0; cost = 2.0; fabclasses = { psi = fab { metal = {numinium = 2.0}; crystal = {beryllium = 1.0}; }; bio = fab { crystal = {beryllium = 1.0}; }; }; }; usukwinya = { --value for 'money'; no weapons; no hovertech (they are birds) -- NOTA BENE: the ususkwinya *do* have weapons of their own; however, -- they are extremely restricted and never made available except to a -- very select number of that species. consequently, usuk players -- of a certain scenario may have usuk starting weapons, but these must -- be manually encoded to avoid injecting them into the overall crafting -- /loot system. because there are so few of these weapons in existence, -- all so tightly controlled, the odds of the weapons or plans winding -- up on Farthest Shadow are basically zero unless you bring them yourself name = 'Usukwinya', adj = 'Usuk'; super = 'starsoul'; techs = lib.tbl.set('tool', 'electric', 'electronic', 'suit', 'gravitic'); power = 2.0; efficiency = 2.0; reliability = 2.0; cost = 0.5; fabclasses = { basis = fab { crystal = {aluminum = 5}; -- ruby }; }; }; eluthrai = { --super-tier name = 'Eluthrai', adj = 'Eluthran'; super = 'starsoul'; techs = {tool = true, electric = true, electronic = true, weapon = true, gravitic = true, gravweapon = true, suit = true, combatSuit = true, hover = 'grav'}; power = 4.0; efficiency = 4.0; reliability = 4.0; cost = 4.0; fabclasses = { basis = fab { crystal = {carbon = 5}; -- diamond }; special = fab { metal = {technetium=1, cinderstone=1} }; }; }; ----------------------- -- Forevanished Ones -- ----------------------- firstborn = { --god-tier name = 'Firstborn', adj = 'Firstborn'; super = 'forevanished'; techs = {tool = true, electric = true, electronic = true, suit = true, psi = true, combatSuit = true, weapon = true, gravitic = true, gravweapon = true}; power = 10.0; efficiency = 5.0; reliability = 3.0; cost = 10.0; fabclasses = { basis = fab { metal = {technetium=2, neodymium=3, sunsteel=1}; crystal = {astrite=1}; }; }; }; forevanisher = { --godslayer-tier name = 'Forevanisher', adj = 'Forevanisher'; super = 'forevanished'; techs = {tool = true, electric = true, electronic = true, suit = true, psi = true, combatSuit = true, weapon = true, gravitic = true, gravweapon = true}; power = 20.0; efficiency = 1.0; reliability = 2.0; cost = 100.0; fabclasses = { basis = fab { metal = {}; crystal = {}; }; }; }; } |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/ui.lua version [e13ae59c08].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
local lib = starsoul.mod.lib starsoul.ui = {} starsoul.type.ui = lib.class { name = 'starsoul:ui'; __index = { action = function(self, user, state, fields) local pg = self.pages[state.page or 'index'] if not pg then return end if pg.handle then local redraw, reset = pg.handle(state, user, fields) if reset then pg.setupState(state,user) end if redraw then self:show(user) end end if fields.quit then self:cb('onClose', user) end end; cb = function(self, name, user, ...) local state = self:begin(user) if self[name] then self[name](state, user, ...) end local pcb = self.pages[state.page][name] if pcb then pcb(state, user, ...) end end; begin = function(self, user, page, ...) local state = starsoul.activeUI[user.name] if state and state.form ~= self.id then state = nil starsoul.activeUI[user.name] = nil end local created = state == nil if not state then state = { page = page or 'index'; form = self.id; } starsoul.activeUI[user.name] = state self:cb('setupState', user, ...) elseif page ~= nil and state.page ~= page then state.page = page local psetup = self.pages[state.page].setupState if psetup then psetup(state,user, ...) end end return state, created end; render = function(self, state, user) return self.pages[state.page].render(state, user) end; show = function(self, user) local state = self:begin(user) minetest.show_formspec(user.name, self.id,self:render(state, user)) end; open = function(self, user, page, ...) user:suitSound 'starsoul-nav' self:begin(user, page, ...) self:show(user) end; close = function(self, user) local state = starsoul.activeUI[user.name] if state and state.form == self.id then self:cb('onClose', user) starsoul.activeUI[user.name] = nil minetest.close_formspec(user.name, self.id) end end; }; construct = function(p) if not p.id then error('UI missing id') end p.pages = p.pages or {} return p end; } function starsoul.interface.install(ui) starsoul.interface.link(ui.id, ui) end function starsoul.ui.build(def, parent) local clr = def.color if clr and lib.color.id(clr) then clr = clr:to_hsl_o() end local state = { x = (def.x or 0); y = (def.y or 0); w = def.w or 0, h = def.h or 0; fixed = def.fixed or false; spacing = def.spacing or 0; padding = def.padding or 0; align = def.align or (parent and parent.align) or 'left'; lines = {}; mode = def.mode or (parent and parent.mode or nil); -- hw or sw gen = (parent and parent.gen or 0) + 1; fg = def.fg or (parent and parent.fg); color = clr or (parent and parent.color) or { hue = 260, sat = 0, lum = 0 }; } local lines = state.lines local cmod = string.format('^[hsl:%s:%s:%s', state.color.hue, state.color.sat*0xff, state.color.lum*0xff) local E = minetest.formspec_escape if state.padding/2 > state.x then state.x = state.padding/2 end if state.padding/2 > state.y then state.y = state.padding/2 end local function btnColorDef(sel) local function climg(state,img) local selstr if sel == nil then selstr = string.format( 'button%s,' .. 'button_exit%s,' .. 'image_button%s,' .. 'item_image_button%s', state, state, state, state) else selstr = E(sel) .. state end return string.format('%s[%s;' .. 'bgimg=%s;' .. 'bgimg_middle=16;' .. 'content_offset=0,0' .. ']', sel and 'style' or 'style_type', selstr, E(img..'^[resize:48x48'..cmod)) end return climg('', 'starsoul-ui-button-sw.png') .. climg(':hovered', 'starsoul-ui-button-sw-hover.png') .. climg(':pressed', 'starsoul-ui-button-sw-press.png') end local function widget(...) table.insert(lines, string.format(...)) end if def.kind == 'vert' then for _, w in ipairs(def) do local src, st = starsoul.ui.build(w, state) widget('container[%s,%s]%scontainer_end[]', state.x, state.y, src) state.y=state.y + state.spacing + st.h state.w = math.max(state.w, st.w) end state.w = state.w + state.padding state.h = state.y + state.padding/2 elseif def.kind == 'hztl' then for _, w in ipairs(def) do local src, st = starsoul.ui.build(w, state) widget('container[%s,%s]%scontainer_end[]', state.x, state.y, src) -- TODO alignments state.x=state.x + state.spacing + st.w state.h = math.max(state.h, st.h) end state.h = state.h + state.padding state.w = state.x + state.padding/2 elseif def.kind == 'list' then local slotTypes = { plain = {hue = 200, sat = -.1, lum = 0}; element = {hue = 20, sat = -.3, lum = 0}; chip = {hue = 0, sat = -1, lum = 0}; psi = {hue = 300, sat = 0, lum = 0}; power = {hue = 50, sat = 0, lum = .2}; } local img if state.mode == 'hw' then img = lib.image('starsoul-ui-slot-physical.png'); else img = lib.image('starsoul-ui-slot.png'):shift(slotTypes[def.listContent or 'plain']); end local spac = state.spacing widget('style_type[list;spacing=%s,%s]',spac,spac) assert(def.w and def.h, 'ui-lists require a fixed size') for lx = 0, def.w-1 do for ly = 0, def.h-1 do local ox, oy = state.x + lx*(1+spac), state.y + ly*(1+spac) table.insert(lines, string.format('image[%s,%s;1.1,1.1;%s]', ox-0.05,oy-0.05, img:render())) end end table.insert(lines, string.format('listcolors[#00000000;#ffffff10]')) -- FIXME table.insert(lines, string.format('list[%s;%s;%s,%s;%s,%s;%s]', E(def.target), E(def.inv), state.x, state.y, def.w, def.h, def.idx)) local sm = 1 state.w = def.w * sm + (spac * (def.w - 1)) state.h = def.h * sm + (spac * (def.h - 1)) elseif def.kind == 'contact' then if def.color then table.insert(lines, btnColorDef(def.id)) end widget('image_button%s[%s,%s;%s,%s;%s;%s;%s]', def.close and '_exit' or '', state.x, state.y, def.w, def.h, E(def.img), E(def.id), E(def.label or '')) elseif def.kind == 'button' then if def.color then table.insert(lines, btnColorDef(def.id)) end local label = E(def.label or '') if state.fg then label = lib.color(state.fg):fmt(label) end widget('button%s[%s,%s;%s,%s;%s;%s]', def.close and '_exit' or '', state.x, state.y, def.w, def.h, E(def.id), label) elseif def.kind == 'img' then widget('%s[%s,%s;%s,%s;%s]', def.item and 'item_image' or 'image', state.x, state.y, def.w, def.h, E(def.item or def.img)) elseif def.kind == 'label' then local txt = E(def.text) if state.fg then txt = lib.color(state.fg):fmt(txt) end widget('label[%s,%s;%s]', state.x, state.y + def.h*.5, txt) elseif def.kind == 'text' then -- TODO paragraph formatter widget('hypertext[%s,%s;%s,%s;%s;%s]', state.x, state.y, def.w, def.h, E(def.id), E(def.text)) elseif def.kind == 'hbar' or def.kind == 'vbar' then -- TODO fancy image bars local cl = lib.color(state.color) local fg = state.fg or cl:readable(.8,1) local wfac, hfac = 1,1 local clamp = math.min(math.max(def.fac, 0), 1) if def.kind == 'hbar' then wfac = wfac * clamp else hfac = hfac * clamp end local x,y, w,h = state.x, state.y, def.w, def.h widget('box[%s,%s;%s,%s;%s]', x,y, w,h, cl:brighten(0.2):hex()) widget('box[%s,%s;%s,%s;%s]', x, y + (h*(1-hfac)), w * wfac, h * hfac, cl:hex()) if def.text then widget('hypertext[%s,%s;%s,%s;;%s]', state.x, state.y, def.w, def.h, string.format('<global halign=center valign=middle color=%s>%s', fg:hex(), E(def.text))) end end if def.desc then widget('tooltip[%s,%s;%s,%s;%s]', state.x, state.y, def.w, def.h, E(def.desc)) end local originX = (parent and parent.x or 0) local originY = (parent and parent.y or 0) local l = table.concat(lines) -- if state.fixed and (state.w < state.x or state.h < state.y) then -- l = string.format('scroll_container[%s,%s;%s,%s;scroll_%s;%s]%sscroll_container_end[]', -- (parent and parent.x or 0), (parent and parent.y or 0), -- state.w, state.h, state.gen, -- (state.x > state.w) and 'horizontal' or 'vertical', l) -- end if def.mode or def.container then if def.mode then l = string.format('background9[%s,%s;%s,%s;%s;false;64]', originX, originY, state.w, state.h, E(string.format('starsoul-ui-bg-%s.png%s^[resize:128x128', (def.mode == 'sw') and 'digital' or 'panel', cmod))) .. l end if parent == nil or state.color ~= parent.color then l = btnColorDef() .. l end end if not parent then return string.format('formspec_version[6]size[%s,%s]%s', state.w, state.h, l), state else return l, state end end starsoul.ui.tooltip = lib.ui.tooltipper { colors = { -- generic notes neutral = lib.color(.5,.5,.5); good = lib.color(.2,1,.2); bad = lib.color(1,.2,.2); info = lib.color(.4,.4,1); -- chip notes schemaic = lib.color(.2,.7,1); ability = lib.color(.7,.2,1); driver = lib.color(1,.7,.2); }; } |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/user.lua version [5326333d8a].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 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 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 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 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 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 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 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 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 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 |
-- [ʞ] user.lua -- ~ lexi hale <lexi@hale.su> -- © EUPL v1.2 -- ? defines the starsoul.type.user class, which is -- the main interface between the game world and the -- client. it provides for initial signup and join, -- managing the HUD, skinning the player model, -- effecting weather changes, etc. local lib = starsoul.mod.lib local function hudAdjustBacklight(img) local night = math.abs(minetest.get_timeofday() - .5) * 2 local opacity = night*0.8 return img:fade(opacity) end local userStore = lib.marshal.metaStore { persona = { key = 'starsoul:persona'; type = starsoul.store.persona; }; } local suitStore = starsoul.store.suitMeta starsoul.type.user = lib.class { name = 'starsoul:user'; construct = function(ident) local name, luser if type(ident) == 'string' then name = ident luser = minetest.get_player_by_name(name) else luser = ident name = luser:get_player_name() end return { entity = luser; name = name; hud = { elt = {}; }; tree = {}; action = { bits = 0; -- for control deltas prog = {}; -- for recording action progress on a node; reset on refocus tgt = {type='nothing'}; sfx = {}; fx = {}; }; actMode = 'off'; power = { nano = {primary = nil, secondary = nil}; 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' 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 base if abs then local min, max min, max, base = self:statRange(stat) if d == true then d = max elseif d == false then d = min end end if stat == 'health' then self.entity:set_hp(abs and d or (self.entity:get_hp() + d), cause) elseif stat == 'breath' then self.entity:set_breath(abs and d or (self.entity:get_breath() + d)) else if abs then dt[stat] = d - base else dt[stat] = dt[stat] + d end self:pushPersona() end self:updateHUD() -- TODO trigger relevant animations? end; lookupSpecies = function(self) return starsoul.world.species.lookup(self.persona.species, self.persona.speciesVariant) end; phenoTrait = function(self, trait) local s,v = self:lookupSpecies() return v.traits[trait] or s.traits[trait] or 0 end; statRange = function(self, stat) --> min, max, base return starsoul.world.species.statRange( self.persona.species, self.persona.speciesVariant, stat) end; effectiveStat = function(self, stat) local val local min, max, base = self:statRange(stat) if stat == 'health' then val = self.entity:get_hp() elseif stat == 'breath' then val = self.entity:get_breath() 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; alignment = def.align; position = def.pos; offset = def.ofs; z_index = def.z; } if def.update then img.update = function() def.update(user, function(prop, val) user:hud_change(img.id, prop, val) end, def) end end return img end; attachMeter = function(self, def) local luser = self.entity local m = {} local w = def.size or 80 local szf = w / 80 local h = szf * 260 m.meter = luser:hud_add { type = 'image'; scale = {x = szf, y = szf}; alignment = def.align; position = def.pos; offset = def.ofs; z_index = def.z or 0; } local cx = def.ofs.x + (w/2)*def.align.x local cy = def.ofs.y + (h/2)*def.align.y local oy = cy + h/2 - 42 -- this is so fucking fragile holy fuck m.readout = luser:hud_add { type = 'text'; scale = {x = w, y = h}; size = szf; style = 4; position = def.pos; alignment = {x=0,0}; offset = {x = cx, y = oy}; z_index = (def.z or 0)+1; number = 0xffffff; } m.destroy = function() luser:hud_remove(m.meter) luser:hud_remove(m.readout) end m.update = function() local v,txt,color,txtcolor = def.measure(luser,def) v = math.max(0, math.min(1, v)) local n = math.floor(v*16) + 1 local img = hudAdjustBacklight(lib.image('starsoul-ui-meter.png')) :colorize(color or def.color) if def.flipX then img = img:transform 'FX' end img = img:render() img = img .. '^[verticalframe:17:' .. tostring(17 - n) luser:hud_change(m.meter, 'text', img) if txt then luser:hud_change(m.readout, 'text', txt) end if txtcolor then luser:hud_change(m.readout, 'number', txtcolor:hex()) end end return m end; attachTextBox = function(self, def) local luser = self.entity local box = {} box.id = luser:hud_add { type = 'text'; text = ''; alignment = def.align; number = def.color and def.color:int24() or 0xFFffFF; scale = def.bound; size = {x = def.size, y=0}; style = def.style; position = def.pos; offset = def.ofs; } box.update = function() local text, color = def.text(self, box, def) luser:hud_change(box.id, 'text', text) if color then luser:hud_change(box.id, 'number', color:int24()) end end return box end; attachStatBar = function(self, def) local luser = self.entity local bar = {} local img = lib.image 'starsoul-ui-bar.png' local colorized = img if type(def.color) ~= 'function' then colorized = colorized:shift(def.color) end bar.id = luser:hud_add { type = 'statbar'; position = def.pos; offset = def.ofs; name = def.name; text = colorized:render(); text2 = img:tint{hue=0, sat=-1, lum = -0.5}:fade(0.5):render(); number = def.size; item = def.size; direction = def.dir; alignment = def.align; size = {x=4,y=24}; } bar.update = function() local sv, sf = def.stat(self, bar, def) luser:hud_change(bar.id, 'number', def.size * sf) if type(def.color) == 'function' then local clr = def.color(sv, luser, sv, sf) luser:hud_change(bar.id, 'text', img:tint(clr):render()) end end return bar, {x=3 * def.size, y=16} -- x*2??? what end; createHUD = function(self) local function basicStat(statName) return function(user, bar) return self:effectiveStat(statName) end end local function batteryLookup(user) local max = user:suitPowerCapacity() if max == 0 then return 0, 0 end local ch = user:suitCharge() return (ch/max)*100, ch/max end local function C(h,s,l) return {hue=h,sat=s,lum=l} end local hbofs = (1+self.entity:hud_get_hotbar_itemcount()) * 25 local bpad = 8 self.hud.elt.health = self:attachStatBar { name = 'health', stat = basicStat 'health'; color = C(340,0,.3), size = 100; pos = {x=0.5, y=1}, ofs = {x = -hbofs, y=-48 - bpad}; dir = 1; align = {x=-1, y=-1}; } self.hud.elt.stamina = self:attachStatBar { name = 'stamina', stat = basicStat 'stamina'; color = C(60,0,.2), size = 100; pos = {x=0.5, y=1}, ofs = {x = -hbofs, y=-24 - bpad}; dir = 1; align = {x=-1, y=-1}; } self.hud.elt.bat = self:attachStatBar { name = 'battery', stat = batteryLookup; color = C(190,0,.2), size = 100; pos = {x=0.5, y=1}, ofs = {x = hbofs - 4, y=-48 - bpad}; dir = 0; align = {x=1, y=-1}; } self.hud.elt.psi = self:attachStatBar { name = 'psi', stat = basicStat 'psi'; color = C(320,0,.2), size = 100; pos = {x=0.5, y=1}, ofs = {x = hbofs - 4, y=-24 - bpad}; dir = 0; align = {x=1, y=-1}; } self.hud.elt.time = self:attachTextBox { name = 'time'; align = {x=0, y=1}; pos = {x=0.5, y=1}; ofs = {x=0,y=-95}; text = function(user) local cal = starsoul.world.time.calendar[user.pref.calendar] return cal.time(minetest.get_timeofday()) end; } self.hud.elt.temp = self:attachMeter { name = 'temp'; align = {x=1, y=-1}; pos = {x=0, y=1}; ofs = {x=20, y=-20}; measure = function(user) local warm = self:effectiveStat 'warmth' local n, color if warm < 0 then n = math.min(100, -warm) color = lib.color(0.1,0.3,1):lerp(lib.color(0.7, 1, 1), math.min(1, n/50)) else n = math.min(100, warm) color = lib.color(0.1,0.3,1):lerp(lib.color(1, 0, 0), math.min(1, n/50)) end local txt = string.format("%s°", math.floor(warm)) return (n/50), txt, color end; } self.hud.elt.geiger = self:attachMeter { name = 'geiger'; align = {x=-1, y=-1}; pos = {x=1, y=1}; ofs = {x=-20, y=-20}; flipX = true; measure = function(user) local hot = self:effectiveStat 'irradiation' local color = self:uiColor():lerp(lib.color(0.3, 1, 0), math.min(1, hot/5)) local txt = string.format("%sGy", math.floor(hot)) return (hot/5), txt, color end; } self.hud.elt.crosshair = self:attachImage { name = 'crosshair '; tex = ''; pos = {x=.5, y=.5}; scale = {x=1,y=1}; ofs = {x=0, y=0}; align = {x=0, y=0}; update = function(user, set) local imgs = { off = ''; nano = 'starsoul-ui-crosshair-nano.png'; psi = 'starsoul-ui-crosshair-psi.png'; weapon = 'starsoul-ui-crosshair-weapon.png'; } set('text', imgs[self.actMode] or imgs.off) end; }; local hudCenterBG = lib.image 'starsoul-ui-hud-bg.png':colorize(self:uiColor()) self.hud.elt.bg = self:attachImage { name = 'hudBg'; tex = hudCenterBG:render(); pos = {x=.5, y=1}; scale = {x=1,y=1}; ofs = {x=0, y=0}; align = {x=0, y=-1}; z = -1; update = function(user, set) set('text', hudAdjustBacklight(hudCenterBG):render()) end; }; end; onModeChange = function(self, oldMode, silent) self.hud.elt.crosshair.update() if not silent then local sfxt = { off = 'starsoul-mode-off'; nano = 'starsoul-mode-nano'; psi = 'starsoul-mode-psi'; weapon = 'starsoul-mode-weapon'; } local sfx = self.actMode and sfxt[self.actMode] or sfxt.off self:suitSound(sfx) end end; actModeSet = function(self, mode, silent) if not mode then mode = 'off' end local oldMode = self.actMode self.actMode = mode self:onModeChange(oldMode, silent) if mode ~= oldMode then starsoul.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('starsoul_suit', 1) -- your environment suit (change at wardrobe) inv:set_size('starsoul_cfg', 1) -- the item you're reconfiguring / container you're accessing local scenario for _, e in pairs(starsoul.world.scenario) do if e.id == starsoul.world.defaultScenario then scenario = e break end end assert(scenario) self.persona = starsoul.world.species.birth(scenario.species, scenario.speciesVariant, self.entity) self.persona.name = self.entity:get_player_name() -- a reasonable default self.persona.background = starsoul.world.defaultScenario self:pushPersona() local gifts = scenario.startingItems local inv = self.entity:get_inventory() inv:set_stack('starsoul_suit', 1, starsoul.item.mk(gifts.suit, self, {gift=true})) self:getSuit():establishInventories(self.entity) local function giveGifts(name, list) if inv:get_size(name) > 0 then for i, e in ipairs(list) do inv:add_item(name, starsoul.item.mk(e, self, {gift=true})) end end end giveGifts('starsoul_suit_bat', gifts.suitBatteries) giveGifts('starsoul_suit_chips', gifts.suitChips) giveGifts('starsoul_suit_guns', gifts.suitGuns) giveGifts('starsoul_suit_ammo', gifts.suitAmmo) giveGifts('starsoul_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 startPoint 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 = starsoul.world.climate.eval(startPoint,.5,.5).surfaceTemp end if cx > 10000 then break end -- avoid infiniloop in pathological conditions until temp > -2 self.entity:set_pos(startPoint) meta:set_string('starsoul_spawn', startPoint:to_string()) end; onDie = function(self, reason) local inv = self.entity:get_inventory() local where = self.entity:get_pos() local function dropInv(lst) local l = inv:get_list(lst) for i, o in ipairs(l) do if o and not o:is_empty() then minetest.item_drop(o, self.entity, where) end end inv:set_list(lst, {}) end dropInv 'main' dropInv 'starsoul_suit' self:statDelta('psi', 0, 'death', true) self:statDelta('hunger', 0, 'death', true) self:statDelta('thirst', 0, 'death', true) self:statDelta('fatigue', 0, 'death', true) self:statDelta('stamina', 0, 'death', true) self:updateSuit() end; onRespawn = function(self) local meta = self.entity:get_meta() self.entity:set_pos(vector.from_string(meta:get_string'starsoul_spawn')) self:updateSuit() return true end; onJoin = function(self) local me = self.entity local meta = me:get_meta() self:pullPersona() -- formspec_version and real_coordinates are apparently just -- completely ignored here me:set_formspec_prepend [[ bgcolor[#00000000;true] style_type[button,button_exit,image_button,item_image_button;border=false] style_type[button;bgimg=starsoul-ui-button-hw.png;bgimg_middle=8;content_offset=0,-2] style_type[button:hovered;bgimg=starsoul-ui-button-hw-hover.png;bgimg_middle=8] style_type[button:pressed;bgimg=starsoul-ui-button-hw-press.png;bgimg_middle=8;content_offset=0,1] ]] local hotbarSlots = me:get_inventory():get_size 'main'; -- local slotTex = 'starsoul-ui-slot.png' -- local hbimg = string.format('[combine:%sx128', 128 * hotbarSlots) -- for i = 0, hotbarSlots-1 do -- hbimg = hbimg .. string.format(':%s,0=%s', 128 * i, slotTex) -- end --me:hud_set_hotbar_image(lib.image(hbimg):colorize(self:uiColor()):fade(.36):render()) -- me:hud_set_hotbar_selected_image(lib.image(slotTex):colorize(self:uiColor()):render()) me:hud_set_hotbar_image('[fill:1x24:0,0:' .. self:uiColor():fade(.1):hex()) me:hud_set_hotbar_selected_image( '[fill:1x24,0,0:' .. self:uiColor():fade(.4):hex() .. '^[fill:1x1:0,23:#ffFFffff' ) me:hud_set_hotbar_itemcount(hotbarSlots) me:hud_set_flags { hotbar = true; healthbar = false; breathbar = false; basic_debug = false; crosshair = false; } -- disable builtin crafting local inv = me:get_inventory() inv:set_size('craftpreview', 0) inv:set_size('craftresult', 0) inv:set_size('craft', 0) me:set_stars { day_opacity = 0.7; } me:set_sky { sky_color = { day_sky = '#a7c2cd', day_horizon = '#ddeeff'; dawn_sky = '#003964', dawn_horizon = '#87ebff'; night_sky = '#000000', night_horizon = '#000E29'; fog_sun_tint = '#72e4ff'; fog_moon_tint = '#2983d0'; fog_tint_type = 'custom'; }; fog = { -- not respected?? -- TODO make this seasonal & vary with weather fog_distance = 40; fog_start = 0.3; }; } me:set_sun { texture = 'starsoul-sun.png'; sunrise = 'sunrisebg.png^[hsl:180:1:.7'; tonemap = 'sun_tonemap.png^[hsl:180:1:.7'; scale = 0.8; } me:set_lighting { shadows = { intensity = .5; }; exposure = { luminance_max = 3.0; speed_dark_bright = 0.5; speed_bright_dark = 1.0; }; volumetric_light = { strength = 0.3; }; } me:set_eye_offset(nil, vector.new(3,-.2,10)) -- TODO set_clouds speed in accordance with wind starsoul.world.species.setupEntity(me, self.persona) starsoul.ui.setupForUser(self) self:createHUD() self:updateSuit() end; suitStack = function(self) return self.entity:get_inventory():get_stack('starsoul_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; suitPowerStateSet = function(self, state, silent) -- necessary to enable reacting to power state changes -- e.g. to play sound effects, display warnings local os self:forSuit(function(s) os=s:powerState() s:powerStateSet(state) end) if state == 'off' then if self.actMode == 'nano' or self.actMode == 'weapon' then self:actModeSet('off', silent) end end if not silent and os ~= state then local sfx if state == 'off' then sfx = 'starsoul-power-down' elseif os == 'off' then sfx = 'starsoul-power-up' elseif state == 'powerSave' or os == 'powerSave' then sfx = 'starsoul-configure' end if sfx then self:suitSound(sfx) end end end; species = function(self) return starsoul.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()._starsoul.suit suit.adorn(adornment, suitStack, self.persona) end starsoul.world.species.updateTextures(self.entity, self.persona, adornment) end; updateSuit = function(self) self:updateBody() local inv = self.entity:get_inventory() local sst = suitStore(self:suitStack()) if self:naked() then starsoul.type.suit.purgeInventories(self.entity) if self.actMode == 'nano' or self.actMode == 'weapon' then self:actModeSet 'off' end else local suit = self:getSuit() suit:establishInventories(self.entity) if self:suitCharge() <= 0 then self:suitPowerStateSet 'off' end end self:updateHUD() end; reconfigureSuit = function(self) -- and here's where things get ugly -- you can't have an inventory inside another item. to hack around this, -- we use the player as the location of the suit inventories, and whenever -- there's a change in the content of these inventories, this function is -- called to serialize those inventories out to the suit stack if self:naked() then return end local suit = self:getSuit() suit:onReconfigure(self.entity:get_inventory()) self:setSuit(suit) -- reconfiguring the suit can affect player abilities: e.g. removing -- / inserting a chip with a minimap program end; getSuit = function(self) local st = self:suitStack() if st:is_empty() then return nil end return starsoul.type.suit(st) end; setSuit = function(self, suit) self.entity:get_inventory():set_stack('starsoul_suit', 1, suit.item) end; changeSuit = function(self, ...) self:setSuit(...) self:updateSuit() end; forSuit = function(self, fn) local s = self:getSuit() if fn(s) ~= false then self:setSuit(s) end end; suitPowerCapacity = function(self) -- TODO optimize if self:naked() then return 0 end return self:getSuit():powerCapacity() end; suitCharge = function(self) -- TODO optimize if self:naked() then return 0 end return self:getSuit():powerLeft() end; suitDrawCurrent = function(self, power, time, whatFor, min) if self:naked() then return 0,0 end local inv = self.entity:get_inventory() local bl = inv:get_list('starsoul_suit_bat') local supply = 0 local wasteHeat = 0 --TODO handle internally for slot, ps in ipairs(bl) do if not ps:is_empty() then local p, h = starsoul.mod.electronics.dynamo.drawCurrent(ps, power - supply, time) supply = supply + p wasteHeat = wasteHeat + h if power-supply <= 0 then break end end end if min and supply < min then return 0,0 end inv:set_list('starsoul_suit_bat', bl) self:reconfigureSuit() if whatFor then -- TODO display power use icon end return supply, wasteHeat end; naked = function(self) return self:suitStack():is_empty() end; onPart = function(self) starsoul.liveUI [self.name] = nil starsoul.activeUI [self.name] = nil starsoul.activeUsers[self.name] = nil end; openUI = function(self, id, page, ...) local ui = assert(starsoul.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) --print('trigger', which, dump(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 = {} elseif wld and not wld:is_empty() then local wdef = wld:get_definition() if wdef._starsoul and wdef._starsoul.tool then p = {tool = wdef._starsoul.tool} end elseif self.actMode ~= 'off' then p = self.power[self.actMode][which] end if p == nil then return false end local ctx, run = { how = how; } if p.chipID then local inv = self.entity:get_inventory() local chips = inv:get_list 'starsoul_suit_chips' for chSlot, ch in pairs(chips) do if ch and not ch:is_empty() then local d = starsoul.mod.electronics.chip.read(ch) if d.uuid == p.chipID then local pgm = assert(d.files[p.pgmIndex], 'file missing for ability') ctx.file = starsoul.mod.electronics.chip.fileHandle(ch, p.pgmIndex) ctx.saveChip = function() inv:set_slot('starsoul_suit_chips', chSlot, ch) end local sw = starsoul.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 if is 'powder' then if self:naked() then return item end local cans = inv:get_list 'starsoul_suit_canisters' if cans and next(cans) then for i, st in ipairs(cans) do local lst = string.format('starsoul_canister_%u_elem', i) item = inv:add_item(lst, item) if item:is_empty() then break end end end self:forSuit(function(x) x:pushCanisters(inv) end) end return item else return inv:add_item('main', item) end end; thrustUpon = function(self, item) local r = self:give(st) if not r:is_empty() then return minetest.add_item(self.entity:get_pos(), r) end end; }; } local biointerval = 3.0 starsoul.startJob('starsoul:bio', biointerval, function(delta) for id, u in pairs(starsoul.activeUsers) do end end) local cbit = { up = 0x001; down = 0x002; left = 0x004; right= 0x008; jump = 0x010; manv = 0x020; snk = 0x040; dig = 0x080; put = 0x100; zoom = 0x200; } -- this is the painful part minetest.register_globalstep(function(delta) local doNothing,mustInit,mustHalt = 0,1,2 for id, user in pairs(starsoul.activeUsers) do local ent = user.entity local bits = ent:get_player_control_bits() local function what(b) if bit.band(bits, b) ~= 0 and bit.band(user.action.bits, b) == 0 then return mustInit elseif bit.band(bits, b) == 0 and bit.band(user.action.bits, b) ~= 0 then 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) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted mods/starsoul/world.lua version [20212b373d].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 121 122 123 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 149 150 151 152 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 194 195 196 197 |
local lib = starsoul.mod.lib local world = starsoul.world function world.date() local days = minetest.get_day_count() local year = math.floor(days / world.planet.orbit); local day = days % world.planet.orbit; return { year = year, day = day; season = day / world.planet.orbit; } end local lerp = lib.math.lerp local function gradient(grad, pos) local n = #grad if n == 1 then return grad[1] end local op = pos*(n-1) local idx = math.floor(op) local t = op-idx return lerp(t, grad[1 + idx], grad[2 + idx]) end local altitudeCooling = 10 / 100 -- this function provides the basis for temperature calculation, -- which is performed by adding this value to the ambient temperature, -- determined by querying nearby group:heatSource items in accordance -- with the inverse-square law function world.climate.eval(pos, tod, season) local data = minetest.get_biome_data(pos) local biome = world.ecology.biomes.db[minetest.get_biome_name(data.biome)] local heat, humid = data.heat, data.humidity tod = tod or minetest.get_timeofday() heat = lerp(math.abs(tod - 0.5)*2, heat, heat + biome.nightTempDelta) local td = world.date() heat = heat + gradient(biome.seasonalTemp, season or td.season) if pos.y > 0 then heat = heat - pos.y*altitudeCooling end return { surfaceTemp = heat; waterTemp = heat + biome.waterTempDelta; surfaceHumid = humid; } end local vdsq = lib.math.vdsq function world.climate.temp(pos) --> irradiance at pos in W local cl = world.climate.eval(pos) local radCenters = starsoul.region.radiator.store:get_areas_for_pos(pos, false, true) local irradiance = 0 for _,e in pairs(radCenters) do local rpos = minetest.string_to_pos(e.data) local rdef = assert(minetest.registered_nodes[assert(minetest.get_node(rpos)).name]) local rc = rdef._starsoul.radiator local r_max = rc.radius(rpos) local dist_sq = vdsq(rpos,pos) if dist_sq <= r_max^2 then -- cheap bad way -- if minetest.line_of_sight(rpos,pos) then -- -- expensive way local obstruct = 0 local ray = Raycast(rpos, pos, true, true) for p in ray do if p.type == 'node' then obstruct = obstruct + 1 end end if obstruct < 4 then local power, customFalloff = rc.radiate(rpos, pos) -- okay this isn't the real inverse square law but i -- couldn't figure out a better way to simplify the -- model without checking an ENORMOUS number of nodes -- maybe someone else who isn't completely -- mathtarded can do better. if not customFalloff then power = power * (1 - (dist_sq / ((r_max+1)^2))) end power = power * (1 - (obstruct/5)) irradiance = irradiance + power end end end return irradiance + cl.surfaceTemp end world.ecology.biomes.foreach('starsoul:biome-gen', {}, function(id, b) b.def.name = id minetest.register_biome(b.def) end) world.ecology.biomes.link('starsoul:steppe', { nightTempDelta = -30; waterTempDelta = 0; -- W Sp Su Au W seasonalTemp = {-50, -10, 5, 5, -20, -50}; def = { node_top = 'starsoul:greengraze', depth_top = 1; node_filler = 'starsoul:soil', depth_filler = 4; node_riverbed = 'starsoul:sand', depth_riverbed = 4; y_min = 0; y_max = 512; heat_point = 10; humidity_point = 30; }; }) world.ecology.biomes.link('starsoul:ocean', { nightTempDelta = -35; waterTempDelta = 5; seasonalTemp = {0}; -- no seasonal variance def = { y_max = 3; y_min = -512; heat_point = 15; humidity_point = 50; node_top = 'starsoul:sand', depth_top = 1; node_filler = 'starsoul:sand', depth_filler = 3; }; }) local toward = lib.math.toward local hfinterval = 1.5 starsoul.startJob('starsoul:heatflow', hfinterval, function(delta) -- our base thermal conductivity (κ) is measured in °C/°C/s. say the -- player is in -30°C weather, and has an internal temperature of -- 10°C. then: -- κ = .1°C/C/s (which is apparently 100mHz) -- Tₚ = 10°C -- Tₑ = -30°C -- d = Tₑ − Tₚ = -40°C -- ΔT = κ×d = -.4°C/s -- our final change in temperature is computed as tΔC where t is time local kappa = .05 for name,user in pairs(starsoul.activeUsers) do local tr = user:species().tempRange local t = starsoul.world.climate.temp(user.entity:get_pos()) local insul = 0 local naked = user:naked() local suitDef if not naked then suitDef = user:suitStack():get_definition() insul = suitDef._starsoul.suit.temp.insulation end local warm = user:effectiveStat 'warmth' local tSafeMin, tSafeMax = tr.survivable[1], tr.survivable[2] local tComfMin, tComfMax = tr.comfort[1], tr.comfort[2] local tDelta = (kappa * (1-insul)) * (t - warm) * hfinterval local tgt = warm + tDelta -- old logic: we move the user towards the exterior temperature, modulated -- by her suit insulation. --local tgt = toward(warm, t, hfinterval * thermalConductivity * (1 - insul)) if not naked then local suit = user:getSuit() local suitPower = suit:powerState() local suitPowerLeft = suit:powerLeft() if suitPower ~= 'off' then local coilPower = 1.0 local st = suitDef._starsoul.suit.temp if suitPower == 'powerSave' and (tgt >= tSafeMin and tgt <= tSafeMax) then coilPower = 0.5 end if tgt < tComfMin and st.maxHeat > 0 then local availPower = user:suitDrawCurrent(st.heatPower*coilPower, hfinterval) tgt = tgt + (availPower / st.heatPower) * st.maxHeat * coilPower * hfinterval end if tgt > tComfMax and st.maxCool > 0 then local availPower = user:suitDrawCurrent(st.coolPower*coilPower, hfinterval) tgt = tgt - (availPower / st.coolPower) * st.maxCool * coilPower * hfinterval end end end user:statDelta('warmth', tgt - warm) -- dopey but w/e warm = tgt -- for the sake of readable code if warm < tSafeMin or warm > tSafeMax then local dv if warm < tSafeMin then dv = math.abs(warm - tSafeMin) else dv = math.abs(warm - tSafeMax) end -- for every degree of difference you suffer 2 points of damage/s local dmg = math.ceil(dv * 2) user:statDelta('health', -dmg) end end end) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Modified src/lore.ct from [8a0d33ccae] to [176ec871ae].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
..
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
|
# starsoul lore ! spoilers ahoy! ## Thinking Few the Galaxy teems with life, but only one in a trillion of its creatures is fully sophont, with a soul and mentality of their own. ### Lesser Races the majority of the Thinking Few are held in thrall to the Starsouled, their null psionic potential locking them out of the higher levels of civilizational power and attainment. #### Humans The weeds of the galactic flowerbed. Humans are one of the Lesser Races, excluded from the ranks of the Greater Races by souls that lack, in normal circumstances, external psionic channels. Their mastery of the universe cut unexpectedly short, forever locked out of FTL travel, short-lived without augments, and alternately pitied or scorned by the lowest of the low, humans flourish nonetheless due to a capacity for adaptation all but unmatched among the Thinking Few, terrifyingly rapid reproductive cycles -- and a keen facility for bribery. While the lack of human psions remains a sensitive topic, humans (unlike the bitter and emotional Kruthandi) are practical enough to hire the talent they cannot possess, and have even built a small number of symbiotic civilizations with the more indulging of the Powers. In a galaxy where nearly all sophont life is specialized to a fault, humans have found the unique niche of occupying no particular niche. #### Kruthandi The Kruthandi are a race of four-armed marsupialoids, with the rough body proportions of meerkats, though much larger. Geographically, they occupy a small net of systems linked only through their home system. Unable to accept the reality that their lack of psionics doomed them to a subservient, sublight role, the Kruthandi indulged in a brief, petulant, and entirely futile war with a Su'ikuri state[^lizard-war], and then retreated to their home web to sulk. Access to their space is tightly controlled, and psionic races are absolutely barred from their worlds, even the mere Greater Races, tho this is hilariously unenforcible. In practice they are particularly vulnerable to psicrime – when you ban psionics, only criminals will have mind powers. The Kruthandi are generally viewed with pity and amusement as a pathetic basket case of a civilization, and engage in little intercourse with the broader galaxy. lizard-war: The Su'ikuri sovereign in question was of a decidedly philosophical bent and was commendably gentle dealing with the upstarts, seeming more bemused than angered by the attack. He took a few of their leaders as battle trophies and spanked the remainder out of his system. There were no casualties on either side. However, the Kruthandi are an ancient and sophisticated civilization, and there is much more to their culture than mere wallowing in victimhood (though this certainly has a special place in the Kruthandi heart). Their hate-crush on the Starsouled Races has lead to an utter obsession with metric technology, and the Kruthandi have, by sheer brute force and fanatic tenacity, built a surprisingly sophisticated gravitics industry. While this makes little economic sense, it soothes the Kruthandic psyche. #### Qir a race of religious fanatics cleaving to no particular faith, Qir enthusiastically adopt and syncretize new religions as fast as Heaven can churn out prophets. their fanaticism seems to be a cultural evolution to compensate for exceptionally weak mememmune systems; unable to properly critique new ideas, the Qir need to outsource much of their reasoning to systems of sacred commandments, ideally those developed by species who know how to deal with memes. good old-fashioned natural selection does the rest. ### Greater Races a handful of species have souls that are just barely capable of developing external psionic channels. the respected vassals of the Starsouled, they can touch the spirits of others, sending messages and reading thoughtforms -- or attacking with torturous sensations and ruinous emotion. however, their power does not extend to the physical universe; they rely entirely on their Starsouled masters for access to faster-than-light travel. ### Starsouled the unquestioned lords of space and time, the Starsouled Races are those lucky few to have evolved cognitive architectures that allow a soul to reach its full development potential, progressing from merely cogitating about the universe to manipulating it directly with innate power. seemingly as a consequence of the necessary neural architecture, the Starsouled are all ferociously intelligent -- IQs below 150 are unheard of. however, they are marred by a proportional tendency toward mental instability and psychosis. their civilizations are known as the Powers. #### Su'ikuri (sg. Su'ikutra, adj. Su'ikuruk) a reptilian race of artists, aesthetes, hedonists, monks, and philosophers, the Su'ikuri are an idle, contemplative, and aristocratic people whose massive psionic sophistication numbers them among the Powers -- much to the annoyance of the Eluthrai. as any adult has the requisite level of finesse and raw power to tweak individual alleles throughout the whole body of a living organism, the Su'ikuri are a race of peerless organgineers. they eschew "dead" hylotechnology, and insist on using biotech wherever remotely practicable. their 'spacecraft' are massive tree-like organisms housing whole ecosystems, propelled and protected against radiation by the psionic power of their crew. sometimes they are equipped with technology produced by a vassal race, but only when unavoidable. Su'ikuri generally use Lesser Races for manual labor, and Greater Races to overseer these laborers. whether these are paid and respected laborers or outright slaves depends entirely on the ethos of the local civilization. Su'ikuruk society is strictly feudal, with a hierarchy based on psionic skill and wit. virtually all conflict is resolved with either a polite, prolonged philosophical debate (the Su'ikuruk version of a duel) or a brute psionic struggle -- the party overpowered by the greater psion is compelled to submit totally, and may achieve freedom only by strengthening their soul to the point of being able to overpower their former superior. even other members of other Starsouled Races can wind up enslaved this way. Su'ikuri relations with the Eluthrai are, as a rule, extremely strained, and many small but high-energy wars have been fought between the Su'ikuruk Powers and the Corcordance. a motivated and talented Su'ikutra can reach astropathic levels of psionic power with only a century of practice, something otherwise unheard of among Starsouls. #### Usukwinya (sg. Usukwinti, adj. Usuk) the Usukwinya, known affectionately as the "Tradebirds", are a psionic avian race. their adults range in height from 1 to 1.3m, and 20-30kg in weight. they lack precise manipulatory appendages and are physically weak, forcing them to rely heavily on their psionics for everyday dasks. however, they remain fully capable of flight even without psionic assistance. culturally, the Usukwinya are a mercantile race. they exert their power not through physical force, but through obscene wealth, garnered by selling their painstakingly [^drm value-engineered] technologies to the highest bidder. some rare few Usukwinya will also rent out their psionics, even to the Lesser Races (if they can afford their prodigious fees). drm: Usukwinya DRM is some of the most powerful in the Reach. the enthusiastic capitalism of the Usukwinya is tempered by a hardwired loyalty drive so powerful that before First Contact they had no concept of contract law. they are also noteworthy for having never fought a war among themselves, and seem utterly unwilling to resort to force unless physically provoked. their governments all work diligently to maintain peace among the other races, and the somewhat absurd spectacle of a Starsouled diplomat gently negotiating with two hysterical Lesser ambassadors tends to crop up when two factions the Usukwinya have good relationships with threaten war on one another. (the Eluthrai find this patently ridiculous, and prefer to maintain peace with a judicious application of preventive violence. several Usukwinya-organized peace conferences have dissolved when the Eluthrai summarily shattered the offending governments without a note of forewarning.) their willingness to trade with or work for anyone and everyone mean that the Usukwinya are the main reason the Lesser Races have any ability to travel beyond the Great Web. however, Usuk astropaths are very selective: they will not use their powers to help their employers to commit acts of aggression, no matter how much you offer to pay them. many human captains chafe under the restrictions of their Tradebird astropaths, but short of relativistic travel, they have no other way to escape the confines of the Web. Usukwinya get along with everyone and make excellent diplomats, so long as they can restrain their urge to make a quick profit at the first available opportunity. #### Eluthrai (sg. Eluthra) the greatest and most aloof of the living Starsouled races, the Eluthrai are a race of psionic warrior-poets. they are slim humanoids with subtly iridescent dark grey skin, lustrous white hair, red~violet eyes, and tapered, expressive ears. they are very few in number, with no more than ten thousand Eluthrai in the entire galaxy. it is popularly said, not without some reason, that the only reason the Eluthrai haven't conquered that entire galaxy is because they don't care to. natural immortals with a very low reproduction rate, the Eluthrai all have an exceptionally long-term worldview that frequently confounds mortal morals. little about them is known for certain, and they interact with the non-Starsouled very rarely, usually to deliver some form of unforeseen intervention that they typically refuse to explain. the Eluthrai see themselves as the masters of the Thinking Few, and spare no expense in ensuring they maintain their position. to them, the Great Web is a garden, a place to be tended carefully and protected from the storms outside. their civilization is dedicated to combatting extra-Web threats -- in particular, guarding against the possible return of the Forevanishers. they have cultivated a strong & highly spiritual warrior ethos in consequence within the Web itself, they mostly by clandestine means, using "Agents" selected from the Greater (and, occasionally, Lesser) Races to act on their behalf. in general they act directly only when overwhelming force is required, such as to exclude the Kuradoqshe, or to excise Suldibrand. it is known that the Eluthrai are of great intelligence: a 200pt IQ makes you a laughable simpleton in their eyes. it is estimated that the average individual has an IQ of 290, close to the theoretical maximum where organized intelligence dissolves into a sea of blinding psychosis. consequently, they are very conservative and cautious of new ideas; their culture emphasises skepticism and avoiding rash action. ................................................................................ Eluthrai have two genders, and dramatic dimorphism. their women are much more intelligent than their men, and proportionately more prone to psychosis. traditionally most of their societies were matriarchal -- with the brains and psionic brawn to overpower the males, there was very little that could keep the Clan-Queens from exerting their will. the First Philosopher recognized however that the lesser intelligence of men was useful, due to their stabler psyches, and proposed patriarchy as part of his solution. this was made possible through a previously obscure psionic technique known as quelling -- with enough intimate exposure to the soul of another, it becomes possible to negate their psionics, even if that psion is stronger. among the modern Concordant Eluthrai, a female's mate is expected to be capable of quelling her psionics. female Eluthrai generally cooperate with the practice; it is difficult to learn to quell someone who actively tries to stymie you. it is widely understood, however, that the female sex will only cooperate so long as their men rule wisely: in a celebrated case on a far-flung world where the men began to take too many liberties, the women carefully organized to overpower one another's mates and instituted a compensatory subjugation of the local males for a proportionate period, which the Philosopher-King himself agreed was the just and proper punishment. Eluthran technology can be tidily summarized as "uncompromising." the Eluthrai demand excellence from their machines as much as one another, and will happily incur absurd expense to eliminate the smallest flaw. languishing for thousands of years of under such attentions, abetted by the most ferocious living intelligence to be found in the Reach, has created a technological ecosystem that is succeeded in its phenomenal capabilities only by its preposterous expense. an Eluthran computer requires about ten times the time and a hundred times the energy input to fabricate as does a conventional human computer, despite the vast gulf in manufacturing capabilities, but you can be [!damn] sure it'll still be working in ten million years' time. they enjoy a post-scarcity economy that is the envy of even the other Starsouled. very few of the Greater Races, and vanishingly few of the Lesser Races, have ever had the opportunity to visit an Eluthran world. they admit only their mysterious Agents and the occasional individual subjected to penal servitude for some great crime against the interests of "the Garden". while even their females are not nearly the psionic match of the Su'ikuri, they are nonetheless vastly powerful. their psionics are not as seamlessly integrated into their nervous system as in Usuk neurology, and deploying their power is consequently more effortful, requiring some concentration and intent, but they can bring far more energy to bear. where the Usukwinya have finesse and the Su'ikuri have brute power, the Eluthrai have technique: they can do things with their psionics that the other races never would have imagined possible. the Eluthrai put a great deal of effort into foremodeling the universe, seeking to predict future events and trends. their models are far from infallible, but reliable enough that some supersitious Lessers have come to believe that Eluthran psionics can be used to see the future. intelligence-gathering is in the modern era the prime industry of that exalted race, second only to warfare. ### Forevanished Ones #### Forevanishers a mysterious race or power thought to be responsible for exterminating a number of Forevanished Ones. no one knows for sure that they have themselves Vanished -- for all we know, they could be one of the contemporary Powers… #### firstborn the architects of the Great Web, the Firstborn were the first civilization of which traces remain within the Reach. their psionic and scientific mastery, developed over ten million years of energetic industry, reached levels even the greatest of the modern Starsouled Races cannot hope to equal. while their Continuum Bridges form the backbone of the Lesser civilizations, little else of their manufacture seems to have survived into the present era. practically every trace of their existence that does remain is scored with weaponsfire. the artifact which tore an external channel into the player's soul in the backstory is of Firstborn design and uncertain purpose. Commune scholars had hitherto ascertained only that it was a machine seemingly able to produce psionic effects -- something that [!should] have been a contradiction in terms. ! possible plot: the Firstborn devised a means to produce psionic effects with carefully cultured neurons embedded in a mechanical matrix. essentially creating slaved psionic AI dedicated to a single purpose. while these rudimentary consciousnesses, barely fit to called souls, did not suffer, some other race or perhaps a faction among the Firstborn seems to have taken exception to the practice of trapping souls in metal, outside the thread of reincarnation, and exterminated the civilization to prevent its heresy. ! if this was a real proper AAA game the player would face some epic choice to release the secret and free the Lesser Races (or a subset of them) from the dominion of the Starsouled, turning psionics into a mere commodity; keeping the secret but placing her power at the disposal of the Commune (or Empire, in return for elevation to the ranks of nobility); or joining the Eluthrai as an honorary citizen in recompense for keeping the secret. alas, i don't have a budget. ## psionics the ability of the soul to extend its will beyond the confines of its substrate. this power is technically defined as the presence of one or more external psionic channels in the structure of the soul. such a channel allows the soul to direct excess numina into other souls or into the numon field of the physical universe. the delicate interlink between soul and body relies on quantum phenomena, and only carbon-based life seems able to maintain such a link. silicon-based intelligence is at most a simulacrum of true thought. ### farspeakers |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
..
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
|
# starlit lore ! spoilers ahoy! ## Thinking Few the Galaxy teems with life, but only one in a trillion of its creatures is fully sophont, with a soul and mentality of their own. ### Lesser Races the majority of the Thinking Few are held in thrall to the Starlit, their null psionic potential locking them out of the higher levels of civilizational power and attainment. #### Humans The weeds of the galactic flowerbed. Humans are one of the Lesser Races, excluded from the ranks of the Greater Races by souls that lack, in normal circumstances, external psionic channels. Their mastery of the universe cut unexpectedly short, forever locked out of FTL travel, short-lived without augments, and alternately pitied or scorned by the lowest of the low, humans flourish nonetheless due to a capacity for adaptation all but unmatched among the Thinking Few, terrifyingly rapid reproductive cycles -- and a keen facility for bribery. While the lack of human psions remains a sensitive topic, humans (unlike the bitter and emotional Kruthandi) are practical enough to hire the talent they cannot possess, and have even built a small number of symbiotic civilizations with the more indulging of the Powers. In a galaxy where nearly all sophont life is specialized to a fault, humans have found the unique niche of occupying no particular niche. #### Kruthandi The Kruthandi are a race of four-armed marsupialoids, with the rough body proportions of meerkats, though much larger. Geographically, they occupy a small net of systems linked only through their home system. Unable to accept the reality that their lack of psionics doomed them to a subservient, sublight role, the Kruthandi indulged in a brief, petulant, and entirely futile war with a Su'ikuri state[^lizard-war], and then retreated to their home web to sulk. Access to their space is tightly controlled, and psionic races are absolutely barred from their worlds, even the mere Greater Races, tho this is hilariously unenforcible. In practice they are particularly vulnerable to psicrime – when you ban psionics, only criminals will have mind powers. The Kruthandi are generally viewed with pity and amusement as a pathetic basket case of a civilization, and engage in little intercourse with the broader galaxy. lizard-war: The Su'ikuri sovereign in question was of a decidedly philosophical bent and was commendably gentle dealing with the upstarts, seeming more bemused than angered by the attack. He took a few of their leaders as battle trophies and spanked the remainder out of his system. There were no casualties on either side. However, the Kruthandi are an ancient and sophisticated civilization, and there is much more to their culture than mere wallowing in victimhood (though this certainly has a special place in the Kruthandi heart). Their hate-crush on the Starlit Races has lead to an utter obsession with metric technology, and the Kruthandi have, by sheer brute force and fanatic tenacity, built a surprisingly sophisticated gravitics industry. While this makes little economic sense, it soothes the Kruthandic psyche. #### Qir a race of religious fanatics cleaving to no particular faith, Qir enthusiastically adopt and syncretize new religions as fast as Heaven can churn out prophets. their fanaticism seems to be a cultural evolution to compensate for exceptionally weak mememmune systems; unable to properly critique new ideas, the Qir need to outsource much of their reasoning to systems of sacred commandments, ideally those developed by species who know how to deal with memes. good old-fashioned natural selection does the rest. ### Greater Races a handful of species have souls that are just barely capable of developing external psionic channels. the respected vassals of the Starlit, they can touch the spirits of others, sending messages and reading thoughtforms -- or attacking with torturous sensations and ruinous emotion. however, their power does not extend to the physical universe; they rely entirely on their Starlit masters for access to faster-than-light travel. ### Starlit Races the unquestioned lords of space and time, the Starlit Races are those lucky few to have evolved cognitive architectures that allow a soul to reach its full development potential, progressing from merely cogitating about the universe to manipulating it directly with innate power. seemingly as a consequence of the necessary neural architecture, the Starlit are all ferociously intelligent -- IQs below 150 are unheard of. however, they are marred by a proportional tendency toward mental instability and psychosis. their civilizations are known as the Powers. #### Su'ikuri (sg. Su'ikutra, adj. Su'ikuruk) a reptilian race of artists, aesthetes, hedonists, monks, and philosophers, the Su'ikuri are an idle, contemplative, and aristocratic people whose massive psionic sophistication numbers them among the Powers -- much to the annoyance of the Eluthrai. as any adult has the requisite level of finesse and raw power to tweak individual alleles throughout the whole body of a living organism, the Su'ikuri are a race of peerless organgineers. they eschew "dead" hylotechnology, and insist on using biotech wherever remotely practicable. their 'spacecraft' are massive tree-like organisms housing whole ecosystems, propelled and protected against radiation by the psionic power of their crew. sometimes they are equipped with technology produced by a vassal race, but only when unavoidable. Su'ikuri generally use Lesser Races for manual labor, and Greater Races to overseer these laborers. whether these are paid and respected laborers or outright slaves depends entirely on the ethos of the local civilization. Su'ikuruk society is strictly feudal, with a hierarchy based on psionic skill and wit. virtually all conflict is resolved with either a polite, prolonged philosophical debate (the Su'ikuruk version of a duel) or a brute psionic struggle -- the party overpowered by the greater psion is compelled to submit totally, and may achieve freedom only by strengthening their soul to the point of being able to overpower their former superior. even other members of other Starlit Races can wind up enslaved this way. Su'ikuri relations with the Eluthrai are, as a rule, extremely strained, and many small but high-energy wars have been fought between the Su'ikuruk Powers and the Corcordance. a motivated and talented Su'ikutra can reach astropathic levels of psionic power with only a century of practice, something otherwise unheard of among the Starlit. #### Usukwinya (sg. Usukwinti, adj. Usuk) the Usukwinya, known affectionately as the "Tradebirds", are a psionic avian race. their adults range in height from 1 to 1.3m, and 20-30kg in weight. they lack precise manipulatory appendages and are physically weak, forcing them to rely heavily on their psionics for everyday dasks. however, they remain fully capable of flight even without psionic assistance. culturally, the Usukwinya are a mercantile race. they exert their power not through physical force, but through obscene wealth, garnered by selling their painstakingly [^drm value-engineered] technologies to the highest bidder. some rare few Usukwinya will also rent out their psionics, even to the Lesser Races (if they can afford their prodigious fees). drm: Usukwinya DRM is some of the most powerful in the Reach. the enthusiastic capitalism of the Usukwinya is tempered by a hardwired loyalty drive so powerful that before First Contact they had no concept of contract law. they are also noteworthy for having never fought a war among themselves, and seem utterly unwilling to resort to force unless physically provoked. their governments all work diligently to maintain peace among the other races, and the somewhat absurd spectacle of a Starlit diplomat gently negotiating with two hysterical Lesser ambassadors tends to crop up when two factions the Usukwinya have good relationships with threaten war on one another. (the Eluthrai find this patently ridiculous, and prefer to maintain peace with a judicious application of preventive violence. several Usukwinya-organized peace conferences have dissolved when the Eluthrai summarily shattered the offending governments without a note of forewarning.) their willingness to trade with or work for anyone and everyone mean that the Usukwinya are the main reason the Lesser Races have any ability to travel beyond the Great Web. however, Usuk astropaths are very selective: they will not use their powers to help their employers to commit acts of aggression, no matter how much you offer to pay them. many human captains chafe under the restrictions of their Tradebird astropaths, but short of relativistic travel, they have no other way to escape the confines of the Web. Usukwinya get along with everyone and make excellent diplomats, so long as they can restrain their urge to make a quick profit at the first available opportunity. #### Eluthrai (sg. Eluthra) the greatest and most aloof of the living Starlit races, the Eluthrai are a race of psionic warrior-poets. they are slim humanoids with subtly iridescent dark grey skin, lustrous white hair, red~violet eyes, and tapered, expressive ears. they are very few in number, with no more than ten thousand Eluthrai in the entire galaxy. it is popularly said, not without some reason, that the only reason the Eluthrai haven't conquered that entire galaxy is because they don't care to. natural immortals with a very low reproduction rate, the Eluthrai all have an exceptionally long-term worldview that frequently confounds mortal morals. little about them is known for certain, and they interact openly with the non-Starlit very rarely, usually to deliver some form of unforeseen intervention that they typically refuse to explain. the Eluthrai see themselves as the masters of the Thinking Few, and spare no expense in ensuring they maintain their position. to them, the Great Web is a garden, a place to be tended carefully and protected from the storms outside. their civilization is dedicated to combatting extra-Web threats -- in particular, guarding against the possible return of the Forevanishers. they have cultivated a strong & highly spiritual warrior ethos in consequence within the Web itself, they mostly by clandestine means, using "Agents" selected from the Greater (and, occasionally, Lesser) Races to act on their behalf. in general they act directly only when overwhelming force is required, such as to exclude the Kuradoqshe, or to excise Suldibrand. it is known that the Eluthrai are of great intelligence: a 200pt IQ makes you a laughable simpleton in their eyes. it is estimated that the average individual has an IQ of 290, close to the theoretical maximum where organized intelligence dissolves into a sea of blinding psychosis. consequently, they are very conservative and cautious of new ideas; their culture emphasises skepticism and avoiding rash action. ................................................................................ Eluthrai have two genders, and dramatic dimorphism. their women are much more intelligent than their men, and proportionately more prone to psychosis. traditionally most of their societies were matriarchal -- with the brains and psionic brawn to overpower the males, there was very little that could keep the Clan-Queens from exerting their will. the First Philosopher recognized however that the lesser intelligence of men was useful, due to their stabler psyches, and proposed patriarchy as part of his solution. this was made possible through a previously obscure psionic technique known as quelling -- with enough intimate exposure to the soul of another, it becomes possible to negate their psionics, even if that psion is stronger. among the modern Concordant Eluthrai, a female's mate is expected to be capable of quelling her psionics. female Eluthrai generally cooperate with the practice; it is difficult to learn to quell someone who actively tries to stymie you. it is widely understood, however, that the female sex will only cooperate so long as their men rule wisely: in a celebrated case on a far-flung world where the men began to take too many liberties, the women carefully organized to overpower one another's mates and instituted a compensatory subjugation of the local males for a proportionate period, which the Philosopher-King himself agreed was the just and proper punishment. Eluthran technology can be tidily summarized as "uncompromising." the Eluthrai demand excellence from their machines as much as one another, and will happily incur absurd expense to eliminate the smallest flaw. languishing for thousands of years of under such attentions, abetted by the most ferocious living intelligence to be found in the Reach, has created a technological ecosystem that is succeeded in its phenomenal capabilities only by its preposterous expense. an Eluthran computer requires about ten times the time and a hundred times the energy input to fabricate as does a conventional human computer, despite the vast gulf in manufacturing capabilities, but you can be [!damn] sure it'll still be working in ten million years' time. they enjoy a post-scarcity economy that is the envy of even the other Starlit. very few of the Greater Races, and vanishingly few of the Lesser Races, have ever had the opportunity to visit an Eluthran world. they admit only their mysterious Agents and the occasional individual subjected to penal servitude for some great crime against the interests of "the Garden". while even their females are not nearly the psionic match of the Su'ikuri, they are nonetheless vastly powerful. their psionics are not as seamlessly integrated into their nervous system as in Usuk neurology, and deploying their power is consequently more effortful, requiring some concentration and intent, but they can bring far more energy to bear. where the Usukwinya have finesse and the Su'ikuri have brute power, the Eluthrai have technique: they can do things with their psionics that the other races never would have imagined possible. the Eluthrai put a great deal of effort into foremodeling the universe, seeking to predict future events and trends. their models are far from infallible, but reliable enough that some supersitious Lessers have come to believe that Eluthran psionics can be used to see the future. intelligence-gathering is in the modern era the prime industry of that exalted race, second only to warfare. ### Forevanished Ones #### Forevanishers a mysterious race or power thought to be responsible for exterminating a number of Forevanished Ones. no one knows for sure that they have themselves Vanished -- for all we know, they could be one of the contemporary Powers… #### firstborn the architects of the Great Web, the Firstborn were the first civilization of which traces remain within the Reach. their psionic and scientific mastery, developed over ten million years of energetic industry, reached heights even the greatest of the modern Starlit Races cannot hope to equal. while their Continuum Bridges form the backbone of the Lesser civilizations, little else of their manufacture seems to have survived into the present era. practically every trace of their existence that does remain is scored with weaponsfire. the artifact which tore an external channel into the player's soul in the backstory is of Firstborn design and uncertain purpose. Commune scholars had hitherto ascertained only that it was a machine seemingly able to produce psionic effects -- something that [!should] have been a contradiction in terms. ! possible plot: the Firstborn devised a means to produce psionic effects with carefully cultured neurons embedded in a mechanical matrix. essentially creating slaved psionic AI dedicated to a single purpose. while these rudimentary consciousnesses, barely fit to called souls, did not suffer, some other race or perhaps a faction among the Firstborn seems to have taken exception to the practice of trapping souls in metal, outside the thread of reincarnation, and exterminated the civilization to prevent its heresy. ! if this was a real proper AAA game the player would face some epic choice to release the secret and free the Lesser Races (or a subset of them) from the dominion of the Starlit, turning psionics into a mere commodity; keeping the secret but placing her power at the disposal of the Commune (or Empire, in return for elevation to the ranks of nobility); or joining the Eluthrai as an honorary citizen in recompense for keeping the secret. alas, i don't have a budget. ## psionics the ability of the soul to extend its will beyond the confines of its substrate. this power is technically defined as the presence of one or more external psionic channels in the structure of the soul. such a channel allows the soul to direct excess numina into other souls or into the numon field of the physical universe. the delicate interlink between soul and body relies on quantum phenomena, and only carbon-based life seems able to maintain such a link. silicon-based intelligence is at most a simulacrum of true thought. ### farspeakers |
Modified src/sem.ct from [5017b1c243] to [770f4fca7d].
1 2 3 4 5 6 7 8 |
# starsoul semantics
## tool levels
some items have a particular level requirement to enable digging. in general, level 0 should be used for things that can be dug by hand
nanotech can dismantle anything up to level 2
* sediment
** 0: sand, dirt (diggable with hand)
|
| |
1 2 3 4 5 6 7 8 |
# starlit semantics
## tool levels
some items have a particular level requirement to enable digging. in general, level 0 should be used for things that can be dug by hand
nanotech can dismantle anything up to level 2
* sediment
** 0: sand, dirt (diggable with hand)
|
Modified src/sfx/conf.lua from [7fad9bea38] to [ee9864718b].
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
local function envarg(n, default) local v = os.getenv(n) if v then return tonumber(v) end return default end local baseSeed = envarg('starsoul_sfx_vary', 420691917) local variants = envarg('starsoul_sfx_variants', 4) local fmt = os.getenv 'starsoul_sfx_fmt' or 'wav' local rules = {} local all = {} if fmt ~= 'ogg' then table.insert(rules, 'out/starsoul-%.ogg: %.' .. fmt .. '\n' .. '\tffmpeg -y -i "$<" "$@"\n') end local function rule(out, file, seed) if fmt == 'ogg' then table.insert(rules, string.format( 'out/starsoul-%s.ogg: %s.csd digital.orc physical.orc psi.orc dqual.inc\n' .. '\tcsound --omacro:seed=%d --format=ogg -o "$@" "$<"\n', out, file, seed )) else table.insert(rules, string.format( '%s.%s: %s.csd digital.orc physical.orc psi.orc dqual.inc\n' .. '\tcsound --omacro:seed=%d --format=%s -o "$@" "$<"\n', out, fmt, file, seed, fmt )) end table.insert(all, string.format('out/starsoul-%s.ogg', out)) end for _, v in ipairs(polyfx) do local bn = v:match '^(.+).n.csd$' for i=1, variants do rule(bn .. '.' .. tostring(i), bn .. '.n', baseSeed + 4096*i) end end |
| | | | | | |
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
local function envarg(n, default) local v = os.getenv(n) if v then return tonumber(v) end return default end local baseSeed = envarg('starlit_sfx_vary', 420691917) local variants = envarg('starlit_sfx_variants', 4) local fmt = os.getenv 'starlit_sfx_fmt' or 'wav' local rules = {} local all = {} if fmt ~= 'ogg' then table.insert(rules, 'out/starlit-%.ogg: %.' .. fmt .. '\n' .. '\tffmpeg -y -i "$<" "$@"\n') end local function rule(out, file, seed) if fmt == 'ogg' then table.insert(rules, string.format( 'out/starlit-%s.ogg: %s.csd digital.orc physical.orc psi.orc dqual.inc\n' .. '\tcsound --omacro:seed=%d --format=ogg -o "$@" "$<"\n', out, file, seed )) else table.insert(rules, string.format( '%s.%s: %s.csd digital.orc physical.orc psi.orc dqual.inc\n' .. '\tcsound --omacro:seed=%d --format=%s -o "$@" "$<"\n', out, fmt, file, seed, fmt )) end table.insert(all, string.format('out/starlit-%s.ogg', out)) end for _, v in ipairs(polyfx) do local bn = v:match '^(.+).n.csd$' for i=1, variants do rule(bn .. '.' .. tostring(i), bn .. '.n', baseSeed + 4096*i) end end |
Added starlit.ct version [5320fb7a47].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# starlit [*starlit] is a sci-fi survival game. you play the survivor of a disaster in space, stranded on a just-barely-habitable world with nothing but your escape pod, your survival suit, and some handy new psionic powers that the accident seems to have unlocked in you. scavenge resources, search alien ruins, and build a base where you can survive indefinitely, but be careful: winter approaches, and your starting suit heater is already struggling to keep you alive in the comparatively warm days of "summer". ## story ### "Imperial Expat" background about a month ago, you woke up to unexpected good news. your application to join the new Commune colony at Thousand Petal, submitted in a moment of utter existential weariness and almost in jest, was actually accepted. your skillset was a "perfect match" for the budding colony's needs, claimed the Population Control Authority, and you'd earned yourself a free trip to your new home -- on a swanky state transport, no less. it took a few discreet threats and bribes from Commune diplomats, but after a week of wrangling with the surly Crown Service for Comings & Goings -- whose bureaucrats seemed outright [!offended] that you actually managed to find a way off that hateful rock -- you secured grudging clearance to depart. you celebrated by telling your slackjawed boss exactly what you thought of him in a meeting room packed with his fellow parasites -- and left House Taladran with a new appreciation for the value of your labor, as the nobs found themselves desperately scrabbling for a replacement on short notice. you almost couldn't believe it when the Commune ship -- a sleek, solid piece of engineering whose graceful descent onto the landing pad seemed to sneer at the lurching rattletraps arrayed all around it -- actually showed up. in a daze you handed over your worldly possessions -- all three of them -- to a valet with impeccable manners, and climbed up out of the wagie nightmare into high orbit around your homeworld. the mercenary psion aboard, a preening Usukwinti with her very own luxury suite, tore a bleeding hole in the spacetime metric, and five hundred hopeful souls dove through towards fancied salvation. "sure," you thought to yourself as you slipped into your sleek new nanotech environment suit, itself worth more than the sum total of your earnings on Flame of Unyielding Purification, "life won't be easy -- but damn it, it'll [!mean] something out there." a free life on the wild frontier with a nation of comrades to have your back, with the best tech humans can make, fresh, clean water that isn't laced with compliance serum, and -- best of all -- never having to worry about paying rent again. it was too good to be true, you mused. clearly, the terrorists who blew up your ship agreed. you're still not certain what happened. all you know for sure is that transport was carrying more than just people. in those last hectic moments, you caught a glimpse of something -- maybe machine, maybe artwork, and [!definitely] ancient beyond measure. you've seen abhuman artifacts before in museums, of course; in fact, thanks to a childhood fascination, you can still name all the Elder Races and the Forevanished Ones off the top of your head. you have no [!idea] what that [!thing] was or who in the sublimated [!fuck] could possibly have made it. but one thing is for certain: your ship wasn't the only thing it ripped open when it blew. because when you woke up in your tiny escape pod beyond the furthest edge of the Reach, circling Farthest Shadow in a suicide orbit, you discovered yourself transformed into something impossible. a contradiction in terms. a human psion. for years beyond counting, the Starlit species -- three of whom yet live and deign once every so often to notice the Lesser Races -- have held the galaxy in sway through their monopoly on psionic power. of all the Thinking Few, only they are free to wander the distant stars at whim, heedless of the lightspeed barrier. there are no mechanisms for FTL travel or reactionless drive without that innate power, and, they assured us, psionic channels are fixed in the soul. your species either has the power or it doesn't. [!liars], all of them. are there other survivors? have they been similarly changed? what was that artifact and who were those terrorists? important questions, all, but they pale in comparison with the most important one: how the fuck are you going to survive the next 24 hours? ## engine starlit is developed against a bleeding-edge version of minetest. it definitely won't work with anything older than v5.7, and ideally you should build directly from master. starlit is best used with a patched version of minetest, though it is compatible with vanilla. the recommended patches are: * [>p11143 11143] - fix third-person view orientation p11143: https://github.com/minetest/minetest/pull/11143.diff ### shadows i was delighted to see dynamic shadows land in minetest, and i hope the implementation will eventually mature. however, as it stands, there are severe issues with shadows that make them essentially incompatible with complex meshes like the Starlit player character meshes. for the sake of those who don't mind these glitches, Starlit does enable shadows, but i unfortunately have to recommend that you disable them until the minetest devs get their act together on this feature. ## gameplay starlit is somewhat unusual in how it uses the minetest engine. it's a voxel game but not of the minecraft variety. ### controls summon your Suit Interface by pressing the [*E] / [*Inventory] key. this will allow you to move items around in your inventory, but more importantly, it also allows you select or configure your Interaction Mode. the top three buttons can be used to select (or deactivate) an Interaction Mode. an Interaction Mode can be configured by pressing the button immediately below. the active Interaction Mode controls the behavior of the mouse buttons when no item is selected in the hotbar. the modes are: * [*Fabrication]: use your suit's onboard nanotech to directly manipulate matter in the world. ** [*Left Button] / [*Punch]: activate your primary nano program. by default this activates your nanoshredder, reducing the targeted object to monatomic powder and storing the resulting elements in your suit for use with the Matter Compiler ** [*Right Button] / [*Place]: activate your secondary nano program. by default, if your suit compiler can generate sufficiently large objects, creates a block of the configured type directly in the world without having to build it by hand * [*Psionics]: wield the awesome, if illicitly obtained, power of mind over matter ** [*Left Button] / [*Punch]: perform your selected Primary Power ** [*Right Button] / [*Place]: perform your selected Secondary Power * [*Weapon]: military-grade suits have built-in hardpoints for specialized weapon systems that draw directly on your suit battery for power (and in the most exotic cases, your psi reserve) ** [*Left Button] / [*Punch]: fire your primary weapon ** [*Right Button] / [*Place]: fire your offhand weapon / summon your shield to use a tool, select it in the hotbar. even if an Interaction Mode is active, the tool will take priority. press [*Left Button] / [*Punch] to use the tool on a block; for instance, to break a stone with a jackhammer. to configure a tool or use its secondary functions, if any, press [*Right Button] / [*Place]. hold [*Aux1] to activate your selected Maneuver. by default this is Sprint, which will consume stamina to allow you to run much faster. certain suits offer the Flight ability, which allows slow, mid-range flight. you can also unlock the psionic ability Lift, which allows very rapid flight but consumes psi at a prodigious rate. you can only have one Maneuver active at a time, whether this is a Psi Maneuver (consuming psi), a Suit Maneuver (consuming battery), or a Body Maneuver (consuming stamina). Maneuvers are activated in their respective panel. ### psionics there are four types of psionic abilities: Manual, Maneuver, Ritual, and Contextual. you can assign two Manual abilities at any given time and access them with the mouse buttons in Psionics mode. you can select a Psi Maneuver in the Psionics panel and activate it by holding [*Aux1]. 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. 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. |
Deleted starsoul.ct version [81ada79a74].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# starsoul [*starsoul] is a sci-fi survival game. you play the survivor of a disaster in space, stranded on a just-barely-habitable world with nothing but your escape pod, your survival suit, and some handy new psionic powers that the accident seems to have unlocked in you. scavenge resources, search alien ruins, and build a base where you can survive indefinitely, but be careful: winter approaches, and your starting suit heater is already struggling to keep you alive in the comparatively warm days of "summer". ## story about a month ago, you woke up to unexpected good news. your application to join the new Commune colony at Thousand Petal, submitted in a moment of utter existential weariness and almost in jest, was actually accepted. your skillset was a "perfect match" for the budding colony's needs, claimed the Population Control Authority, and you'd earned yourself a free trip to your new home -- on a swanky state transport, no less. it took a few discreet threats and bribes from Commune diplomats, but after a week of wrangling with the surly Crown Service for Comings & Goings -- whose bureaucrats seemed outright [!offended] that you actually managed to find a way off that hateful rock -- you secured grudging clearance to depart. you celebrated by telling your slackjawed boss exactly what you thought of him in a meeting room packed with his fellow parasites -- and left Circumsolar Megascale with a new appreciation for the value of your labor, as they found themselves desperately scrabbling for a replacement on short notice. you almost couldn't believe it when the Commune ship -- a sleek, solid piece of engineering whose graceful descent onto the landing pad seemed to sneer at the lurching rattletraps arrayed all around it -- actually showed up. in a daze you handed over your worldly possessions -- all three of them -- to a valet with impeccable manners, and climbed up out of the wagie nightmare into high orbit around your homeworld. the mercenary psion aboard, a preening Usukwinti with her very own luxury suite, tore a bleeding hole in the spacetime metric, and five hundred hopeful souls dove through towards fancied salvation. "sure," you thought to yourself as you slipped into your sleek new nanotech environment suit, itself worth more than the sum total of your earnings on Flame of Unyielding Purification, "life won't be easy -- but damn it, it'll [!mean] something out there." a free life on the wild frontier with a nation of comrades to have your back, with the best tech humans can make, fresh, clean water that isn't laced with compliance serum, and -- best of all -- never having to worry about paying rent again. it was too good to be true, you mused. clearly, the terrorists who blew up your ship agreed. you're still not certain what happened. all you know for sure is that transport was carrying more than just people. in those last hectic moments, you caught a glimpse of something -- maybe machine, maybe artwork, and [!definitely] ancient beyond measure. you've seen artifacts before in museums, of course; in fact, thanks to a childhood fascination, you can still name all the Elder Races and the Forevanished Ones off the top of your head. you have no [!idea] what that [!thing] was or who in the sublimated [!fuck] could possibly have made it. but one thing is for certain: your ship wasn't the only thing it ripped open when it blew. because when you woke up in your tiny escape pod beyond the furthest edge of the Reach, circling Farthest Shadow in a suicide orbit, you discovered yourself transformed into something impossible. a contradiction in terms. a human psionic. for years beyond counting, the Starsouled species -- three of whom yet live and deign once every so often to notice the Lesser Races -- have held the galaxy in sway through their monopoly on psionic power. of all the Thinking Few, only they are free to wander the distant stars at whim, heedless of the lightspeed barrier. there are no mechanisms for FTL travel or reactionless drive without that innate power, and, they assured us, psionic channels are fixed in the soul. your species either has the power or it doesn't. [!liars], all of them. are there other survivors? have they been similarly changed? what was that artifact and who were those terrorists? important questions, all, but they pale in comparison with the most important one: how the fuck are you going to survive the next 24 hours? ## engine starsoul is developed against a bleeding-edge version of minetest. it definitely won't work with anything older than v5.7, and ideally you should build directly from master. starsoul is best used with a patched version of minetest, though it is compatible with vanilla. the recommended patches are: * [>p11143 11143] - fix third-person view orientation p11143: https://github.com/minetest/minetest/pull/11143.diff ### shadows i was delighted to see dynamic shadows land in minetest, and i hope the implementation will eventually mature. however, as it stands, there are severe issues with shadows that make them essentially incompatible with complex meshes like the Starsouled player character meshes. for the sake of those who don't mind these glitches, Starsoul does enable shadows, but i unfortunately have to recommend that you disable them until the minetest devs get their act together on this feature. ## gameplay starsoul is somewhat unusual in how it uses the minetest engine. it's a voxel game but not of the minecraft variety. ### controls summon your Suit Interface by pressing the [*E] / [*Inventory] key. this will allow you to move items around in your inventory, but more importantly, it also allows you select or configure your Interaction Mode. the top three buttons can be used to select (or deactivate) an Interaction Mode. an Interaction Mode can be configured by pressing the button immediately below. the active Interaction Mode controls the behavior of the mouse buttons when no item is selected in the hotbar. the modes are: * [*Fabrication]: use your suit's onboard nanotech to directly manipulate matter in the world. ** [*Left Button] / [*Punch]: activate your primary nano program. by default this activates your nanoshredder, reducing the targeted object to monatomic powder and storing the resulting elements in your suit for use with the Matter Compiler ** [*Right Button] / [*Place]: activate your secondary nano program. by default, if your suit compiler can generate sufficiently large objects, creates a block of the configured type directly in the world without having to build it by hand * [*Psionics]: wield the awesome, if illicitly obtained, power of mind over matter ** [*Left Button] / [*Punch]: perform your selected Primary Power ** [*Right Button] / [*Place]: perform your selected Secondary Power * [*Weapon]: military-grade suits have built-in hardpoints for specialized weapon systems that draw directly on your suit battery for power (and in the most exotic cases, your psi reserve) ** [*Left Button] / [*Punch]: fire your primary weapon ** [*Right Button] / [*Place]: fire your offhand weapon / summon your shield to use a tool, select it in the hotbar. even if an Interaction Mode is active, the tool will take priority. press [*Left Button] / [*Punch] to use the tool on a block; for instance, to break a stone with a jackhammer. to configure a tool or use its secondary functions, if any, press [*Right Button] / [*Place]. hold [*Aux1] to activate your selected Maneuver. by default this is Sprint, which will consume stamina to allow you to run much faster. certain suits offer the Flight ability, which allows slow, mid-range flight. you can also unlock the psionic ability Lift, which allows very rapid flight but consumes psi at a prodigious rate. you can only have one Maneuver active at a time, whether this is a Psi Maneuver (consuming psi), a Suit Maneuver (consuming battery), or a Body Maneuver (consuming stamina). Maneuvers are activated in their respective panel. ### psionics there are four types of psionic abilities: Manual, Maneuver, Ritual, and Contextual. you can assign two Manual abilities at any given time and access them with the mouse buttons in Psionics mode. you can select a Psi Maneuver in the Psionics panel and activate it by holding [*Aux1]. 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 Starsouled 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. 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 Starsouled practitioners maintain their own Psionics Lab. |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |