Compare commits

...

1337 commits
1.2 ... master

Author SHA1 Message Date
Stranger1992
cd4c9b4645
Update bug_report.yaml 2025-04-27 09:33:01 +01:00
Stranger1992
05949c1cb2
Update bug_report.yaml 2025-04-27 09:31:36 +01:00
Stranger1992
f8c0f1afda
Update bug_report.yaml
Cleaned up the bug report and streamlined the process of creating it.
2025-04-27 09:30:01 +01:00
Lwmte
ae7e38a9da Update license 2025-04-18 12:16:46 +02:00
Jakub
84a74a8057
Update AUTHORS.md 2025-03-31 12:33:57 +01:00
Lwmte
8fbf10b362
Update README.md 2025-03-31 08:59:27 +03:00
Jakub
6e5d23db6a Update AUTHORS.md
Update AUTHORS.md

Add TrainWreck to AUTHORS.md

Update AUTHORS.md
2025-03-31 00:01:18 +01:00
Stranger1992
7fe9f7ab9f Merge branch 'develop' 2025-03-29 16:11:13 +00:00
Stranger1992
5c83574a0e Updated changelog for release version
Bug Fixes moved to top (as per other version entries)
2025-03-29 14:16:09 +00:00
Lwmte
410250bf2d Update CHANGELOG.md 2025-03-29 09:55:09 +01:00
Lwmte
3e2a1ccf37 Update documentation 2025-03-28 23:39:49 +01:00
Lwmte
74b7688c7b Update Settings.lua 2025-03-28 23:23:43 +01:00
Stranger1992
83f71eaf3b Revert "Test commit for Discord"
This reverts commit d4c8b72bbe.
2025-03-28 21:39:40 +00:00
Stranger1992
d4c8b72bbe Test commit for Discord 2025-03-28 21:37:07 +00:00
Lwmte
9fc370833b Implement flyby spline looping in lua API 2025-03-28 21:36:37 +01:00
Lwmte
7ba9370281 Update CHANGELOG.md 2025-03-28 07:11:04 +01:00
Nemoel-Tomo
9469a0e63a
Tomo - fireflies (#1597)
* tests only - nothing to see

* update

* update

* update

* update corpse

* update savegame, formatting

* import develop

* formatting

* fix broken streamer vertex

* update firefly streamer spawn

* update effect

* Add in objects for fireflies

* fixed all reviews, formatting, changing on-off cycle

* formatting

* reduced the emitted light to two lights per cluster

* adding antitrigger

* formatting

* fixed corpse

* fixed a bug with corpse

* fixed corpse fall bug

* formatting

* Small refactors

* Fix merge

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-03-28 07:44:07 +02:00
Lwmte
8e5f045d02 Update PostProcess.fx 2025-03-26 22:58:32 +01:00
Lwmte
917a86863e Changed lensflare formula 2025-03-26 19:41:59 +01:00
Sezz
539c19bdee
Update README.md 2025-03-27 03:43:10 +11:00
Sezz
22195b3267 Fix key binding settings saving for current session after hitting Esc to cancel 2025-03-26 23:26:00 +11:00
Sezz
c2c6b5f072 Minor changes 2025-03-25 15:05:07 +11:00
Lwmte
78e8d34c00 Fixed console on Win11 and monkey pathfinding 2025-03-24 22:03:52 +01:00
Lwmte
ac77c5f9ad Rollback MovingLaserFlags rename, bump version number 2025-03-23 08:24:36 +01:00
Lwmte
382b9d1ef5 Fixed incorrect debug line 2025-03-22 10:56:17 +01:00
Lwmte
8427ae7706 Update winmain.cpp 2025-03-22 10:37:36 +01:00
Lwmte
e27ec86e85 Allow to run console input in freeze modes as well 2025-03-22 10:28:12 +01:00
Lwmte
f0f733519f Simplify error message 2025-03-22 10:01:51 +01:00
Lwmte
d47e8d0e69 Don't try to execute empty lines 2025-03-22 09:57:25 +01:00
Lwmte
075c438b5d Update CHANGELOG.md 2025-03-22 09:49:03 +01:00
Lwmte
b1ee6a190e Added ability to perform Lua commands in the console window in realtime 2025-03-22 09:42:01 +01:00
Lwmte
8c26702678 Always warn about different game version 2025-03-22 08:31:05 +01:00
Lwmte
c608e6dec5 Update CHANGELOG.md 2025-03-22 00:30:52 +01:00
Lwmte
64a2d60d2f Added safeguards for problems surfaced in #1618 2025-03-22 00:29:33 +01:00
Sezz
dfa8170597 Demagic a number 2025-03-19 16:42:07 +11:00
Sezz
e5d9285033 Adjust Probe preview 2025-03-19 16:39:32 +11:00
Sezz
e0927a9415 Minor refactors to moving laser 2025-03-19 16:36:19 +11:00
Sezz
b07ee8eed7 tr5_movinglaser -> MovingLaser 2025-03-19 15:36:42 +11:00
Lwmte
5bdff1bf3c Update CHANGELOG.md 2025-03-17 23:35:58 +01:00
Lwmte
67f4fe3d1d Added probe constructor without room number 2025-03-17 23:29:44 +01:00
Lwmte
092346925e Fixed particles being canceled by fog bulbs 2025-03-17 21:01:15 +01:00
Lwmte
4ed26b1c2d Move InitializeObjets back, only reinit regenerated pickups counter 2025-03-16 20:48:40 +01:00
Lwmte
cb37bda329 Move particle init to proper function, always reinit objects on level reload 2025-03-16 18:17:11 +01:00
Lwmte
74e13fc678 Remove formatting leftovers from doc 2025-03-16 15:41:47 +01:00
Stranger1992
a4b3019ce9 Update changelog for 1.8 release 2025-03-16 10:56:18 +00:00
Stranger1992
9a08c05a19 Update bug_report.yaml 2025-03-16 10:52:59 +00:00
Stranger1992
d9cc241502 Merge branch 'develop' 2025-03-16 10:34:44 +00:00
Lwmte
db3b9cc411 Update version.h 2025-03-16 10:47:34 +01:00
Lwmte
e521e12a02 Some doc revisions 2025-03-16 10:47:10 +01:00
Lwmte
6b1ca045d0 Update documentation for streamer feather mode 2025-03-15 20:19:13 +01:00
Lwmte
33dd1461ad Change fog distance data type to float 2025-03-15 16:27:57 +01:00
Lwmte
edf3fbf68f Added script API methods for static mesh collision 2025-03-15 10:51:59 +01:00
Lwmte
f2d7044dfb Bypass rendering for objects and statics with near-zero transparency 2025-03-15 08:42:25 +01:00
Sezz
8bbc2df8eb Update EffectsFunctions.cpp 2025-03-15 18:06:04 +11:00
Sezz
92b8ed821f Update Lua advanced particle emitter 2025-03-15 18:01:27 +11:00
Lwmte
b9c3856c09 Fix alpha tested polys not fading 2025-03-15 02:55:30 +01:00
Lwmte
234192f9ed Update CHANGELOG.md 2025-03-15 01:03:09 +01:00
Lwmte
f758bab187 Rollback Starfield naming 2025-03-15 01:01:05 +01:00
TrainWrack
bed466be9d
Fix damage Commit (#1614)
* Fix Commit

* Revision
2025-03-15 01:49:18 +02:00
Lwmte
d7d9aad0d9 Update RoomObject.cpp 2025-03-14 08:34:16 +01:00
Lwmte
fa9a56f627 Throw exception if unknown object slot is encountered 2025-03-14 08:29:37 +01:00
Sezz
6d4bc4207e Fix input action conflicts not updating properly 2025-03-14 15:41:47 +11:00
Lwmte
61ca421fb8 Fixed fast moving items interpolation 2025-03-14 02:42:23 +01:00
Lwmte
66f5f67620 Add more cross-references to documentation 2025-03-13 22:39:26 +01:00
Lwmte
e983bcb2d3 Fixed docs 2025-03-13 22:13:31 +01:00
Lwmte
6a71b857f0 Fixes to starfield 2025-03-13 21:15:57 +01:00
Lwmte
973e35c10a Move underwater floor trapdoor animation to slot 442 2025-03-13 20:58:07 +01:00
Lwmte
28773e2387 Fix crash with old panel objects present in a level 2025-03-13 20:32:14 +01:00
Lwmte
1daf39471a Update Objects.LaraObject.html 2025-03-13 20:16:22 +01:00
Lwmte
ed342f089d Update Lara docs and dummy title level 2025-03-13 20:03:32 +01:00
Lwmte
c49619d80e Fixed underwater dust particles overflowing when camera is underwater 2025-03-13 18:48:17 +01:00
Lwmte
5c230aaeae Update CHANGELOG.md 2025-03-13 18:23:58 +01:00
Lwmte
f0ddc22da8 Fixed AA settings not updating correctly after screen resolution change 2025-03-13 12:57:12 +01:00
Lwmte
1d60524269 Update CHANGELOG.md 2025-03-13 11:17:13 +01:00
Lwmte
e7003e1ad5 Bypass unnecessary sky calculations if it's not active 2025-03-13 09:44:23 +01:00
Stranger1992
7babcb33a8 Update changelog with links to assets 2025-03-12 20:32:17 +00:00
Lwmte
506deb966d Don't draw horizon if transparency value is too low 2025-03-12 09:45:10 +01:00
Lwmte
892b69fc80 Fixed some Level parameters being write-only 2025-03-12 08:14:52 +01:00
Lwmte
0ea5f3cdc0 Update data types in LDoc 2025-03-12 00:41:16 +01:00
Lwmte
74fa394bd7 Update enums and documentation 2025-03-11 23:52:11 +01:00
Lwmte
ed938bfeb9 Type updates 2025-03-11 23:46:31 +01:00
Lwmte
3046f5f4a2
Refactor scripting documentation and flow data types to use fields instead of getters and setters (#1609)
* Initial commit

* Minor changes

* Rename Flow.Starfield to Flow.StarField

* Added cross-referencing, fixed some descriptions

* Start page LDoc cleanup

* Proofread sound module documentation

* Fixed Vec2 docs

* Update FlowLevel.cpp

* Fixed incorrect tag

* Update CHANGELOG.md

* Allow to refresh starfield in runtime

* Update weather.cpp

* Dynamically change amount of stars

* Update CHANGELOG.md

* Update CHANGELOG.md

* Remove brackets in room class documentation

* Added operators for Rotation class

* Rephrase a little

* Minor nitpicks

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2025-03-12 00:34:48 +02:00
Lwmte
f01ad441d3 Squashed commit of the following:
commit 8ea54ea7d4
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Mar 11 23:34:57 2025 +0100

    Fix merge

commit 566b694c9d
Merge: 935b26bd6 e97c47315
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Mar 11 23:33:43 2025 +0100

    Merge branch 'develop' into pr/1496

commit 935b26bd66
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Mar 11 23:31:57 2025 +0100

    Use script to generate object list, also migrate flatbuffer to powershell script

commit a6ec51e9d4
Merge: 7bf0ffef6 676762e11
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Dec 29 12:53:23 2024 +0100

    Merge branch 'develop' into mod

commit 676762e115
Merge: ae0488410 5597750c2
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Dec 29 12:48:24 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 7bf0ffef6c
Merge: b876f347c ae0488410
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Thu Dec 26 11:59:22 2024 +0100

    Merge branch 'develop' into mod

commit ae04884102
Merge: e1c61aca0 8fb4668df
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Thu Dec 26 11:58:28 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit b876f347c7
Merge: 0669b6f01 e1c61aca0
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Wed Dec 18 19:24:15 2024 +0100

    Merge branch 'develop' into mod

commit e1c61aca03
Merge: f557f0081 88bbe4862
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Wed Dec 18 19:23:31 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 0669b6f012
Merge: f978ad1b5 f557f0081
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Wed Dec 18 19:21:52 2024 +0100

    Merge branch 'develop' into mod

commit f557f00815
Merge: 34beccfcc ae7924797
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Mon Dec 16 18:45:13 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 34beccfccc
Merge: 6516a8b87 2f6114deb
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Fri Dec 13 20:23:25 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 6516a8b87b
Merge: babdad1a7 e674b8526
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Wed Dec 11 15:48:14 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit f978ad1b53
Merge: 96663d564 babdad1a7
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sat Dec 7 08:37:39 2024 +0100

    Merge branch 'develop' into mod

commit babdad1a74
Merge: c3a97f411 d96bf4ba8
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sat Dec 7 08:35:23 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 96663d5646
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Thu Nov 28 13:35:50 2024 +0100

    add SpriteConstants

commit 89c27fc19d
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Thu Nov 28 12:50:59 2024 +0100

    Revert "conversion error"

    This reverts commit f811117458.

commit 3dd8e76d8b
Merge: 4195bc042 c3a97f411
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Thu Nov 28 12:50:48 2024 +0100

    Merge branch 'develop' into mod

commit c3a97f4113
Merge: 4af4d14db db7f86485
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Thu Nov 28 12:49:21 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 4af4d14db8
Merge: 5e87ac32e 27d3ca7bb
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Nov 24 13:23:35 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 4195bc042a
Merge: f81111745 5e87ac32e
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Nov 24 13:20:32 2024 +0100

    Merge branch 'mod' of https://github.com/davidmarr/TombEngine into mod

commit f811117458
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Nov 24 13:17:53 2024 +0100

    conversion error

commit 5e87ac32e6
Merge: ef1a7d2d0 19be42911
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Nov 24 13:15:38 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 7643d19082
Merge: 202c94f02 d256fe1ef
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Nov 24 13:15:03 2024 +0100

    Merge branch 'MontyTRC89:master' into mod

commit ef1a7d2d0f
Merge: 9e94324e9 202c94f02
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Nov 24 13:04:23 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit 9e94324e93
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Fri Nov 8 20:19:10 2024 +0100

    Revert "function description LevelFuncs.OnUseItem"

    This reverts commit 2478afca68.

commit 2478afca68
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Fri Nov 8 20:18:56 2024 +0100

    function description LevelFuncs.OnUseItem

commit 37be06150e
Merge: c7c3451c1 222e67e49
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Fri Nov 8 20:01:48 2024 +0100

    Merge branch 'MontyTRC89:develop' into develop

commit d256fe1efe
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Nov 3 22:19:11 2024 +0000

    Update bug_report.yaml

commit 710f04596b
Merge: 1affcce9b 0b5ea0db7
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Nov 3 18:37:17 2024 +0100

    Merge branch 'develop'

commit 1affcce9b9
Author: Jakub <80340234+Jakub768@users.noreply.github.com>
Date:   Sun Nov 3 14:18:34 2024 +0000

    Update CHANGELOG.md

commit fae8744e4b
Author: Jakub <80340234+Jakub768@users.noreply.github.com>
Date:   Sun Nov 3 14:16:05 2024 +0000

    Update CHANGELOG.md

commit 815498ec8a
Merge: d81fce22a c3365f3a3
Author: Jakub <kubabilinski03@gmail.com>
Date:   Sun Nov 3 13:51:09 2024 +0000

    Merge branch 'develop'

commit d81fce22ad
Merge: 30e053dc9 a962a17f6
Author: Jakub <kubabilinski03@gmail.com>
Date:   Sun Nov 3 12:35:58 2024 +0000

    Merge branch 'develop'

commit 30e053dc93
Author: Nemoel-Tomo <tomo_669@hotmail.com>
Date:   Wed Oct 30 18:00:21 2024 +0100

    Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

commit c7c3451c1c
Merge: 4fc411abe e4098c458
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Fri Oct 25 14:44:24 2024 +0200

    Merge branch 'MontyTRC89:develop' into develop

commit 4fc411abe9
Merge: bfcd93f52 f7a22ea1a
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Oct 20 21:38:01 2024 +0200

    Merge branch 'MontyTRC89:develop' into develop

commit bfcd93f52c
Merge: bdea82d74 deec54ff8
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Wed Oct 9 19:59:14 2024 +0200

    Merge branch 'MontyTRC89:develop' into develop

commit eca79130a0
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Tue Sep 3 09:45:21 2024 +0100

    Update bug_report.yaml

commit 7fee647c84
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Tue Sep 3 09:44:53 2024 +0100

    Update to bug report form

commit bdea82d742
Merge: c3b4ecf9b 60b9055d0
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Fri Aug 30 12:56:53 2024 +0200

    Merge branch 'MontyTRC89:develop' into develop

commit c3b4ecf9b6
Merge: b107327f0 37f056bfa
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Tue Aug 13 22:46:38 2024 +0200

    Merge branch 'MontyTRC89:develop' into develop

commit b107327f0a
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Thu Jul 25 13:19:30 2024 +0200

    Update CHANGELOG.md

commit 25aaba6316
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Thu Jul 25 13:16:58 2024 +0200

    Update VolumeObject.cpp

    fixed Volume:GetActive() method
2025-03-11 23:38:35 +01:00
Sezz
e97c47315e Remove m prefix from private logic handler fields 2025-03-12 00:10:05 +11:00
Lwmte
6f6a07e696 Update CHANGELOG.md 2025-03-11 08:28:01 +01:00
Lwmte
b2ab5f65b1 Upgrade to O2 optimizations 2025-03-11 08:25:51 +01:00
Lwmte
c3cdace9fb Update CHANGELOG.md 2025-03-10 08:42:57 +01:00
Lwmte
fcba21dd34 Update SoundSourceObject.cpp 2025-03-10 08:29:46 +01:00
Lwmte
431b5e2fa7 Update CHANGELOG.md 2025-03-10 00:38:26 +01:00
Lwmte
3064535f62 Removed unnecessary enabled flag from fog structure 2025-03-09 23:58:27 +01:00
Lwmte
df3fda8d33 Rename method 2025-03-09 23:23:56 +01:00
Lwmte
163594a6b9 Fix secrets property being write-only 2025-03-09 22:27:37 +01:00
Lwmte
839d9a4719 Added back laser sound 2025-03-09 20:42:01 +01:00
Lwmte
9d0fe6a1d8 Ribbon particle (#1606) 2025-03-09 20:01:43 +01:00
Lwmte
d33ebbe417 Fixed level medipacks count in Flow.Statistics class 2025-03-09 19:29:45 +01:00
Lwmte
f87d592ce6 Added Flow.GetTotalSecretCount() 2025-03-09 19:14:39 +01:00
Lwmte
cb73477b90 Added pickups count to Flow.Statistics class 2025-03-09 19:07:28 +01:00
Lwmte
39a23fb755 Update ObjectIDs.h 2025-03-09 09:00:00 +01:00
Lwmte
248fb11452 Fixed backwards alpha color conversion 2025-03-09 08:31:35 +01:00
Lwmte
019dccae57 Fixed slot problems 2025-03-09 08:24:48 +01:00
Lwmte
3acb07fe53 Fix sound IDs again 2025-03-09 06:51:19 +01:00
Lwmte
45a58d4e5b Fix sound IDs 2025-03-09 06:27:32 +01:00
Lwmte
e85a1e63d1 Fixed sound IDs 2025-03-09 06:22:08 +01:00
TrainWrack
9b119d03a2
TR5 Moving Laser (#1598)
* First

* First

* Finished

* Update CHANGELOG.md

* Change default speed to 10

* SimplifyLogic

* Revert "SimplifyLogic"

This reverts commit be0aeefaa4.

* Reapply "SimplifyLogic"

This reverts commit c7b8e1442e.

* Add moving sound.

* Add acceleration

* Update tr5_movinglaser.cpp

* Fix merge

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-03-09 07:08:06 +02:00
TrainWrack
e0b50439b3
Custom bar module (#1492)
* Update to bug report form

* Update bug_report.yaml

* Update AUTHORS.md

- Tomo (general coding, special FX coding, bug fixing)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update bug_report.yaml

* Add files via upload

* Update CHANGELOG.md

* Add files via upload

* Update CustomBar.lua

* Fix bugs with bar property setters

* Update CustomBar.lua

* Update documentation

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
Co-authored-by: Jakub <kubabilinski03@gmail.com>
Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-03-09 06:59:49 +02:00
TrainWrack
602acb4bae
Underwater Floor trapdoor (#1600)
* First Committ

* Finished

* Update CHANGELOG.md

* Declog conditions.

* Formatting

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-03-09 06:44:10 +02:00
TrainWrack
d35bd90a12
Tr4 statue plinth (#1580)
* Update to bug report form

* Update bug_report.yaml

* Update AUTHORS.md

- Tomo (general coding, special FX coding, bug fixing)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update bug_report.yaml

* FirstComitt

* Committ

* Update

* MeshBit

* Backup

* Working

* Fixed

* Final

* Squashed commit of the following:

commit 77d0865c8a
Author: Nemoel-Tomo <tomo_669@hotmail.com>
Date:   Sun Feb 16 08:20:58 2025 +0100

    Waterfall emitter formatting fix (#1570)

commit 92329741ad
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Feb 15 07:46:37 2025 +0100

    Clarify EmitSpotLight description

commit 562637f599
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Feb 14 23:10:40 2025 +0100

    Fixed #1574

commit a64825b6f1
Author: Sezz <sezzary@outlook.com>
Date:   Fri Feb 14 04:30:00 2025 +1100

    Add missing shift in EmitParticle()

commit 455d547de7
Author: Sezz <sezzary@outlook.com>
Date:   Fri Feb 14 04:25:55 2025 +1100

    Add lock to parallel task class

commit 918237113f
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 13 14:59:16 2025 +1100

    Make script utils more idiomatic to C++

commit b78376b0ab
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 13 12:44:06 2025 +1100

    Use correct angle conversion in EmitPatricle()

commit 3e00302ade
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 13 05:38:56 2025 +1100

    Update Rotation.cpp

commit f1c1fd2f63
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 13 04:12:34 2025 +1100

    Add Lerp() method to script Rotation class

commit 6ef9675bcb
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Feb 12 09:08:45 2025 +0100

    Update CHANGELOG.md

commit ca56f62f54
Author: Sezz <sezzary@outlook.com>
Date:   Wed Feb 12 18:59:54 2025 +1100

    Multithreading (#1541)

    * Create Worker class for multithreading

    * Update TombEngine.vcxproj

    * Rename GetWorkerCount() to GetThreadCount()

    * Add ProcessInParallel template for vectors

    * Add multiThreaded flag to settings, process sprites in parallel

    * Update Flow.Settings.html

    * Refine WorkerManager class conventions; deinit threads properly

    * Don't require explicit destruction

    * Address basic PR notes

    * Update Worker.cpp

    * Simplify ThreadManager class

    * Add method for running single task

    * Use singleton pattern; use more appropriate Controller suffix

    * Update WorkerController template method

    * Revise method

    * Handle exception in ~WorkerController()

    * Grammar

    * Correctly init single-threaded mode

    * Update CHANGELOG.md

    * Defer thread init until g_GameFlow is valid

    * unsigned int -> int

    * Rename class

    ---------

    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>

commit d37ac17a39
Author: Sezz <sezzary@outlook.com>
Date:   Wed Feb 12 17:36:12 2025 +1100

    Formatting

commit 62ce2f043d
Author: Sezz <sezzary@outlook.com>
Date:   Tue Feb 11 18:05:07 2025 +1100

    Fix bridges moving the player when the player is underwater

commit 0bb9af9894
Author: Sezz <sezzary@outlook.com>
Date:   Tue Feb 11 17:31:51 2025 +1100

    puzzle_keys.cpp formatting

commit 7d18d7506f
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Feb 9 15:58:15 2025 +0100

    Update Type module (#1569)

    * Update VolumeObject.cpp

    fixed Volume:GetActive() method

    * Update CHANGELOG.md

    * function description LevelFuncs.OnUseItem

    * Revert "function description LevelFuncs.OnUseItem"

    This reverts commit 2478afca68.

    * Update Type.lua

commit 909f631c2f
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Feb 8 11:04:40 2025 +0100

    Fixed mistake in electricity rendering

commit a840c2200c
Merge: 2c6331f58 a31faffec
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 8 02:58:13 2025 +1100

    Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop

commit 2c6331f583
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 8 02:58:03 2025 +1100

    Deprecate CalculateDistance() script function

commit a31faffec5
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Feb 7 08:31:14 2025 +0100

    Update CHANGELOG.md

commit 1f81ccf44d
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Thu Feb 6 18:05:25 2025 -0500

    Diary module (TEN side) (#1509)

    * Update to bug report form

    * Update bug_report.yaml

    * Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update bug_report.yaml

    * Add files via upload

    * Add files via upload

    * Add ID_DIARY_SPRITES (1384) and DIARY_ENTRY_SPRITES (1385)

    * Delete Scripts/Engine/Diarymodule.lua

    * Add files via upload

    * Add files via upload

    * Add files via upload

    * Add files via upload

    * Delete Scripts/Engine/CustomBar.lua

    * Add files via upload

    * Update CustomDiary.lua

    * Add files via upload

    * Add files via upload

    * Add files via upload

    * Update CustomDiary.lua

    * Upload

    * Update CHANGELOG.md

    * Revisions

    * Doc revisions

    * Remove GameVars.Engine everywhere.

    * Added error warnings by integrating type module

    * Update bug_report.yaml

    * Fixed the bug with GameVars resetting each level.

    * Added .Engine back.

    * Added missing checks.

    * Removed nil from textOptions loop

    * Added full TEN name for printlogs.

    * LatestChanges

    * Added section for import.

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>

commit 34ff933e5b
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 18:48:45 2025 +1100

    Update LogicHandler.cpp

commit 5500b13659
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 18:47:33 2025 +1100

    Update LogicHandler.cpp

commit acb1bb1518
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 18:46:16 2025 +1100

    Enforce proper convention for the few Lua table constants that didn't use it yet

commit 89d5b74298
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Thu Feb 6 02:09:53 2025 -0500

    Fix Trigger Triggerer (#1565)

    * Fix

    * Update CHANGELOG.md

    * Update trigger.cpp

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>

commit 8316062e3a
Merge: 5f447d95c 599a651b6
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Thu Feb 6 06:03:41 2025 +0000

    Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop

commit 5f447d95c5
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Thu Feb 6 06:03:18 2025 +0000

    Fix stopwatch display in inventory (due to removal of trademarked item)

    Now the trademarked item from Tomb Raider Chronicles has been removed and replaced with the stopwatch from Tomb Raider III

commit 599a651b6f
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 16:56:19 2025 +1100

    Update lens flare and starfield Lua docs

commit 94ede801bc
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 16:35:59 2025 +1100

    Misc. script doc tidying; move some script classes to namespaces

commit be8048407e
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Wed Feb 5 23:51:35 2025 -0500

    Underwater keys/puzzles (#1529)

    * Update to bug report form

    * Update bug_report.yaml

    * Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update bug_report.yaml

    * First Push

    * Updae LaraStruct to use animation 280

    * Update puzzles_keys.cpp

    * Update CHANGELOG.md

    * Squashed commit of the following:

    commit e50a4f8c27
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 20:07:56 2024 -0500

        Doc Revision

    commit 39a6e713ce
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 20:02:47 2024 -0500

        Doc revisions

    commit c330343820
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 19:52:59 2024 -0500

        Update CHANGELOG.md

    commit ad62d7b605
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 19:42:32 2024 -0500

        Check

    commit e135195641
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 19:26:23 2024 -0500

        First Commit

    commit 4a6c6ee270
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 19:19:05 2024 -0500

        Update EffectsFunctions.cpp

    * Revert "Squashed commit of the following:"

    This reverts commit ff2e49c6ed.

    * Formatting.

    * Formatting.

    * Update CHANGELOG.md

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
    Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
    Co-authored-by: Jakub <kubabilinski03@gmail.com>
    Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Co-authored-by: TrainWreck <barry@DESKTOP-HC53CPN>

commit 106787d911
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 04:34:28 2025 +1100

    Doc corrections

commit 737f5aa742
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 04:28:58 2025 +1100

    Make getters const

commit 2b41ae743a
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 02:35:16 2025 +1100

    Tidy up Lua doc for Static class

commit 437ce7c139
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 01:22:59 2025 +1100

    Complete short -> int conversions for Lua

commit 237ceca0f4
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Feb 5 08:04:23 2025 +0100

    Fixed custom shatter sounds

commit 64e0c303ba
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Feb 4 20:58:09 2025 +0100

    Update CHANGELOG.md

commit d2b692cb3b
Author: Sezz <sezzary@outlook.com>
Date:   Mon Feb 3 16:15:41 2025 +1100

    ShadowMode::Lara -> ShadowMode::Player

commit ebb20121ac
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 23:57:54 2025 +1100

    -1 -> NO_VALUE and other formatting

commit 4a6f30a152
Author: Jakub <80340234+Jakub768@users.noreply.github.com>
Date:   Sat Feb 1 12:07:10 2025 +0000

    Update LICENSE

commit 45d46e0e6b
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 22:55:35 2025 +1100

    Fix display pickup string not being interpolated in 60FPS mode

commit 903fdf288f
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 22:40:50 2025 +1100

    Optimise BitField class (#1511)

    * Optimise BitField class

    * DebugBuild -> DEBUG_BUILD

    * Fix merge error

commit 48902b00a9
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Sat Feb 1 06:23:04 2025 -0500

    Emit air bubble exposed (#1537)

    * Update to bug report form

    * Update bug_report.yaml

    * Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update bug_report.yaml

    * Update EffectsFunctions.cpp

    * First Commit

    * Check

    * Update CHANGELOG.md

    * Doc revisions

    * Doc Revision

    * Update EffectsFunctions.cpp

    * Remove room from arguements.

    * Doc clarification.

    * Make Size and Amplitude Optional

    * Update EffectsFunctions.cpp

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
    Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
    Co-authored-by: Jakub <kubabilinski03@gmail.com>
    Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Co-authored-by: Sezz <sezzary@outlook.com>

commit fa0e125f59
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Sat Feb 1 06:22:49 2025 -0500

    Emit particle upgrade (#1542)

    * Update to bug report form

    * Update bug_report.yaml

    * Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update bug_report.yaml

    * Expose ObjectSlot

    * Docs

    * Code Update

    * FinalPush

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Expose startRot

    * Expose startRot

    * Remove Space.

    * Code cleanup, revise doc comment

    * Nicer defaults in doc

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
    Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
    Co-authored-by: Jakub <kubabilinski03@gmail.com>
    Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Co-authored-by: Sezz <sezzary@outlook.com>

commit bcbe216508
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 20:17:11 2025 +1100

    Update Vec3.cpp

commit 06c33908d9
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 20:13:34 2025 +1100

    Explicit conversion

commit c99b8abf25
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 20:01:55 2025 +1100

    Add Translate() functions to script Vec2 and Vec3 classes

commit d8c646fabd
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 17:47:29 2025 +1100

    Start restructuring script Moveable class

commit 98b01ff64a
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Jan 30 22:25:57 2025 +0100

    Fixed #1562

commit d163d1ec30
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Mon Jan 27 19:27:08 2025 +0100

    Fixed #1558

commit 6ef8a562e5
Author: Sezz <sezzary@outlook.com>
Date:   Tue Jan 28 04:10:12 2025 +1100

    Update doc comment for moveable

commit d919e16790
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Jan 25 23:03:28 2025 +0100

    Fixed player model submerging into the floor while swimming underwater

commit faf17cd03a
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Jan 24 23:18:14 2025 +0100

    Fixed #1557

commit 5f402e380b
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Jan 24 22:55:08 2025 +0100

    Fixed #1556

commit ae20a49eb2
Author: Sezz <sezzary@outlook.com>
Date:   Thu Jan 23 17:02:26 2025 +1100

    Update VS hint comments for various classes

commit fb97628ac9
Author: Sezz <sezzary@outlook.com>
Date:   Thu Jan 23 16:48:46 2025 +1100

    Add `Scale` field to `Pose` class (#1546)

    * Reapply "Add functional Scale field to Pose class"

    This reverts commit 92305a5d25.

    * Update lara.cpp

commit fc0c260ea1
Author: Sezz <sezzary@outlook.com>
Date:   Thu Jan 23 15:25:09 2025 +1100

    Update script Rotation class and its doc

commit 0dd0061a94
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Jan 21 20:55:57 2025 +0100

    Update CHANGELOG.md

commit 7735f660ab
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Jan 21 20:49:32 2025 +0100

    Fixed #1553

commit 5764965230
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Jan 21 20:47:06 2025 +0100

    Fixed #1554

commit b55675b9ce
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Jan 21 20:46:47 2025 +0100

    Fixed #1552

commit 7be096f86a
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Jan 19 05:41:05 2025 +0100

    Fixed dynamic light shadows not being handled correctly

commit db2649e936
Author: Sezz <sezzary@outlook.com>
Date:   Sat Jan 18 19:54:04 2025 +1100

    Fix two block platform sometimes not traversing room portals correctly; cleanup

commit ff41900bd9
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 12 16:35:44 2025 +0000

    Updated to reflect develop branch after merging Pull Request

commit 2a89abe66d
Author: Nemoel-Tomo <tomo_669@hotmail.com>
Date:   Sun Jan 12 17:15:45 2025 +0100

    Tr1 electric ball (#1413)

    Implementation of the Electric Ball from Tomb Raider I, inside Level 5: St Francis Folly

commit a97548467e
Author: Nemoel-Tomo <tomo_669@hotmail.com>
Date:   Fri Jan 10 23:27:48 2025 +0100

    Waterfall_Emitter (#1359)

    * waterfall emitter. warning for the test I changed waterfallmist object

    * test

    * update

    * update

    * update

    * formatting

    * formatting

    * formatting

    * update mist color

    * import develop

    * bugfix sprite

    * added mor sprite particles

    * waterfall intensity

    * update waterfall to be more filled

    * update

    * Create a new sprite sequence for waterfall

    * Fixed texture order

    * removed GetParticleDistanceFade() from waterfall

    * Order

    * Update Particle struct; update waterfall

    * Update Particle struct

    * Simplify waterfall emitter

    * Simplify waterfall emitter

    * Simplify waterfall emitter

    * Simplify

    * Fix waterfall density; move sprite slot

    * Update Waterfall.cpp

    * fixed some small bugs with the waterfall after simplify

    * commit

    * imported develop 60fps branch

    * fixed waterfall errors

    * update

    * commit

    * update/test

    * update Waterfall

    * fixed last bugs

    * formatting

    * added option for sound

    * update

    * update, fixing errors

    * update

    * formatting

    * Formatting

    * fixed color issue, now only savegame is left

    * formatting

    * added new fields to savegame

    * targetpos is now Vector3

    * import develop

    * Fixes according to GH notes + move waterfall-specific code out of effects.cpp

    * Rollback object ID rename, add missing enum

    * renamed ID_WATERFALL_ into ID_WATERFALL_SPRITES

    * fixed SpriteSeqID for spark in spark.cpp

    * Update CHANGELOG.md

    * rewritten waterfall code

    * Update waterfall impact point

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
    Co-authored-by: Sezz <sezzary@outlook.com>
    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>

commit bfe07f6e21
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 5 16:45:30 2025 +0000

    Bump version in Lua API for development

commit 4dce157665
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 5 16:27:38 2025 +0000

    Bump Tomb Editor version for next dev cycle

    Amended also in the Development branch for TE

commit c25c7a1c96
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 5 15:10:17 2025 +0000

    Update CHANGELOG.md

commit 2da7401c28
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 5 15:09:41 2025 +0000

    Update changelog with template for the next version.

commit 66d7e51f5d
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sat Jan 4 18:41:11 2025 +0000

    Bump dev version to 1.7.2

    This has been done to draw a line post-release and any commits after this to form the next release. Version number may change.

commit cebc0175c9
Author: Sezz <sezzary@outlook.com>
Date:   Sat Jan 4 17:03:46 2025 +1100

    Move splash effect to its own file

commit b81b28039c
Author: Sezz <sezzary@outlook.com>
Date:   Sat Jan 4 16:03:07 2025 +1100

    Fix gravity being applied when vaulting on the same frame as the player lands

commit 5444ede2a8
Author: Sezz <sezzary@outlook.com>
Date:   Sat Jan 4 15:51:52 2025 +1100

    Reliably stop at edge when running at it while holding Walk

commit 43d5bb9639
Author: Sezz <sezzary@outlook.com>
Date:   Fri Jan 3 23:31:59 2025 +1100

    Update CHANGELOG.md

commit fb7e4dce70
Merge: 9baf5222f 12ac1219e
Author: Sezz <sezzary@outlook.com>
Date:   Fri Jan 3 23:06:28 2025 +1100

    Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop

commit 9baf5222f8
Author: Sezz <sezzary@outlook.com>
Date:   Fri Jan 3 23:06:25 2025 +1100

    Reset IsAirborne flag properly when exiting fly cheat

commit 12ac1219e9
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Jan 2 13:10:57 2025 +0100

    Update spark.cpp

commit 7f071b5ec5
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Jan 2 11:15:04 2025 +0100

    Port additional ricochet effect from tomb4

* Revert "Squashed commit of the following:"

This reverts commit f4570211a5.

* Update CHANGELOG.md

* Demagicify framenumber

* Fix Statue Plinth

* Fix bounds and add ItemFlag[0] for custom activation frame

* Rename files

* Cleanup

* Remove unnecessary includes

* Update documentation

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
Co-authored-by: Jakub <kubabilinski03@gmail.com>
Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2025-03-09 06:36:56 +02:00
TrainWrack
f1bb383baa
Horizon Effects (#1583)
* First Commit

* Check

* Check

* Check

* Is it done?

* GetHorizonDone

* Add HorizonSwap

* Fix Interpolation

* Update CHANGELOG.md

* Update EffectsFunctions.cpp

* Update EffectsFunctions.h

* Docs

* Create Horizon Class and implement it.

* Fix Interpolation

* Make HorizonObject part of WeatherController

* Implement horizon fading

* Save horizon parameters

* Update CHANGELOG.md

* Update RendererDraw.cpp

* Update EffectsFunctions.cpp

* Invert current horizon transition value if another transition is queued

* Add missing shader change

* StartPosition

* Cleaup

* Start working on Position

* Code commit

* Fix interpolation checks distance for position.

* Docs

* Update CHANGELOG.md

* SavePosition in savegame

* Major cleanup

* Update weather.cpp

* First commit

* Some fixes

* Reworked horizon workflow

* Update CHANGELOG.md

* Update docs

* Added missing rumble parameter

* String constants; use TypeOrNil; general cleanup

* Fix documentation

* Fix documentation

* Update documentation

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2025-03-09 06:30:07 +02:00
TrainWrack
2f675b0069
TR1 hammer (#1588)
* First

* Implementation

* Add retract

* Fixing

* Finished

* Update CHANGELOG.md

* Fix Incorrect Death height

* Rename file

* Formatting; minor fixes

* Update Hammer.cpp

* Update Hammer.cpp

* Reset scale when enabling fly cheat

* Update tr1_objects.cpp

* Address comments.

* Fix Hammer code

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2025-03-09 06:20:18 +02:00
TrainWrack
e047da7257
Streamer emitter (#1589)
* WIP

* Committ

* LUA DOCS

* Expose Moveable Scale (#1587)

* Done

* Update CHANGELOG.md

* Cleanup

* Minor fixes

* Update CHANGELOG.md

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>

* Cleanup

* Improve streamer effect implementation

* Shorten names

* Update Streamer.h

* Point to Moveable

* Change to Const

* Doc revisions

* Doc revisions

* Update CHANGELOG.md

* Use precise names

* Do things over seconds

* Make tag optional again

* Integrate start and end colour

* Update doc comment

* Regenerate docs; update strings

* Update doc comment

* Update ReservedScriptNames.h

* Update EffectsFunctions.cpp

* Minor fixes

* Update EffectsFunctions.cpp

* Update EffectsFunctions.cpp

* Update EffectsFunctions.cpp

* Rename Item

* Renames

* Add compiled docs

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2025-03-09 06:19:08 +02:00
TrainWrack
e54fba1e4a
Advanced particles (#1555)
* Update to bug report form

* Update bug_report.yaml

* Update AUTHORS.md

- Tomo (general coding, special FX coding, bug fixing)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update bug_report.yaml

* Expose ObjectSlot

* Docs

* Code Update

* FinalPush

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Expose startRot

* Expose startRot

* Remove Space.

* FirstCommit

* Cast gameobj to int

* Update

* WIP

* WIP

* wip

* Checks

* WIP

* WIP

* WIP

* Enum

* Enum revision

* Update CHANGELOG.md

* Revisions due to Develop changes

* CodeCleanup

* Revision to Code as per revised structure.

* Add friction and YVel

* Revisions

* LUA Docs

* RevisionDocs

* x

* Revisions

* Docs

* Docs

* Docs

* Docs

* Make wind default

* Light effect

* LightFinish

* Added Sounds

* Add sound docs

* Update particle anim types file

* Update EffectsFunctions.cpp

* Update EffectsFunctions.cpp

* FixAngle

* Fix StartRot

* Cleanup

* Add Savegame fields

* Add loadgame data.

* Fix bug on develop related to save value of xVel

* Address Comments

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
Co-authored-by: Jakub <kubabilinski03@gmail.com>
Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2025-03-09 05:45:25 +02:00
Lwmte
ba99f41fa0 Update version.h 2025-03-08 13:53:51 +01:00
iagoesp
b3b8b73c55
Sort Screenshots by YYYYMMDD #1372 (#1599)
* Sort screenshots by YYYYMMDD (#1372)

* Sort screenshots by YYYYMMDD (#1372)

* Fixes

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-03-08 13:39:24 +02:00
Sezz
a9482054cd
Input binding manager (#1566)
* First clean iteration of BindingManager class

* Fix key alignment errors; allow keys to have analog values

* Allow any input action to have an analog value

* Update comment

* Update key names

* Update queue state

* Update names

* Add comments

* Fix merge

* Fix mouse input

* Update constant name

* Fix merge errors

* Update formatting

* Fix merge errors; count game frames in input action class properly

* float -> unsigned long, renames

* Formatting

* Constants for key offsets

* New registry version

* Minor changes

* Fix saving

* Update InputAction.cpp

* Renames

* Minor changes

* Temporary rename + comments

* Update InputAction.cpp

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-03-08 13:32:02 +02:00
davidmarr
7701fb5fd0
Update CSS of Lua documentation (#1576)
* Update VolumeObject.cpp

fixed Volume:GetActive() method

* Update CHANGELOG.md

* function description LevelFuncs.OnUseItem

* Revert "function description LevelFuncs.OnUseItem"

This reverts commit 2478afca68.

* Update ldoc.css
2025-03-08 11:57:10 +02:00
Lwmte
7381ff0843 Update CHANGELOG.md 2025-03-08 02:51:19 +01:00
Lwmte
e58ae8cf07 Fixed typos in probe code 2025-03-05 23:27:17 +01:00
Lwmte
8b6ea97cf6 Update CHANGELOG.md 2025-03-05 09:20:42 +01:00
TrainWrack
d19d56acee
Collision Class (#1579)
* First Commit

* Make ScriptCollision class

* Fix typo

* Return correct types

* Use NO_HEIGHT

* Implement GetSurfaceMaterial

* Add enums

* Added death, climbable wall, monkeybar

* FIx angle

* Revise things

* Make "Collision" script module; add IsOutOfBounds() method

* Rename IsOutOfBounds()

* Allow getting floor or ceiling material type

* Don't need IsWall()

* Update Collision.cpp

* Restore IsWall()

* Rename class to "Probe"; start docs; make steepness inquirers return an optional

* Update Probe.cpp

* Update class name in doc

* Update Probe.cpp

* add GetRoomName

* UpdateEnums

* Update MaterialType enum; make room name getting more local

* Revise constructors; update doc

* Update Probe.cpp

* Generate html docs

* Slightly clearer doc comments

* Convert spaces to tabs

* Update Probe.cpp

* Update Probe.cpp

* Rename parameters

* Update Probe.cpp

* ScriptProbe -> Probe

* ExposeGetRoom

* Register Collision.MaterialType table; Enable ClimbWall again.

* Cleanup

* Docs revision

* Update CHANGELOG.md

* Moved names to script reserved names

* Fixed incorrect namespace for probe

* Use consistent names; cleanup

* Make argument optional; fix doc

* Update Probe.cpp

* Add missing includes

* Add Preview() method; update docs

* Add constant

---------

Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-03-05 10:13:48 +02:00
Sezz
d3d885e78f Update moveable scale function doc comments 2025-03-05 15:34:08 +11:00
Sezz
1881e08c98 Spawn streamer segments at a rate of 10 per second; update TestGlobalTimeInterval() function 2025-03-03 17:20:55 +11:00
Sezz
6bf0ff2c43 Allow streamers to have start and end colours 2025-03-03 16:12:21 +11:00
Sezz
9b9c27937a Update Streamer.h 2025-03-02 14:50:02 +11:00
Sezz
7a94f00180 Shorten names 2025-03-02 14:14:56 +11:00
Sezz
1e143c6e0a Improve streamer effect implementation 2025-03-02 14:07:43 +11:00
Sezz
44128eda43 Cleanup 2025-02-28 13:48:21 +11:00
TrainWrack
2c8af9fa20
Expose Moveable Scale (#1587)
* Done

* Update CHANGELOG.md

* Cleanup

* Minor fixes

* Update CHANGELOG.md

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-02-28 03:54:26 +02:00
Lwmte
eff0f4d363 Changed alpha threshold to epsilon 2025-02-27 23:51:55 +01:00
Lwmte
9b6424f2d5
Alpha tint (#1572)
* Work

* Work

* Move RendererRoom out of union

* Update CHANGELOG.md

* Correctly mix alpha for all blend modes

* Introduce alpha threshold

* Cleanup and formatting; also add correct alpha convertion in RGBAColor8Byte to its Color() operator

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2025-02-28 00:44:27 +02:00
Adngel
ae71ba601a Fix: Combat Camera in quick sands rooms
Combat Camera was taking "position.y" value that is the quick sand bottom, instead of "TopHeight" (formely maxceiling) that is the top surface of the quicksands camera.

When Lara digs enough in the quicksands, she automatically draws her weapons stopping the combat camera, that's why the bug only happened on certain range.
2025-02-27 10:31:27 +01:00
Sezz
1715fdb7b0 Update conventions, formatting, docs 2025-02-23 21:59:44 +11:00
Lwmte
1d884a4d62 Added functions to get flyby sequence parameters at a specified time point 2025-02-23 03:36:04 +01:00
Jakub
945ddd4738
Update CHANGELOG.md 2025-02-22 11:00:05 +00:00
Jakub
015a2b9be5
Update CHANGELOG.md 2025-02-22 10:59:21 +00:00
Nemoel-Tomo
77d0865c8a
Waterfall emitter formatting fix (#1570) 2025-02-16 09:20:58 +02:00
Lwmte
92329741ad Clarify EmitSpotLight description 2025-02-15 07:46:37 +01:00
Lwmte
562637f599 Fixed #1574 2025-02-14 23:10:40 +01:00
Sezz
a64825b6f1 Add missing shift in EmitParticle() 2025-02-14 04:30:00 +11:00
Sezz
455d547de7 Add lock to parallel task class 2025-02-14 04:25:55 +11:00
Sezz
918237113f Make script utils more idiomatic to C++ 2025-02-13 14:59:16 +11:00
Sezz
b78376b0ab Use correct angle conversion in EmitPatricle() 2025-02-13 12:44:06 +11:00
Sezz
3e00302ade Update Rotation.cpp 2025-02-13 05:38:56 +11:00
Sezz
f1c1fd2f63 Add Lerp() method to script Rotation class 2025-02-13 04:12:34 +11:00
Lwmte
6ef9675bcb Update CHANGELOG.md 2025-02-12 09:08:45 +01:00
Sezz
ca56f62f54
Multithreading (#1541)
* Create Worker class for multithreading

* Update TombEngine.vcxproj

* Rename GetWorkerCount() to GetThreadCount()

* Add ProcessInParallel template for vectors

* Add multiThreaded flag to settings, process sprites in parallel

* Update Flow.Settings.html

* Refine WorkerManager class conventions; deinit threads properly

* Don't require explicit destruction

* Address basic PR notes

* Update Worker.cpp

* Simplify ThreadManager class

* Add method for running single task

* Use singleton pattern; use more appropriate Controller suffix

* Update WorkerController template method

* Revise method

* Handle exception in ~WorkerController()

* Grammar

* Correctly init single-threaded mode

* Update CHANGELOG.md

* Defer thread init until g_GameFlow is valid

* unsigned int -> int

* Rename class

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-02-12 09:59:54 +02:00
Sezz
d37ac17a39 Formatting 2025-02-12 17:36:12 +11:00
Sezz
62ce2f043d Fix bridges moving the player when the player is underwater 2025-02-11 18:05:07 +11:00
Sezz
0bb9af9894 puzzle_keys.cpp formatting 2025-02-11 17:31:51 +11:00
davidmarr
7d18d7506f
Update Type module (#1569)
* Update VolumeObject.cpp

fixed Volume:GetActive() method

* Update CHANGELOG.md

* function description LevelFuncs.OnUseItem

* Revert "function description LevelFuncs.OnUseItem"

This reverts commit 2478afca68.

* Update Type.lua
2025-02-09 14:58:15 +00:00
Lwmte
909f631c2f Fixed mistake in electricity rendering 2025-02-08 11:04:40 +01:00
Sezz
a840c2200c Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2025-02-08 02:58:13 +11:00
Sezz
2c6331f583 Deprecate CalculateDistance() script function 2025-02-08 02:58:03 +11:00
Lwmte
a31faffec5 Update CHANGELOG.md 2025-02-07 08:31:14 +01:00
TrainWrack
1f81ccf44d
Diary module (TEN side) (#1509)
* Update to bug report form

* Update bug_report.yaml

* Update AUTHORS.md

- Tomo (general coding, special FX coding, bug fixing)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update bug_report.yaml

* Add files via upload

* Add files via upload

* Add ID_DIARY_SPRITES (1384) and DIARY_ENTRY_SPRITES (1385)

* Delete Scripts/Engine/Diarymodule.lua

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Delete Scripts/Engine/CustomBar.lua

* Add files via upload

* Update CustomDiary.lua

* Add files via upload

* Add files via upload

* Add files via upload

* Update CustomDiary.lua

* Upload

* Update CHANGELOG.md

* Revisions

* Doc revisions

* Remove GameVars.Engine everywhere.

* Added error warnings by integrating type module

* Update bug_report.yaml

* Fixed the bug with GameVars resetting each level.

* Added .Engine back.

* Added missing checks.

* Removed nil from textOptions loop

* Added full TEN name for printlogs.

* LatestChanges

* Added section for import.

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
2025-02-06 23:05:25 +00:00
Sezz
34ff933e5b Update LogicHandler.cpp 2025-02-06 18:48:45 +11:00
Sezz
5500b13659 Update LogicHandler.cpp 2025-02-06 18:47:33 +11:00
Sezz
acb1bb1518 Enforce proper convention for the few Lua table constants that didn't use it yet 2025-02-06 18:46:16 +11:00
TrainWrack
89d5b74298
Fix Trigger Triggerer (#1565)
* Fix

* Update CHANGELOG.md

* Update trigger.cpp

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
2025-02-06 07:09:53 +00:00
Stranger1992
8316062e3a Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2025-02-06 06:03:41 +00:00
Stranger1992
5f447d95c5 Fix stopwatch display in inventory (due to removal of trademarked item)
Now the trademarked item from Tomb Raider Chronicles has been removed and replaced with the stopwatch from Tomb Raider III
2025-02-06 06:03:18 +00:00
Sezz
599a651b6f Update lens flare and starfield Lua docs 2025-02-06 16:56:19 +11:00
Sezz
94ede801bc Misc. script doc tidying; move some script classes to namespaces 2025-02-06 16:35:59 +11:00
TrainWrack
be8048407e
Underwater keys/puzzles (#1529)
* Update to bug report form

* Update bug_report.yaml

* Update AUTHORS.md

- Tomo (general coding, special FX coding, bug fixing)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update bug_report.yaml

* First Push

* Updae LaraStruct to use animation 280

* Update puzzles_keys.cpp

* Update CHANGELOG.md

* Squashed commit of the following:

commit e50a4f8c27
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Wed Dec 25 20:07:56 2024 -0500

    Doc Revision

commit 39a6e713ce
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Wed Dec 25 20:02:47 2024 -0500

    Doc revisions

commit c330343820
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Wed Dec 25 19:52:59 2024 -0500

    Update CHANGELOG.md

commit ad62d7b605
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Wed Dec 25 19:42:32 2024 -0500

    Check

commit e135195641
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Wed Dec 25 19:26:23 2024 -0500

    First Commit

commit 4a6c6ee270
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Wed Dec 25 19:19:05 2024 -0500

    Update EffectsFunctions.cpp

* Revert "Squashed commit of the following:"

This reverts commit ff2e49c6ed.

* Formatting.

* Formatting.

* Update CHANGELOG.md

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
Co-authored-by: Jakub <kubabilinski03@gmail.com>
Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
Co-authored-by: TrainWreck <barry@DESKTOP-HC53CPN>
2025-02-06 04:51:35 +00:00
Sezz
106787d911 Doc corrections 2025-02-06 04:34:28 +11:00
Sezz
737f5aa742 Make getters const 2025-02-06 04:28:58 +11:00
Sezz
2b41ae743a Tidy up Lua doc for Static class 2025-02-06 02:35:16 +11:00
Sezz
437ce7c139 Complete short -> int conversions for Lua 2025-02-06 01:22:59 +11:00
Lwmte
237ceca0f4 Fixed custom shatter sounds 2025-02-05 08:04:23 +01:00
Lwmte
64e0c303ba Update CHANGELOG.md 2025-02-04 20:58:09 +01:00
Sezz
d2b692cb3b ShadowMode::Lara -> ShadowMode::Player 2025-02-03 16:15:41 +11:00
Sezz
ebb20121ac -1 -> NO_VALUE and other formatting 2025-02-01 23:57:54 +11:00
Jakub
4a6f30a152
Update LICENSE 2025-02-01 12:07:10 +00:00
Jakub
75144d877e
Update LICENSE 2025-02-01 12:06:42 +00:00
Sezz
45d46e0e6b Fix display pickup string not being interpolated in 60FPS mode 2025-02-01 22:55:35 +11:00
Sezz
903fdf288f
Optimise BitField class (#1511)
* Optimise BitField class

* DebugBuild -> DEBUG_BUILD

* Fix merge error
2025-02-01 22:40:50 +11:00
TrainWrack
48902b00a9
Emit air bubble exposed (#1537)
* Update to bug report form

* Update bug_report.yaml

* Update AUTHORS.md

- Tomo (general coding, special FX coding, bug fixing)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update bug_report.yaml

* Update EffectsFunctions.cpp

* First Commit

* Check

* Update CHANGELOG.md

* Doc revisions

* Doc Revision

* Update EffectsFunctions.cpp

* Remove room from arguements.

* Doc clarification.

* Make Size and Amplitude Optional

* Update EffectsFunctions.cpp

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
Co-authored-by: Jakub <kubabilinski03@gmail.com>
Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2025-02-01 11:23:04 +00:00
TrainWrack
fa0e125f59
Emit particle upgrade (#1542)
* Update to bug report form

* Update bug_report.yaml

* Update AUTHORS.md

- Tomo (general coding, special FX coding, bug fixing)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update bug_report.yaml

* Expose ObjectSlot

* Docs

* Code Update

* FinalPush

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Expose startRot

* Expose startRot

* Remove Space.

* Code cleanup, revise doc comment

* Nicer defaults in doc

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
Co-authored-by: Jakub <kubabilinski03@gmail.com>
Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2025-02-01 11:22:49 +00:00
Sezz
bcbe216508 Update Vec3.cpp 2025-02-01 20:17:11 +11:00
Sezz
06c33908d9 Explicit conversion 2025-02-01 20:13:34 +11:00
Sezz
c99b8abf25 Add Translate() functions to script Vec2 and Vec3 classes 2025-02-01 20:01:55 +11:00
Sezz
d8c646fabd Start restructuring script Moveable class 2025-02-01 17:47:29 +11:00
Lwmte
98b01ff64a Fixed #1562 2025-01-30 22:25:57 +01:00
Lwmte
d163d1ec30 Fixed #1558 2025-01-27 19:27:37 +01:00
Sezz
6ef8a562e5 Update doc comment for moveable 2025-01-28 04:10:12 +11:00
Lwmte
d919e16790 Fixed player model submerging into the floor while swimming underwater 2025-01-25 23:03:28 +01:00
Lwmte
faf17cd03a Fixed #1557 2025-01-24 23:18:14 +01:00
Lwmte
5f402e380b Fixed #1556 2025-01-24 22:55:08 +01:00
Sezz
ae20a49eb2 Update VS hint comments for various classes 2025-01-23 17:02:26 +11:00
Sezz
fb97628ac9
Add Scale field to Pose class (#1546)
* Reapply "Add functional Scale field to Pose class"

This reverts commit 92305a5d25.

* Update lara.cpp
2025-01-23 16:48:46 +11:00
Sezz
fc0c260ea1 Update script Rotation class and its doc 2025-01-23 15:25:09 +11:00
Lwmte
0dd0061a94 Update CHANGELOG.md 2025-01-21 20:55:57 +01:00
Lwmte
7735f660ab Fixed #1553 2025-01-21 20:49:32 +01:00
Lwmte
5764965230 Fixed #1554 2025-01-21 20:47:06 +01:00
Lwmte
b55675b9ce Fixed #1552 2025-01-21 20:46:47 +01:00
Lwmte
7be096f86a Fixed dynamic light shadows not being handled correctly 2025-01-19 05:42:16 +01:00
Sezz
db2649e936 Fix two block platform sometimes not traversing room portals correctly; cleanup 2025-01-18 19:54:04 +11:00
Stranger1992
ff41900bd9 Updated to reflect develop branch after merging Pull Request 2025-01-12 16:39:17 +00:00
Nemoel-Tomo
2a89abe66d
Tr1 electric ball (#1413)
Implementation of the Electric Ball from Tomb Raider I, inside Level 5: St Francis Folly
2025-01-12 16:15:45 +00:00
Nemoel-Tomo
a97548467e
Waterfall_Emitter (#1359)
* waterfall emitter. warning for the test I changed waterfallmist object

* test

* update

* update

* update

* formatting

* formatting

* formatting

* update mist color

* import develop

* bugfix sprite

* added mor sprite particles

* waterfall intensity

* update waterfall to be more filled

* update

* Create a new sprite sequence for waterfall

* Fixed texture order

* removed GetParticleDistanceFade() from waterfall

* Order

* Update Particle struct; update waterfall

* Update Particle struct

* Simplify waterfall emitter

* Simplify waterfall emitter

* Simplify waterfall emitter

* Simplify

* Fix waterfall density; move sprite slot

* Update Waterfall.cpp

* fixed some small bugs with the waterfall after simplify

* commit

* imported develop 60fps branch

* fixed waterfall errors

* update

* commit

* update/test

* update Waterfall

* fixed last bugs

* formatting

* added option for sound

* update

* update, fixing errors

* update

* formatting

* Formatting

* fixed color issue, now only savegame is left

* formatting

* added new fields to savegame

* targetpos is now Vector3

* import develop

* Fixes according to GH notes + move waterfall-specific code out of effects.cpp

* Rollback object ID rename, add missing enum

* renamed ID_WATERFALL_ into ID_WATERFALL_SPRITES

* fixed SpriteSeqID for spark in spark.cpp

* Update CHANGELOG.md

* rewritten waterfall code

* Update waterfall impact point

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2025-01-10 22:27:48 +00:00
Stranger1992
bfe07f6e21 Bump version in Lua API for development 2025-01-05 16:45:30 +00:00
Stranger1992
4dce157665 Bump Tomb Editor version for next dev cycle
Amended also in the Development branch for TE
2025-01-05 16:27:38 +00:00
Stranger1992
c25c7a1c96 Update CHANGELOG.md 2025-01-05 15:10:17 +00:00
Stranger1992
2da7401c28 Update changelog with template for the next version. 2025-01-05 15:09:41 +00:00
Stranger1992
66d7e51f5d Bump dev version to 1.7.2
This has been done to draw a line post-release and any commits after this to form the next release. Version number may change.
2025-01-04 18:41:11 +00:00
Stranger1992
f61c9cf4ff Release cleanup 2025-01-04 18:32:12 +00:00
Stranger1992
1ba4924473 Merge branch 'develop' 2025-01-04 17:31:39 +00:00
Sezz
cebc0175c9 Move splash effect to its own file 2025-01-04 17:03:46 +11:00
Sezz
b81b28039c Fix gravity being applied when vaulting on the same frame as the player lands 2025-01-04 16:03:07 +11:00
Sezz
5444ede2a8 Reliably stop at edge when running at it while holding Walk 2025-01-04 15:51:52 +11:00
Sezz
43d5bb9639 Update CHANGELOG.md 2025-01-03 23:31:59 +11:00
Sezz
fb7e4dce70 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2025-01-03 23:06:28 +11:00
Sezz
9baf5222f8 Reset IsAirborne flag properly when exiting fly cheat 2025-01-03 23:06:25 +11:00
Lwmte
12ac1219e9 Update spark.cpp 2025-01-02 13:10:57 +01:00
Lwmte
7f071b5ec5 Port additional ricochet effect from tomb4 2025-01-02 11:15:04 +01:00
Sezz
06ee8fa4b8 Update debug interaction box drawing 2025-01-01 19:15:47 +11:00
Lwmte
01129f62b5 Revert object camera position fix 2024-12-31 18:46:56 +01:00
Lwmte
b353557c7f Also check VC runtimes version before launching 2024-12-31 11:57:44 +01:00
Lwmte
610b7dc942 Add missing blob shadows for objects 2024-12-30 19:12:18 +01:00
Lwmte
cf8d642e4e Reduce blob shadow intensity in half 2024-12-30 18:56:50 +01:00
Lwmte
7bb719d337 Invert blob shadow intensity to be consistent with dynamic shadows 2024-12-30 18:15:02 +01:00
Lwmte
2b69daeb5e Put compiled shaders into version subdirectory 2024-12-30 17:57:34 +01:00
Lwmte
9df0004e6b Remove unused shadow calculations 2024-12-30 13:24:30 +01:00
Sezz
92305a5d25 Revert "Add functional Scale field to Pose class"
This reverts commit 62bf124830.
2024-12-30 22:04:27 +11:00
Sezz
62bf124830 Add functional Scale field to Pose class 2024-12-30 21:46:12 +11:00
Sezz
6b32f73f7a Remove TriggerGunSmoke_SubFunction(); old -> Prev prefix 2024-12-30 20:18:40 +11:00
Lwmte
b0d1b4d8bf Update README.md 2024-12-30 08:56:56 +01:00
Lwmte
147aea42f5 Fix underwater switch jump spam issue 2024-12-30 08:33:00 +01:00
Lwmte
5597750c2c Remove abandoned diary code, convert all inventory items to pickups, fix diary savegame 2024-12-28 11:20:25 +01:00
Stranger1992
b27ba26b6b Update bug_report.yaml 2024-12-27 21:22:13 +00:00
Lwmte
382f778fa2 Bump version, add runtime checks 2024-12-27 19:45:31 +01:00
Lwmte
36fa14f5ac Update CHANGELOG.md 2024-12-27 18:16:36 +01:00
Lwmte
9ced5c52e2 Fixed occasional crashes with creatures stuck in a sector with no pathfinding set 2024-12-27 16:50:32 +01:00
Lwmte
957151cd56 Update CHANGELOG.md 2024-12-27 14:45:18 +01:00
Lwmte
9b9699c134 Fixed engine not starting from non-Latin paths 2024-12-27 14:29:29 +01:00
Lwmte
505f666545 Fixed #1536 2024-12-27 09:34:11 +01:00
Lwmte
6b2797ee2e Proper fix for previously reverted commit 2024-12-27 09:22:02 +01:00
Lwmte
be627f1005 Revert "Fixed rare case of not being able to start a new game or exit game from main menu"
This reverts commit 89183bbb4e.
2024-12-27 05:06:57 +01:00
Lwmte
89183bbb4e Fixed rare case of not being able to start a new game or exit game from main menu 2024-12-27 01:29:22 +01:00
Lwmte
0c4402468b Update ShaderManager.cpp 2024-12-26 22:23:33 +01:00
Lwmte
2d9f34901b Fixed SMAA shaders recompiling with another filenames 2024-12-26 22:14:47 +01:00
Lwmte
315c5ebbf7 Update CHANGELOG.md 2024-12-26 19:02:12 +01:00
Lwmte
0bc6648c78 Fixed #1539 2024-12-26 19:00:43 +01:00
Lwmte
d028a5ffd0 Fixed #1535 2024-12-26 18:52:24 +01:00
Lwmte
9100406a23 Update CHANGELOG.md 2024-12-26 16:45:55 +01:00
Lwmte
74401de755 Fixed #1540 2024-12-26 16:43:51 +01:00
Lwmte
c67e777271 Add bounds check for shaders 2024-12-26 16:13:41 +01:00
Lwmte
8fb4668dfc Update CHANGELOG.md 2024-12-26 11:19:19 +01:00
Lwmte
575bf6cfb1
Shader manager (#1534)
* Squashed commit of the following:

commit 0543f9146a
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 25 13:49:12 2024 +0100

    Fix bad merge

commit 529ffb50dd
Merge: 329bb52ec a1c76af54
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 25 13:47:26 2024 +0100

    Merge branch 'develop' into realtime_shader_reloading

commit 329bb52ec7
Merge: 3f749b485 ac800ff1e
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 25 13:40:38 2024 +0100

    Merge branch 'develop_mirrors' into realtime_shader_reloading

commit ac800ff1e1
Author: Sezz <sezzary@outlook.com>
Date:   Wed Dec 25 18:23:26 2024 +1100

    Final formatting pass

commit c2b2756725
Author: Sezz <sezzary@outlook.com>
Date:   Wed Dec 25 18:22:59 2024 +1100

    Update RendererFrame.cpp

commit d32eeac7ab
Author: Sezz <sezzary@outlook.com>
Date:   Wed Dec 25 18:15:45 2024 +1100

    Reapply "Formatting pass"

    This reverts commit becd24da93.

commit becd24da93
Author: Sezz <sezzary@outlook.com>
Date:   Wed Dec 25 18:12:43 2024 +1100

    Revert "Formatting pass"

    This reverts commit 88a8ba24de.

commit 88a8ba24de
Author: Sezz <sezzary@outlook.com>
Date:   Wed Dec 25 17:35:39 2024 +1100

    Formatting pass

commit a56d487e30
Author: Jakub <80340234+Jakub768@users.noreply.github.com>
Date:   Wed Dec 25 00:31:11 2024 +0000

    Update CHANGELOG.md

commit 86aad7c7b1
Merge: daa65d4e3 e3ecc3acd
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 23:22:50 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit daa65d4e3c
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 20:46:01 2024 +0100

    Update CHANGELOG.md

commit 58fc1a8ea0
Merge: 63ffb77bd ee867fb9d
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 20:15:28 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit 63ffb77bd6
Merge: b3195ae83 3ea8978c6
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 20:07:53 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit b3195ae83f
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 20:01:11 2024 +0100

    Update RendererInit.cpp

commit 3f749b4856
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 09:10:07 2024 +0100

    Reload shaders on graphic settings change

commit f04dcd42cc
Merge: 63539e4f9 1118b2fb2
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 08:57:13 2024 +0100

    Merge branch 'develop_mirrors' into realtime_shader_reloading

commit 1118b2fb22
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 08:47:33 2024 +0100

    Disable self-shadowing for now, as it's causing visual glitches

commit 63539e4f98
Merge: ca25f8773 32f94c2f2
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 01:49:18 2024 +0100

    Merge branch 'develop_mirrors' into realtime_shader_reloading

commit 32f94c2f2a
Merge: e6f4e9e24 c757422f2
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 01:35:17 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit e6f4e9e24b
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 01:07:20 2024 +0100

    Implement proper soft shadows

commit 9fbcc7b8cf
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 00:52:32 2024 +0100

    Fixed silent crash if several Lara objects are present in level near dynamic light with shadow

commit ca25f87738
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 24 00:51:11 2024 +0100

    Work

commit 12dae8df99
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 22 22:55:50 2024 +0100

    Fixed #1524

commit 9df39696a2
Merge: 724953329 6d38bb361
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 22 20:45:10 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit 7249533290
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 22 20:42:06 2024 +0100

    Fixed postprocessing order and precompile shaders to speed-up loading

commit b75d14184f
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 22 19:57:12 2024 +0100

    Fix #1525

commit bcf4dc5497
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 22 16:11:53 2024 +0100

    Interpolate Lara flare light, hide target highlighters in binos

commit 9e923a36bb
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 22 15:06:27 2024 +0100

    Fixed #1522

commit 1de0d48d58
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Dec 21 22:07:46 2024 +0100

    Fixed #1521

commit 7076014c9b
Merge: af097017f b62c8d7c4
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Dec 21 20:03:47 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit af097017f5
Merge: 4ed353000 04b659b41
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Dec 21 19:32:07 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit 4ed353000d
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Dec 21 12:20:05 2024 +0100

    Update title.bin

commit 7a3aea07fa
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Dec 21 11:26:49 2024 +0100

    Rename CamOldPos and use more consistent type for it

commit 1748eef34a
Merge: 839f05b3d 0ae388845
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Dec 21 10:25:23 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit 839f05b3d1
Merge: e4fe73ffd d75bba698
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Dec 21 01:05:36 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit e4fe73ffd5
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Dec 21 00:02:53 2024 +0100

    Don't perform binocular animations if binocular meshswap is not present

commit 3f7942eb76
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Dec 20 23:55:15 2024 +0100

    Fix meshswap, if object is not present

commit 40cfaaeb0d
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Dec 20 23:43:27 2024 +0100

    Fixed various binocular / lasersight issues

commit 1ef9a0e7e6
Merge: ca578885a 64c177702
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Dec 19 22:36:14 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit ca578885a2
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Thu Dec 19 12:19:07 2024 +0100

    Fixed 3D non-sorted sprites mirroring

commit 288a413400
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Dec 19 08:59:37 2024 +0100

    Continuation of previous fix

commit 60cfe1e2f4
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Dec 19 08:47:24 2024 +0100

    Fix Lara not mirroring when using binoculars

commit 2e17f7f8d9
Merge: a6687654b 625131fbb
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Dec 19 01:40:19 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit a6687654b0
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 22:44:00 2024 +0100

    Fixed mirrors on savegame reloading

commit 22460e0edf
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 17:01:19 2024 +0100

    Update CHANGELOG.md

commit 842d6345fe
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 16:40:25 2024 +0100

    Add option to reflect sprites

commit ab79156bfe
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 15:37:25 2024 +0100

    Update RendererDraw.cpp

commit 8ba6a2c286
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 14:56:24 2024 +0100

    Spawn mirrored dynamic lights in Lara rooms too

commit 1afa98169f
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 14:17:59 2024 +0100

    Rename

commit 6d195dbb63
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 13:35:47 2024 +0100

    Reorganize renderer code a little

commit 1f002745f3
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 13:14:08 2024 +0100

    Rename fields for consistency

commit 79a8db0655
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 12:51:17 2024 +0100

    Remove lambdas

commit b586dce76e
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 12:36:58 2024 +0100

    Remove unrelated changes

commit 8cebdae0ef
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 12:23:23 2024 +0100

    Draw reflections also when Lara is in any of mirrored rooms, not only camera

commit 38b210013f
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 11:55:00 2024 +0100

    Fixed 3D sprites mirroring

commit 2705a5ae7b
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 11:09:56 2024 +0100

    Update RendererDraw.cpp

commit 14f6ab36d0
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 10:57:59 2024 +0100

    Fixed dynamic lights

commit 85469cb4d4
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 10:18:04 2024 +0100

    Fixed billboards, reintroduce ReflectVectorOptionally

commit 3535eddf5d
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 03:12:13 2024 +0100

    Remove unnecessary inline, dont reflect room lights

commit bf6ef08c57
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 02:44:49 2024 +0100

    More decopypasting, rename inline function, remove unneeded arg

commit 569644f1d4
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 02:28:31 2024 +0100

    Decopypaste more mirror-related code

commit 60a59caa53
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 01:09:47 2024 +0100

    Update level.cpp

commit 5dd8db198c
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Dec 18 01:07:10 2024 +0100

    Simplify renderer code, remove excessive if statements

commit 31d278a780
Merge: f09b81fc8 88bbe4862
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Dec 17 19:48:18 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit f09b81fc87
Merge: 3e6e9cb61 ab7e25aa7
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Sun Dec 15 06:56:26 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit 3e6e9cb61e
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Sun Dec 15 06:56:05 2024 +0100

    Added logic for hiding selectively reflecting moveables and statics;
    Added enabled flag for disabling mirrros in the future with LUA;

commit c5f10777e7
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Fri Dec 13 05:20:23 2024 +0100

    Fixed random statics positions while shooting

commit 8978a51bfe
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Fri Dec 13 04:51:07 2024 +0100

    Fixed ambient occlusion in mirrored rooms

commit f3afaf47cf
Merge: 4e08942d8 6198975be
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Thu Dec 12 06:10:39 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit 4e08942d86
Merge: 265283aac e674b8526
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Wed Dec 11 13:43:19 2024 +0100

    Merge branch 'develop' into develop_mirrors;
    Removed RendererMirror references in methods calls;

commit 265283aacf
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Wed Dec 11 04:35:20 2024 +0100

    Cached reflection matrix of mirrors at load time;
    Fixed lighting in mirrored items;
    Added bad cull mode after mirrore debrises drawing;
    Added mirroring of dynamic lights;

commit f44a04d057
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Tue Dec 10 14:57:00 2024 +0100

    Fixed items culling in mirror rooms;
    Changed file format for having also virtual room;

commit 74c74042d5
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 8 21:41:51 2024 +0100

    Use emplace_back and camera room number

commit 41b16e5696
Merge: 39f0c131c 9a241dbc8
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 8 21:07:44 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit 39f0c131c3
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Dec 8 21:02:00 2024 +0100

    Use default argument value for ReadCount

commit c4330a777c
Merge: 374abb27d d96bf4ba8
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Sat Dec 7 05:03:24 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit 374abb27d3
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Sat Dec 7 05:02:09 2024 +0100

    Implemented mirrors in file format

commit 3e89391776
Merge: ec6ab6413 f9f1f59eb
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Fri Dec 6 15:10:05 2024 +0100

    Merge branch 'develop' into develop_mirrors

commit ec6ab64131
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Fri Dec 6 15:08:54 2024 +0100

    Added debris and effects drawing in mirrored rooms;
    Some optimizations for early skip non mirrored items;

commit 2218dd3179
Merge: 92eb4af79 2ee37ea7d
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Thu Dec 5 11:54:59 2024 +0100

    Merge branch 'develop' into develop_mirrors;
    Added correct lights to reflected items;

commit 92eb4af796
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Thu Dec 5 11:19:11 2024 +0100

    Fixed broken previous commit

commit bb3d0b993b
Author: MontyTRC89 <montyhammet@hotmail.it>
Date:   Thu Dec 5 10:20:32 2024 +0100

    WIP TR4 style mirrors

* Code etiquette

* Recompile AA shaders on graphics settings change

* Fixed code under ifdef

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-12-26 11:48:14 +02:00
Lwmte
6105f707db Update RendererFrame.cpp 2024-12-26 10:52:57 +01:00
Lwmte
a9af06e2fa Revert light collector changes which caused static mesh lights to break 2024-12-26 10:52:13 +01:00
Sezz
e888b9d7e1 Move light effect functions to new file; lara_optics.cpp/h -> Optics.cpp/h; Trigger -> Spawn prefix for light effect functions 2024-12-26 15:31:43 +11:00
Jakub
749d6626f9
include description of OnUseItem and OnFreeze 2024-12-26 00:05:24 +00:00
Jakub
7f064da0d6
Merge pull request #1531 from MontyTRC89/develop
Update CHANGELOG.md
2024-12-25 11:14:18 +00:00
Jakub
a1c76af546
Update CHANGELOG.md 2024-12-25 11:13:17 +00:00
Jakub
ff62122c56
Merge pull request #1530 from MontyTRC89/develop
Develop to TEN (new release)
2024-12-25 11:10:59 +00:00
Lwmte
74499ccef2 Revert variable shadow softness for performance reasons 2024-12-25 11:27:58 +01:00
Lwmte
d0527bf2dd Remove GetNextLevel, as it is broken 2024-12-25 09:32:34 +01:00
Lwmte
222177f45b
Mirrors (#1519)
* WIP TR4 style mirrors

* Fixed broken previous commit

* Added debris and effects drawing in mirrored rooms;
Some optimizations for early skip non mirrored items;

* Implemented mirrors in file format

* Use default argument value for ReadCount

* Use emplace_back and camera room number

* Fixed items culling in mirror rooms;
Changed file format for having also virtual room;

* Cached reflection matrix of mirrors at load time;
Fixed lighting in mirrored items;
Added bad cull mode after mirrore debrises drawing;
Added mirroring of dynamic lights;

* Fixed ambient occlusion in mirrored rooms

* Fixed random statics positions while shooting

* Added logic for hiding selectively reflecting moveables and statics;
Added enabled flag for disabling mirrros in the future with LUA;

* Simplify renderer code, remove excessive if statements

* Update level.cpp

* Decopypaste more mirror-related code

* More decopypasting, rename inline function, remove unneeded arg

* Remove unnecessary inline, dont reflect room lights

* Fixed billboards, reintroduce ReflectVectorOptionally

* Fixed dynamic lights

* Update RendererDraw.cpp

* Fixed 3D sprites mirroring

* Draw reflections also when Lara is in any of mirrored rooms, not only camera

* Remove unrelated changes

* Remove lambdas

* Rename fields for consistency

* Reorganize renderer code a little

* Rename

* Spawn mirrored dynamic lights in Lara rooms too

* Update RendererDraw.cpp

* Add option to reflect sprites

* Update CHANGELOG.md

* Fixed mirrors on savegame reloading

* Fix Lara not mirroring when using binoculars

* Continuation of previous fix

* Fixed 3D non-sorted sprites mirroring

* Fixed various binocular / lasersight issues

* Fix meshswap, if object is not present

* Don't perform binocular animations if binocular meshswap is not present

* Rename CamOldPos and use more consistent type for it

* Update title.bin

* Fixed #1521

* Fixed #1522

* Interpolate Lara flare light, hide target highlighters in binos

* Fix #1525

* Fixed postprocessing order and precompile shaders to speed-up loading

* Fixed #1524

* Fixed silent crash if several Lara objects are present in level near dynamic light with shadow

* Implement proper soft shadows

* Disable self-shadowing for now, as it's causing visual glitches

* Update RendererInit.cpp

* Update CHANGELOG.md

* Update CHANGELOG.md

* Formatting pass

* Revert "Formatting pass"

This reverts commit 88a8ba24de.

* Reapply "Formatting pass"

This reverts commit becd24da93.

* Update RendererFrame.cpp

* Final formatting pass

---------

Co-authored-by: MontyTRC89 <montyhammet@hotmail.it>
Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2024-12-25 10:21:07 +02:00
Jakub
4e41bb97de
Update CHANGELOG.md 2024-12-25 00:30:06 +00:00
Lwmte
e3ecc3acdb Update docs, fix savegame formatting, bump version 2024-12-24 23:22:02 +01:00
Lwmte
ee867fb9dc Don't try to draw debug box in non-debug mode 2024-12-24 20:15:12 +01:00
Lwmte
3ea8978c67 Rename traveledDistance to distanceTraveled 2024-12-24 20:04:13 +01:00
Sezz
96f41ef9e8 Draw interaction box in collision stats debug page 2024-12-24 13:13:45 +11:00
Lwmte
c757422f2d Fixed #1526 2024-12-24 01:34:36 +01:00
Lwmte
6d38bb3617 Prevent log spam with fish swarm 2024-12-22 20:42:42 +01:00
Lwmte
b62c8d7c40 Minor changes 2024-12-21 20:01:55 +01:00
Lwmte
04b659b410 Revert joint connection code, fix compass needle 2024-12-21 19:31:24 +01:00
Lwmte
0620e83125 Don't give LDoc warning on compile 2024-12-21 12:40:24 +01:00
Lwmte
0ae3888456 Expose different animation slots to scripting API 2024-12-21 10:09:00 +01:00
Lwmte
d75bba698f Fixed Electricity Wires object not doing instant kill when Lara is in close proximity 2024-12-21 01:02:50 +01:00
Lwmte
13bad20803 Fixed #1514, #1515, #1516 2024-12-21 00:31:10 +01:00
Lwmte
64c1777023 Use level time for volume timestamps, as using GlobalCounter may be inconsistent 2024-12-19 22:34:02 +01:00
Lwmte
625131fbb7 Use O1 optimizations 2024-12-19 01:26:39 +01:00
Lwmte
078a2ab07b Remove unsafe reference usage 2024-12-19 00:58:48 +01:00
Lwmte
88bbe48621 Fixed infinite loop if bridges with zero thickness exist in level 2024-12-17 01:39:11 +01:00
Lwmte
dad63f98c5 Fixed climbable pushables clipping Lara under the bridges when pulled 2024-12-17 01:17:02 +01:00
Lwmte
db717658fc Added references to Type module 2024-12-16 21:33:37 +01:00
davidmarr
e9f057eb64
Add Type module (#1506)
* Update VolumeObject.cpp

fixed Volume:GetActive() method

* Update CHANGELOG.md

* function description LevelFuncs.OnUseItem

* Revert "function description LevelFuncs.OnUseItem"

This reverts commit 2478afca68.

* Add Type module

Allows to check the data type of a variable

* Update Type.lua

update description

* Update Type.lua

* Update Type.lua

* Update Type.lua

* Update Type.lua

* Update Type.lua
2024-12-16 22:25:17 +02:00
Lwmte
0bf151b8cf Update CHANGELOG.md 2024-12-16 21:30:04 +01:00
Lwmte
86161f0922 Update Time.cpp 2024-12-16 21:23:26 +01:00
Lwmte
5ba92095f6 Fixed secrets display 2024-12-16 20:56:10 +01:00
Lwmte
ae79247979 Update docs 2024-12-16 08:10:26 +01:00
Lwmte
117a4962b3 Update WeaponTypes.h 2024-12-16 08:09:55 +01:00
Lwmte
f0ec62b888 Don't pollute native game enum with script-only entries 2024-12-16 07:48:27 +01:00
Sezz
be820cac3d Some cleanup 2024-12-16 15:14:42 +11:00
Lwmte
0abb8981ba Add separate binoculars/lasersight camera types, correct default gunflash ranges 2024-12-16 01:11:33 +01:00
Lwmte
51a6b741db Update CHANGELOG.md 2024-12-16 00:08:07 +01:00
Lwmte
8d08a9feb5 Move statistics back to TEN classes 2024-12-15 23:59:31 +01:00
Lwmte
63a5464d00 Clarify documentation 2024-12-15 23:50:41 +01:00
Lwmte
773c5bb7bb Remove obsolete mirror class, more cleanups in documentation 2024-12-15 23:47:59 +01:00
Lwmte
becadbf046 Fixed time reference 2024-12-15 23:40:58 +01:00
Lwmte
f97d257fd1 Add optics camera type, big enum cleanup and doc formatting 2024-12-15 23:34:33 +01:00
Lwmte
4f66f24bbd Return flyby camera type, if flyby is active 2024-12-15 21:21:55 +01:00
Lwmte
f8dc369ccb Update savegame.cpp 2024-12-15 19:49:10 +01:00
Lwmte
050142d3b6 Save time variables in savegames 2024-12-15 19:45:13 +01:00
Lwmte
f4763bd4a2
Customize global hardcoded parameters (#1495)
* Initial commit

* Update CHANGELOG.md

* Tint flare smoke

* Expose GetCustomizations

* Added lensflare and flicker customization options for flare

* Update LensFlare.cpp

* Remove unnecessary code

* Update lara_flare.cpp

* Massive refactor to merge animations, settings and customizations

* Add HUD customization options

* Customize weapons

* Fixed flare, renamed recoil to interval, fixed lensflare default

* Occlude flare lensflares

* Update Settings.cpp

* Use alternate damage for lasersight mode

* Added hair cust

* Fix comment

* Fix another comment

* Fix link

* Fix placeholder table names

* Reorganize types

* Add missing initializers for hair settings

* Added physics cust

* Clarify description

* Update settings.lua

* Update CHANGELOG.md

* Add gun smoke, gun shells and ammo pickup counts

* Fix naming ambiguity

* Remove missing features from documentation

* Fix comment

* Fix parameter name, change default settings file

* Fixed pitch black

* Rollback DoDistanceFogForVertex

* Add camera cust

* Change binocular/lasersight toggle to color

* Update lara_basic.cpp

* Add time and statistics classes and script API for it

* Fix comment

* Use DoDamage on Lara helpers to register with statistics

* Update Time.cpp

* Fix documentation

* Fix default flare timeout

* Update Settings.lua

* Add flare muzzle offset customization

* Remove young Lara limitations

* Fix lasersight color

* Push full settings.lua

* Update RendererCompatibility.cpp

* Allow to customize root meshes, decopypaste hair and joint init code

* Added sol Time operator overloads

* Some changes to docs, add meaningful error for unknown fields

* Use existing new index template, add gunflash color settings, add shotgun muzzle

* Remove excessive usage of GetSettings()

* Cleanups

* Update Settings.lua

* Clarify parameter name

* Fix InitializeWeaponInfo

* PR review code tidying

* Fix bad merge

* Update FlowHandler.cpp

* Remove tabs for LDoc comments

* Use different comment style to preserve formatting

* Update lara_fire.cpp

* Some cleanups

* Fixed GetTimeUnits

* Fix typo

* Update Time.cpp

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-12-15 18:24:49 +02:00
Sezz
ab7e25aa7a Pass position reference instead of copy when collecting lights 2024-12-15 00:19:57 +11:00
Lwmte
19528ea3bd Fix hair vertices linking incorrectly in some cases 2024-12-14 12:25:50 +01:00
Lwmte
2f6114deba Disable camera interpolation when going from/to binoculars 2024-12-13 09:08:09 +01:00
Lwmte
6198975bea Don't unroll float loops, as they slow down game launch 2024-12-12 01:31:10 +01:00
Lwmte
bddc52f632 Optimize debris drawing 2024-12-11 19:04:41 +01:00
Lwmte
4fe20b2a92 Fix moving bridges again without breaking bridge tilts 2024-12-11 16:50:37 +01:00
Lwmte
e674b85268 Add puzzle/key items in DOZY mode only if shift is held 2024-12-11 01:34:54 +01:00
Lwmte
3e5a3609cc Fix #1508 2024-12-11 01:06:31 +01:00
Lwmte
c7a1440131 Adjust room lighting influence 2024-12-10 22:53:31 +01:00
Lwmte
11d9661d6b
Renderer enhancements (#1507)
* Implement light masking, merge shadow functions, precalculate radians for spotlights

* Update CHANGELOG.md

* Fixed spotlight shadow calculation

* Disable shadow pass if it is disabled in settings

* Move all step functions into appropriate mask blocks
2024-12-10 23:11:53 +02:00
Lwmte
0a2d82ab11 Revert "Less frequent updates to CalculateBounce"
This reverts commit 8d20d6f1ca.
2024-12-10 03:01:23 +01:00
Lwmte
8d20d6f1ca Less frequent updates to CalculateBounce 2024-12-09 09:40:57 +01:00
Lwmte
45f515c444 Update savegame.cpp 2024-12-09 02:59:39 +01:00
Lwmte
a71cf7bced Prevent loading of incorrect savegames 2024-12-09 02:46:26 +01:00
Lwmte
8da4d36b54 Update CHANGELOG.md 2024-12-09 01:37:06 +01:00
Lwmte
5df743ac9b Allow camera bounce during flybys 2024-12-09 01:36:50 +01:00
Lwmte
b582601abf Fixed original issue with weapon hotkeys available in binoculars or lasersight mode 2024-12-08 22:40:34 +01:00
Lwmte
9a241dbc80 Fix issues with timer 2024-12-08 19:15:26 +01:00
Lwmte
59ebbc9e11 Debranch vertex effects 2024-12-07 22:44:26 +01:00
Lwmte
d90c98cc60 Rollback DoDistanceFogForVertex 2024-12-07 21:59:23 +01:00
Lwmte
5d293cc75a Fix compile errors with latest VS 2024-12-07 13:09:12 +01:00
Lwmte
d96bf4ba8c Debranch fog calculations 2024-12-06 21:53:45 +01:00
Lwmte
15aee1c5b0 Don't collect shadow casting lights of unsupported types 2024-12-06 21:04:53 +01:00
Lwmte
87373b4c78 Fixed black rooms in case brightest light is not point or spot 2024-12-06 20:36:46 +01:00
Lwmte
f9f1f59eba Fix blob shadows not showing 2024-12-06 00:29:37 +01:00
Lwmte
4a76487620 Update ShaderLight.hlsli 2024-12-06 00:18:29 +01:00
Lwmte
e6dbd363b6 Remove branching in some light functions 2024-12-05 23:46:23 +01:00
Lwmte
936d2ff98e Additional shader optimizations 2024-12-05 22:07:16 +01:00
Lwmte
2ee37ea7de Remove branching from ShaderLight as well 2024-12-05 08:59:57 +01:00
Lwmte
d6cf25f16e Remove light calculation branching which caused extreme slowdowns 2024-12-05 08:49:10 +01:00
l.m. (Leif Melles)
4f2a0b0554
Fixed SetIntroImagePath() (#1498)
* Fixed SetIntroImagePath()

Fixed SetIntroImagePath() not using the correct path

* Update RendererDrawMenu.cpp

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2024-12-05 03:27:26 +02:00
Lwmte
e17080c95c Draw HUD and display sprites only when game status is normal 2024-12-04 20:58:05 +01:00
Lwmte
ecddc1a330 Fixed #1491 2024-12-04 02:59:50 +01:00
Lwmte
a2681cb0d3 Fix error for infinite strings showing without extra args 2024-12-04 01:21:17 +01:00
Lwmte
8679e63b89 Rollback room point light formula 2024-12-03 00:12:59 +01:00
Lwmte
1f1d3b2aad Delete old documentation when recompiling 2024-12-02 23:57:17 +01:00
Lwmte
9642118cfa Fixed #1499 2024-12-02 02:56:09 +01:00
Lwmte
dbaaf9fd5e Fixed #1500 2024-12-02 02:48:28 +01:00
Lwmte
364458d68a Update RendererDraw.cpp 2024-12-02 01:19:05 +01:00
Lwmte
bc25908726 Simplify and optimize light collector 2024-12-01 23:47:19 +01:00
Lwmte
bc20b9194f Remove double light calculations from the room shader 2024-12-01 23:43:22 +01:00
Lwmte
5ab627af83 Revert unneeded code, as display adapter name is showing in debug page 2024-12-01 22:31:56 +01:00
Lwmte
dc0b746c31 Fixed BitField class bottleneck 2024-12-01 22:27:33 +01:00
Lwmte
8edd446a6d Simplify light and shadow light collection 2024-11-30 18:43:39 +01:00
Lwmte
a4d620d7fd Rollback ShaderLight 2024-11-29 01:08:16 +01:00
Lwmte
12ea734bb7 Fixed #1493 2024-11-29 00:08:30 +01:00
Lwmte
c0a93612ae
Dynamic spotlight (#1488)
* Initial commit

* Update CHANGELOG.md

* Provide helper method to convert rotation to directional vector

* Remove unneeded arguments, add missing function registration

* Fixed direction

* Fixed incorrect behaviour of Moveable:GetJointRotation() function

* Add optional castShadow flag to light functions

* Fixed spotlight shadows

* Cleanup

* Fix spotlight shadows again

* Interpolate dynamic lights, if unique ID is given

* Use name instead of unique ID

* Update docs

* Update CHANGELOG.md

* Fix incorrect variable type

* Update CHANGELOG.md
2024-11-28 23:09:29 +02:00
Lwmte
db7f864858 Fix savegames not loading from menus 2024-11-27 22:36:10 +01:00
Lwmte
554661d913 Fix problems with lensflares not appearing in binocular/lasersight mode 2024-11-27 16:32:19 +01:00
Lwmte
4f09ca48f8 Fixed #1490 2024-11-25 03:30:02 +01:00
Lwmte
ab9debfb94 Add anamorphic glare to a lensflare 2024-11-24 18:09:18 +01:00
Lwmte
0395adfdc9 Tint sun sprite according to lensflare color 2024-11-24 13:59:11 +01:00
Lwmte
27d3ca7bb9 Update sound.cpp 2024-11-24 13:26:51 +01:00
Lwmte
19be42911c Revert "Add simple safety checks to RNG"
This reverts commit 202c94f02e.
2024-11-24 13:20:56 +01:00
Lwmte
a74bb4e584 Fix debris garbage frame on 60 FPS 2024-11-24 13:13:17 +01:00
Lwmte
4e934ee050 Remove unnecessary hardcoded default sound effect condition 2024-11-24 13:13:17 +01:00
Sezz
202c94f02e Add simple safety checks to RNG 2024-11-24 21:28:07 +11:00
Lwmte
e5e625d78b Restore ldoc comment 2024-11-23 14:22:23 +01:00
Lwmte
9f8ea9e13c Revert "Changed/Fixed enemy jeep behaviour (#1395)"
This reverts commit 267fecb063.
2024-11-23 02:40:13 +01:00
Lwmte
a7a28285ba Move default radius discard to item control function 2024-11-22 20:53:29 +01:00
Lwmte
546a72c5c8 Implement OCB for lensflare objects which specifies falloff in sectors 2024-11-22 20:40:25 +01:00
Lwmte
df4a664020 Remove unused block 2024-11-22 20:24:51 +01:00
Lwmte
e093b07c8c Further optimizations to lensflare code 2024-11-22 20:24:40 +01:00
Lwmte
63277cc29d Update CHANGELOG.md 2024-11-21 18:40:35 +01:00
Lwmte
71f6cf5b64 Remove boulder death from special death anims, as it's causing troubles 2024-11-21 18:28:31 +01:00
Lwmte
b5fa51b2dc Recompile docs for 1.6, version update 2024-11-21 18:27:42 +01:00
Lwmte
3a649d612d Fix issues with lensflare occlusion 2024-11-21 17:25:15 +01:00
Lwmte
9fad7eec50 Fix mistake in documentation 2024-11-21 16:41:18 +01:00
Lwmte
f455dd1c91 Remove hardcoded ricochet sound condition 2024-11-21 16:11:04 +01:00
Lwmte
ddfeaf5fa2 Update CHANGELOG.md 2024-11-21 15:42:23 +01:00
Lwmte
1695f4eeb8 Update version.h 2024-11-21 15:24:12 +01:00
Lwmte
9e54c8dd0b Add safeguards in level reader to throw exception if data sizes are inconsistent 2024-11-21 15:17:54 +01:00
Lwmte
c484f8aa84 Enhanced lensflare effects, fix ray test, discard flares behind camera 2024-11-21 14:55:59 +01:00
Lwmte
81a8922f1d Fixed global lensflare distance calculations, add object culling, add fade on visibility 2024-11-21 12:14:19 +01:00
Lwmte
1267effe08 Fixed Util.HasLineOfSight not accounting for statics 2024-11-21 11:10:22 +01:00
Lwmte
86cee87ecd Bump version 2024-11-21 02:41:54 +01:00
Lwmte
64b100561a Don't pollute DumpGameScene with unrelated flag 2024-11-21 02:09:15 +01:00
Lwmte
85a456a4f1 Update CHANGELOG.md 2024-11-21 00:11:40 +01:00
Lwmte
bdf67b75dc
Decoupled sound conditions (#1484)
* Convert sound conditions to a new format

* Convert animcommands to int

* Update flipeffect.cpp

* Update winmain.cpp
2024-11-21 01:02:00 +02:00
Lwmte
3bf00ad18c Implement StaticHandler class to properly handle LUT 2024-11-20 23:00:05 +01:00
Lwmte
91081c9f0d
Freeze event and OnUseItem callbacks (#1463)
* Add use item callbacks, add menu boilerplate code

* Set up scripting API for game mode

* Use word "break" instead of "menu" everywhere

* Rudimentary implementation of BreakPhase

* More progress

* Doc update

* Always active lensflare

* Update CHANGELOG.md

* Fix hair interpolation for spectator mode

* Update target highlighter and other HUD elements while in break mode

* Fix various problems

* Don't try to update camera in spectator mode

* Cleanup camera code

* Rename break to freeze

* Update FlowHandler.cpp

* Update Flow.html

* Update control.cpp

* Rename method

* Add displaystring owner to differentiate between freeze modes

* Disable triggers and volumes in freeze mode

* Update control.cpp

* Rename enum

* Update CHANGELOG.md

* Implement full freeze, correct callback behaviour

* Update RendererDrawMenu.cpp

* Allow DumpGameScene to do partial pass

* Remove hack

* Introduce render mode enum to render scene without any postprocess

* Tidying

* Fix problems with items or renderer not updating before entering freeze mode

* Fix rebase errors

* Use interpolation factor getter instead of raw value everywhere

* Update documentation

* Fixed setting freeze mode from OnStart

* Fixed freeze mode not working in OnStart event

* Revert "Fixed freeze mode not working in OnStart event"

This reverts commit 87f6fbdc6d.

* Bypass cinematic bars and fades for non-full render modes

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2024-11-20 20:54:39 +00:00
Sezz
88f21a98d4
Update CONTRIBUTING.md 2024-11-20 21:51:22 +11:00
MontyTRC89
116c3d71fc Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-11-20 09:09:31 +01:00
MontyTRC89
1de0f27511 Switched to LUT for statics 2024-11-20 09:09:23 +01:00
Lwmte
f218644703 Update CHANGELOG.md 2024-11-20 08:21:56 +01:00
Joey Quint
db68eeaca3
Expose wind (#1465)
* Expose wind

* Update EffectsFunctions.cpp

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-11-20 09:15:26 +02:00
Lwmte
2d0d13c620 Implement GetCollidable and SetCollidable lua methods for moveables 2024-11-20 08:18:27 +01:00
MontyTRC89
e3510c3204 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-11-20 05:59:14 +01:00
MontyTRC89
e7fd154fc9 Fixed dual paraboloid scene rendering in prevision of adding better ambient lighting 2024-11-20 05:59:04 +01:00
Sezz
d070bc756f Use Quaternion::Identity 2024-11-19 15:48:13 +11:00
Lwmte
545378d967 Update CHANGELOG.md 2024-11-19 01:38:16 +01:00
Lwmte
534ac3876a Fixed occasional crashes if there are static meshes placed within room border walls 2024-11-18 23:32:35 +01:00
Lwmte
9ea15571a8 Fixed quaternion conversion crash if 2 hair joints share the same offset 2024-11-18 20:48:20 +01:00
Sezz
483ead3203 Fix missed illegal -> steep rename 2024-11-18 22:12:15 +11:00
Sezz
231bea0c1e Remove MAX_STATICS constant 2024-11-18 19:31:28 +11:00
Sezz
57a151af2a "Num" prefix -> "count" suffix 2024-11-18 19:24:57 +11:00
Sezz
f9c25a1862 Remove "Num" prefix from level load logs 2024-11-18 18:58:32 +11:00
Sezz
cfbba4b741 Don't create static object copies in loops; readability 2024-11-18 17:38:21 +11:00
MontyTRC89
bb975cda58 Fixed changelog 2024-11-18 06:27:39 +01:00
MontyTRC89
59139b373c Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-11-18 06:25:49 +01:00
MontyTRC89
68114b03e2 Removed statics limit 2024-11-18 06:25:43 +01:00
Lwmte
b41d591d93 Fixed stutter during jumps between cameras in a flyby sequence 2024-11-18 01:49:42 +01:00
Lwmte
c380a74bb0 Fixed falling through tilted bridges 2024-11-18 01:23:34 +01:00
Lwmte
ab979b8487 Fix event status not saving 2024-11-17 12:43:52 +01:00
Lwmte
ec170b8aee Fixed pickup trigger for search object 4 as well 2024-11-17 11:00:43 +01:00
Lwmte
656158874c Another attempt at fixing #1483 2024-11-16 23:42:02 +01:00
Lwmte
540150a41a Revert "Fixed #1483"
This reverts commit 08137298cc.
2024-11-16 23:41:42 +01:00
Sezz
26adcee7b1 Slight floordata optimisation 2024-11-17 03:33:14 +11:00
Sezz
ef19187f83 Formatting 2024-11-16 22:19:32 +11:00
Sezz
2624c5bed2 Formatting 2024-11-16 22:07:57 +11:00
Lwmte
99a4c68417 Add GetNextLevel and make load game behaviour consistent with scripting API 2024-11-16 10:49:52 +01:00
Lwmte
7f4242521a Fixed crash on leveljump, if collision callbacks were assigned 2024-11-16 10:20:28 +01:00
Lwmte
3bdb0b367b Fixed #1481 2024-11-16 09:58:09 +01:00
Lwmte
08137298cc Fixed #1483 2024-11-16 04:35:20 +01:00
Lwmte
e02e1db1ba Fixed #1482 2024-11-16 03:15:22 +01:00
Lwmte
f39d2ce250 Fixed incorrect loading of dummy title file in case of real levels are missing 2024-11-16 02:07:56 +01:00
Lwmte
8ff1e5ed0d Fixed #1480 2024-11-16 00:56:50 +01:00
Sezz
040fd06afe Optimise Vector2i and Vector3i distance methods 2024-11-15 21:13:33 +11:00
Sezz
2ddd482a80 Correct sound conditions 2024-11-15 21:03:30 +11:00
Stranger1992
c74d110654 Removed orphaned header file
Moved ldoc comments .
2024-11-15 01:30:46 +00:00
Lwmte
eb0390a23b Fix default looped track fade-in 2024-11-15 02:18:12 +01:00
Stranger1992
d3e0cc30d8 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-11-15 00:53:11 +00:00
Stranger1992
3cb6eec261 Add Strings.DisplayString Enum page
Also:

* bumped up engine version with a developers tag.
* Increased margin of the menu to accommodate changes
2024-11-15 00:52:36 +00:00
Lwmte
ee6a661ed0 Move DumpGameScene call to appropriate place 2024-11-15 01:40:26 +01:00
Lwmte
03d93bcb2a Prioritize ogg format over mp3, bump version 2024-11-15 01:00:57 +01:00
Lwmte
74f1d2640e Update CHANGELOG.md 2024-11-15 00:25:09 +01:00
Lwmte
3737613b1a Fixes to previous commit 2024-11-15 00:16:48 +01:00
Lwmte
43fd531689 Reorganize game loop to support load cameras 2024-11-14 23:47:18 +01:00
Lwmte
ee3df1680b Fixed incorrect clipping of scaled static meshes 2024-11-14 22:47:56 +01:00
Lwmte
849e0557da Reduce load menu timeout on death to 15 game frames 2024-11-14 19:37:46 +01:00
Lwmte
8bb955cc08 Fix collision callbacks for Lara 2024-11-14 19:31:53 +01:00
Lwmte
d8b522343f Fixed DOZY memory corruption 2024-11-14 00:52:51 +01:00
Lwmte
5254cc1daf Update CHANGELOG.md 2024-11-13 23:59:00 +01:00
Lwmte
4aa1bb81d1 Hide dummy title level from user 2024-11-13 23:57:23 +01:00
Lwmte
8a82b0c891 Fixed DisplayString class not supporting empty lines in multiline strings 2024-11-13 23:31:29 +01:00
Lwmte
369e1ca967 Fixed GetTargetOnLOS returning incorrect results with fixed LOS function 2024-11-13 22:29:52 +01:00
Lwmte
09e354a33a Fixed dart emitter not deactivating 2024-11-13 01:14:27 +01:00
Lwmte
0ab2e40257 More correct way of initializing bridges on load game 2024-11-13 01:13:38 +01:00
Jakub
4761feb45d
Update CHANGELOG.md 2024-11-12 11:27:29 +00:00
Lwmte
3606f7637b Update CHANGELOG.md 2024-11-12 09:24:05 +01:00
Lwmte
0d8b83cf42 Fixed #1473 2024-11-12 09:20:28 +01:00
Lwmte
372afb4e8f Update CHANGELOG.md 2024-11-12 08:06:59 +01:00
Lwmte
cd30a9e5e8 Reallocate item renderer array according to real moveable count 2024-11-12 08:03:22 +01:00
Lwmte
5f884cd84c Autocreate /data directory for title level, if it does not exist 2024-11-12 08:02:05 +01:00
Lwmte
3e605e1dda Add safeguard against silent crashes if file format is incorrect 2024-11-12 08:02:05 +01:00
Sezz
d32c1350b7 Remove pointless loop 2024-11-12 16:12:29 +11:00
Sezz
890dd5720f Improve fly cheat 2024-11-12 16:01:38 +11:00
Lwmte
1f89c23537 Rename title.ten to title.bin 2024-11-11 16:11:31 +01:00
Lwmte
5db8e98611 Restore old condition for look camera as well 2024-11-11 16:01:49 +01:00
Lwmte
5f43caf7a2 Update version.h 2024-11-11 15:49:52 +01:00
Lwmte
84ac5652b9 Embed simple title level 2024-11-11 15:35:33 +01:00
Lwmte
8a10fba156 Update version.h 2024-11-11 14:04:50 +01:00
Lwmte
5825bdee09 Add more clean version management and test build warning 2024-11-11 13:32:52 +01:00
Lwmte
784f957596
Fast reload (#1445)
* Initial commit
* Fix crash on title
* Update level.cpp
* Update CHANGELOG.md
* Do slight audiotrack fade-ins and fade-outs on leveljumps
* Implement hash checks
* Fixes
* Rename rapid to fast
* Fixed flipmaps and bridge reinit
* Bypass reinitializing renderer for fast reload
* Fix issue when title and last loaded level are the same files
* Update CHANGELOG.md
* Additional fixes
* Wrap savegame loading code into a try-catch
* Update CHANGELOG.md
* Remove door collision on fast reload, restore stopper flag
* Display log message if level file was not found
* Update CHANGELOG.md
* Clear blocked flag for boxes
* Implement fast reload setting
* Add defaults to settings
* Consistent naming
* Smooth level loading audio crossfades
* Stop non-ambience soundtracks early
* Minor formatting
* Update Settings.lua 
---------

Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2024-11-11 12:55:50 +02:00
Lwmte
b79d662b13 Fixed bubbles performance 2024-11-11 11:48:59 +01:00
Lwmte
87b9c3caac Fixed #1451 2024-11-11 10:13:48 +01:00
Lwmte
78c0ba3ff0 Add ricochet sound and increase amount of particles 2024-11-11 09:40:34 +01:00
Lwmte
abf69f120e Fixed #1468 2024-11-11 09:10:54 +01:00
Lwmte
6c848dcfae Fixed #1467 2024-11-11 08:50:53 +01:00
Lwmte
d562b9f433 Less snappy behaviour when exiting look to chase camera 2024-11-11 08:31:36 +01:00
Lwmte
9494c999e7 Fixed incorrect camera movement near walls after leaving look mode 2024-11-11 08:04:58 +01:00
Lwmte
3b252848c5
Displaystring alpha (#1453)
* Support for alpha in display strings

* Update CHANGELOG.md

* Fix alpha

* Potentially fix problems with alpha blend
2024-11-10 18:23:17 +02:00
Lwmte
6eca0548c6 Exclude some ToBoundingOrientedBox conversions 2024-11-10 17:23:50 +01:00
Lwmte
f057770696 Fixed incorrect object camera position 2024-11-10 08:07:18 +01:00
Lwmte
908fc61283 Fix inventory item cancel 2024-11-09 23:12:29 +01:00
Lwmte
b5502103c0 Fix lensflare object 2024-11-09 18:59:22 +01:00
Lwmte
2b5e1cf448 Simplify action queue code, fix potential problem with scripted keypresses 2024-11-09 08:23:28 +01:00
Lwmte
718997336b Fix git merge error 2024-11-09 08:10:52 +01:00
Lwmte
ed434aaffe Update room.cpp 2024-11-09 00:13:27 +01:00
Lwmte
50a7fa3b17 Return false for IsPointInRoom for inactive flipped rooms 2024-11-09 00:06:40 +01:00
Lwmte
506ee0f088 Fixed #1459 2024-11-08 23:26:15 +01:00
Lwmte
06a70d364f Fixed #1455 2024-11-08 23:19:52 +01:00
Lwmte
222e67e493 Fixed weather particles performance 2024-11-07 23:44:31 +01:00
Sezz
43c8f8658b Improve code clarity 2024-11-07 19:36:54 +11:00
Lwmte
b8684a9587 Fixed enemy pickups dropping on death sectors 2024-11-07 08:23:58 +01:00
Lwmte
542597ada2 Don't use static variable for flyby interpolation, as it may be very slow 2024-11-07 00:24:37 +01:00
Lwmte
3153da6779 Change SpawnDustParticles method to avoid spamming bridge checks 2024-11-07 00:13:56 +01:00
Lwmte
516c340ac5 Add bridge result cache, only fetch nearby blocks for debug if debug page is CollisionStats 2024-11-07 00:13:19 +01:00
Lwmte
3897c4e02d Update CHANGELOG.md 2024-11-06 09:18:13 +01:00
Lwmte
efe34ad9c9 Faster bridge checks 2024-11-06 08:45:27 +01:00
Sezz
aafb59eaab Add forgotten argument 2024-11-04 18:43:19 +11:00
Sezz
1456b00907 Pass player global as argument 2024-11-04 15:41:44 +11:00
Stranger1992
d256fe1efe Update bug_report.yaml 2024-11-03 22:19:11 +00:00
Lwmte
710f04596b Merge branch 'develop' 2024-11-03 18:37:17 +01:00
Lwmte
0b5ea0db77 Update Resources.rc 2024-11-03 18:01:18 +01:00
Sezz
46d75a3e8f Fix water tread climbout take 2 2024-11-04 02:47:24 +11:00
Jakub
04da97479d
Update CHANGELOG.md 2024-11-03 14:19:01 +00:00
Jakub
1affcce9b9
Update CHANGELOG.md 2024-11-03 14:18:34 +00:00
Jakub
0712af9d5b
Update CHANGELOG.md 2024-11-03 14:17:12 +00:00
Jakub
fae8744e4b
Update CHANGELOG.md 2024-11-03 14:16:05 +00:00
Jakub
815498ec8a Merge branch 'develop' 2024-11-03 13:51:09 +00:00
Lwmte
c3365f3a38
Update README.md 2024-11-03 14:53:22 +02:00
Lwmte
7c50d26ca8 Delete unneeded resource file 2024-11-03 13:57:19 +01:00
Jakub
d81fce22ad Merge branch 'develop' 2024-11-03 12:35:58 +00:00
Lwmte
98d6463613 Delete TombEngine - Backup.vcxproj.user 2024-11-03 13:33:46 +01:00
Lwmte
82c2ba37cb Rebranding 2024-11-03 13:21:26 +01:00
Adngel
a962a17f66
Adngel fix death camera (#1449)
* Update function

* Adding comments.

* Function renamed

Function renamed and conditionals separated for clarity.

* Extracted Camera data modifications

Extracted commands that changes camera data, from the camera code, and moved them into the objects coded to follow similar logic with other cases (like enemies that change these values from their control code).

Leaving a simplified CalculateDeathCamera() function.

* Update tr5_rollingball.cpp

Not really needed, so I removed it to keep consistency with the other calls.

Also edited values adding the .0f for same reasons.
2024-11-03 14:08:59 +02:00
Lwmte
4aac07c0c4 Fixed binocular bugs 2024-11-03 12:11:49 +01:00
Lwmte
f51d499e68 Regenerate documentation 2024-11-03 11:39:16 +01:00
Lwmte
d72208cfb3 Remove another unnecessary flag assignment 2024-11-03 10:56:52 +01:00
Lwmte
4a8d66d077 Update Objects.ObjID.html 2024-11-03 10:54:23 +01:00
Lwmte
8a809877c4 Disable flyby camera interpolation in cases when it travels too far 2024-11-03 10:50:46 +01:00
Lwmte
83981b369e Bump version to reflect release state 2024-11-03 09:30:44 +01:00
Lwmte
856dccad1f Swap debug pages 2024-11-03 09:30:44 +01:00
Lwmte
f1f2d165c5 Fixed camera behaviour on sloped surfaces after player's death 2024-11-03 09:30:43 +01:00
Sezz
79679554e3 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-11-03 18:26:47 +11:00
Sezz
bc5df6af98 Format recent merges 2024-11-03 18:23:48 +11:00
Stranger1992
2b020b664d Fixed the CUSTOM_AMMO_GRAPHIC not appearing in the Lua API 2024-11-02 22:12:35 +00:00
Lwmte
eec17d9f30 Add comment 2024-11-02 20:03:51 +01:00
Lwmte
7d4e29ee07 Fixed room clipping when flyby path goes out of room bounds 2024-11-02 19:51:40 +01:00
Lwmte
45fca95385 Added recompiled doc files 2024-11-02 19:26:40 +01:00
Lwmte
78cefae27b Fixed broken ropes after loading a savegame 2024-11-02 19:26:22 +01:00
Stranger1992
7196210da9
Custom bars (#1432)
* Add Custom Bar Graphic slot

* Add ID_CUSTOM_AMMO_GRAPHIC slot

Used for nodes in Tomb Editor
2024-11-02 14:46:02 +02:00
Joey Quint
840b32fb71
Expose moveable's target state to Lua API (#1436) 2024-11-02 14:44:27 +02:00
Lwmte
c20cfffabd Bump version 2024-11-02 12:36:48 +01:00
Lwmte
35319cc811
Load after death (#1442)
* Initial commit

* Don't open load game dialog if no savegames are present

* Block empty load menu only only on death events

* Update CHANGELOG.md

* Add braces and define constant

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-11-02 13:30:24 +02:00
Sezz
132517993b Update hang swing in check 2024-11-02 18:55:41 +11:00
Lwmte
aac4316e71 Fixed incorrect swing ledge grabs with steep grab angles 2024-11-02 08:31:28 +01:00
Lwmte
d49d322766 Fix incorrect bridge thickness calculation 2024-11-02 07:24:52 +01:00
Sezz
e186f48b3f Conventions 2024-11-02 16:25:00 +11:00
Lwmte
8060cc8706
Update README.md 2024-11-01 23:37:00 +02:00
Lwmte
b82054f1ad Skip recompiling savegame.cpp, if flatbuffers schema files weren't changed 2024-11-01 21:45:51 +01:00
Lwmte
c6165da100 Update camera.cpp 2024-11-01 09:19:31 +01:00
Lwmte
c6e7fae88f Display portal corners and camera room number in portal debug mode, reduce depth to 1 2024-11-01 09:03:06 +01:00
Lwmte
f00576340a Always treat static meshes as solid when Lara is in the shimmy mode 2024-11-01 08:47:02 +01:00
Lwmte
00cc4b93a9 Move some text stats to portal debug page 2024-11-01 07:46:26 +01:00
Lwmte
55c5070453 Update camera.cpp 2024-11-01 00:31:10 +01:00
Lwmte
dbe1a59650 Debug portals 2024-11-01 00:17:23 +01:00
Lwmte
61e82c0033 Throw warning instead of error if level script was not found 2024-10-31 20:46:23 +01:00
Lwmte
3a928a99a9 Fix build action paths 2024-10-31 20:45:07 +01:00
Sezz
20c6c6f95c Reduce distance for water tread climb out check 2024-10-31 21:09:26 +11:00
Lwmte
cbcbde8e6d Update post-build events and automatically copy scripts 2024-10-31 00:36:44 +01:00
Nemoel-Tomo
30e053dc93
Update AUTHORS.md
- Tomo (general coding, special FX coding, bug fixing)
2024-10-30 18:00:21 +01:00
Lwmte
8afb19cdd0
Update pull_request_template.md 2024-10-30 16:14:47 +02:00
Lwmte
7f584c76aa
Update README.md 2024-10-30 12:30:06 +02:00
Lwmte
5b4b60fcda
Update README.md 2024-10-30 12:06:38 +02:00
Lwmte
e936507b52
Update README.md 2024-10-30 12:04:06 +02:00
Lwmte
9d31f26dd8 Update CHANGELOG.md 2024-10-30 10:42:55 +01:00
Lwmte
f7f347986b Decopypaste cam matrix update 2024-10-30 09:21:24 +01:00
MontyTRC89
1ba72e669c Merge branch 'develop_60fps' into develop 2024-10-30 06:19:10 +01:00
MontyTRC89
f9f727122b Fixed controls settings rendering 2024-10-30 06:17:32 +01:00
Lwmte
29dff9016c Rearrange control loop a little more 2024-10-30 00:28:35 +01:00
Lwmte
b3b8356731 Fix title flyby interpolation 2024-10-30 00:19:42 +01:00
Lwmte
6b35cae804 Fix garbage frame when switching from/to flyby camera 2024-10-29 21:44:37 +01:00
Lwmte
999c6e8314 Save camera position in a savegame 2024-10-29 21:16:46 +01:00
Lwmte
eb820fad63 Update CHANGELOG.md 2024-10-29 08:44:12 +01:00
Lwmte
21f42c0a4c Optimize GetCollidedObjects to avoid extra bound conversions 2024-10-29 08:43:38 +01:00
Lwmte
2c93dad6f0 Fixed display sprites and display strings rendering in the inventory background 2024-10-29 08:10:25 +01:00
MontyTRC89
91faf6406c Merge branch 'develop' into develop_60fps 2024-10-29 06:01:19 +01:00
MontyTRC89
5f974935b4 Fixed black screen and stretched strings after changing display settings in game. 2024-10-29 06:01:03 +01:00
Adngel
0faa11a882 Update collide_item.cpp
Updated non-collidable volumes check.
2024-10-28 15:05:50 +01:00
Adngel
0a8eb65de3 Fix Collision with no-collidable statics.
With this check, statics with Extents 0 in the three axis, will be ignored for the collision checks.
2024-10-28 12:28:16 +01:00
Sezz
1e89416098 Fix water tread climb out 2024-10-28 21:50:08 +11:00
Lwmte
4bbc30b0e5 Decopypaste camera interpolation 2024-10-28 07:46:28 +01:00
Lwmte
2ded586f1a Added visible mouse pointer in windowed mode 2024-10-28 00:29:39 +01:00
Lwmte
407ae147bd Reapply death block flag fix, because the bug was still present in 1.2 as well 2024-10-27 23:28:25 +01:00
Lwmte
1c2945de62 Revert "Fixed incorrect detection of death flag in L-shaped portal setups"
This reverts commit 20229be3b1.
2024-10-27 21:33:42 +01:00
Lwmte
20229be3b1 Fixed incorrect detection of death flag in L-shaped portal setups 2024-10-27 21:12:51 +01:00
Lwmte
23ab42cfa3 Fixed #1435 2024-10-27 20:21:47 +01:00
Lwmte
f85376ddca Fix garbage frames for newly drawn or triggered items 2024-10-27 18:41:54 +01:00
TokyoSU
5acb5f6483
Fixed bitwise for itemStatus (#1434)
* Fixed bitwise for itemStatus
* Update items.h
2024-10-27 18:55:54 +02:00
Adngel
317edbf5cb Fix underwater buoyant pushables.
Moved the lines:

pushableColl.FloorHeight = pointColl.GetFloorHeight();
pushableColl.CeilingHeight = pointColl.GetCeilingHeight();

Inside of the previous if, because if it uses bridge collision, this must be deactivated to avoid wrong results.

and "auto pushableColl = PushableCollisionData{};" was moved before the if so it were ready.
2024-10-27 13:00:55 +01:00
Lwmte
67c8e33570 Another attempt at fixing item status 2024-10-27 11:14:16 +01:00
Lwmte
627a30dc78 Update CHANGELOG.md 2024-10-27 10:32:12 +01:00
Lwmte
47088ba978 Another attempt at fixing item status bug 2024-10-27 09:53:25 +01:00
Lwmte
7b5129a6d8 Possibly fix antitriggering 2024-10-27 09:33:28 +01:00
Lwmte
cfdf7af404 Fix pickups pushed above climbable blocks 2024-10-27 09:30:18 +01:00
Lwmte
b3ae526726 Update CHANGELOG.md 2024-10-27 08:58:07 +01:00
Lwmte
e3ca14f84f Fix problem with player falling through dynamic bridges 2024-10-27 08:55:05 +01:00
Lwmte
d08f881d1e Rearrange some control phase calls 2024-10-26 22:25:14 +02:00
Lwmte
2e3fdc2942 Hide menu looping option from UI 2024-10-26 21:54:23 +02:00
Lwmte
e89647d1b5 Update CHANGELOG.md 2024-10-26 21:50:23 +02:00
Lwmte
e566319f44 Update CHANGELOG.md 2024-10-26 21:49:46 +02:00
Adngel
7b7eb8f49f
Adngel smoke emitter update (#1377)
* Update tr5_smoke_emitter.cpp

* Created namespace and more update

* Update tr5_smoke_emitter.cpp

* Update tr5_smoke_emitter.cpp

* New OCBs

* Update tr5_smoke_emitter.cpp

- Removed displacement (So no more OCB &1) to avoid conflict with other formulas
- Removed deprecated OCB 111
- Changed OCB for no Damage from &2 to <0, to avoid conflict with other formulas.
- Changed OCB&8 to OCB > 0 to simplify formula
- Enhaced steam shot particles direction to accept Pitch rotations
- Created ItemFlags[3] to contain bit flags (Fla (0) used for the No-Damage setting)
- Created enum for the flags used in ItemFlags[3]

* Fix: Pitch <-> Yaw formula

* Formatting

* Updated feedback

* Sounds update

* Formatting

* Rename file

* Constants

* SFX Reorganisation (#1384)

* Updated SFX for Steam Emitter.

Additionally, divided the SFX List with comments for better readability and revised names for custom footstep enums.

* Updated sound_effects.h.

* Refined comments and names.

---------

Co-authored-by: Sezz <sezzary@outlook.com>

* Define various constants

* Fix Vel Y when pointing downwards

* Reduced damage time Interval

Reduced damage time Interval to increase the damage received by the steam shot.

* Squashed commit of the following:

commit bc6bf2c5a4
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Mon Sep 30 11:44:09 2024 +0200

    Fix speedboat / rubber boat on leveljumps

commit 5f7ac065d1
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Mon Sep 30 11:42:50 2024 +0200

    Show relative anim number in stats

commit ca87efa4fd
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Sep 28 18:59:09 2024 +0200

    Possibly fix minecart wrench

commit 3a0e703d1d
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Sep 28 18:45:34 2024 +0200

    Fix Lara meshswaps after kayak dismount after leveljump

commit ef751cf236
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Sep 28 17:29:27 2024 +0200

    Fixed #1405

commit 3dafdf44e3
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Mon Sep 23 19:58:56 2024 +0200

    Update kayak.cpp

commit 90effcdc58
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Mon Sep 23 01:17:15 2024 +0200

    Update CHANGELOG.md

commit 1b95946db9
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Mon Sep 23 01:04:19 2024 +0200

    Fix kayak and minecart meshswaps on leveljump

commit f8f20a071c
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Mon Sep 23 00:23:49 2024 +0200

    Fixed leveljump vehicle transfer

commit d62ae232ee
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Sep 22 09:48:22 2024 +0100

    Change player HAIR enumerations

commit 29c2363141
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Sep 15 21:24:16 2024 +0100

    Update CHANGELOG.md

commit a98ed141cc
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Sep 15 15:13:36 2024 +0100

    Update CHANGELOG.md

    Contains listings for branches to be merged for the next release.

commit feee7c8285
Author: Sezz <sezzary@outlook.com>
Date:   Thu Sep 5 23:02:24 2024 +1000

    Update InputAction class

commit b318179bb9
Author: Sezz <sezzary@outlook.com>
Date:   Sun Sep 1 22:25:40 2024 +1000

    Organise Lua room class

commit a9516cd7f7
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Sep 1 13:13:20 2024 +0200

    Add Room:GetRoomNumber() method (#1401)

    * add RoomGetRoomNumber() method

    * Update CHANGELOG.md

    * fix GetReverbType() method

    * Update MoveableObject.cpp

    Modified Moveable() function description for consistency

commit cccd0d9bfb
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sat Aug 31 15:23:01 2024 +0100

    level.rumble sound effect parameters exposed to user

    Formally: this was hardcoded in TR5 to the klaxon from the Sinking Submarine level. I have moved this to a new sound enum in the default soundmap and removed the pitch shift as this can be set in the XML by the user.  This change has also been documented on the TombEngine website.

commit 60b9055d0a
Author: Sezz <sezzary@outlook.com>
Date:   Tue Aug 27 19:17:57 2024 +1000

    Add F12 as alternative to PrtSc for screenshots

commit 806c052ff5
Author: Sezz <sezzary@outlook.com>
Date:   Sun Aug 18 22:58:50 2024 +1000

    Fix GetBoneOrientation()

commit f85769f3ca
Author: TokyoSU <77746747+TokyoSU@users.noreply.github.com>
Date:   Sat Aug 17 19:10:04 2024 +0200

    Update RendererCompatibility.cpp

* Rename PARTICLE_COUNT_MAX to MAX_PARTICLES

* Update effects.h

* Revert rename

* Revert rename

* Fix typo

---------

Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2024-10-26 22:28:29 +03:00
Lwmte
4e7fbf18ce
Merge pull request #1362 from MontyTRC89/develop_60fps
60 FPS, starfield, lens flare
2024-10-26 22:18:56 +03:00
Lwmte
76dd26bb57 Fix merge errors 2024-10-26 21:19:52 +02:00
Lwmte
c8a49e413f Merge branch 'develop' into develop_60fps 2024-10-26 21:12:37 +02:00
Lwmte
ac63d0b178
Implement pickup count modification via hitpoints (#1426)
* Implement pickup count modification via hitpoints

* Always show picked up ammo and consumable count
2024-10-26 21:52:12 +03:00
TokyoSU
267fecb063
Changed/Fixed enemy jeep behaviour (#1395)
* Enemy Jeep WIP

* Update EnemyJeep.cpp

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Fix CreatureBiteInfo requiring vector3 instead

* Re-enable collision with enemy jeep.

- But still avoid him getting killed with vehicle colliding him !

* Fix formatting

* Formatting

* Update to bug report form

* Update bug_report.yaml

* Revert rename

* Fix errors and typos

* Update effects.cpp

* Remove redundant FindAiTargetObject variation, rename struct

* Fix typo

* Remove jeep hack from DoObjectCollision and replace it with other solution

---------

Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2024-10-26 21:50:37 +03:00
Lwmte
60f3c3d8c5 Fix shaky track camera 2024-10-26 20:43:03 +02:00
porzell
0c15fdbe61
Allow more access to Moveable joint info in Lua (#1386)
* Allow more access to Moveable joint info in Lua
- Added Lua support to correspond with optional 'offset' parameter in GetJointPosition() for moveables
- Added Lua support for GetJointRotation() for moveables

* Update CHANGELOG.md
* Update MoveableObject.cpp
- Fixed typos and clarified doc comments
* Update MoveableObject.cpp

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2024-10-25 22:19:47 +03:00
l.m. (Leif Melles)
ff0b1cc085
Replaced static limiting array for Flame Emitters to unlimited std::vector (#1424)
* Increase active Flame Emitters from 32 to 128
* Replaced static limiting array for Flame Emitters to unlimited std::vector
* Flame Emitter Vector changes
* Update vector for flame
* Include vector
* Remove Default Fire Count
* Remove it again
* Rename on field to fade

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2024-10-25 21:38:10 +03:00
l.m. (Leif Melles)
5f008f4562
Movable GetStatus() fix for antitrigger (#1421)
* Update to bug report form
* Update bug_report.yaml
* get movable status fix
* get movable status fix
* Update CHANGELOG.md

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2024-10-25 21:37:42 +03:00
Lwmte
4bf3b1dfd7 Merge branch 'develop' into develop_60fps 2024-10-25 08:43:58 +02:00
Lwmte
53e7ca0972 Decopypaste sync code, rename variable framerate to high, remove unused variables 2024-10-25 08:43:45 +02:00
Lwmte
e4098c458f Fix typo 2024-10-24 08:23:50 +02:00
Sezz
9a3166b9a1 Final code cleanup 2024-10-24 15:33:42 +11:00
Sezz
4a64e73648 Enforce some conventions with recent changes 2024-10-24 15:02:36 +11:00
Lwmte
a2812d5b34 Unrollback Tokyo's value 2024-10-23 20:28:36 +02:00
Lwmte
6278b59a1d Rollback Tokyo's jump distance constant and add no box checks to CanCreatureJump 2024-10-23 19:53:05 +02:00
Lwmte
4c42cb871a Fix door height and floordata precision loss with NO_HEIGHT variable 2024-10-23 19:49:23 +02:00
Lwmte
b7d9d994c2 Rename other constants in box.h 2024-10-23 16:28:51 +02:00
Lwmte
3f33021869 Fix TargetNearestEntity, refactor box flags, extend search LOT range to 32-bit 2024-10-23 16:22:52 +02:00
Stranger1992
521fdbfa8b Changed hardcoded sound for RAISING_BLOCKS back to the soundID used in TRLE (ID 149) 2024-10-21 23:22:49 +01:00
Stranger1992
f7a22ea1a7 Clarify Lua API for SetOnCollidedWithObject() 2024-10-18 00:14:02 +01:00
Jakub
e5e001b090
Merge branch 'develop' into develop_60fps 2024-10-16 16:19:13 +01:00
Lwmte
0638756d90 Fixed ghost collision with moveables with zero bounds 2024-10-16 00:59:37 +02:00
Lwmte
b1bbd55328 Fixed issue with Lara not rotating together with bridges while picking up items 2024-10-16 00:12:29 +02:00
MontyTRC89
9ea62f711a Fixed vehicle and Lara in HUB system level change 2024-10-15 15:18:51 +02:00
MontyTRC89
9cf2077051 Possibly fixed invisible Lara at HUB level switch 2024-10-14 05:56:34 +02:00
MontyTRC89
6f411f796d Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-10-13 05:53:55 +02:00
MontyTRC89
c5e8ffc50c Merge branch 'develop' into develop_60fps 2024-10-13 05:53:33 +02:00
MontyTRC89
4f3ea36bc1 Temporarly hack for solving invisible Lara in some cases with HUB system 2024-10-13 05:52:52 +02:00
Stranger1992
42d288b165 Merge branch 'develop' into develop_60fps 2024-10-11 01:31:50 +01:00
Lwmte
a3bc674c25
Fixed LOS and laserhead player teleportation/invisibility (#1416) 2024-10-10 12:15:43 +01:00
MontyTRC89
f49a803364 Merge branch 'develop' into develop_60fps 2024-10-08 15:19:51 +02:00
MontyTRC89
e9be29bf9e Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-10-08 15:19:40 +02:00
MontyTRC89
0ac19594f2 Camera is not inteprolated until next control phase if disable interpolation is true;
Added more non-interpolation cases to spotcams;
2024-10-08 15:19:36 +02:00
Lwmte
deec54ff83 Fix lasersight action 2024-10-06 02:02:09 +02:00
Lwmte
f51eed1800 Fixed issues with death sectors and water climbouts with bridges 2024-10-05 11:40:12 +02:00
Lwmte
cb0e8fb23e Merge branch 'develop' into develop_60fps 2024-10-05 00:28:40 +02:00
Lwmte
a77d64b69b Fix binocular bugs 2024-10-05 00:25:36 +02:00
Lwmte
9a6dfef475 Also show relative frame number in debug 2024-10-03 09:14:06 +02:00
Lwmte
1e6c2158ea Add IsStringPresent script function for further usage in nodes 2024-10-02 00:16:26 +02:00
Lwmte
78485fd3ec Fixed UPV transfer on leveljump 2024-09-30 17:11:59 +02:00
Lwmte
bc6bf2c5a4 Fix speedboat / rubber boat on leveljumps 2024-09-30 11:44:09 +02:00
Lwmte
5f7ac065d1 Show relative anim number in stats 2024-09-30 11:42:50 +02:00
MontyTRC89
061d152207 Fixed rolling ball interpolation 2024-09-29 06:16:13 +02:00
Lwmte
ca87efa4fd Possibly fix minecart wrench 2024-09-28 18:59:09 +02:00
Lwmte
3a0e703d1d Fix Lara meshswaps after kayak dismount after leveljump 2024-09-28 18:45:34 +02:00
Lwmte
ef751cf236 Fixed #1405 2024-09-28 17:29:27 +02:00
MontyTRC89
63e0f254f6 Merge branch 'develop' into develop_60fps 2024-09-27 07:24:01 +02:00
MontyTRC89
5c8ca6803e Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-09-27 07:23:51 +02:00
MontyTRC89
5bc065c411 Possibly fixed torch interpolation 2024-09-27 07:23:40 +02:00
Lwmte
3dafdf44e3 Update kayak.cpp 2024-09-23 19:58:56 +02:00
Lwmte
90effcdc58 Update CHANGELOG.md 2024-09-23 01:17:15 +02:00
Lwmte
1b95946db9 Fix kayak and minecart meshswaps on leveljump 2024-09-23 01:04:19 +02:00
Lwmte
f8f20a071c Fixed leveljump vehicle transfer 2024-09-23 00:23:49 +02:00
Stranger1992
aee7c26639 Merge branch 'develop' into develop_60fps 2024-09-22 09:49:10 +01:00
Stranger1992
d62ae232ee Change player HAIR enumerations 2024-09-22 09:48:22 +01:00
Stranger1992
29c2363141 Update CHANGELOG.md 2024-09-15 21:24:16 +01:00
Stranger1992
a98ed141cc Update CHANGELOG.md
Contains listings for branches to be merged for the next release.
2024-09-15 15:13:36 +01:00
MontyTRC89
40b09be39b Added Disable interpolation anim command 2024-09-13 06:37:24 +02:00
MontyTRC89
015adfb9c6 Improved disable interpolation flag for flyby cameras 2024-09-11 11:14:36 +02:00
MontyTRC89
1695caa602 Interpolation disable by default on CreateItem();
Interpolation enabled in renderer always after the first frame;
2024-09-06 06:07:12 +02:00
MontyTRC89
9f45dd21aa Merge branch 'develop' into develop_60fps 2024-09-06 05:51:49 +02:00
Sezz
feee7c8285 Update InputAction class 2024-09-05 23:02:24 +10:00
Stranger1992
eca79130a0 Update bug_report.yaml 2024-09-03 09:50:11 +01:00
Stranger1992
7fee647c84 Update to bug report form 2024-09-03 09:44:53 +01:00
Sezz
f20967dbec Merge branch 'develop' into develop_60fps 2024-09-02 21:59:35 +10:00
Sezz
b318179bb9 Organise Lua room class 2024-09-01 22:25:40 +10:00
davidmarr
a9516cd7f7
Add Room:GetRoomNumber() method (#1401)
* add RoomGetRoomNumber() method

* Update CHANGELOG.md

* fix GetReverbType() method

* Update MoveableObject.cpp

Modified Moveable() function description for consistency
2024-09-01 21:13:20 +10:00
Stranger1992
cccd0d9bfb level.rumble sound effect parameters exposed to user
Formally: this was hardcoded in TR5 to the klaxon from the Sinking Submarine level. I have moved this to a new sound enum in the default soundmap and removed the pitch shift as this can be set in the XML by the user.  This change has also been documented on the TombEngine website.
2024-08-31 15:23:01 +01:00
Sezz
768e5a559d Use std::array to avoid memcpy(); minor cleanup 2024-08-29 16:11:55 +10:00
MontyTRC89
784684f2c0 Merge branch 'develop' into develop_60fps 2024-08-28 11:48:14 +02:00
MontyTRC89
398e3364fa Fixed rope interpolation 2024-08-28 11:48:02 +02:00
Sezz
60b9055d0a Add F12 as alternative to PrtSc for screenshots 2024-08-27 19:17:57 +10:00
MontyTRC89
5fa6b81ff3 Fixed ghost frame in spotcams 2024-08-26 05:12:57 +02:00
MontyTRC89
d23d68e1ba Fixed wrong GIT merge 2024-08-22 15:40:12 +02:00
MontyTRC89
36cf58ba16 Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-08-22 15:16:04 +02:00
MontyTRC89
08f43e146d Fixed strings flickering in title menu 2024-08-22 15:15:59 +02:00
MontyTRC89
dd2d8e8899 Fixed turn switch flickering frame 2024-08-21 16:58:06 +02:00
Sezz
39a52d26a4 Formatting 2024-08-21 17:38:24 +10:00
MontyTRC89
294a7592cd Merge branch 'develop' into develop_60fps
# Conflicts:
#	TombEngine/Renderer/Renderer.h
#	TombEngine/Renderer/RendererFrame.cpp
#	TombEngine/Renderer/RendererHelper.cpp
#	TombEngine/Renderer/Structures/RendererItem.h
2024-08-21 06:20:26 +02:00
MontyTRC89
b3210f93bd Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-08-21 06:03:36 +02:00
MontyTRC89
b46641a2cf Fixed flickering frame on camera transition 2024-08-21 06:03:30 +02:00
Sezz
806c052ff5 Fix GetBoneOrientation() 2024-08-18 22:58:50 +10:00
TokyoSU
f85769f3ca Update RendererCompatibility.cpp 2024-08-17 19:10:04 +02:00
Sezz
5c834a0906
Use BoundingSphere natively (#1339)
* Use BoundingSphere natively; remove CreatureSpheres and LaraSpheres globals

* Remove parameter from GetSpheres()

* Remove useless includes; rename file; add namespace

* Tabs

* Change pointer argument to reference

* Fix weapon crash

* Rename function and return bool

* Pass reference arguments

* Rename parameter

* Fix errors

* Use Intersects() method

* Define local constant

* Rename function; use generic variable names

* Update RendererHelper.cpp

* Fix merge errors

* Fix and further simplify item spheres

* Fix weapon firing

* Add GetSpheres() ItemInfo method

* Fix GenericSphereBoxCollision()
2024-08-17 13:11:36 +01:00
Sezz
7bc21f7d3a Fix formatting 2024-08-16 19:05:52 +10:00
MontyTRC89
3a345e9ccb Merge branch 'develop' into develop_60fps
# Conflicts:
#	TombEngine/Game/control/control.cpp
#	TombEngine/Game/gui.cpp
#	TombEngine/Renderer/RendererDrawMenu.cpp
#	TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp
2024-08-16 10:19:51 +02:00
MontyTRC89
d8aced8ab4 Fixed exploding body parts 2024-08-16 10:05:01 +02:00
Sezz
08d74d6608 Fix formatting 2024-08-16 15:48:36 +10:00
Stranger1992
e4b3124a68 Rename hair objects to distinguish between the two
ID_HAIR -> SINGLE_BRAID_HAIR
ID_HAIR2 -> DUAL_PIGTAIL_HAIR
2024-08-16 00:09:13 +01:00
TokyoSU
349d7e5cdd Fixed hair again (for good this time) 2024-08-15 20:15:46 +02:00
Sezz
e3c1ccf5c2 Integrate GetWaterSurface(), GetWaterDepth() and GetWaterHeight() into PointCollisionData 2024-08-16 02:12:41 +10:00
Sezz
2439e7cd46 Minor cleanup 2024-08-15 22:46:08 +10:00
Sezz
bea2810408 Remove Ptr suffix; use const 2024-08-15 22:44:35 +10:00
Sezz
cce384d3de Remove unused parameter 2024-08-15 22:40:08 +10:00
TokyoSU
6b18d46096 Update CHANGELOG.md 2024-08-15 14:08:47 +02:00
TokyoSU
fcf32b1ad0 Fix young hair drawing. 2024-08-15 13:55:23 +02:00
Sezz
dcfdae10da lara -> player in GUI 2024-08-14 17:50:19 +10:00
Sezz
079a2bfc00 Correct config 2024-08-14 16:09:42 +10:00
Stranger1992
4258b27c0c Changelog typos 2024-08-14 00:52:48 +01:00
Stranger1992
a9c3474b1c Update CHANGELOG.md 2024-08-13 23:19:11 +01:00
Sezz
37f056bfaf Add missing include 2024-08-12 15:13:35 +10:00
Kewin Kupilas
f2c52e61aa
Add "Lara's Home" menu entry (#1394)
* Add "Lara's Home" menu entry

* Minor things

* Move home_level into Strings.lua

* Lara's Home -> Home Level

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-08-11 17:20:09 +01:00
TokyoSU
b9b99af3d2
Seal Mutant Port (#1392)
* begin mutant crawler import

* progress.

* Update MutantCrawler.cpp

* Done seal mutant.

- Changed how ThrowPoison color worked.
The old function didn't really reflect the correct color.

* Update CHANGELOG.md

* Improve ThrowSealMutantGas

* Add ID_SEAL_MUTANT scripting ID

* Update ObjectIDs.h

* Remove IsLaraCrouching()

* Use correct data types for poison effect

* Use references, not pointers

* Use proper spacing

* Use const

* Remove magic numbers, ordering, const, etc

* Remove pointless CreatureBiteInfo constructor; remove global constants;; etc

* Formatting, cleanup

* Replace GetRandomControl() usage

* Comment

* Update CHANGELOG.md

* rename variable

* demagic numbers

* Define constants

* Remove anim data checks

* Update poison gas spawn function

* Update tr3_flamethrower.cpp

* Update SealMutant.cpp

* Update seal mutant

* Update seal light

* Update SealMutant.cpp

* Minor changes

* Update sealMutant

* Update effects.h

* Update flamethrower to have lara as target too.

* Revert feffects from int to uchar

* Cleanup

---------

Co-authored-by: Kubsy <kubadd475@gmail.com>
Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
Co-authored-by: Jakub <kubabilinski03@gmail.com>
2024-08-11 16:48:52 +01:00
Jakub
cb7d5ddccc Merge remote-tracking branch 'origin/master' into develop 2024-08-11 12:13:40 +01:00
Jakub
8b4573787b
Create LICENSE 2024-08-11 12:12:10 +01:00
Kewin Kupilas
0628affb69
Rework menu option looping (#1393)
* Remove menu loop

* Update CHANGELOG.md

* Update config

* Implement new menu logic

* Remove mouse smoothing

* Update Input.cpp

* Allow 3 options for menu looping

* Name adjustment

* Rename RegKey

* String key rename

* General refactors

* Change default menu looping mode

* Update configuration.h

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-08-11 09:59:56 +01:00
Jakub
046e0b26ac
Update AUTHORS.md 2024-08-11 09:57:43 +01:00
Jakub
16112505b6
Update AUTHORS.md 2024-08-11 09:57:14 +01:00
Jakub
da05580213
Update AUTHORS.md 2024-08-11 09:57:05 +01:00
Sezz
77af7e9e22 Update Raptor.cpp 2024-08-08 22:28:52 +10:00
Sezz
587ce7d551 Remove magic number 2024-08-08 22:23:30 +10:00
Sezz
943c552627 Rename raptor files 2024-08-08 22:22:32 +10:00
Sezz
9ae935b8bb Extend functionality of TargetNearestEntity() 2024-08-08 22:12:50 +10:00
Sezz
29e65d0d2e Formatting; simplify 2024-08-08 21:57:12 +10:00
Stranger1992
89c4b0b22d Update CHANGELOG.md 2024-08-08 00:22:19 +01:00
Stranger1992
a46c1af417 Update CHANGELOG.md 2024-08-08 00:21:20 +01:00
TokyoSU
374c42b9fb
Raptor upgrade (#1346)
* Upgrade raptor

OCB 0: NORMAL: Like the original, change zone type to basic to avoid the raptor to try jumping pit when he cant anymore.
OCB 1: ABLE_TO_JUMP: can jump pit, new behaviour.
2024-08-08 00:07:06 +01:00
TokyoSU
27b4e0c2ad Fixed bomb freezing/crashing the game.
- Improved centaur code.
- Now centaur look and rotate the head and torso at the target.
- Removed double ID_PROJ_BOMB init.
-
2024-08-07 21:03:29 +02:00
Sezz
3ad7b1f5de Tidy up room struct 2024-08-06 22:09:58 +10:00
Sezz
3a38e4c576 Rename previously updated creature files 2024-08-06 19:00:49 +10:00
MontyTRC89
33edb092a9 Fixed shockwaves 2024-07-31 11:31:48 +02:00
Sezz
f0603349e2 Update RendererDraw.cpp 2024-07-31 19:21:51 +10:00
davidmarr
8e9ed77e64
Fixed Volume:GetActive() method (#1389)
* Update VolumeObject.cpp

fixed Volume:GetActive() method

* Update CHANGELOG.md
2024-07-27 18:16:13 +01:00
Sezz
0ad1eb9402 Round angle calculation 2024-07-27 01:44:18 +10:00
Sezz
a1675b577a Fix sound effect crash 2024-07-25 01:29:24 +10:00
Sezz
0c1c9d49a5 Comment flipeffects to be converted to anim commands; use NO_VALUE 2024-07-18 00:09:36 +10:00
Sezz
ca92b1a78e Add missing string function to global debug 2024-07-17 21:39:44 +10:00
Sezz
9791bd92e7 Add accessible debug execution time functions 2024-07-16 17:37:01 +10:00
Sezz
5f95c3f0ec Add Remap() math function 2024-07-12 01:20:47 +10:00
Stranger1992
f804c794f8 Fixed an error in the KeyClearAll() function that prevented it from being displayed in the Lua API 2024-07-03 15:49:38 +01:00
Sezz
c76b5cb0c8 Update Utils.cpp 2024-07-03 01:11:19 +10:00
Sezz
ef16d24450 Update PushableStates.cpp 2024-07-02 01:21:36 +10:00
Sezz
a492710f93 Update Utils.cpp 2024-07-02 01:05:28 +10:00
Sezz
acab964930 Update interpolkation functions 2024-07-02 01:01:45 +10:00
Jakub
9ca606c64e
Update CHANGELOG.md 2024-06-30 14:41:56 +01:00
Jakub
438ed01b4f
Update CHANGELOG.md 2024-06-30 14:37:37 +01:00
Sezz
485e384286
Additionally play underwater sound effects in quicksand 2024-06-30 23:36:27 +10:00
Sezz
8aae53c2c2
New animation sound conditions (#1284)
* Base for new sound conditions

* Read flags

* Refine anim sound effect handling

* Add swamp check for DryLand anim sound effect

* Update local variable names

* Use proper math functions

* Update animation.cpp

* Address PR notes

* Syntax

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
2024-06-30 23:25:00 +10:00
Sezz
4c4a0b0510
Global access to debug objects (#1382)
* Make debug object drawing globally accessible and therefore convenient

* Fix case in debug includes

* Add math constants

* Add missing space

* Fix debug strings; reduce debug string scale

* Remove unused method

* Remove commented lines
2024-06-27 21:10:19 +10:00
Sezz
2be193a997 Use NO_VALUE in GetPlayerScrolledWeaponType() 2024-06-26 14:50:39 +10:00
Sezz
e39193497f Update sector class; disambiguate pathfinding boxes 2024-06-25 17:32:02 +10:00
Stranger1992
9f2cf860c5 Update CHANGELOG.md
Small changes to text to improve clarity
2024-06-24 22:14:30 +01:00
Sezz
399c039f21 Prevent more inappropriate tooltips 2024-06-25 01:24:32 +10:00
Sezz
ba6841bb18 Add tab 2024-06-21 13:37:06 +10:00
Sezz
c70bba367e Further organise math 2024-06-21 13:36:27 +10:00
Sezz
8a8e63b8e6 Formatting 2024-06-19 21:15:03 +10:00
Sezz
cb05e0eae8 Grammar 2024-06-19 21:07:26 +10:00
Jakub
2a6a96e03a
Implement KeyClearAll() Lua function (#1374)
* Implement KeyClearAll

* add entry to CHANGELOG.md
2024-06-19 11:20:57 +01:00
Adngel
7248091ca9
Adngel several traps fixes (#1379)
* Fix: Impale animation

When I jump in the TR1 spikes, or when I'm over the emerging TR4 spikes, the value it gets is INTERSECT not CONTAINS

I think it is because the TR1 spikes are 2 clicks, it won't contain Lara's collider. And TR4 when they are emerging, also have smaller collider box, so it will neither CONTAINS Lara's collider until it get already very late (for then, Lara is already dead and doing another death animation).

Removing the condition, it will activate where is INTERSECTS or CONTAINS.

* Fix Rome Hammer damage.

It won't hurt Lara anymore while it's deactivated.

* Fix Traps Collision

Updated the code of 4blades (used by floor and ceiling versions).
Added a condition in the colliion check for non-itelligent objects (like traps) so collision will push Lara only for the traps that have ItemFlags[4] equal to 0.

TODO: Consider a different method.

* Update collide_item.cpp

I tried item->Collidable, but deactivating it, has more consecuencies, where spheres collisions are not detected so traps don't harm Lara, therefore I couldn't use it.

I returned it to coll->Setup.EnableObjectPush boolean, this variable is set in GenericSphereBoxCollision reading the ItemFlags[4] like it was before. (Most of the traps are builded around this) changing it could require a refactor of the traps that uses this feature.

* Fixes and Refactor of Joby Spikes

- Created references variables to replace ItemFlags, to make the code more readable.
- Replaced the deprecated GetRandomControl function from this code by Random::GenerateInt function.
- Fix bug with collider. This trap is using an internal collision detection routine to hurt Lara based in her position and the spike height, instead of the sphers. So it wasn't using the GenericSphereBoxCollision, I removed from its obj->collision to avoid it pushing Lara away.
- Fix bug about endless scale. Before it was increasing the spike far beyond the room bounds, now it will only increase 3 blocks and 1 click (3328, based in Troye's code). Leaving for a future enhace it for it to adapt at other room heights.

* Fix Mine bug

Now when Lara steps on a mine with OCB 1, the death animation will trigger correctly without crash the game.

* Increase damage Statue with Blade

from 20 to 200

So it's more likely it removes Lara more than 1/3 per hit.

* Fix and refactor of Sentry Gun

- Created references variables to replace ItemFlags, to make the code more readable.
- Added new ItemFlags [3] to contain the ID of the deactivator inventory item.
- Changed item pointer for item reference.
- Fix bug sentry gun rotation meshes: Looks like the the function "SetBoneRotationFlags" wasn't set properly.

* Changed namespace in 4blades code.

- Changed namespace name
From
namespace TEN::Entities::TR4
to
namespace TEN::Entities::Traps

- Fixed Damage variables names.

* Fix Rolling Spindle Moving through high steps.

Added extra condition to stop the movement of the rolling spindle blade.

In addition to walls, the spinning blade will stop if:
- Ceiling is lower than 4 clicks
- The floor ahead is lower or higher than 2.5 clicks. (To stop it moving up and down into pits, but still allow it to move through 1 and 2 click slopes).
- If there is a stopper flag (so they can be stopped with pushables).

* Update WreckingBall code

Still pending of refactor, but I think it's better manage that in a different branch.

* Update traps namespace

* Update TR4 traps

Disable trap push for Birdblade, Sethblade and Plough.

Deleted duplicated initialization of Plough and Chain in TR4 objects.

* Merge develop branch

There was a conflict with develop, this should fix it.

* Fix Dart Emitter Antitrigger

* Fix Homing Dart Emiter

When triggering, it was continously spawning darts, that was because the trigger code was resetting the item->Timer variable. This variable is aimed to be used to delay the activation or provoke a timed deactivation. Using the variable adittionaly for internal behaviour like the dart spawn frequency, may lead to uncertain results like this actual bug.

Using a separate variable (ItemFlags[1]) the dart spawn frequency timer won't be influenced by the triggers code.

* Formatting

* Update CHANGELOG.md

* Moved the routine from ControlCog to AnimatingControl

So now Animatings will can have the OCB 666 (used in Train level for the helicopter animating) and OCB 2 to make animating dissapear when Antitriggered. (Legacy features of TR4 code, maybe they got lost in the TR5 code).

* Fix SwingingBlade deactivation

It was happening that when the swinging blade got antitriggered, it was going to the stopping animation, but the object state remained activated, doing control calls on every loop. Now it will remove itself from the control loop calls.

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-06-19 11:19:39 +01:00
Sezz
2e6db4e01c Add TEN::Debug namespace 2024-06-19 15:29:46 +10:00
Sezz
49b014bc88 Avoid inappropriate tooltips 2024-06-19 15:05:03 +10:00
Sezz
87b5e83901 Remove unnecessary framework define; add newlines between label comments to avoid inappropriate tooltips 2024-06-19 15:03:33 +10:00
Sezz
a34bfaa89b Fix project build paths again 2024-06-11 12:07:48 +10:00
Sezz
22176d05f5 Update CHANGELOG.md 2024-06-06 11:46:43 +10:00
Sezz
6fa494b093 Make monkey swing auto jump a player setting; Lua formatting 2024-06-06 11:42:54 +10:00
Sezz
d33d1ced19 Manually fix VS search and replace errors 2024-06-06 11:15:43 +10:00
Sezz
47e22abbe9 Remove Ptrs suffixes 2024-06-06 10:52:59 +10:00
Sezz
063e994213 Merge branch 'develop' into develop_60fps 2024-06-04 19:16:48 +10:00
Adngel
838dab484b Update CHANGELOG.md 2024-05-31 20:59:25 +02:00
Adngel
1f4404d760 Fix: Burning Torch, flame in hand
Changing the commands order to switch off the Lara's hand flame in the same frame when she throws or drops the torch.

(The command must still be after the torch flare has been created, otherwise the torch flare will be switched off when instantiated).

Issue: https://github.com/MontyTRC89/TombEngine/issues/1376
2024-05-31 20:52:57 +02:00
Adngel
01a4b69168 Fix: Larson and Pierre zone.
Human LotType allows enemies to moves up and down of 2 and 4 clicks (like VCI guards) but Larson and Pierre hasn't got climbing animations, so they move over high steps just walking through.

That's why I'm changing them to obj->LotType = LotType::Basic;  So they will do the correct path through 1 click steps.
2024-05-27 10:29:09 +02:00
MontyTRC89
903fbf62f8 Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-20 05:17:57 +02:00
MontyTRC89
dd46927f62 Fixed fish swarms not drawing 2024-05-20 05:17:31 +02:00
Jakub
40966147c9
Fix bullet points 2024-05-19 22:42:03 +01:00
Jakub
891a6d97fa Merge branch 'master' into develop
# Conflicts:
#	CHANGELOG.md
2024-05-19 22:37:52 +01:00
Jakub
192eb6c6f6
Add links to each version to releases page respectively 2024-05-19 22:34:58 +01:00
Jakub
c6dcc5d042 Merge branch 'master' into develop 2024-05-19 22:25:09 +01:00
Jakub
4727417b93
Update IMPORTANT_LINKS.md 2024-05-19 22:24:46 +01:00
Jakub
8f98b1c99d
add TEN logo 2024-05-19 22:22:20 +01:00
Jakub
c1817d9200
Update bug_report.yaml 2024-05-19 21:48:12 +01:00
Jakub
97a31f5bb1
change to lowercase letters 2024-05-19 21:47:31 +01:00
Sezz
be5c525fa2 Remove Ptr suffix 2024-05-19 18:21:01 +10:00
Sezz
210cad57e1 Fix formatting 2024-05-19 18:12:00 +10:00
Sezz
36133ea5e9 Use parentheses 2024-05-19 18:10:45 +10:00
Sezz
978fa30d61 Formatting 2024-05-19 18:09:41 +10:00
MontyTRC89
e50715bd31 Fixed bad interpolation of crosshair position 2024-05-19 05:34:53 +02:00
MontyTRC89
4fd6cceecd Added interpolation for scarabs, locusts, bats, rats, spiders, fishes 2024-05-18 05:40:27 +02:00
Sezz
0b749b0c95 Fix bridge push 2024-05-18 01:37:41 +10:00
MontyTRC89
be95d77d92 Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-17 13:39:10 +02:00
MontyTRC89
815598f425 Fixed fire emitters 2024-05-17 13:39:02 +02:00
Adngel
b9d3f81253 Fix: Electric Cleaner crash
When it detected a sector with no box (a tile marked with "set non-walkable sector" editor flag) the game was crashing.

Changing the detection order solved the issue.

This issue was not happening in previous version (it was originated at some point of the past month) so I'm not registering in the changes.txt
2024-05-17 12:30:14 +02:00
Sezz
de91720fc9 Formatting 2024-05-17 19:40:09 +10:00
Sezz
206fd59e55 Interpolate speedometer 2024-05-17 18:57:22 +10:00
Sezz
411c397027 Minor formatting 2024-05-17 18:52:26 +10:00
MontyTRC89
a39f7ccd2c Disabled interpolation on PuzzleDone 2024-05-16 12:01:33 +02:00
MontyTRC89
6dd320f010 Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-16 11:50:19 +02:00
MontyTRC89
d5ed115d50 Fixed wrong positions for transparent faces in moveable items 2024-05-16 11:49:58 +02:00
Jakub
23254c87ea
Update CHANGELOG.md 2024-05-16 10:14:26 +01:00
Jakub
6b3a266133 Merge branch 'master' into develop 2024-05-16 10:04:21 +01:00
Jakub
b78ba51d76
Create IMPORTANT_LINKS.md 2024-05-16 10:03:37 +01:00
Jakub
2d670175b4
Update CHANGELOG.md 2024-05-16 09:41:48 +01:00
Jakub
a28cc919b7 Merge branch 'master' into develop 2024-05-16 09:34:50 +01:00
Jakub
2681036afe
Update pull_request_template.md 2024-05-16 09:34:09 +01:00
Jakub
2f19cb6244
Update pull_request_template.md 2024-05-16 09:33:56 +01:00
Jakub
085a25940d remove changes.txt from master branch 2024-05-16 09:33:07 +01:00
Jakub
cd2996cb06 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-05-16 09:31:48 +01:00
Jakub
654e3a0381 remove changes.txt (changelog is now in CHANGELOG.md) 2024-05-16 09:31:38 +01:00
Jakub
cec6e74475
Update CHANGELOG.md 2024-05-16 09:30:23 +01:00
Jakub
1e7f314869 Merge branch 'master' into develop 2024-05-16 09:27:57 +01:00
Jakub
4e94f36361 Merge branch 'master' of https://github.com/MontyTRC89/TombEngine 2024-05-16 09:26:58 +01:00
Jakub
fff2c63584 update README and remove stray objID.txt from main folder (already located in Tools folder) 2024-05-16 09:26:47 +01:00
Sezz
9124b3129e Squishy blocks aren't bridges 2024-05-16 18:26:20 +10:00
Jakub
1ef89739b7
Update CHANGELOG.md 2024-05-16 09:25:14 +01:00
Jakub
e7c5da4579
Update CHANGELOG.md 2024-05-16 09:24:18 +01:00
Jakub
7fe250ede3
Create CHANGELOG.md 2024-05-16 09:22:19 +01:00
Jakub
68337904e3
Update CONTRIBUTING.md 2024-05-16 08:24:36 +01:00
Jakub
d24d2114a4
Update CONTRIBUTING.md 2024-05-16 08:21:45 +01:00
Lwmte
0b59e4f724
Update README.md 2024-05-16 09:14:09 +03:00
Lwmte
09445ad0ba
Update README.md 2024-05-16 09:13:36 +03:00
Lwmte
20d591b74f Increase maximum interpolated distance change 2024-05-16 07:53:13 +02:00
MontyTRC89
dc49d1c985 Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-16 05:44:41 +02:00
MontyTRC89
eb7163a472 Fixed meteors drawing 2024-05-16 05:43:54 +02:00
Jakub
3559e7d33f Merge branch 'master' into develop 2024-05-15 23:44:59 +01:00
Jakub
350e86ed0b
Create CODE_OF_CONDUCT.md 2024-05-15 23:43:08 +01:00
Jakub
9e45691869
Update pull_request_template.md 2024-05-15 23:30:18 +01:00
Jakub
78b34345b1
Update pull_request_template.md 2024-05-15 23:25:10 +01:00
Jakub
0ca392980d Merge branch 'master' into develop 2024-05-15 23:23:48 +01:00
Jakub
9d8c484e11
Update README.md 2024-05-15 23:18:09 +01:00
Jakub
64d4b51060
Update README.md 2024-05-15 23:17:22 +01:00
Jakub
e9305dfd19
Update pull_request_template.md 2024-05-15 23:10:40 +01:00
Jakub
c8e61f8b48
Update CONTRIBUTING.md 2024-05-15 23:08:23 +01:00
Jakub
490aebb55e
Update pull_request_template.md 2024-05-15 23:03:55 +01:00
Jakub
cd5db7cb93
Update pull_request_template.md 2024-05-15 23:03:41 +01:00
Jakub
a6e26e7009
Update pull_request_template.md 2024-05-15 23:00:09 +01:00
Jakub
84fccebf00
Create pull_request_template.md 2024-05-15 22:57:34 +01:00
Jakub
361e1d64bb
Update CONTRIBUTING.md 2024-05-15 22:49:08 +01:00
Jakub
06c250c3db
Update CONTRIBUTING.md 2024-05-15 22:48:53 +01:00
Jakub
a92d7a09dd
Update CONTRIBUTING.md 2024-05-15 22:48:31 +01:00
Jakub
7ddbfca0ee
Update CONTRIBUTING.md 2024-05-15 22:44:50 +01:00
Jakub
30ca49448b
Update AUTHORS.md 2024-05-15 22:43:49 +01:00
Jakub
d8432e2e5a
Update README.md 2024-05-15 22:40:04 +01:00
Jakub
2dfbace9a4
Create AUTHORS.md 2024-05-15 22:39:48 +01:00
Jakub
6acdbfa6ee
Update CONTRIBUTING.md 2024-05-15 22:35:44 +01:00
Jakub
4e8892278a
Create CONTRIBUTING.md 2024-05-15 22:34:09 +01:00
Stranger1992
8bcccea7b4 Update feature-request.yaml 2024-05-15 22:10:57 +01:00
Stranger1992
3c7f928e4d Revert "Update bug_report.yaml"
This reverts commit 81d2e83521.
2024-05-15 22:08:40 +01:00
Stranger1992
a6cbef9f88 Revert "Update bug_report.yaml"
This reverts commit 5987e7ad8d.
2024-05-15 22:08:09 +01:00
Stranger1992
5987e7ad8d Update bug_report.yaml 2024-05-15 22:07:14 +01:00
Stranger1992
81d2e83521 Update bug_report.yaml 2024-05-15 22:03:17 +01:00
Sezz
93549b7b07 Revert "Remove unnecessary string constants"
This reverts commit 3ff70b2915.
2024-05-16 07:02:38 +10:00
Stranger1992
460241fdde Update bug_report.yaml 2024-05-15 22:00:29 +01:00
Sezz
359df969e1 Update doc comments; minor formatting 2024-05-16 06:58:21 +10:00
Stranger1992
00ad42a6e3 Update bug_report.yaml 2024-05-15 21:56:26 +01:00
Stranger1992
356480b5dc
Create bug report and feature request forms (#1366)
In order to promote concise bug reports / feature requests for TombEngine : myself and @Jakub768 have created some forms in YAML for the TombEngine Github page.

Due to the nature of this being online and rendered by Github , previewing the changes cannot be done with an external tool. So this initial branch will now be merged and then further tweaks will take place if needs be.

Co-authored-by: Jakub <kubabilinski03@gmail.com>
2024-05-15 21:40:05 +01:00
MontyTRC89
cc560727da Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-15 09:01:26 +02:00
MontyTRC89
15da2fcc87 Removed old numberFrames field from camera 2024-05-15 09:01:03 +02:00
Stranger1992
04c0238936 Create config.yml
Disable choice to create a blank issue to promote usage of templates
2024-05-15 01:17:13 +01:00
Stranger1992
19e80d24bd Added Bug Report and Feature Request forms.
Created a template for new bugs and feature requests with prompts to promote proper reporting.
2024-05-15 01:06:59 +01:00
Lwmte
f9963f7940 Fix slow text fade 2024-05-14 22:51:47 +02:00
MontyTRC89
2a0f018247 Fixed flickering transparent sorted faces;
Fixed variable framerate always on even if disabled in settings;
2024-05-14 12:15:06 +02:00
MontyTRC89
46b3b383a6 Added animated textures support in G-Buffer creation for rooms 2024-05-14 11:53:54 +02:00
MontyTRC89
53170e7eab Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-13 06:06:28 +02:00
MontyTRC89
24a8dd3b47 Fix occasional freeze after calling pause menu 2024-05-13 06:06:17 +02:00
Sezz
a7b697e756 Minor formatting 2024-05-13 00:16:07 +10:00
MontyTRC89
bb4e648121 WIP fixes for other menus 2024-05-12 15:45:44 +02:00
MontyTRC89
5bd68efc10 WIP better inventory game loop 2024-05-12 06:02:09 +02:00
MontyTRC89
98f4caec80 Fixed missing strings in inventory 2024-05-12 05:42:08 +02:00
Lwmte
796354e67a Invert SetRot interpolation condition 2024-05-11 10:59:07 +02:00
Lwmte
09d756a75f Only disable interpolation in scripting method if difference is big enough 2024-05-11 10:38:35 +02:00
Lwmte
8aa34857e0 Fix inventory spinback 2024-05-11 10:12:42 +02:00
MontyTRC89
48d1c9ca10 Better handling of legacy 30 fps 2024-05-11 06:02:24 +02:00
MontyTRC89
9e29d08b92 Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-11 05:54:29 +02:00
MontyTRC89
895660fe5a Fixed look at camera; 2024-05-11 05:54:25 +02:00
Sezz
715df20e1f Fix crosshair angle interpolation 2024-05-11 01:51:48 +10:00
Sezz
af5bce3cf8 Update TargetHighlighter.h 2024-05-11 00:29:21 +10:00
Sezz
5239892743 Update starfield error handling 2024-05-10 23:33:55 +10:00
Sezz
fa697cc26e Update lens flare Lua API 2024-05-10 22:20:04 +10:00
MontyTRC89
afab268a13 Fixed ammos ring speed 2024-05-09 13:38:08 +02:00
MontyTRC89
1bd1832037 WIP solution for variable monitor refresh rates 2024-05-09 05:42:36 +02:00
Sezz
caa13a659c Update TombEngine.vcxproj 2024-05-09 03:33:11 +10:00
Sezz
367cb77758 Fix doc comments 2024-05-09 01:43:20 +10:00
Sezz
ce723b5a7e Clean up more effects 2024-05-08 22:59:14 +10:00
Sezz
c398f4ae58 Prepare interpolation data for speedometer 2024-05-08 20:46:17 +10:00
Sezz
272db66772 Interpolate crosshairs (partial solution) 2024-05-08 20:37:41 +10:00
Sezz
460d2d9edb Add interpolation for pickup summary; more clean up 2024-05-08 19:49:52 +10:00
Sezz
6240cce4e8 Clean up effect rendering 2024-05-08 19:21:29 +10:00
Sezz
59e74212ad Clean up effect and HUD objects 2024-05-08 18:47:10 +10:00
Sezz
4adf279e2f Merge branch 'develop' into develop_60fps 2024-05-08 14:38:59 +10:00
Sezz
3ff70b2915 Remove unnecessary string constants 2024-05-08 14:38:00 +10:00
Sezz
d49dd998be Merge branch 'develop' into develop_60fps 2024-05-08 14:11:51 +10:00
Sezz
e7889d2b88 Update colour objects 2024-05-08 14:10:45 +10:00
Sezz
2eb2ff877d Starfield and lens flare cleanup 2024-05-08 13:47:47 +10:00
Sezz
fb0519eb49 Lens flare cleanup 2024-05-08 03:13:52 +10:00
MontyTRC89
8995973c2a Fixed GPU not reset after sun sprite draw 2024-05-07 05:33:54 +02:00
MontyTRC89
9f27821ff6 Fixed hairs detached from head 2024-05-06 05:56:08 +02:00
MontyTRC89
ee3bc5db0f Fixed display sprites multiple draws 2024-05-05 05:54:21 +02:00
MontyTRC89
5bc44d4db7 Merge branch 'develop' into develop_60fps 2024-05-04 06:03:01 +02:00
MontyTRC89
fdd4948ee2 Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-04 06:02:48 +02:00
MontyTRC89
7b7473b660 Added laser beams interpolation;
Addes streamer interpolation;
Added sparks interpolation;
Cleaned smoke interpolation;
2024-05-04 06:02:44 +02:00
Sezz
3faf25c92f Fix pushable grab ban 2024-05-03 23:59:38 +10:00
Sezz
1bf60287e3 Fix bouncing pushables 2024-05-03 23:55:31 +10:00
Lwmte
83b6258de0 Merge branch 'develop' into develop_60fps 2024-05-03 10:16:48 +03:00
Lwmte
57cf323d73 Add autodelete parameter to ShowString 2024-05-03 10:16:32 +03:00
MontyTRC89
46d2de40a0 Possible fix for debug mode 2024-05-03 09:01:36 +02:00
MontyTRC89
2c59e89fc4 Fixed vsync at screen initialization;
Fixed rotation of inventory objects;
2024-05-02 16:31:22 +02:00
MontyTRC89
cd7583ab7a Enabled vsync;
Better mode switch between 30 and 60 fps;
Fixed flickering of strings in level (except for title);
2024-05-02 14:19:34 +02:00
Lwmte
4e9d4940a7 Simplify code 2024-05-02 09:53:21 +03:00
Lwmte
0a28ddb7cf Fix garbage frames on game status change 2024-05-02 09:47:53 +03:00
Lwmte
63a8de13f8 Fix leveljumps if 60 FPS is off 2024-05-02 09:42:00 +03:00
Lwmte
e3563bffb1 Fix hair glitch when stepping up 2024-05-02 09:19:25 +03:00
MontyTRC89
2d09926ff9 Merge branch 'develop' into develop_60fps
# Conflicts:
#	TombEngine/Game/effects/bubble.cpp
2024-05-02 06:07:33 +02:00
MontyTRC89
54346e4d85 Fixed flickering of strings in title level 2024-05-02 06:03:56 +02:00
Sezz
37267e99f1 Give meaning to magic numbers 2024-05-02 00:39:12 +10:00
Sezz
b868d6c518 Remove Ptr suffix usage in floordata 2024-05-01 21:27:54 +10:00
Sezz
39d638d1a5
Merge pull request #1211 from MontyTRC89/sezz_point_collision_class
PointCollisionData class
2024-05-01 15:51:52 +10:00
Sezz
07b4c1a997 Merge branch 'develop' into sezz_point_collision_class 2024-05-01 15:29:19 +10:00
MontyTRC89
62d28a08d1 Fixed corrupted items 2024-05-01 06:11:49 +02:00
MontyTRC89
cedad8c3ab Merge branch 'develop' into develop_60fps 2024-05-01 05:33:10 +02:00
MontyTRC89
81a44919f0 Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps 2024-05-01 05:32:55 +02:00
MontyTRC89
368f74e754 Added interpolation for title level 2024-05-01 05:32:49 +02:00
Stranger1992
c4f532268d Updated system strings for Variable Framerate 2024-04-30 16:19:30 +01:00
MontyTRC89
b2f7c83372 Started working on 60 fps inventory 2024-04-30 11:16:14 +02:00
MontyTRC89
2122a14d0e Variable framerate now is optional 2024-04-30 10:29:01 +02:00
Jakub
d83a84036e
Update Changes.txt 2024-04-29 12:55:59 +01:00
MontyTRC89
539bafc1b0 Added debris interpolation;
Added effects interpolation (missiles, projectiles...);
2024-04-29 09:09:35 +02:00
MontyTRC89
47e37d3960 Merge branch 'develop' into develop_60fps 2024-04-29 05:22:30 +02:00
MontyTRC89
80518e0abe Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps
# Conflicts:
#	TombEngine/Renderer/RendererDraw.cpp
2024-04-29 05:22:11 +02:00
MontyTRC89
f5adbec7c8 Added DisableInterpolation flag for items;
Fixed shaders;
2024-04-29 05:18:11 +02:00
Joey Quint
77cd0644f7 Fixed camera rotating with Lara's hips when climbing out of water - fixes #1311 2024-04-28 22:05:58 +02:00
Lwmte
33807e8198 Remove locked flag checks from internal renderer sprite methods 2024-04-28 15:51:51 +02:00
Lwmte
f2a55eec8b Fix double-draw in menus and title flyby 2024-04-28 15:18:24 +02:00
Lwmte
08ca9bacef Update Renderer.h 2024-04-28 13:59:56 +02:00
Lwmte
6e551b97df Make some order in renderer functions, fix flickering user strings and sprites 2024-04-28 13:54:16 +02:00
Lwmte
b3f8261c9b Fix sound update 2024-04-28 11:07:03 +02:00
MontyTRC89
b03480e926 Merge branch 'develop' into develop_60fps
# Conflicts:
#	TombEngine/Game/control/control.cpp
2024-04-28 06:36:13 +02:00
MontyTRC89
1d30d43aac Added color to lens flare;
Added LUA api for starfield and lens flare;
2024-04-28 06:31:59 +02:00
Nemoel-Tomo
7f9f5c619a
Bugfix: ember emitter, cleaner and squishy block (get valid sector) (#1358)
* bugfix, ember emitter, cleaner and squishy

* formatting

* Update misc.cpp

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-04-28 04:48:39 +10:00
Lwmte
9df06e0a63 Update Changes.txt 2024-04-27 11:00:18 +02:00
Sezz
dbfaf6c7fd Update water functions 2024-04-27 04:13:18 +10:00
Joey Quint
1e38baf8fc Fix incorrect diving animation when swandiving from a high place 2024-04-26 18:48:54 +02:00
Sezz
abb77ced54 Remove pointless const 2024-04-27 01:25:16 +10:00
Sezz
4d59cb6d03 Fix water ripple, splash sound, vehicle bugs 2024-04-27 00:24:09 +10:00
Sezz
1d2f3dc64d Rename struct member 2024-04-27 00:01:08 +10:00
Jakub
05376122b0 fix compile error 2024-04-26 10:52:01 +01:00
Kubsy
571f87f35f
Update Changes.txt 2024-04-26 10:45:40 +01:00
Lwmte
29a5fc7411
UseItem event (#1357)
* Implement UseItem event, refactor item usage a bit

* Decopypaste medipack hotkey usage code

* Prevent small and large medipacks from being used simultaneously; tidy up UseItem()

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-04-26 10:43:10 +01:00
Sezz
ad82158301 Illegal slope -> steep slope 2024-04-26 17:51:53 +10:00
Sezz
b73b31784c Add TODOs 2024-04-26 16:30:44 +10:00
Sezz
0817b64926 Merge branch 'develop' into sezz_point_collision_class 2024-04-26 16:25:06 +10:00
Sezz
bcd689b8ba Update collide_room.cpp 2024-04-26 15:53:02 +10:00
Sezz
546034ecc1 Apply PR comments 2024-04-26 15:49:27 +10:00
Sezz
15bd3d5217 Add bridge check to player move test over death sectors 2024-04-25 19:38:39 +10:00
Adngel
9dc4f9e24e
Adngel small fixes (#1355)
* Update tr2_worker_shotgun.cpp

Added enumerators for Animations and States.

* Fix Worker Shotgun walking attack

Now the enemy can hurt Lara with every shooting he does while walking.

* Fix skidoo driver lack of AI

* Update tr2_worker_shotgun.cpp

Use TestProbability()

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-04-24 15:25:22 +10:00
Lwmte
30a72eb71b Filter out code bits for legacy soundtrack mask 2024-04-24 00:42:00 +02:00
Lwmte
809d54d811 Update docs 2024-04-24 00:32:32 +02:00
Lwmte
46d1987816 Implement lua API functions to get last chosen inventory item 2024-04-24 00:30:02 +02:00
Lwmte
c13bd9ab19 Fixed original issue with classic switch off trigger wrongly activating some trigger actions 2024-04-24 00:21:00 +02:00
MontyTRC89
c241af1c70 Meteors interpolation 2024-04-23 20:59:43 +02:00
MontyTRC89
c55b252393 Global lens flare;
Starry night simulation;
2024-04-23 20:40:51 +02:00
Lwmte
29ebdf452f Update effects.cpp 2024-04-22 20:58:46 +02:00
Stranger1992
612e7de243 Squashed commit of the following:
commit a7dcba12ce
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Apr 21 19:39:05 2024 +0200

    Fix directional flame emitters
2024-04-21 22:58:19 +01:00
Lwmte
a7dcba12ce Fix directional flame emitters 2024-04-21 19:39:05 +02:00
Jakub
cb3cd84f6d Merge remote-tracking branch 'origin/develop' 2024-04-21 15:43:10 +01:00
Lwmte
fed654b06c Update Changes.txt 2024-04-21 16:30:55 +02:00
Lwmte
5cc7c24186 Rollback old incorrect collision check for top/bottom of solid statics 2024-04-21 15:24:58 +02:00
Lwmte
83e2bac286 Fix ember emitter color range as well 2024-04-21 12:07:41 +02:00
Lwmte
064d08e4f4 Properly calculate light values for dynamic light objects 2024-04-21 11:23:52 +02:00
Lwmte
7b1eec808e Allow arbitrary rotation of a directional flame emitter 2024-04-20 23:03:22 +02:00
Jakub
0d49a053ec Merge branch 'develop' 2024-04-20 14:27:43 +01:00
Lwmte
faf09d6f9c Merge both fixes from previous commits 2024-04-20 07:59:14 +02:00
MontyTRC89
e73bb7b92b Increased max lens flares to 8;
Choose the nearest 8 ones;
Make the code in post process optional only if some lens flares are available;
Max distance of lens flare 20 blocks and they must be in the same room of camera;
2024-04-20 06:44:12 +02:00
MontyTRC89
994dc69ce0 Implemented ID_LENS_FARE objects 2024-04-20 06:30:00 +02:00
MontyTRC89
c384a78e19 Merge branch 'develop' into develop_60fps 2024-04-20 06:01:50 +02:00
MontyTRC89
5468734174 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-04-20 06:01:35 +02:00
MontyTRC89
063b6cb010 Fixed blinking of water when there are debris 2024-04-20 06:01:31 +02:00
MontyTRC89
374697f38b WIP ID_LENS_FLARE objects 2024-04-20 05:41:01 +02:00
Lwmte
91c00db0f2 Fixed drawing of transparent surfaces when debris are present in scene 2024-04-20 00:49:37 +02:00
Lwmte
d53b3cf03d Add timeout for Moveable:Enable(), delete output.xml on build, update ldoc version label 2024-04-19 23:39:58 +02:00
Lwmte
a4227dddb0 Fixed collision for solid static meshes with zero collision box 2024-04-19 22:58:09 +02:00
Adngel
dbd164cf68 Update Changes.txt 2024-04-19 17:02:42 +02:00
Adngel
4943fa22a0 Bug Fix: Pushables wrong collision
Fixed a bug where climbable pushables reactivated their bridge collisions during the continuous push/pulling.
2024-04-19 14:59:26 +02:00
Adngel
7880d275b1 Bug fix: Trex double rotation
The rotation from AI.angle was correct, but looks like it was applying the same rotation to the head and the neck (was at the end was like duplicating the value).

I've edited these two lines to add only half of the angle the head and other half to the neck.
2024-04-19 13:34:45 +02:00
MontyTRC89
7d66d49742 Lens flares set by code and not hardcoded in shader 2024-04-18 16:58:07 +02:00
MontyTRC89
4bbf52abf5 Polished the lens flare pixel shader 2024-04-18 15:58:47 +02:00
MontyTRC89
ce403506b5 Lens flares prototype 2024-04-18 09:38:15 +02:00
MontyTRC89
d0a451f866 Merge branch 'develop' into develop_60fps 2024-04-18 05:52:43 +02:00
MontyTRC89
ff32cca745 Shadow maps interpolation 2024-04-18 05:52:20 +02:00
Lwmte
633d6dc4c5 Update footprint.cpp 2024-04-17 11:59:57 +02:00
Sezz
2632a28398 Add bridge check to pad trigger 2024-04-17 17:58:22 +10:00
Lwmte
65fe27c272 Update lara_collide.cpp 2024-04-17 09:56:48 +02:00
MontyTRC89
c11eab5f58 Hairs interpolation 2024-04-17 05:31:19 +02:00
Lwmte
4a95305191 Fix #1350 2024-04-17 02:24:49 +02:00
Lwmte
b6d22844bf Fixed fire item effect not extinguishing in water 2024-04-17 02:16:14 +02:00
Lwmte
ccf77beff3 Fixed diving when swimming over sinks 2024-04-17 01:37:32 +02:00
MontyTRC89
d37eb6a69b Fixed bug with last merge 2024-04-16 09:18:56 +02:00
MontyTRC89
d636d58c1b Merge branch 'develop' into develop_60fps
# Conflicts:
#	TombEngine/Renderer/Structures/RendererItem.h
2024-04-16 09:16:16 +02:00
MontyTRC89
146c89b888 Added interpolation for.
- splashed
- ripples
- blood
- drips
- underwater dust
- rain
- snow
- shockwaves
- all effects based on Particle class
2024-04-16 09:15:00 +02:00
Sezz
4aa91ea877 Remove NO_ROOM constant 2024-04-16 16:55:19 +10:00
Sezz
c455fc6a39 Merge 2024-04-16 16:50:49 +10:00
Sezz
a4d0250cfb Merge branch 'develop' into sezz_point_collision_class 2024-04-16 16:50:13 +10:00
Sezz
3e9addf98a Add bridge check to ProcessSectorFlags(); remove NO_BOX and NO_ZONE constants 2024-04-16 16:08:40 +10:00
Lwmte
178f3c96b5 Fix infinite loop on leveljump to start position 2024-04-16 01:17:58 +02:00
MontyTRC89
d5526be208 Improved fires and flares interpolation;
Fixed fast blinking of dynamic lights;
2024-04-15 11:08:41 +02:00
MontyTRC89
1187910439 Merge branch 'develop' into develop_60fps 2024-04-15 08:33:29 +02:00
MontyTRC89
f4a5644dc0 Gunshells interpolation 2024-04-15 08:33:10 +02:00
Lwmte
f6cfb0508d Add Timer.Delete() method to a timer module 2024-04-15 01:10:36 +02:00
Lwmte
8d3eeb6879 Fix possible crashes with incorrectly passed particle lifetime in lua 2024-04-14 01:43:37 +02:00
Nemoel-Tomo
b0767730ed
Hotfix - Laserbeam light (#1349) 2024-04-14 00:23:26 +03:00
Lwmte
3189b8093d Upgrade version number 2024-04-13 12:41:21 +02:00
Sezz
7b0ae84c22 Fix crosshairs not disappearing for peripherally exploded items 2024-04-13 18:32:54 +10:00
MontyTRC89
98c8f6df91 Cleaning smoke sparks code 2024-04-13 06:02:58 +02:00
Stranger1992
3890f0d224 Update Lua API parameters correctly 2024-04-13 02:05:43 +01:00
Stranger1992
1a94a53c56 Update moveable Lua API 2024-04-13 01:58:58 +01:00
Lwmte
266f1b5b6a
Use real bounding box tests for GetCollidedObjects (#1343)
* Initial commit

* Use DX bboxes for item collision tests

* Update collide_item.cpp

* Update collide_item.cpp

* Update collide_item.cpp

* Code cleanup

* More code cleanup and optimizations

* Remove obsolete radius argument for GetCollidedObjects, mode code cleanups

* Fix ITEM_INVISIBLE typos

* Restore custom radius where needed

* Restore custom radius for polerope

* Function cleanup

* Simplify function

* Remove CollidedItems and CollidedMeshes globals

* Use custom radius for pickups and explosion

* Add changelog, shuffle function order

* Simplify

* fix syntax error

* Rename local variable

* Update collide_item.cpp

---------

Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Jakub <kubabilinski03@gmail.com>
2024-04-12 23:34:30 +10:00
Lwmte
35bf1470c9 Fix issues with squishy block, rollback incorrect ItemPushItem fix 2024-04-12 00:46:53 +02:00
MontyTRC89
44c52201d0 Added partial support to fire and smoke interpolation 2024-04-11 14:27:53 +02:00
MontyTRC89
180f03e960 Merge branch 'develop' into develop_60fps 2024-04-11 14:06:44 +02:00
MontyTRC89
30a2c20cd4 Fixed possible crashes with bats emitter 2024-04-11 14:04:22 +02:00
MontyTRC89
62e2394a61 Fixed crashes with scarabs drawing 2024-04-11 14:02:09 +02:00
MontyTRC89
72b3aa9b7e Blood interpolation 2024-04-11 12:08:22 +02:00
MontyTRC89
d10423c0e0 Smokes particles interpolation 2024-04-11 10:47:03 +02:00
MontyTRC89
35c4ce79ee Merge branch 'develop' into develop_60fps
# Conflicts:
#	Documentation/output.xml
#	TombEngine/Game/control/control.cpp
#	TombEngine/Specific/clock.cpp
#	TombEngine/Specific/clock.h
2024-04-11 06:06:24 +02:00
MontyTRC89
2e5867407e Fixed wrong alpha test in alpha blended faces 2024-04-10 06:08:47 +02:00
Lwmte
743022820d Update Changes.txt 2024-04-07 08:16:37 +02:00
Stranger1992
6c7f7e7419 Update Changes.txt 2024-04-06 08:11:09 +01:00
Stranger1992
1e9fcdf001 Update Changes.txt 2024-04-06 08:10:26 +01:00
Nemoel-Tomo
036a846a69
TR4 squishy block 1 + 2 (#1337)
* port of the TR4 squishy blocks 1 and 2
* Slot rename:  ID_SQUISHY_BLOCK_HORIZONTAL , ID_SQUISHY_BLOCK_VERTICAL
---------

Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
2024-04-06 08:06:48 +01:00
Lwmte
b8b76cd9c9 Update Flow.html 2024-04-06 08:24:22 +02:00
Lwmte
e7b36e5692 Update FlowHandler.cpp 2024-04-06 08:21:14 +02:00
Lwmte
86f9cb86c7 Add GetFlipMapStatus function 2024-04-06 08:16:07 +02:00
MontyTRC89
3c3acaeb82 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-04-06 06:12:45 +02:00
MontyTRC89
8ece609f4c Fixed slpha blended sprites 2024-04-06 06:12:38 +02:00
Lwmte
08b1bfa9ab Uncomment flipmap documentation 2024-04-04 21:33:49 +02:00
Kubsy
0511afab9f
Update Changes.txt 2024-04-01 16:06:11 +01:00
Nemoel-Tomo
9bd6bc09da
Fish Emitter (#1332)
* Tomo - FishEmitter

* nothing to see

* update fishes

* update fishes

todo: draw fishes, formatting, leader fish movement, make fish species with ocb

* update leader fish movement

* Update little fishes behaviour

Calculate the distance to the other fish

* update fish and appearance

* formatting

* formatting

* Formatting and cleanup

* Formatting

* Formatting; remove unused variables

* update ocb codes and behavior of the fishes

* update fish escape behavior

* formatting

* removed debug lethal value

* changing number of fish by changing HitPoints

* formatting

* update randomTargetGenerator

* add wiggle to the fish, clear fish swarm with level change

* update wiggle

* fish movement update

* update OCB values and update wiggle

* Use std::vector; formatting + cleanup

* Simplify; formatting

* Delete fish.h

* Rename files; update water surface clamp

* make fish not targetable

* update randomtarget: sphere radius, don't spawn outside room

* formatting

* fish can now follow a path (AI_FOLLOW)

You can use any number of Ai objects for a lap. If you then want to make a round for another school of fish, you simply leave out an OCB number. This way the engine knows which numbers belong to which swarm. the first AI_FOLLOW (the one that's on the same square as the fishemitter does not count for patrolling but also needs an OCB)

* Formatting

* Add constant

* Formatting

* Remove unused parameter

* Add GeneratePointInSpheroid() function

* create random target within a spheroid

* steering fishes with UPV

* using Pose.Position for savegame

* Remove includes; update math function; change back to float positions; save fish

* bugfix and resolving conversations

* Fix formatting

* Remove space

* Update fish update interval; add TestGlobalTimeInterval() function

* Update clock.cpp

* Add offset argument to TestGlobalTimeInterval()

* Update clock functions

* Update clock.cpp

* fixed last bugs, resolve conversation

* fixed errors

* Minor formatting

* Remove player state check

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-04-01 16:05:08 +01:00
Sezz
5b7816eb96 Further organise ItemInfo struct 2024-03-31 23:50:19 +11:00
Sezz
369a6dd539 Additionally replace NO_JOINT with NO_VALUE 2024-03-31 23:19:37 +11:00
Sezz
414970ccff
Adopt NO_VALUE constant (#1340) 2024-03-31 22:37:06 +11:00
Sezz
9a98ce04c1 Reorganise script VolumeObject.cpp according to standards 2024-03-31 02:02:48 +11:00
Sezz
2309c85374 Reset spotcam changes to be merged from branch 2024-03-29 23:59:21 +11:00
Sezz
bd4a735c84 Update SPOTCAM struct 2024-03-29 17:55:49 +11:00
Sezz
0cb2bf9148 Update spotcam.h 2024-03-28 21:08:07 +11:00
Sezz
75753286ea Update spotcam.h 2024-03-28 21:03:39 +11:00
Sezz
fcf10a61d6 Merge branch 'develop' into sezz_point_collision_class 2024-03-28 19:27:15 +11:00
Sezz
bab278f9df Rename file and namespace 2024-03-28 19:27:02 +11:00
Sezz
feeac00746 Use Vector3i for spot camera positions 2024-03-28 19:04:13 +11:00
Sezz
4564084997 Update spotcam.cpp 2024-03-28 18:37:31 +11:00
Sezz
5d635d93b0 Update lara.cpp 2024-03-27 19:41:07 +11:00
Sezz
8e55fe316e Update comments 2024-03-27 16:42:43 +11:00
Sezz
d9d89b4139 Add comment 2024-03-27 16:40:37 +11:00
Sezz
e17543c497 Update comments 2024-03-27 16:38:55 +11:00
Sezz
181c9cdc56 Merge branch 'develop' into sezz_point_collision_class 2024-03-27 16:33:04 +11:00
Sezz
fbb6236e40 Convert CollisionType to enum class 2024-03-27 16:29:21 +11:00
Sezz
1ae94fcffc Update TombEngine.vcxproj 2024-03-26 23:17:26 +11:00
Sezz
bb0cc0d491 Update comments 2024-03-26 16:05:48 +11:00
Sezz
1d0916a768 Remove caps from laser cylinder 2024-03-26 15:48:58 +11:00
Sezz
bb74da582e Use newer light function for laser beam 2024-03-26 15:45:50 +11:00
Sezz
31b01122a6 Fix tabulation 2024-03-26 15:12:30 +11:00
Lwmte
982eb38f15 Update Objects.ObjID.html 2024-03-25 23:38:05 +01:00
Nemoel-Tomo
3bc9d08f17 Bugfix. single laserbeams were active before they were triggered. 2024-03-25 22:45:25 +01:00
Jakub
370f4269a2 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-03-25 21:25:14 +00:00
Jakub
f74158a326 fix incorrect laser noiseValue calculation 2024-03-25 21:25:03 +00:00
Kubsy
d2ff29cf4d
Update Changes.txt 2024-03-25 20:55:51 +00:00
Nemoel-Tomo
ffd9c21d50
Single Laser tr3 - Tripwire (#1336)
* first try of the TR3 laserbeams - nothing to see

* update laser; thickness with ocb

* fixed collision

* SLOT-change.  NEWSlot ID #426

replaced ID_TRIPWIRE with ID_LASER_BEAM

* Rename files

* Rename functions

* Cleanup

* Simplify laser beam

* Cleanup

* Construct laser beam cylinder

* changed shader

* Set beam radius with OCB

* fixed shader for laserbeam

* fixed shader for single laserbeam; finished code

* Update TombEngine.sln

Win32 restored on lines 18 and 19

* update ObjectsIDs.h

* moved laser effects into SpriteEffects.hlsli

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-03-25 20:50:21 +00:00
Lwmte
c8cbb73ed9 Expose static mesh hit points to lua API, cleanup script code a bit 2024-03-25 20:31:31 +01:00
Lwmte
6c9f644cd3 Added GetMeshCount script function 2024-03-24 13:38:55 +01:00
Sezz
8cac8807aa Remove useless includes; add NO_VALUE constant 2024-03-24 17:10:58 +11:00
Kubsy
d2e3f596fe
Update Changes.txt 2024-03-22 22:57:53 +00:00
Nemoel-Tomo
50ea54dd95
EmberEmitter (#1335)
* EmberEmitter - nothing to see right now!!!

* update

* added object tint to ember color, renamed ID_HIGH_OBJECT2 to ID_EMBER_EMITTER

* added color black (0,0,0)

* added the color grey

* Rename file

* Fix formatting

* More formatting

* Use namespace

* Convert legacy math

* adjustable size with ocb

* fixed random color variation

* Formatting

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-03-22 22:56:29 +00:00
Sezz
bf3d5849ac Fix target highlighter crash 2024-03-21 20:39:04 +11:00
Lwmte
5383dc0056
Hub system (#1333)
* Work in progress

* Work in progress 2

* Work

* Fix Lara meshswaps appearing

* Update control.cpp

* Work

* Update lara_initialise.cpp

* Solve some problems with default weapon and holsters

* Implement actual resetHub script command, add comments and safeguards

* Update Changes.txt

* Update Changes.txt

* Additional refactors

* Add logging for hub system

* Add savegame verification, correct size writing method

* Transfer vehicles between levels

* Log vehicle transfer

* Update lara_initialise.cpp

* Move all vehicle transfer code to InitializeLaraLevelJump

* Also move health initialization to appropriate function

* Put all static fields into SaveGame class

* Formatting

* Fix pelvis pistol

* Partially fix hub vehicle loading

* WIP

* Introduce a hack for reinitializing vehicles on a hub

* Fix holsters again

* Update lara_initialise.cpp

* Better way to preserve crawl state on leveljump

* Update lara_initialise.cpp

* Update savegame.cpp

* Add IsOnHub

* Fix disappearing Lara

* Update lara_initialise.cpp

* Move inventory objects init to subfunction

* Fix issues with LevelVars reset and default soundtrack on hub restore

* Update lara_initialise.cpp

* Formatting

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-03-21 09:06:50 +00:00
Adngel
4beded7cad Dragon pickups OCB 1
- deleted the collision of the bones corpse
- now if Dragon dies for Dagger, the pickups will be received to Lara instead of spawn on the floor.
2024-03-19 11:47:34 +01:00
Adngel
9d1102222d Update Dragon.cpp
- Changed dagger pickup animation.
- Changed smoke render shader
2024-03-19 11:14:55 +01:00
Adngel
b65bbd4b82 Update Bartoli.cpp
Fixed the default time to spawn the 3rd sphere before Marco dies for finishing his animation.
2024-03-18 23:55:20 +01:00
Sezz
f12353633a Have target highlights expand from origin instead of closing in on origin when spawning 2024-03-19 01:39:56 +11:00
Sezz
7c0a72f4fe Allow source files with identical names to exist in different directories 2024-03-19 01:35:01 +11:00
Sezz
2366bcbd1d Update comment; rename methods 2024-03-10 15:49:42 +11:00
Sezz
118c6d6105 Fix final bug; remove debug code 2024-03-10 15:05:02 +11:00
Sezz
4fc4450c4b Tidy up HUD methods 2024-03-10 13:31:24 +11:00
Sezz
067cc73dce Remove legacy math struct 2024-03-07 16:26:56 +11:00
MontyTRC89
c44e4d40be Clock reset on window focus change, inventory and pause menu 2024-03-07 06:16:12 +01:00
MontyTRC89
96d08f41e7 Fixed lag logic 2024-03-06 14:50:16 +01:00
MontyTRC89
f9589155d5 Fixed horizon flickering 2024-03-05 09:53:12 +01:00
MontyTRC89
e84ddfa36d First experiment with 60 fps 2024-03-05 09:25:26 +01:00
Lwmte
9f99115003 Update Changes.txt 2024-02-26 03:23:11 +01:00
Lwmte
9fc85346ac Update Changes.txt 2024-02-26 03:20:15 +01:00
Lwmte
a479606481 Fixed #1246 2024-02-26 03:18:20 +01:00
Sezz
389ba72c44 Remove debug code 2024-02-23 21:56:33 +11:00
Sezz
4ee1dda75f Remove last remnants of GetCollision() and related structs; remove old collision functions 2024-02-23 20:41:22 +11:00
Sezz
5a1b1c89bd Remove GetCollision() overload 2024-02-23 18:12:27 +11:00
Sezz
f38968b23b Merge branch 'develop' into sezz_point_collision_class 2024-02-23 13:57:11 +11:00
Sezz
ab40e95ae9 Fix legacy slide angles 2024-02-19 16:51:37 +11:00
Sezz
3549107013 Improve HUD element appearance 2024-02-19 16:31:59 +11:00
Lwmte
fcd22caafb Display missing string ID instead of "string not found" message 2024-02-18 18:36:31 +01:00
Lwmte
60a89741c6 Fixed #1303 2024-02-18 12:47:03 +01:00
Lwmte
35e411b9a8 Fixed #1312 2024-02-18 02:48:07 +01:00
Jakub
fec6828928
Update Changes.txt 2024-02-17 10:16:06 +00:00
Jakub
b08f2fcbe2
Update Changes.txt 2024-02-17 10:14:56 +00:00
Nemoel-Tomo
8a16b9db86
Revised spiky wall (#1327)
* remove collision for geo for spiky wall

To stop the wall, just make a volume trigger and set the OCB of the wall to 0

* fixed pushing object

* update spiky wall

it explodes shatterable statics, kill enemies, stops at another spiky wall

* Update SpikyWall.cpp

* Update SpikyWall.cpp

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2024-02-17 10:13:01 +00:00
Lwmte
878a34a85d Fixed #1321 2024-02-17 10:18:03 +01:00
Lwmte
a645086112 Fixed #1328 2024-02-17 10:00:37 +01:00
Sezz
3f219f85d4 Add comments to floordata; simplify 2024-02-14 18:51:28 +11:00
Lwmte
3c4b6fcf3c
Ldoc portable compiler (#1330)
* Add portable ldoc compiler and build event

* Remove broken ldoc compiler

* Remove unneeded file, remove timestamp

* Update documentation
2024-02-13 17:02:25 +00:00
Sezz
f9fd81266f Remove remaining output arguments from floordata functions 2024-02-12 20:25:07 +11:00
Sezz
f286f70387 Remove output arguments from floordata functions 2024-02-12 19:41:33 +11:00
Sezz
bda2650862 Move WALL_PLANE constant 2024-02-12 18:22:40 +11:00
Sezz
3001e3ccfe Simplify floordata 2024-02-12 18:05:25 +11:00
Sezz
4ac616bbc1 Simplify floordata 2024-02-12 17:29:14 +11:00
Sezz
b98995bf32 Update BitField 2024-02-12 16:54:56 +11:00
Sezz
2b2b06aebd Formatting 2024-02-12 16:51:02 +11:00
Stranger1992
a77ada8f45 Add Lua API generator and regenerated docs 2024-02-11 22:28:56 +00:00
Adngel
6a863a86e9 Update Changes.txt 2024-02-11 19:37:43 +01:00
Adngel
cc333e2f32 Dragon Implementation Merge
commit 03d9df2199
Merge: 3ad6a1b25 acf385693
Author: Adngel <adngel@hotmail.com>
Date:   Sun Feb 11 19:23:38 2024 +0100

    Merge branch 'develop' into Dragon_update

commit 3ad6a1b252
Author: Adngel <adngel@hotmail.com>
Date:   Mon Feb 5 12:54:21 2024 +0100

    Added safety measures for Bartoli ItemFlags[3]

    Now if the 3rd explosion is deactivated using a negative value for ItemFlags[3], Bartoli will still be destroyed activate heavy trigger when its animation reaches to the end.

commit beccf36bf0
Merge: 1e5d93b93 919ff5065
Author: Adngel <60930991+Adngel@users.noreply.github.com>
Date:   Mon Feb 5 12:15:33 2024 +0100

    Merge branch 'develop' into Dragon_update

commit 1e5d93b93b
Author: Adngel <adngel@hotmail.com>
Date:   Tue Jan 30 19:37:06 2024 +0100

    Update collision function for bones.

commit 83a2e53150
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Tue Jan 30 18:14:05 2024 +0000

    Issue fix

    * Add shadow for the dragon bones, and back half of the dragon
    * Added randomness to the fire and smoke sprite

commit b7b7dd008d
Author: Adngel <adngel@hotmail.com>
Date:   Tue Jan 30 17:28:44 2024 +0100

    fix collision routine for ocb 1.

commit 259063bc00
Author: Adngel <adngel@hotmail.com>
Date:   Tue Jan 30 16:32:20 2024 +0100

    Rename :Zero for :Identity

commit fc7cf96355
Merge: 993f19899 38503b262
Author: Adngel <adngel@hotmail.com>
Date:   Tue Jan 30 16:26:58 2024 +0100

    Merge branch 'develop' into Dragon_update

commit 993f19899e
Merge: dd6eaceff 94bfadca5
Author: Adngel <adngel@hotmail.com>
Date:   Tue Jan 30 16:01:20 2024 +0100

    Merge branch 'develop' into Dragon_update

commit dd6eaceff8
Author: Sezz <sezzary@outlook.com>
Date:   Mon Jan 29 14:28:03 2024 +1100

    Fix formatting

commit 49477d2954
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Mon Jan 29 01:51:43 2024 +0000

    Add additional lighting to Bartolli transformation

    In the original TR2: the light was white. But that, I believe, is because their engine did not have coloured lighting in. But for the TEN version I have changed it to  green to match the "Sphere of Doom" colour.

commit e45bce8107
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Mon Jan 29 00:43:59 2024 +0000

    Add and restore missing TR2 sound effect

commit 745063d740
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sat Jan 27 00:21:52 2024 +0000

    symbol rename for clarity

commit e38a5d2690
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sat Jan 27 00:20:27 2024 +0000

    Add inhale effect before fire

commit 3a06a7a546
Merge: 0bbcefd2b b29b68e36
Author: Adngel <adngel@hotmail.com>
Date:   Thu Jan 25 12:03:16 2024 +0100

    Merge branch 'develop' into Dragon_update

commit 0bbcefd2b8
Author: Adngel <adngel@hotmail.com>
Date:   Thu Jan 25 11:49:55 2024 +0100

    Back Collision reactivated

    It still is deactivating the collision of the back half to make easier for the player to reach the dagger.

commit a35315bb65
Author: Adngel <adngel@hotmail.com>
Date:   Thu Jan 25 11:32:54 2024 +0100

    Explosion times in ItemFlags

    So builders will can customize them if they change Bartoli's animation.

commit 03ffcfeaf7
Author: Adngel <adngel@hotmail.com>
Date:   Thu Jan 25 11:23:43 2024 +0100

    Heavy Trigger activation

    Bartoli now activates the heavy Triggers located in its sector. It doesn't use the ItemFlag[1] to decide what enemy spawn..

commit 15bb5e22fa
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 21 21:24:50 2024 +0000

    Add missing TR2_Dragon_fall SFX

commit c84e0aedf6
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 21 17:00:13 2024 +0000

    Add SFX to transformation effect

commit d70c19f55e
Author: Adngel <adngel@hotmail.com>
Date:   Sun Jan 21 16:52:13 2024 +0100

    BlendMode update

    Updating to the new formatting.

commit 0da4308460
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 21 15:25:41 2024 +0000

    Squashed commit of the following:

    commit b29b68e36b
    Author: Adngel <adngel@hotmail.com>
    Date:   Wed Jan 17 20:16:51 2024 +0100

        Updated comment (unused animation 442)

    commit 74425602ae
    Author: MontyTRC89 <montyhammet@hotmail.it>
    Date:   Sat Jan 13 13:17:54 2024 +0100

        Fixed darker title

    commit f92fa759e2
    Merge: 188b69e5a 8df57a57c
    Author: MontyTRC89 <montyhammet@hotmail.it>
    Date:   Fri Jan 12 05:47:40 2024 +0100

        Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop

    commit 188b69e5aa
    Author: MontyTRC89 <montyhammet@hotmail.it>
    Date:   Fri Jan 12 05:47:35 2024 +0100

        Fixed drawing of fullscreen sprites in title

    commit 8df57a57c7
    Author: Sezz <sezzary@outlook.com>
    Date:   Thu Jan 11 16:43:29 2024 +1100

        Add GetBoundingBox() and GetClosestPointOnLinePerp() functions; minor updates to geometry math

    commit 1635f9048f
    Author: Sezz <sezzary@outlook.com>
    Date:   Thu Jan 11 16:31:29 2024 +1100

        Update TranslatePoint() overloads

    commit 1d06a4c9b8
    Author: Sezz <sezzary@outlook.com>
    Date:   Thu Jan 11 02:50:15 2024 +1100

        Use AxisAngle in TranslatePoint() overload

    commit ba648e1383
    Author: hispidence <squidshirehimself@gmail.com>
    Date:   Sun Jan 7 13:22:52 2024 +0000

        Fixed doc comments and regenerated docs.

    commit d1c0050d8d
    Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Date:   Sat Jan 6 15:26:46 2024 +0100

        Update config.ld

    commit 657e3fb2ee
    Author: Jakub <80340234+Kubsy@users.noreply.github.com>
    Date:   Sat Jan 6 13:52:24 2024 +0000

        merge (#1313)

        * Update New_Level.lua

        * Revert "Update New_Level.lua"

        This reverts commit 90b2d00644.

        * Revert "Revert "Update New_Level.lua""

        This reverts commit 7fc3191b4d.

    commit 9f38943005
    Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Date:   Sat Jan 6 13:36:18 2024 +0100

        Update scripts structure

    commit 860b44223b
    Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Date:   Sat Jan 6 13:16:26 2024 +0100

        Update Changes.txt

    commit 9192981417
    Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Date:   Sat Jan 6 12:35:29 2024 +0100

        Update Resources.rc

    commit a0a5ee68db
    Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Date:   Sat Jan 6 12:33:42 2024 +0100

        Update Changes.txt

    commit 4ea26e5de7
    Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Date:   Sat Jan 6 09:07:16 2024 +0100

        Fix several issues with LARA_START_POS

    commit e4b286e41d
    Author: Jakub <80340234+Kubsy@users.noreply.github.com>
    Date:   Fri Jan 5 22:23:22 2024 +0000

        Update Changes.txt

    commit b63cf527bc
    Author: Jakub <80340234+Kubsy@users.noreply.github.com>
    Date:   Fri Jan 5 22:19:52 2024 +0000

        Renderer refactor (#1287)

        * Fixed vertex effects for statics

        * WIP implementing SMAA antialiasing

        * Fixed double drawing of additive faces

        * SMAA working, need to tune it

        * Cleaned SMAA code, ready for test

        * Formatting

        * Renderer cleaning Tier 1

        * Renamed Renderer11 to Renderer;
        Forced DX11 hardware support;
        Removed m_ from private fields in Renderer class;
        Refactored render targets: optional depth buffer creation;
        Back buffer now is a RenderTarget2D;

        * Converting constexpr and defines to enum classes

        * Converting enum to enum classes, tier 2;

        * Converting constexpr to enum classes, tier 3

        * Cleaned enums and constants; Renamed some files;

        * Fixed shaders; Renamed AlphaTestModes;

        * Cleaned shaders directory; Reverted hardware version to DX10.1 (we'll upgrade it again in the next releases);

        * Renamed private fields;
        Refactored quad vertex buffer for sprites;
        Refactored HUD renderer structure;
        Minor fixes;

        * Deleted some files

        * Removed reference to RendererQuad.h

        * New renderer transparency, for rooms now

        * Continue work

        * Implemented WOIT for alpha blending;
        Started renderer refactoring;

        * Removed redundant DirectX api calls

        * Added G-Buffer with normals and depth

        * Refactored and optimized particle enemies (rats, bats, locusts, beetles);
        Extended G-Buffer to effects, rats, bats, locusts, beetles, gunshells, debris;
        Extended animated textures sequence to 256;
        Fixed normal mapping for items and statics;

        * Removed WOIT code;
        Removed write to depth map in opaque pass (it's already done in depth pre-pass);
        Cleaned debris rendering code;

        * Removed legacy render targets;
        Added dual paraboloid mapping for ambient light (to finish the light shader):
        Minor cleanups;

        * Implement additive debug triangles

        * Unhardcode value

        * Finished room ambient code, commented for now (need to write a gaussian filter);

        * Fixed wrong constant buffer slot in SMAA

        * Renamed draw effects functions;
        Moved prepare sprites function before the draw code;
        Restored additive drawing;
        Restored alpha blend faces collect;

        * Enhance visual debug object API

        * Partially restored sprites drawing

        * Rename local variables

        * Improve spheres

        * Update names

        * Rename function

        * Remove unneeded parameter

        * Formatting

        * Cleaned and optimized drawing of opaque/additive sprites

        * Removed legacy alpha blend code;
        Added drawing of alpha blended faces for rooms;

        * Completed drawing of non commutative objects;
        FIxed compilation;
        Removed old files;

        * Refactored debug statistics for renderer

        * Fixed FPS calculation

        * Removed not used render targets and shaders

        * Added base post-process framework;
        Added monochrome and sepia effects;

        * SSAO WIP

        * SSAO almost working, needs tuning;
        Changes to G-Buffer;

        * Removed .cpp files in Graphics directory and moved constructors to headers;
        Templatized VertexBuffer class;
        New post-process system work in progress;
        New blur stage for SSAO;

        * SSAO at 1/4 resolution

        * Removed FinalPass.fx and moved pixel shader to PostProcess.fx;
        Added color to PostProcessVertex;
        Rewritten the post-process step using new system;
        Tweaked some variables for SSAO;
        Fixed drawing of render targets debug thumbnails;

        * Rewritten antialiasing techniques with new post-process system;
        Made depth target optional when creating render targets using UNKNOWN DXGI format;

        * Fix for empty vertex and index buffers

        * Encode normals to RGBA8 render target;
        Fixed samplers of depth texture;

        * Removed legacy hairs shader

        * Restored SSAO at fuill res for now;
        Fixed shaders structs;

        * Optimized G-Buffer shaders for speed and avoid DX log spam;
        Renamed updateData to UpdateData in constant buffers;

        * Bilateral gaussian filter for SSAO

        * Fixed wrong enum

        * Cleaning constant buffers tier 1

        * Added SSAO setting

        * Fixed shaders

        * Fixed shaders

        * Update SystemStrings.lua

        * Refactored initialization code;
        Added read and write of ambient occlusion in Windows registry;
        Refactored shaders;
        Added LUA API for setting color scheme;
        Removed some old variables;

        * Added effect strength to post-process framework

        * Some shaders cleaning;
        Don't calculate SSAO if disabled;

        * Restored wireframe mode

        * Revert "Merge branch 'develop' into renderer_refactor"

        This reverts commit 4706f46982, reversing
        changes made to bd413d00b2.

        * Add negative postprocess effect

        * Update PostProcess.fx

        * Removed some DX log spam in SMAA shaders;
        Fixed crash while drawing alpha blended faces from static objects;

        * Fixed alpha blended faces drawing double sided for items and statics

        * Fixed flickering of alpha blended faces of rooms

        * Fixed gunflashes

        * Revert "Revert "Merge branch 'develop' into renderer_refactor""

        This reverts commit 536c555b2d.

        * Fixed shadow mapping for point lights

        * Fixed bad merge in speedometer

        * Fix display pickup crash

        * Fix SSAO toggle with left key

        * Refactoring postprocess effect workflow

        - Added exclusion mode
        - Separated effect mode and strength to different functions
        - Reset postprocess on level start
        - Preserve postprocess on savegame reload
        - Enum and function renames to maintain consistency

        * Fix some comments

        * Fix display pickup drawing

        * Rename

        * Fix for lights not affecting big static objects

        * Fixed antialiasing for pickup objects and for inventory scene

        * Fixed ghost polygons bug

        * Fixed SSAO on surfaces with no or invalid normals

        * Fix merge

        * Bypass postprocessing if effect strength is set to 0

        * Update SystemStrings.lua

        * Update Changes.txt

        * Update Changes.txt

        * Turn on SSAO and target highlighter by default

        * Remove duplicated changelog entries

        * Implement LARA_START_POS object handling

        * Update lara_initialise.cpp

        * Update Changes.txt

        * Update Changes.txt

        * Fixed double sided alpha blend faces again

        * Added things to changes.txt

        * Update Changes.txt

        * Don't use LARA_START_POSes with OCB 0

        * Fixed 3D sprites like footprints

        * Fixed glow for statics;
        Fixed world position for items;

        * Per-triangle sorting alpha blend

        * Fixed missing last polygons in alpha blending

        * Code cleaning

        * Added alpha blending to rats, bats, locusts, scarabs

        * Restored fast alpha blend

        * Fixed AA in title level

        * Improved light collector;
        Added code for disable statics instancing if needed for debug;

        * Fixed rare wrong caustics textures sizes

        ---------

        Co-authored-by: MontyTRC89 <montyhammet@hotmail.it>
        Co-authored-by: Sezz <sezzary@outlook.com>
        Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
        Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>

    commit e6c92decf3
    Author: Adngel <adngel@hotmail.com>
    Date:   Thu Jan 4 11:06:18 2024 +0100

        ObjectCollisions update

        For object collisions with other items that are not Lara. (Like vehicles hitting enemies or colliding with pushables or traps).

    commit 48c2158c42
    Author: Joey Quint <joey_quint@hotmail.fr>
    Date:   Mon Jan 1 22:24:38 2024 +0100

        Fix typo in CalculateHorizontalDistance()

    commit 0ad626c1d6
    Author: Adngel <adngel@hotmail.com>
    Date:   Sat Dec 30 12:36:53 2023 +0100

        Wolf new OCB

          - OCB 0: Wolf starts in walking animation, ready to chase Lara.
          - OCB 1: Wolf starts in sleeping animation. (As it was before).

commit 33ee22f142
Author: Adngel <adngel@hotmail.com>
Date:   Fri Dec 29 22:28:26 2023 +0100

    New HitEffect::NonExplosive

    A hitEffect that emit blood on hit, but if killed with explosive ammo, it won't trigger the death. (What will avoid the explosion, and in case like Dragon, it will can keep doing its custom death routine).

commit ba80130222
Author: Adngel <adngel@hotmail.com>
Date:   Fri Dec 29 21:41:41 2023 +0100

    Update Collision function

    Now Collision function is only used by front half, removing mentions and references to the back half.

commit fd11ecf3d2
Author: Adngel <adngel@hotmail.com>
Date:   Fri Dec 29 20:46:03 2023 +0100

    HACK collision conflict fix

    I had to deactivate the collision of the back half to avoid the shacking of the dragon due to the collision hit with the front half.

commit 9b5141e9b8
Author: Adngel <adngel@hotmail.com>
Date:   Fri Dec 29 15:55:56 2023 +0100

    OBC note comment

commit 9c56a58cc1
Author: Adngel <adngel@hotmail.com>
Date:   Fri Dec 29 12:27:23 2023 +0100

    Removed Bartoli extra OCB

    Now Bartoli only spawns at the level beggining, and only will activate its action when he gets triggered. Ignoring Lara distance.

    To make him dissapear or reappear, builder can use the SetInvisible and SetStatus functions.

commit 41e23b2815
Merge: ece01e7a4 07879bc74
Author: Adngel <adngel@hotmail.com>
Date:   Fri Dec 29 11:54:34 2023 +0100

    Merge branch 'develop' into Dragon_update

commit ece01e7a40
Author: Adngel <adngel@hotmail.com>
Date:   Sun Dec 10 15:53:47 2023 +0100

    Bartoli ItemFlags 0

commit 5f32f7546b
Author: Adngel <adngel@hotmail.com>
Date:   Sun Dec 10 14:25:15 2023 +0100

    Deactivate Collision during death effect

    So Lara won't collide with dragon meshes while these are sinking in the floor.

commit 269069521e
Author: Adngel <adngel@hotmail.com>
Date:   Sun Dec 10 13:43:30 2023 +0100

    Fix mesh color issue

commit 56e8b29e99
Author: Adngel <adngel@hotmail.com>
Date:   Sun Dec 10 13:39:30 2023 +0100

    Code recovered from old branch
2024-02-11 19:29:14 +01:00
Lwmte
acf385693e Fixed sounds resuming in pause mode while switching between apps 2024-02-09 02:32:09 +01:00
Lwmte
05788b7978 Update Changes.txt 2024-02-07 17:39:20 +01:00
Stranger1992
919ff5065a Added comment to changelog 2024-02-04 20:55:29 +00:00
Jakub
1cdb4e0726
Update Changes.txt 2024-02-04 17:11:39 +00:00
Jakub
4fdedc3b00
Update Changes.txt 2024-02-04 17:00:40 +00:00
Sezz
52dff03342
Winston port (#1272)
* Port Winston

* Update sfx enums for Winston

* Add ObjectIDs for Winston

* added hitroutine to Winston

* Added suggested Winston animation enumerators

Change at will if you do not agree.

* Simplify Winston enumerators

* Renames

* Remove unused function

* removed ID_ARMY_WINSTON, OCB for behaviour

Winston with OCB is army winston. he can guard and has no cup sounds
Winston without OCB is normal winston. He can not guard and has cup sounds when walking

* Added ID_MESHSWAP_WINSTON_ARMY_OUTFIT. Slot ID #1120

* IDK if this solution about targeting is ok

* revert aiming changes

* made Winston with OCB0 not targetable

* fixed issue when changing OCB in runtime

* removed hardcoded cup sound effect

* Formatting

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
2024-02-04 16:57:35 +00:00
Sezz
f2b4b6a893 Simplify floordata methods 2024-02-04 22:14:11 +11:00
Sezz
41508a2217 Revert "WIP Floordata 1"
This reverts commit dbeccb885e.
2024-02-04 01:06:48 +11:00
Sezz
dbeccb885e WIP Floordata 1 2024-02-04 01:05:43 +11:00
Sezz
0fdf829b3b Remove 1 GetCollision() overload 2024-02-02 01:22:28 +11:00
Sezz
b72766b460 Remove 2 GetCollision() overloads 2024-02-02 00:58:59 +11:00
Sezz
253aa3816d Remove 3 GetCollision() overloads 2024-02-02 00:44:52 +11:00
Sezz
75c8ff2601 Simplify 2024-02-02 00:07:05 +11:00
Sezz
4d6f22a948 Remove comment 2024-02-01 23:44:09 +11:00
Sezz
923790041d Process tilts correctly for bridges rotated on X and Z axes 2024-02-01 23:43:41 +11:00
Sezz
e9012c619e Remove parameters 2024-02-01 17:30:31 +11:00
Sezz
68899231f4 Update PointCollisionData class; adopt PointCollisionData in camera.cpp 2024-02-01 17:23:36 +11:00
Sezz
bedc4ee37a Update bridge parenting; let bridge orientation influence collision 2024-02-01 16:45:26 +11:00
Sezz
d0f0118d13 Merge branch 'develop' into sezz_point_collision_class 2024-01-31 19:05:49 +11:00
Sezz
85c35e4eb3 Remove warning 2024-01-31 18:35:45 +11:00
Sezz
7c21558f18 Implement missing method 2024-01-31 18:19:59 +11:00
Adngel
38503b2620 Rename EulerAngles::Zero to EulerAngles::Identity
Replacing some missing EulerAngles::Zero for the new EulerAngles::Identity
2024-01-30 16:24:57 +01:00
Sezz
b3651cd5b6 Merge branch 'develop' into sezz_point_collision_class 2024-01-31 01:30:21 +11:00
Sezz
94bfadca5f Rename EulerAngles::Zero to EulerAngles::Identity; update AxisAngle 2024-01-30 16:20:20 +11:00
Sezz
d5ac711da4 Extend GetPointCollision(); fix merge errors 2024-01-21 23:40:06 +11:00
Sezz
31f8859cd4 Merge branch 'develop' into sezz_point_collision_class 2024-01-21 18:56:14 +11:00
Adngel
b29b68e36b Updated comment (unused animation 442) 2024-01-17 20:16:51 +01:00
MontyTRC89
74425602ae Fixed darker title 2024-01-13 13:17:54 +01:00
Sezz
52de01b9f7 Merge branch 'develop' into sezz_point_collision_class 2024-01-12 17:31:51 +11:00
MontyTRC89
f92fa759e2 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2024-01-12 05:47:40 +01:00
MontyTRC89
188b69e5aa Fixed drawing of fullscreen sprites in title 2024-01-12 05:47:35 +01:00
Sezz
8df57a57c7 Add GetBoundingBox() and GetClosestPointOnLinePerp() functions; minor updates to geometry math 2024-01-11 16:43:29 +11:00
Sezz
1635f9048f Update TranslatePoint() overloads 2024-01-11 16:31:29 +11:00
Sezz
1d06a4c9b8 Use AxisAngle in TranslatePoint() overload 2024-01-11 02:50:15 +11:00
hispidence
ba648e1383 Fixed doc comments and regenerated docs. 2024-01-07 13:22:52 +00:00
Kubsy
6633e5512f Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine 2024-01-06 15:24:31 +00:00
Lwmte
d1c0050d8d Update config.ld 2024-01-06 15:26:58 +01:00
Kubsy
94e807a4e6 Merge branch 'develop' 2024-01-06 13:53:29 +00:00
Jakub
657e3fb2ee
merge (#1313)
* Update New_Level.lua

* Revert "Update New_Level.lua"

This reverts commit 90b2d00644.

* Revert "Revert "Update New_Level.lua""

This reverts commit 7fc3191b4d.
2024-01-06 13:52:24 +00:00
Kubsy
6da51210de Revert "Revert "Update New_Level.lua""
This reverts commit 7fc3191b4d.
2024-01-06 13:50:00 +00:00
Kubsy
7fc3191b4d Revert "Update New_Level.lua"
This reverts commit 90b2d00644.
2024-01-06 13:49:21 +00:00
Kubsy
2e0835da2e Merge branch 'develop' 2024-01-06 13:43:55 +00:00
Lwmte
9f38943005 Update scripts structure 2024-01-06 13:36:18 +01:00
Kubsy
28e49094d2 Merge branch 'develop' 2024-01-06 12:33:08 +00:00
Lwmte
860b44223b Update Changes.txt 2024-01-06 13:16:26 +01:00
Lwmte
9192981417 Update Resources.rc 2024-01-06 12:35:29 +01:00
Lwmte
a0a5ee68db Update Changes.txt 2024-01-06 12:33:42 +01:00
Lwmte
4ea26e5de7 Fix several issues with LARA_START_POS 2024-01-06 09:07:16 +01:00
Jakub
e4b286e41d
Update Changes.txt 2024-01-05 22:23:22 +00:00
Jakub
b63cf527bc
Renderer refactor (#1287)
* Fixed vertex effects for statics

* WIP implementing SMAA antialiasing

* Fixed double drawing of additive faces

* SMAA working, need to tune it

* Cleaned SMAA code, ready for test

* Formatting

* Renderer cleaning Tier 1

* Renamed Renderer11 to Renderer;
Forced DX11 hardware support;
Removed m_ from private fields in Renderer class;
Refactored render targets: optional depth buffer creation;
Back buffer now is a RenderTarget2D;

* Converting constexpr and defines to enum classes

* Converting enum to enum classes, tier 2;

* Converting constexpr to enum classes, tier 3

* Cleaned enums and constants; Renamed some files;

* Fixed shaders; Renamed AlphaTestModes;

* Cleaned shaders directory; Reverted hardware version to DX10.1 (we'll upgrade it again in the next releases);

* Renamed private fields;
Refactored quad vertex buffer for sprites;
Refactored HUD renderer structure;
Minor fixes;

* Deleted some files

* Removed reference to RendererQuad.h

* New renderer transparency, for rooms now

* Continue work

* Implemented WOIT for alpha blending;
Started renderer refactoring;

* Removed redundant DirectX api calls

* Added G-Buffer with normals and depth

* Refactored and optimized particle enemies (rats, bats, locusts, beetles);
Extended G-Buffer to effects, rats, bats, locusts, beetles, gunshells, debris;
Extended animated textures sequence to 256;
Fixed normal mapping for items and statics;

* Removed WOIT code;
Removed write to depth map in opaque pass (it's already done in depth pre-pass);
Cleaned debris rendering code;

* Removed legacy render targets;
Added dual paraboloid mapping for ambient light (to finish the light shader):
Minor cleanups;

* Implement additive debug triangles

* Unhardcode value

* Finished room ambient code, commented for now (need to write a gaussian filter);

* Fixed wrong constant buffer slot in SMAA

* Renamed draw effects functions;
Moved prepare sprites function before the draw code;
Restored additive drawing;
Restored alpha blend faces collect;

* Enhance visual debug object API

* Partially restored sprites drawing

* Rename local variables

* Improve spheres

* Update names

* Rename function

* Remove unneeded parameter

* Formatting

* Cleaned and optimized drawing of opaque/additive sprites

* Removed legacy alpha blend code;
Added drawing of alpha blended faces for rooms;

* Completed drawing of non commutative objects;
FIxed compilation;
Removed old files;

* Refactored debug statistics for renderer

* Fixed FPS calculation

* Removed not used render targets and shaders

* Added base post-process framework;
Added monochrome and sepia effects;

* SSAO WIP

* SSAO almost working, needs tuning;
Changes to G-Buffer;

* Removed .cpp files in Graphics directory and moved constructors to headers;
Templatized VertexBuffer class;
New post-process system work in progress;
New blur stage for SSAO;

* SSAO at 1/4 resolution

* Removed FinalPass.fx and moved pixel shader to PostProcess.fx;
Added color to PostProcessVertex;
Rewritten the post-process step using new system;
Tweaked some variables for SSAO;
Fixed drawing of render targets debug thumbnails;

* Rewritten antialiasing techniques with new post-process system;
Made depth target optional when creating render targets using UNKNOWN DXGI format;

* Fix for empty vertex and index buffers

* Encode normals to RGBA8 render target;
Fixed samplers of depth texture;

* Removed legacy hairs shader

* Restored SSAO at fuill res for now;
Fixed shaders structs;

* Optimized G-Buffer shaders for speed and avoid DX log spam;
Renamed updateData to UpdateData in constant buffers;

* Bilateral gaussian filter for SSAO

* Fixed wrong enum

* Cleaning constant buffers tier 1

* Added SSAO setting

* Fixed shaders

* Fixed shaders

* Update SystemStrings.lua

* Refactored initialization code;
Added read and write of ambient occlusion in Windows registry;
Refactored shaders;
Added LUA API for setting color scheme;
Removed some old variables;

* Added effect strength to post-process framework

* Some shaders cleaning;
Don't calculate SSAO if disabled;

* Restored wireframe mode

* Revert "Merge branch 'develop' into renderer_refactor"

This reverts commit 4706f46982, reversing
changes made to bd413d00b2.

* Add negative postprocess effect

* Update PostProcess.fx

* Removed some DX log spam in SMAA shaders;
Fixed crash while drawing alpha blended faces from static objects;

* Fixed alpha blended faces drawing double sided for items and statics

* Fixed flickering of alpha blended faces of rooms

* Fixed gunflashes

* Revert "Revert "Merge branch 'develop' into renderer_refactor""

This reverts commit 536c555b2d.

* Fixed shadow mapping for point lights

* Fixed bad merge in speedometer

* Fix display pickup crash

* Fix SSAO toggle with left key

* Refactoring postprocess effect workflow

- Added exclusion mode
- Separated effect mode and strength to different functions
- Reset postprocess on level start
- Preserve postprocess on savegame reload
- Enum and function renames to maintain consistency

* Fix some comments

* Fix display pickup drawing

* Rename

* Fix for lights not affecting big static objects

* Fixed antialiasing for pickup objects and for inventory scene

* Fixed ghost polygons bug

* Fixed SSAO on surfaces with no or invalid normals

* Fix merge

* Bypass postprocessing if effect strength is set to 0

* Update SystemStrings.lua

* Update Changes.txt

* Update Changes.txt

* Turn on SSAO and target highlighter by default

* Remove duplicated changelog entries

* Implement LARA_START_POS object handling

* Update lara_initialise.cpp

* Update Changes.txt

* Update Changes.txt

* Fixed double sided alpha blend faces again

* Added things to changes.txt

* Update Changes.txt

* Don't use LARA_START_POSes with OCB 0

* Fixed 3D sprites like footprints

* Fixed glow for statics;
Fixed world position for items;

* Per-triangle sorting alpha blend

* Fixed missing last polygons in alpha blending

* Code cleaning

* Added alpha blending to rats, bats, locusts, scarabs

* Restored fast alpha blend

* Fixed AA in title level

* Improved light collector;
Added code for disable statics instancing if needed for debug;

* Fixed rare wrong caustics textures sizes

---------

Co-authored-by: MontyTRC89 <montyhammet@hotmail.it>
Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2024-01-05 22:19:52 +00:00
Adngel
e6c92decf3 ObjectCollisions update
For object collisions with other items that are not Lara. (Like vehicles hitting enemies or colliding with pushables or traps).
2024-01-04 11:06:18 +01:00
Joey Quint
48c2158c42 Fix typo in CalculateHorizontalDistance() 2024-01-01 22:24:38 +01:00
Adngel
0ad626c1d6 Wolf new OCB
- OCB 0: Wolf starts in walking animation, ready to chase Lara.
  - OCB 1: Wolf starts in sleeping animation. (As it was before).
2023-12-30 12:36:53 +01:00
Sezz
07879bc744 Minor formatting 2023-12-29 18:19:52 +11:00
Lwmte
bfc7d1b3d4
Lara start position handling (#1301)
* Implement LARA_START_POS object handling

* Update lara_initialise.cpp

* Update Changes.txt

* Update Changes.txt

* Don't use LARA_START_POSes with OCB 0
2023-12-28 20:36:33 +00:00
Sezz
99f9acbd34 Fix AFK pose for real 2023-12-28 21:33:42 +11:00
Sezz
7a1424b7f8 Remove comment 2023-12-28 15:15:33 +11:00
Sezz
d1573144ba Fix AFK pose 2023-12-27 21:37:25 +11:00
hispidence
a70159d95b Regen docs and fix some doc comments 2023-12-26 20:47:08 +00:00
Lwmte
b22584baf6 Remove unnecessary warning if callback function body is missing 2023-12-26 03:54:19 +01:00
Lwmte
737af2ecbd Update ViewHandler.cpp 2023-12-26 03:20:12 +01:00
Lwmte
9a6837865c Make extension order consistent with TE enumeration 2023-12-26 02:41:31 +01:00
Lwmte
b7ed52c53d Fix playback issues with audio tracks placed in subfolders 2023-12-26 00:56:11 +01:00
Lwmte
72ae2842e7 Bump version to 1.3 2023-12-25 14:10:43 +01:00
Lwmte
b94d21b20d Update FlowHandler.cpp 2023-12-25 09:29:26 +01:00
Lwmte
14fa1d39aa
Pick objects by cursor (#1296)
* Initial Commit
* Move GetRayFrom2DPosition to los.cpp
* Quick formatting
* Reogranize conversions a bit
* Update ViewHandler.cpp
* Remove unued temp variables
* Update Changes.txt

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2023-12-25 10:15:57 +02:00
Lwmte
dacc2c8f6a
Get game status (#1297)
* Initial commit
* Fix issues with getting game status correctly
* Remove wrong test code
* Fix merge
2023-12-25 10:15:39 +02:00
Sezz
fe6ee8ba1c
Bridge refactor (#1277)
* Implement BridgeObject ItemData variant
* Fix merge errors
* Remove destructor
* Update BridgeObject.cpp
* Fix merge

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
2023-12-25 10:01:29 +02:00
Lwmte
edce4087c9 Move MoveableStatus enum to Objects namespace 2023-12-21 14:32:23 +01:00
Lwmte
8ba30a102a Also properly show pistol ammo in inventory 2023-12-21 01:56:48 +01:00
Lwmte
16a2358835 Add deprecated controlphase callback enums to maintain backwards compatibility 2023-12-21 01:50:10 +01:00
Lwmte
8cb7ef6418 Fix several issues with limited pistol ammo 2023-12-21 01:19:29 +01:00
Jakub
59187f4db6
Update New_Level.lua 2023-12-20 20:58:51 +00:00
Jakub
90b2d00644
Update New_Level.lua 2023-12-20 20:13:18 +00:00
Lwmte
6a3ede73eb
Rename OnControlPhase to OnLoop, fix bug with post-loop callbacks (#1283) 2023-12-20 20:12:47 +00:00
Sezz
958e56642e Fix busy hand status on crawl 180 turn and monkey swing jump 2023-12-20 13:19:00 +11:00
Lwmte
640cbefed8 Add Room:GetColor lua function 2023-12-20 02:35:18 +01:00
Lwmte
f96922956d Fix ldoc error 2023-12-20 02:26:19 +01:00
Lwmte
c921c0dd62 Add GetCameraPosition and GetCameraTarget lua functions 2023-12-20 02:01:53 +01:00
Jakub
bbee2abc67
Update Changes.txt 2023-12-18 09:44:52 +00:00
Sezz
556c06332f Fix AFK posing 2023-12-18 19:16:40 +11:00
Lwmte
dbf69de04f Implement GetCameraRoom instead of GetCameraUnderwater 2023-12-18 01:58:05 +01:00
Lwmte
15b4ad2b46 Add GetCameraUnderwater() to lua API 2023-12-18 01:08:28 +01:00
Lwmte
87da98178c Fix #1280 2023-12-17 11:27:05 +01:00
Lwmte
921dcb905a Update Changes.txt 2023-12-16 12:36:02 +01:00
Lwmte
969a0f0bed Revert "Update Changes.txt"
This reverts commit a0f2cd973a.
2023-12-16 12:34:22 +01:00
Sezz
a0f2cd973a Update Changes.txt 2023-12-14 18:16:30 +11:00
Sezz
718bc86c4d Fix jittery camera when performing crawl-to-hang 2023-12-14 14:56:45 +11:00
Sezz
59104e388b Fix crawl weapon bug; fix sprint jump bug 2023-12-14 14:27:57 +11:00
Lwmte
99a04a6718 Merge branch 'event_set_editor_enhancements' into develop 2023-12-11 08:20:01 +01:00
Lwmte
0c444e6434 Rename GameStatus enum to better reflect the meaning 2023-12-10 21:52:05 +01:00
Lwmte
bd4d3cc58d Really provide player item index to a HandleEvent function without activator specified 2023-12-10 21:41:15 +01:00
Sezz
e78e21b8d9
Soft intelligent object collision (#1270)
* Update collide_item.cpp

* Add Lerp() methods to Vector3i(); simplify

* Calculate accurate box for creature collision

* Avoid creature collision interfering with player object interactions

* Add comment

* Fix errors

* Use clearer names
2023-12-10 11:18:38 +00:00
Jakub
8ad6966c3b
Update Changes.txt 2023-12-10 10:46:02 +00:00
Sezz
0e3533023e
Implement speedometer (#1276)
* Implement speedometer

* Prepare HandleVehicleSpeedometer()

* Update position and scale

* Show speedometer when on certain vehicles

* Change values

---------

Co-authored-by: Kubsy <kubadd475@gmail.com>
2023-12-10 10:39:13 +00:00
Sezz
c1f78e8855 Update PlayerContext.cpp 2023-12-09 21:59:26 +11:00
Sezz
2aa78da89a Maybe fix rare edge case stutter when landing on edges 2023-12-09 21:58:26 +11:00
Sezz
397ffd56f8 Add GetEffectiveGravity() function 2023-12-09 21:10:07 +11:00
Sezz
3c17ffe3c3 Make 2 room functions static 2023-12-09 18:31:04 +11:00
Sezz
c4cb594fc2 Reserve tread water vault states 2023-12-08 21:45:16 +11:00
Sezz
626b6c587b Remove _cdecl thing 2023-12-08 01:43:21 +11:00
Sezz
3e0dc4d021 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2023-12-04 21:49:40 +11:00
Sezz
6ae964b731 Formatting 2023-12-04 21:49:10 +11:00
Jakub
fb74011e69
Update Changes.txt 2023-12-04 10:48:31 +00:00
Nemoel-Tomo
528b92c0d3
keyholes: keep or loose key from inventory with ocb (#1275)
* keyholes keep or loose key from inventory with ocb

* OCB 0 will remove key from inventrory
2023-12-04 09:33:44 +00:00
Lwmte
432d9eb7c7 Fix enums 2023-12-03 11:55:12 +01:00
Lwmte
2c8dd72f6f Fix copypaste bug 2023-12-03 11:51:43 +01:00
Lwmte
ad7b05ddda Merge branch 'develop' into event_set_editor_enhancements 2023-12-03 11:46:33 +01:00
Sezz
101ac9f76a Fix GetStaticObjectLos() 2023-12-03 21:21:41 +11:00
Adngel
4ca16b887c Uncommented Enemies Damage underwater 2023-12-03 10:16:46 +01:00
Lwmte
c5aa4c7fac Update Changes.txt 2023-12-03 07:51:41 +01:00
Lwmte
637218d74d Update Changes.txt 2023-12-03 07:51:03 +01:00
Lwmte
cf7ed097c2 Update Changes.txt 2023-12-03 07:50:34 +01:00
Lwmte
202380d696 Update Changes.txt 2023-12-03 07:49:48 +01:00
Lwmte
cc147e4a92 Merge branch 'develop' into event_set_editor_enhancements 2023-12-03 07:42:44 +01:00
Lwmte
710022ca4e Safeguard for cases when unknown event type is found in level 2023-12-03 07:42:26 +01:00
Jakub
762889ed02
Update Changes.txt 2023-12-02 10:49:14 +00:00
Sezz
81e09e77a6 Add geometry functions for point intersection tests 2023-12-02 21:13:31 +11:00
Adngel
820f4bef5b
Lua moveables functions (#1245)
* Added SetStatus

* Added GetInteractedObj

* Update LaraObject.cpp

* Formatting

* Register new enum

* Function name change: GetInteractedMoveable

* Name reverted to GetPlayerInteractedMoveable

* Update LaraObject.h

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2023-12-02 09:41:31 +00:00
Adngel
09e4f02e66
Adngel varied fixes (#1256)
* Lara 6 click landing

Changed the freefall threshold value from 131 to 126.

In that way, Lara will pass to freefall animation a bit sooner what will activate the strong landing from the 6 clicks heights.

* Fix: Swamp camera shaking

I explored the code a bit, but fixing one thing ended breaking another, and all that with a camera refactor in the roadmap.

So I the end I decided keep it simple with a hack to avoid the solve the current. Once the code keep improving, surelly this will can be handled better.

* Fix tabs

* Update lara.h

* Fix comment and math

* Fix value

* Update flame_emitters.cpp

- Fixed the small flame alpha transition when used intermittent jet flame.
- Removed unused code
- Added references variables to replace the ItemFlags names, to enhace readibility.

* LARA_FREEFALL_VELOCITY restored

Caused the strong landing when Lara is doing a jump from 4 clicks to the ground. This was not a desired behaviour, so the value has been reverted.

* Fix: Pushables waterHeight in swamp rooms.

* Update Flame Emitter 1 code

Restored the alpha shifts to recover missing behaviour.

* Fix: Grabbing and throwing Torch from crouch state.

* Lara flare pose, excluded animations list

Lara now won't do the flare pose if the animation is in the exclussion list. (Usually animations where the logic is that Lara also use the left hand, like several switches).

* Drop torch if is swimming in water.

* Update lara_fire.cpp

* Fix formatting

* Update flame_emitters.cpp

* Update burning_torch.cpp

- Changed flame sprites
- Changed GetRandomControl by Random:: functions

* Fix pushables bug when dropped to 1 click pit

* Update burning_torch.cpp

- Changed flame spawn position
- Removed randomness of initial rotation to perform better with fire sprites.

* Fix Pushable initialized underwater

- Underwater pushables won't spawn ripples if the waterheight is bigger than the actual pushable height.
- Added a missing state transtion.

* Update lara_fire.cpp

That break was making that the flare routines don't activate during the exception animations.

(In this animations, the flare pose in the left hand shall not activate, that's why the ControlLeft is set to false, but other effects like Light and Sparks are still needed, so it can't exit the cycle with break here yet.

* Fix Swamp Rooms camera

The previous hack, worked in the quick sand room I had. But then I tested different level with differnt swamp pool and a different error arised. That took me to keep researching.

Also when I fixed a bug in the pushables related to the swamps, I learned also a couple of more things.

So this version return to the original line, but changes the g_Level.Rooms[probe.RoomNumber].y for g_Level.Rooms[probe.RoomNumber].maxceiling

In that way, it ensures the camera goes over the swamp room, independently of the pool depth.

After this I didn't got shacking camera issue anymore on any of the two pools, and it neither sink the camera into the swamp pool (as happened before with the HACK in the deep swamp pools).

* Fix formatting

* Formatting

* Entity Sounds fix

- Now it will recalculate the entity room position according to the root of their transform, instead of using the item->RoomNumber that contains an offset of 2 clicks.

- Removed the camera room ENV_FLAGS requirements.

* Update animation.cpp

* Revert anim sound fix for now

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2023-12-02 09:39:33 +00:00
Sezz
d409db5f79 Use current conventions in jump and monkey swing states 2023-12-02 19:48:56 +11:00
Sezz
1ce333108a Remove obsolete test functions 2023-12-02 18:59:27 +11:00
Lwmte
6dca91e2b2 Possibly fix savegame issues 2023-12-02 01:26:51 +01:00
Lwmte
4ca4ed8178 Update level.cpp 2023-12-01 20:28:10 +01:00
Lwmte
455903e172 Extend script event enum, fix ldoc formatting for logic handler 2023-11-30 21:51:40 +01:00
MontyTRC89
1c076dd2f9 Fixed statics not rotating with LUA 2023-11-30 13:48:47 +01:00
Sezz
e418b84d6c Update Random.h 2023-11-30 17:17:25 +11:00
Lwmte
9fd737d1d8 Simplify event set search 2023-11-30 00:10:01 +01:00
Lwmte
8e7384f5f3 Add handling of global events 2023-11-29 19:53:25 +01:00
Sezz
afa22619e0 De-copypaste floodata function 2023-11-29 19:14:17 +11:00
Sezz
b63ef90d6c Accurately rotate display sprites around pivot 2023-11-29 00:34:32 +11:00
Jakub
19264a5ee1
Update Changes.txt 2023-11-28 11:07:12 +00:00
Sezz
22cdf68fd0 Update Changes.txt 2023-11-28 22:02:52 +11:00
Nemoel-Tomo
16a6e5001a
TR3- gun turret (#1259)
* make puzzle_done show up if there is o trigger set

* -

* -

* Added TR3 Gun-Turret

* Gun Turret update

- Added standard collision routine.
- Added Notes at beggining providing information about the ItemFlags uses for this object.
- Updated the GetRandomControl for a normalized UCHAR version of GenerateFloat
- Changed the  origin of LOS to make it detect from the turret mesh instead of the transform origin (Which has a notable height offset).

* Cleanup

* Formatting

* Move comments

* Underscores

* Rename file

* fixed aim direction of the turret

* fixrd crash when shooting with rocketlauncher

* Update names

* Update TombEngine.vcxproj

* changed name to two-turret-auto-gun

* changed name to TwinAutoGun

* Remaining renames

* Name back VCI auto gun

---------

Co-authored-by: Adngel <adngel@hotmail.com>
Co-authored-by: Sezz <sezzary@outlook.com>
2023-11-28 21:57:01 +11:00
Sezz
9c16f3d4c9 Update Changes.txt 2023-11-28 21:45:06 +11:00
Sezz
7e8988a8eb Rewrite player water surface effects function; fix lack of splash in edge case scenario 2023-11-28 21:44:01 +11:00
Sezz
07d6d5d57f Fix GetSurfaceTilt() 2023-11-26 23:19:51 +11:00
Lwmte
55b3535b22 Bump TE compiler version to 1.7 2023-11-26 09:18:16 +01:00
Lwmte
d5bea6d820 Load global and volume event sets separately 2023-11-25 22:58:21 +01:00
Sezz
3533d49d9d Fix monkey swing bridges below illegal ceilings 2023-11-25 20:19:27 +11:00
Sezz
26a54e211c Fix slopes 2023-11-25 20:10:04 +11:00
Sezz
bb21ea285e Update collide_room.cpp 2023-11-25 20:03:02 +11:00
Sezz
b8a8d6f26b Merge branch 'develop' into sezz_point_collision_class 2023-11-25 14:58:17 +11:00
Sezz
35dfdcb5da Fix IsWall() 2023-11-25 14:12:37 +11:00
Sezz
527ef6fcc5 Revert "Temporarily revert #1258"
This reverts commit b3d3bf840e.
2023-11-25 14:00:14 +11:00
Sezz
392f895142 More pushable cleanup 2023-11-25 11:52:05 +11:00
Sezz
8e1cd2d0dd Fix edge case crash with Get2DPosition() 2023-11-24 00:48:56 +11:00
Adngel
2c042c42de Fix savegame corruption
At some point of the merge, lines

control.add_count(countOffset);
and
control.add_is_low(Lara.Control.IsLow);

While
control.add_jump_direction((int)Lara.Control.JumpDirection);
and
control.add_move_angle(Lara.Control.MoveAngle);

Got overwitten. This commit aims to solve that.
2023-11-22 11:10:58 +01:00
Sezz
b5f9d1b179 Restore shift behaviour 2023-11-22 20:51:20 +11:00
Sezz
b3d3bf840e Temporarily revert #1258 2023-11-21 22:04:37 +11:00
Sezz
aedba0a92d Fix sidestep context test 2023-11-21 20:01:25 +11:00
Sezz
0ea21d2c3c Adjust illegal floor slope angle 2023-11-21 17:11:06 +11:00
Sezz
7ea55baafa Move GetStaticObjectLos() to los.cpp 2023-11-19 16:26:46 +11:00
Sezz
2a83b014e0 Alphabetise; remove unused macros; make static object LOS function clearer 2023-11-19 16:06:47 +11:00
Sezz
faec314ddd
Enhance debug objects (#1260)
* Implement additive debug triangles

* Unhardcode value

* Enhance visual debug object API

* Rename local variables

* Improve spheres

* Update names

* Rename function

* Remove unneeded parameter

* Formatting
2023-11-18 23:32:12 +11:00
Sezz
d3f9a43f3b Use Contains() function 2023-11-18 22:48:20 +11:00
Sezz
9b7c040935 Restore std:: prefixes 2023-11-18 21:41:34 +11:00
Sezz
dfabcbabf2 Address namespace discrepancy 2023-11-18 20:40:20 +11:00
Sezz
8b5f475d33
Real floordata planes (#1258)
* Use real planes in floordata

* Update comment

* Remove debug code

* Fix errors

* Remove useless line

* Minor formatting

* Simplify local variable name

* Simplify

* Add comments

* Simplify

* Update floordata.h

* Minor changes

* Update PlayerContext.cpp
2023-11-18 20:34:11 +11:00
Jakub
2bfde737c1
Update Changes.txt 2023-11-18 09:32:37 +00:00
Jakub
481dd3bb82
Update Changes.txt 2023-11-18 09:29:26 +00:00
Nemoel-Tomo
b6084589f5
fix TR5 hydra power up flame and missile shockwave (#1247)
* make puzzle_done show up if there is o trigger set

* -

* -

* fixed TR5 hydra power up flame and bubble

* update hammer roman statue shockwave light

* Add SpawnDynamicLight(); formatting

* Added lighteffect to Demigod1 shockwave, hydra damage mult. for other weapons

* changed the Demigod1 hammer impact light to red

* added light to shockwave effect

* Formatting

* Rename function

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2023-11-18 09:27:27 +00:00
Sezz
2a695dccf8
State refactors tier 3.1 (#1081)
* Split some stuff from states_tier_3 branch

* Shuffle code

* Clarify names

* Fix walk bug

* Revert moving setup to main function

* Fix monkey swing early release; remove unnecessary wall checks

* Remove wall check

* Simplify running jump queue

* Update lara_basic.cpp

* Update lara_basic.cpp

* Update lara_basic.cpp

* Use const

* Add GetPlayerJumpVelocity()

* Update lara_helpers.cpp

* Update SetLaraVault()

* Update constants

* Fix stamina depletion during sprint

* Extend CanTurnFast()

* Simplify wade checks

* Simplify

* Restore IsLow and KeepLow; organise player structs a bit

* Remove TODO comment

* Fix errors

* Prepare PlayerContext class for inheritance

* Formatting

* Update PlayerContext.cpp

* Consider static collision

* Rename new files; remove useless code

* Update TombEngine.vcxproj

* Update ContextData.h

* Update Context.cpp

* Update TombEngine.vcxproj

* Update step heights

* Simplify step handling

* Update Context.cpp

* Include files

* Update file names

* Add comments

* Fix edge case bug

* Update PlayerContext.cpp

* Update collide_item.cpp

* Update PlayerContext.cpp
2023-11-18 09:26:39 +00:00
Lwmte
3746d880db Fix issues with activating volumes in adjacent inactive rooms 2023-11-16 23:36:10 +01:00
Sezz
3ae5fa613e Update Changes.txt 2023-11-17 00:19:45 +11:00
Sezz
2f98192253 Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop 2023-11-17 00:18:07 +11:00
Sezz
e3753015fd Fix GetNeighborRoomNumbers() for real 2023-11-17 00:17:58 +11:00
Jakub
cb21a95917
Update Changes.txt 2023-11-16 12:05:51 +00:00
Jakub
00a527a5ac
Update Changes.txt 2023-11-16 12:05:36 +00:00
Sezz
7783631573 Fix GetNeighborRoomNumbers() 2023-11-16 22:20:33 +11:00
Sezz
2fd5829325 Fix GetNeighborRoomNumbers() 2023-11-16 21:56:43 +11:00
Sezz
66cbade66e Improve fly cheat collision 2023-11-16 14:46:01 +11:00
Sezz
61dce4195b Optimise GetNeighborRoomGridCoords() 2023-11-15 15:27:59 +11:00
Sezz
95d22a41d8 Simplify call 2023-11-15 11:40:44 +11:00
Sezz
18be86a183 Compare to 0 2023-11-15 11:39:32 +11:00
Sezz
9a08e5b1f2
Room fixes (#1253)
* Fix neighbour getter function

* Refactor room collector

* Simplify

* Remove debug code; minor adjustments
2023-11-14 20:17:01 +00:00
Sezz
96fd330b3f Remove debug spheres 2023-11-15 02:10:53 +11:00
Sezz
3f5d4eb056 Simplify xLOS and zLOS 2023-11-15 02:06:04 +11:00
Sezz
66a9e88954 Fix DoRayBox() edge case crash 2023-11-15 01:50:42 +11:00
Sezz
c64a769f58 Remove commented line 2023-11-15 01:17:34 +11:00
Sezz
b45d9d8bad Remove commented spline function 2023-11-15 01:16:54 +11:00
Sezz
abb4c1a76c Improve look-around check 2023-11-15 00:35:24 +11:00
Sezz
59c90ad8d5 Update lara.cpp 2023-11-15 00:20:58 +11:00
Sezz
8402ad2133 Update cheat function 2023-11-15 00:18:29 +11:00
Sezz
c0877af85c Update lara_helpers.cpp 2023-11-14 23:39:28 +11:00
Sezz
e5577a4354 Move fly cheat code to lara_helpers.cpp 2023-11-14 23:39:10 +11:00
Sezz
91cffc1c27 Make Sprint input action increase velocity in fly cheat; minor fly cheat improvements 2023-11-14 23:18:17 +11:00
Sezz
b884c8e69b Separate underwater switch objects 2023-11-14 20:06:52 +11:00
Sezz
15a9c0f76e Update RoomVector object 2023-11-14 19:18:02 +11:00
Sezz
f868a922c8 Minor floordata function formatting 2023-11-14 18:30:51 +11:00
Sezz
0135791354 Remove TR5 level check hack from trapdoor 2023-11-14 16:18:24 +11:00
Lwmte
98d0da2370 Process adjacent volumes flag 2023-11-14 00:23:05 +01:00
Lwmte
8d504d6907 Read adjacent rooms flag for volumes 2023-11-14 00:18:32 +01:00
Lwmte
fbebfe8407 Fix small mistake 2023-11-13 23:43:58 +01:00
Lwmte
0945ea5eed Read volume enabled flag 2023-11-13 23:41:37 +01:00
Sezz
8542e86963 Tabs 2023-11-13 10:50:44 +11:00
Lwmte
80a140e509 Update volume.cpp 2023-11-12 23:25:43 +01:00
Lwmte
b3560043ab Add script functions to enable and disable events 2023-11-12 22:34:05 +01:00
Lwmte
574a8fd802 Fix speedhack for looped event sets 2023-11-12 16:54:03 +01:00
Lwmte
fe4a6263d3 Merge branch 'develop' into event_set_editor_enhancements 2023-11-12 16:31:11 +01:00
Lwmte
0409590ceb Initial commit 2023-11-12 16:26:52 +01:00
Adngel
e22501a646 Update Changes.txt 2023-11-12 14:06:41 +01:00
Adngel
5e61a6613d Update PushableStates.cpp
HACK: Activate the pushable ending only if it's doing the Object pushable animations.

Reason is because the block animations, have a long pause between the pushable movement and the end of the animation.

This fix is temporal and need a better solution for these kind of cases.
2023-11-12 12:29:34 +01:00
Adngel
e2b9103cf4 Update PlayerStateMachine.cpp
Camera restored for the animation of pushable edge slips.
2023-11-12 12:00:37 +01:00
Adngel
71f3e78e8f Update PushableObject.cpp
Corrected the logic initialization of the flag DoCenterAlign
2023-11-12 11:21:04 +01:00
Adngel
c5253f7950
Electric cleaner refactor (#1244)
* Revert "Late electric cleaner formatting"

This reverts commit 4db8e8ca69.

* Update functions names

* Removal of the old functions for pushables

From now on, the EC will relies in the floor Stoppable flag to don't pass through pushables. (As pushables got refactored to manage these better).

* Reformat: Comments and Initialize

* Reformat: From header to static methods

Changed the auxiliar functions to static methods defined uniquely in the source code. And deleted from the header code.

As these functions are unique for the Electric Cleaner and don't need to be known by external codes. So are moved away from the header.

For this, the order of the functions in the source code also has been changed, so now Control and Collide functions are at the bottom.

* Reformat: IsNextSectorValid

- Reformatted code from the IsNextSectorValid function
- Added also the GetCollision overload:

CollisionResult GetCollision(const Vector3i& pos, int roomNumber, const Vector3& dir, float dist);

* Reformat: Minor functions

* Reformat: ControlElectricCleaner

* Update comments

* Minor formatting

* Update Slopes alignments

GetShortestAngle is converting the diferenceof slopeAngle and dirAngle to short type, (What will not take the value 32768 anymore)

Therefore the next if have been modified to check only the forward (angle 0) and backwards (angle 180).

Note: ANGLE (180) and ANGLE (-180) gives both the same result, -32768, Although ANGLE 180 is positive, the short type rounds it back to the negative side. However in the code I think will be easier to read using the positive 180 angle.

* Update ElectricCleaner.cpp

---------

Co-authored-by: Sezz <sezzary@outlook.com>
2023-11-12 20:25:31 +11:00
Lwmte
9aca4187dc Rename GetCursorDisplayPosition 2023-11-12 08:38:37 +01:00
Lwmte
371cc2e033 Fix crashing if title logo is removed from Textures folder 2023-11-12 08:08:32 +01:00
Sezz
26dc407148 Simplify 2023-11-12 17:58:31 +11:00
Sezz
5a9a4c1de9 Simplify switch case 2023-11-12 17:56:30 +11:00
Sezz
ebbeb8b2bb Add "." string 2023-11-12 17:51:21 +11:00
Sezz
79a2300211
Simplify player state machine (#1237)
* Convert loose state function lists to maps

* Simplify

* Shorten names

* Unity functions

* Convert to array

* Update function name

* Remove duplicate include

* Simplify switch case

* Update PlayerStateMachine.cpp

* Add comments

* Use clearer names
2023-11-12 17:43:58 +11:00
Sezz
4f67330b31 Add handy floordata functions 2023-11-12 15:58:31 +11:00
Sezz
ef2dd30d6d Improve crosshair appearance 2023-11-12 15:44:45 +11:00
Sezz
d67b052ece Update InputHandler.cpp 2023-11-12 14:58:34 +11:00
Sezz
9f0d09729f Update internal cursor position getter to C++ naming standards 2023-11-12 14:58:16 +11:00
Sezz
ceb789f899 Rename SCREEN_SPACE_RES to DISPLAY_SPACE_RES for consistency 2023-11-12 14:55:02 +11:00
Kubsy
a788d0291c Update ScriptDisplaySprite.cpp 2023-11-11 17:01:29 +00:00
Kubsy
60d882b564 fix lua doc mistake 2023-11-11 16:25:36 +00:00
Kubsy
d0fe2444ab Update Util.cpp 2023-11-11 16:18:46 +00:00
Sezz
45c3060841
Refactor bridge functions (#1239)
* Refactor bridge functions

* Update Setup.h

* Make comments clearer

* Improve comment clarity

* Add and update comments

* Update comments

* Update tr5_expandingplatform.cpp

* Minor changes

* Remove AlterFloorHeight()
2023-11-11 14:41:33 +00:00
Sezz
583ee31e77 Merge branch 'develop' into sezz_point_collision_class 2023-11-05 10:42:25 +11:00
Sezz
fb20bf5355 Complete method set 2023-11-04 23:08:32 +11:00
Sezz
7aa1a8338b Update PointCollision.cpp 2023-10-28 16:50:41 +11:00
Sezz
b35367339e Formatting 2023-10-28 16:49:02 +11:00
Sezz
b38d9754d7 Add error handling; don't duplicate constants 2023-10-16 16:20:52 +11:00
Sezz
ba3bd0b1d3 Include files 2023-10-13 12:41:36 +11:00
Sezz
00bea67107 Merge branch 'develop' into sezz_point_collision_class 2023-10-13 12:41:14 +11:00
Sezz
cb78e08d89 Fix compile error 2023-09-08 00:29:41 +10:00
Sezz
a0eea51fbf Move hack to deprecated functions. 2023-09-08 00:23:52 +10:00
Sezz
2a19651480 Update PointCollision.h 2023-09-07 23:47:31 +10:00
Sezz
53ad627f72 Update PointCollision.cpp 2023-09-07 23:04:33 +10:00
Sezz
8d80e272f6 Merge branch 'develop' into sezz_point_collision_class 2023-09-07 22:52:44 +10:00
Sezz
0d62bbb823 Remove dummy method to keep it simple for now 2023-09-03 21:59:19 +10:00
Sezz
2833c35ac9 Add stub function for object intersections 2023-08-29 23:41:07 +10:00
Sezz
0d2d22fa8d Add comment 2023-08-29 19:57:32 +10:00
Sezz
fde6c2827b Add comment 2023-08-27 18:58:34 +10:00
Sezz
a286dbdd03 Update collide_room.cpp 2023-08-27 18:51:30 +10:00
Sezz
a8b1f6575a Update comments; make parameters const 2023-08-27 18:45:44 +10:00
Sezz
666fe3e605 Simplify 2023-08-27 17:41:37 +10:00
Sezz
7acff7d912 Update constant 2023-08-22 16:45:43 +10:00
Sezz
104066b915 Get bridge surface normals 2023-08-20 23:43:55 +10:00
Sezz
bacb5c99ce Get location 2023-08-20 17:56:53 +10:00
Sezz
be03784dda Add helper functions to simplify 2023-08-20 17:52:08 +10:00
Sezz
a97f5954ce Update comment 2023-08-17 20:05:24 +10:00
Sezz
8c47b06721 Update comments 2023-08-16 23:01:23 +10:00
Sezz
426b99d75d Update methods 2023-08-16 22:27:23 +10:00
Sezz
23efb49677 Add GetCeilingBridgeItemID(); consider bridges above when checking for slippery ceiling 2023-08-16 21:46:16 +10:00
Sezz
b2da3a9d03 Optimise 2023-08-16 20:23:28 +10:00
Sezz
70ffe72e1f Rename class 2023-08-16 16:58:43 +10:00
Sezz
7da15230ee Update collide_room.cpp 2023-08-16 01:41:44 +10:00
Sezz
14af4269e1 Update slope query methods 2023-08-16 00:50:11 +10:00
Sezz
bc212b041d Remove deprecated GetCollision() overload 2023-08-15 23:59:34 +10:00
Sezz
433f877d82 Fix room number probing; Wrap GetPointCollision() calls in old GetCollision() 2023-08-15 23:44:22 +10:00
Sezz
d9e8275ca3 Update namespace 2023-08-15 14:48:11 +10:00
Sezz
102d317b08 Organise 2023-08-15 14:41:19 +10:00
Sezz
210b24fb6d Remove wrappers; simplify 2023-08-15 14:36:03 +10:00
Sezz
5433a11297 Move RoomVector struct 2023-08-14 19:26:59 +10:00
Sezz
ffe819e4a6 Update methods 2023-08-14 18:39:02 +10:00
Sezz
cf4429f7d7 Update collide_room.cpp 2023-08-14 03:01:04 +10:00
Sezz
a0aa4548b3 Move function into namespace to avoid name clash 2023-08-14 02:52:30 +10:00
Sezz
de3ef51378 Remove unneeded method 2023-08-14 02:28:54 +10:00
Sezz
5de236b76e Prototype PointCollision class 2023-08-14 02:20:41 +10:00
972 changed files with 119060 additions and 50112 deletions

83
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View file

@ -0,0 +1,83 @@
name: Bug report
description: Create a report to help us understand and triage your issue.
labels:
- Awaiting Triage
body:
- type: markdown
attributes:
value: |
## Bug Report
Please follow this document carefully to report a bug.
> **Important**: It is highly recommended that you use the latest version before submitting a bug report.
- type: dropdown
attributes:
label: Tomb Engine Version
description: |
Please select the TombEngine version you are using.
options:
- v1.8.1 (latest release)
- v1.8.0
validations:
required: true
- type: checkboxes
attributes:
label: Development Version
description: Are you submitting this report from a development build that has not been officially released?
options:
- label: "I am using an unofficial development version."
- label: "I am using an official release."
validations:
required: true
- type: textarea
attributes:
label: Describe the Bug
description: |
Please provide a clear and concise description of what the issue is.
placeholder: |
Your bug report here.
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: |
Please provide detailed steps to reproduce the issue.
**Note**: If the bug cannot be reproduced or the issue is not clearly explained, it may be closed without further investigation.
placeholder: |
Step-by-step reproduction instructions here.
validations:
required: true
- type: textarea
attributes:
label: Expected Behaviour
description: |
What did you expect to happen?
**Note**: If the bug cannot be reproduced or the issue is not clearly explained, it may be closed without further investigation.
placeholder: |
A description of what should happen here.
validations:
required: true
- type: textarea
attributes:
label: Minimal Reproduction Project
description: |
Please upload a .zip file (10 MB max) containing your level and all assets needed to compile the level, including a minimal version where the bug occurs.
Alternatively, provide a download link from a cloud storage service (e.g., Google Drive, Dropbox).
> **Important**: If you do not provide a minimal reproduction project, your issue may be rejected.
placeholder: |
Download link to your project or attach a .zip file.
validations:
required: true

17
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,17 @@
blank_issues_enabled: false
# Disable blank issue
blank_issues_enabled: false
# Bug Report Form
name: Bug Report
description: File a bug report.
title: "[Bug]: "
labels: [ "Awaiting Triage" ]
# Initial Message
body:
type: markdown
attributes:
value: Thanks for taking the time to fill out this bug report! Please use the steps below to log the report for the development team.

View file

@ -0,0 +1,34 @@
name: Feature Request
description: Suggest an idea for TombEngine
title: "[ Feature Request ] "
labels: Awaiting Triage
body:
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
placeholder: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
placeholder: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: textarea
attributes:
label: Additional context
placeholder: Add any other context or screenshots about the feature request here. (as a link to screenshot or drop the screenshot into this textbox)
validations:
required: false

53
AUTHORS.md Normal file
View file

@ -0,0 +1,53 @@
# Credit List
This is the credit list of **all** the people who contributed to TombEngine in any shape or form.
## Developers
- MontyTRC (Project Leader)
- Gancian (general coding
- Krystian (general coding)
- Kubsy (Some cleanups and fixes)
- l.m. (general coding, Lua enhancements, bug fixing)
- Lwmte (sound refactoring, general coding, code cleanups, bug fixing)
- Moooonyeah (Jumanji) (entity decompilation)
- Raildex (renderer refactoring, particle coding, general coding)
- RicardoLuis0 (general coding)
- Sezz (player state refactoring, general coding, code cleanups, bug fixing, assets)
- Squidshire (Hispidence) (Lua implementation, bug fixing)
- Stranger1992 (sound asset refactoring and organisation, assets)
- TrainWreck (asset coding and Lua enhancements
- TokyoSU (entity and vehicle decompilation)
- Tomo (general coding, special FX coding, bug fixing)
- Troye (general coding, refactoring)
- Nickelony (general coding)
- JesseG, aka WolfCheese (general coding)
## Testers
- Adngel
- Caesum
- Dustie
- GeckoKid
- JoeyQuint
- Kamillos
- Kubsy
- LGG_PRODUCTION
- Lore
- RemRem
- Stranger1992
- WolfCheese
## Assets and Miscellaneous
- Geckokid (Sprites and test level creator).
### Animations
- SrDanielPonces (Diagonal shimmy transitions, backwards monkey swinging)
- Krystian (Flexibility crawlspace, slope climbing animations)
- Sezz (Additional Animations for player state refactoring)
- Naotheia (Underwater puzzle placement, crouch 180° turn, crawl 180° turn, water surface 180° turn)
- JoeyQuint (Standing 180° turn, monkey swing 180° turn)
### TombEngine Marketing
- Stranger1992 (Tomb Engine website, Discord, Facebook, Instagram, Youtube, X, and Twitch)

View file

@ -1,6 +1,366 @@
Version 1.2
===========
# Changelog
The dates are in European standard format where date is presented as **YYYY-MM-DD**.
TombEngine releases are located in this repository (alongside with Tomb Editor): https://github.com/TombEngine/TombEditorReleases
## [Version 1.8.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.8.1) - 2025-03-29
### Bug fixes
* Fixed pathfinding for friendly NPCs such as monkeys.
* Fixed particles remaining in the level after reloading from the savegame.
* Fixed particles being canceled by fog bulbs.
* Fixed crash in case hair object is the last object in a level.
* Fixed crash with incorrectly applied animated textures on static meshes.
* Fixed console window not hiding in non-debug mode on Windows 11.
* Fixed key binding settings saving for the current play session after hitting Esc to cancel.
* Fixed lensflare blending formula to avoid screen overbright.
### New features
* Added Firefly Emitter object (ID 1099) with corresponding sprite slot (ID 1379).
* Added live console input to perform Lua commands in realtime.
### Lua API changes
* Added missing constructor for `Collision.Probe` without room number.
* Added optional looping argument for `View.GetFlybyPosition` and `View.GetFlybyRotation` functions.
## [Version 1.8](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.8) - 2025-03-16
### Bug fixes
* Improved engine performance up to 20%.
* Fixed bridges moving the player when the player is underwater.
* Fixed trigger triggerer not working.
* Fixed display pickup numeric string not being interpolated in high framerate mode.
* Fixed two block platform room portal traversal failing in some cases.
* Fixed incorrect handling of dynamic light shadows.
* Fixed ricochet flashes after using explosive weapons.
* Fixed incorrect flare draw in crawl state.
* Fixed starfield remaining active in the next level if it does not have a starfield specified.
* Fixed underwater dust particles overflowing when camera is underwater.
* Fixed wetness player attribute not being preserved in savegames.
* Fixed invisible HK ammo in the inventory.
* Fixed flickering rat emitter.
* Fixed camera glitch when going into quicksand rooms with weapons drawn.
* Fixed player model submerging into the floor while swimming underwater.
* Fixed custom shatter sounds with custom sound IDs not playing correctly.
* Fixed crashes with sound samples larger than 2 megabytes.
### New features
* Added multithreading and an option for it to flow system settings.
* Added ability to use floor trapdoors, keys and puzzle items underwater.
- You must update your Lara object: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Lara/TEN_Lara.wad2
* Added a particle based waterfall emitter object and associated sprite slots.
- You must use this version: https://github.com/TombEngine/Resources/raw/refs/heads/main/Wad2%20Objects/Interactables/TEN_Waterfall_Emitter.wad2
* Added TR1 Hammer.
- You must use this version: <https://github.com/TombEngine/Resources/raw/refs/heads/main/Wad2%20Objects/Traps/TR1_Thor%20Hammer.wad2>
* Added TR3 Moving Laser.
* Added TR4 Statue Plinth.
### Lua API changes
* Added diary module.
* Added custom bar module.
* Added `Collision.Probe` class for basic room collision detection.
* Added `Flow.Horizon` class and two layers of horizons in a `Flow.Level` class.
* Added `Effects.EmitAdvancedParticle` function, allowing animations and other effects.
* Added `Effects.EmitAirBubble` function to spawn air bubbles.
* Added `Effects.EmitStreamer` function to emit streamers.
* Added `Flow.GetTotalSecretCount` function to get total amount of secrets in the game.
* Added `View.GetFlyByPosition` and `View.GetFlyByRotation` functions to get flyby sequence parameters at a specified time point.
* Added `Moveable:GetScale` and `Movebale:SetScale` methods to get or set visible scale of moveables.
* Added `Static:GetCollidable` and `Static:SetCollidable` methods to get or set collision status of static meshes.
* Added `Rotation:Lerp` function to allow linear interpolation between rotations.
* Added ability to perform additive and subtractive operations on `Rotation` class and compare one `Rotation` to another.
* Added various `Translate` methods to `Vec2` and `Vec3` script objects.
* Added alpha transparency functionality for statics and moveables to be used with `SetColor` method.
* Added extra arguments for sprite object slots and starting rotation value for `EmitParticle` function.
* Added ability to dynamically change `Flow.Level` weather and environment parameters and save them to a savegame.
* Added pickup count to `Flow.Statistics` class.
* Changed `Flow.Starfield` and `Flow.LensFlare` primitive types to use parameters instead of getters and setters.
* Fixed medipack level count in `Flow.Statistics` class.
## [Version 1.7.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.4) - 2025-04-01
### Bug fixes
* Fixed static meshes with dynamic light mode not accepting room lights.
* Fixed silent crashes if no Visual C++ runtimes are installed and provide a dialog box to download them instead.
* Fixed issues with launching the engine from directories with non-Western characters in the path.
* Fixed rare case of not being able to start a new game or exit game from the main menu on very slow GPUs.
* Fixed occasional crashes with creatures stuck in a sector with no pathfinding set.
* Fixed occasional cases of underwater switch animation not playing, if player spams jump key while pulling the switch.
* Fixed player's blob shadows not rendering on moveables and static meshes.
* Fixed antialiasing quality not changing after changing it in display settings.
* Fixed endless explosion effect for Puna.
* Fixed diary pick-up item inventory state not preserved in the savegame.
* Fixed gravity being applied underwater when exiting the fly cheat.
* Fixed gravity being applied when vaulting on the same frame as the player lands.
### New features
* Added realtime shader reloading in debug mode by pressing F9 key.
* Added load, save, stopwatch and compass as a functional pick-up items with ability to add or remove them from inventory.
* Increased particle limit from 1024 to 4096.
* Added ability for the player to more reliably stop at an edge when running at it while holding Walk.
### Lua API changes
* Fixed Flow.FreezeMode.FULL drawing incorrect background.
* Fixed DisplayString scale argument not being optional, as stated in documentation.
## [Version 1.7](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.3) - 2024-12-25
### Bug fixes
* Significantly improved renderer performance.
* Improved engine performance around bridges.
* Improved engine performance if weather or bubble effects are active.
* Improved engine start-up time.
* Fixed silent crashes if loaded level is corrupted or in incorrect format.
* Fixed occasional crashes if there are static meshes placed within room border walls.
* Fixed climbable pushables clipping Lara under the bridges when pulled.
* Fixed incorrect clipping of scaled off-centered static meshes.
* Fixed incorrect collision detection for off-centered moveables.
* Fixed incorrect slide directions for sub-click geometry.
* Fixed stutter during jumps between cameras in a flyby sequence.
* Fixed uzi targeting issues after using flycheat.
* Fixed hair object vertices not always linking properly.
* Fixed snow particles not always melting on the ground.
* Fixed enemies not damaging Lara if she is staying on the sector where enemies were triggered.
* Fixed enemy pickups dropping on death sectors.
* Fixed Sarcophagus and Search Object pickup triggers.
* Fixed vehicle transfer not happening for levels which were not previously visited.
* Fixed audio tracks placed in subfolders not restoring after loading savegame.
* Fixed initial position and lack of fade-in for looped audio track on level start.
* Fixed shatter debris spawning on incorrect position for the first frame.
* Fixed scripted input events not registering on the same game frame.
* Fixed incorrect object camera position.
* Fixed incorrect camera movement near walls after leaving look mode.
* Fixed binocular or lasersight camera not switching off correctly after flyby.
* Fixed binocular or lasersight camera transitions.
* Fixed target highlighter still being active in binocular or lasersight mode.
* Fixed Lara's Home entry not working.
* Fixed exploding TR3 bosses.
* Fixed original issue with deactivation of Dart Emitter.
* Fixed original issue with weapon hotkeys available in binoculars or lasersight mode.
* Fixed Electricity Wires object not doing instant kill when Lara is in close proximity.
* Fixed Lens Flare object not functioning properly.
* Fixed lens flares not being occluded by static meshes and moveables.
* Fixed spotlight shadows.
* Fixed Skeleton and Mummy not reacting to shotgun hits.
### New features
* Added classic mirror effect with ability to reflect moveables and static meshes.
* Added ability to customize many hardcoded parameters, such as flare, weapon, and hair settings.
* Added dynamic shadow casting on objects and static meshes.
* Added fast savegame reloading.
* Added ricochet sounds and make the effect more prominent.
* Allow camera shake during flybys.
* Allow to run the engine without title level.
* Allow more than 1024 objects in a level.
* Allow more than 1000 static mesh slots in a level.
### Lua API changes
* Added Flow.Statistics class, Flow.GetStatistics() and Flow.SetStatistics() functions.
* Added Flow.GetFreezeMode() and Flow.SetFreezeMode() functions.
* Added Effects.EmitSpotLight() function for directional spotlights.
* Added optional cast shadow and name parameters for Effects.EmitLight() function.
* Added Effects.GetWind() function to get current wind speed vector.
* Added Moveable:GetCollidable() and Moveable:SetCollidable() functions.
* Added Moveable:GetAnimSlot() and optional second argument for Moveable:SetAnim() to access different animation slots.
* Added Rotation:Direction() method to get directional vector.
* Added support for transparency value in Strings.DisplayString class.
* Added extra argument for Sound.SetAmbientTrack() function to specify if new ambient track should play from the beginning.
* Added new View.CameraType enum entries and return it by View.GetCameraType(), when flyby camera or binoculars/lasersight is active.
* Added new primitive Time class, which allows to manipulate and format game time without precision loss.
* Renamed Flow.WeaponType enumeration to Objects.WeaponType, and removed similar Objects.LaraWeaponType enumeration for consistency.
* Renamed Objects.PlayerAmmoType to Objects.AmmoType for consistency.
* Fixed Strings.DisplayString class not supporting some Unicode characters and empty lines in multiline strings.
* Fixed Strings.DisplayString not being deallocated after showing.
* Fixed GameVars not transferring between levels in hub mode.
* Fixed incorrect return value of Moveable:GetAnim() function, if animation from another slot is currently playing.
* Fixed incorrect behaviour of Moveable:GetJointRotation() function.
* Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions.
* Fixed Util.HasLineOfSight() not taking static meshes into consideration.
* Fixed collision callbacks not properly clearing after leveljump.
* Fixed Flow.SetIntroImagePath() not using the correct path.
## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03
### Bug fixes
* Fixed original issue with classic switch off trigger incorrectly activating some trigger actions.
* Fixed moveable status after antitriggering.
* Fixed leveljump vehicle transfer.
* Fixed weapons not properly hitting enemies.
* Fixed falling through dynamic bridges that are moving upwards.
* Fixed Laserhead teleporting Lara and making her invisible on death.
* Fixed pick-ups from Sarcophagus objects.
* Fixed issue with Lara not rotating together with bridges while picking up items.
* Fixed ghost collision with objects with zero bounds.
* Fixed several binocular bugs.
* Fixed faulty death sectors.
* Fixed shimmy softlocks around static meshes with soft collision.
* Fixed incorrect swing ledge grabs with steep grab angles.
* Fixed incorrect climbing out of water on bridge objects and in front of static meshes.
* Fixed incorrect diving animation when swandiving from a high place.
* Fixed room clipping when flyby path goes out of room bounds.
* Fixed camera rotating with the player's hips when climbing out of water.
* Fixed camera behaviour on sloped surfaces after player's death.
* Fixed camera position after loading a savegame.
* Fixed broken ropes after loading a savegame.
* Fixed AI for TR2 Skidoo driver and Worker with shotgun.
* Fixed Ember Emitter crashing when ocb is between -1 and -10.
* Fixed Electric Cleaner and Squishy Block not detecting collision with certain block heights.
* Fixed Squishy Blocks crashing the level.
* Fixed Larson and Pierre pathfinding.
* Fixed Dart Emitters failing with antitrigger.
* Fixed Homing Dart Emitter spawning darts continously when player is on its trigger.
* Fixed Four Blade Trap floor and ceiling collision.
* Fixed Joby Spikes collision and deformation.
* Fixed Sentry Gun joint rotation.
* Fixed Teeth Spikes not triggering the player impale animation.
* Fixed TR4 Mine crash with OCB 1 when triggered.
* Fixed cases where Atlantean Mutant's bombs cause the game to crash.
* Fixed torch flame delay when the player throws or drops a torch.
* Fixed display sprites and display strings rendering in the inventory background.
* Fixed young Lara hair drawing. https://tombengine.com/docs/level-settings/#young_lara
### New features
* Added high framerate mode (also known as 60 FPS mode).
* Added a customisable global lensflare effect. https://tombengine.com/docs/level-settings/#lensflare
* Added a customisable starry sky and meteor effect. https://tombengine.com/docs/level-settings/#stars
* Added the ability to display "Lara's Home" entry in the main menu.
* Added the ability to change pickup item count by modifying item hit points.
* Added F12 as alternative to PrtSc for screenshots.
* Added ability to invoke load game dialog after death by pressing any key.
* Added visible mouse pointer in windowed mode.
* Added portal debug mode.
* Added new sound conditions: Quicksand and Underwater.
- Quicksand - sound effect plays when a moveable is in quicksand.
- Underwater - sound plays when the camera is submerged.
* Added TR3 Seal Mutant. https://tombengine.com/docs/ocb-and-setup-instructions/#sealmutant
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Seal_Mutant.wad2
* Added TR4 Enemy Jeep. https://tombengine.com/docs/ocb-and-setup-instructions/#enemy_jeep
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TR4_Enemy_Jeep.wad2
* Changed TR5 Rome Hammer to not hurt player whilst deactivated.
* Changed TR2 Statue with blade damage from 20 to 200.
* Changed sound effect that is triggered when using the `level.rumble` feature in a level (ID 359 in the soundmap).
* Changed hardcoded sound for Raising Block 1/2 back to the ID used in TRLE (ID 149).
* Enhanced TR2 Rolling Spindle detection to avoid them going down through pits.
* Enhanced Sentry Guns, with a new ItemFlags[3], to contain the ID of the inventory item that deactivates the sentry guns (Puzzle Item 5 by default).
* Enhanced Dart Emitter, with a new ItemFlags[0], to contain the number of frames between shots (by default 32 in Dart Emitter, and 24 in Homing-Dart Emitter).
* Enhanced Raptor behaviour and handling. https://tombengine.com/docs/ocb-and-setup-instructions/#raptor
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Raptor.wad2
* Removed original limit of 32 active Flame Emitters.
### Lua API changes
* Added Flow.EnableHomeLevel() function.
* Added Flow.IsStringPresent() function.
* Added Flow.LensFlare() and Flow.Starfield() classes.
* Added Inventory.GetUsedItem(), Inventory.SetUsedItem() and Inventory.ClearUsedItem() functions.
* Added Input.KeyClearAll() function.
* Added Moveable:GetJointRotation() and optional 'offset' parameter for Moveable.GetJointPosition().
* Added Moveable:GetTargetState() function.
* Added Room:GetRoomNumber() function.
* Removed anims.monkeyAutoJump. It is now a player menu configuration.
* Fixed Volume:GetActive() method.
## [Version 1.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.1) - 2024-04-21
### Bug fixes
* Fixed drawing of display sprites in title level.
* Fixed drawing of smoke sprites and various other sprites.
* Fixed drawing of transparent surfaces when debris are present in scene.
* Fixed player holster state and current vehicle not preserved correctly on level jump.
* Fixed diving when swimming over sinks.
* Fixed fire item effect not extinguishing in water.
* Fixed fade-in and fade-out effects not canceling correctly when next level is loaded.
* Fixed shadows still being visible after shattering a moveable.
* Fixed FOV interpolation at the end of the flyby sequence.
* Fixed sounds resuming in pause mode while switching between apps.
* Fixed slide directions.
* Fixed occasional collision warnings in a log when teeth spikes object was activated.
* Fixed climbable pushables collision during continuous pulling action.
* Fixed collision for solid static meshes with zero collision box.
* Fixed bottom collision for solid static meshes.
* Fixed T-Rex's head rotation.
### New features
* Auto-switch to a crawl state if player start position is in a crawlspace.
* Allow directional flame emitter (negative OCBs) to be rotated at any angle.
* Revise wall spikes:
- Wall spikes now stop when they touch a pushable, another spike wall or a normal wall.
- Wall spikes will shatter any shatter in its path.
- Wall spikes can be stopped by normal antitrigger or with a volume.
* Added hub system to preserve level state on level jumps.
* Added ember emitter.
* Added fish emitter.
* Added laser beam object.
* Added TR2 dragon.
* Added TR3 Winston (requires updated TEN .wad2 on TombEngine.com).
* Added TR4 squishy blocks (requires updated TEN .wad2 on TombEngine.com).
### Lua API changes
* Added resetHub flag to Flow.Level, which allows to reset hub data.
* Added Flow.GetFlipMapStatus() function to get current flipmap status.
* Added Moveable:GetMeshCount() function to get number of moveable meshes.
* Added timeout parameter for Moveable:Enable() function.
* Added Static:GetHP() and Static:SetHP() functions to change shatterable static mesh hit points.
* Fixed Moveable:SetOnCollidedWithObject() callback.
## [Version 1.3](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7) - 2024-01-06
### Bug fixes
* Fixed crash if title logo is removed from Textures folder.
* Fixed crash if unknown player state ID is encountered.
* Fixed bug with OCB 2 on pushables, and some other pushable bugs.
* Fixed pushable camera bug during edge slip.
* Fixed lever switch turn off alignment animation.
* Fixed lack of water splash in certain scenarios.
* Fixed hydra flame not showing when charging.
* Fixed shockwave light for hammer god.
* Fixed camera shaking in some cases when the player is in quicksand room.
* Fixed certain flame emitter OCBs emitting fire in wrong directions.
* Fixed player not being able to pick up a torch when crouching.
* Fixed jittery camera when performing crawl-to-hang.
* Fixed several issues with limited pistol ammo.
* Fixed player not being able to crawl if two-handed weapon is currently equipped.
* Fixed playback issues with audio tracks placed in subfolders.
* Fixed thin caustics outline on the edge of the blocks.
* Fixed big static objects affected wrongly by dynamic lights.
* Fixed legacy trigger leveljumps ignoring provided level index.
* Fixed incorrect light collection in some cases.
* Fixed normal mapping for rooms, items, and statics.'
### New features
* Added ambient occlusion (SSAO).
* Added new post-process workflow (monochrome, negative, exclusion) with tinting.
* Added SMAA antialiasing instead of MSAA.
* Added previously missing player start position object functionality.
* Added fast speed for fly cheat by holding Sprint input action.
* Added speedometer to vehicles.
* Added global node events.
* Totally revised transparency handling.
* Increased the maximum frames for animated sequences from 128 to 256.
* Optimized the renderer.
* Separate underwater wall and ceiling switch objects into two slots each.
* Accurately rotate display sprites around the pivot defined by the align mode.
* Allow walking on slopes when wading in water (similar to quicksand).
* Allow player to pull certain levers with both hands when holding a flare.
* Ported twin auto gun from TR3.
* Revised keyhole OCBs to account for keeping or losing keys:
- OCB 0: Play default animation and lose key.
- Positive OCB: Play anim number and keep key.
- Negative OCB: Play anim number and lose key.
* Revised Wolf OCBs:
- OCB 0: Wolf starts in walking animation, ready to chase Lara.
- OCB 1: Wolf starts in sleeping animation.
### Lua API changes
* Added Lara:GetInteractedMoveable() which returns currently interacted moveable by Lara.
* Added Moveable:SetStatus() to set the current status of the moveable.
* Added Room:GetColor() to get room's ambient light color.
* Added Util.PickMoveableByDisplayPosition() and Util.PickStaticByDisplayPosition() functions.
* Added View.GetCameraPosition(), View.GetCameraTarget() and View.GetCameraRoom() functions.
* Added View.SetPostProcessMode(), View.SetPostProcessStrength() and View.SetPostProcessTint() functions.
## [Version 1.2](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.9) - 2023-11-11
### Bug fixes
* Fix burning torch not working properly if there are more than 256 objects in a level.
* Fix grenade and rocket projectiles smoke offset in certain directions.
* Fix projectiles flying through animating objects.
@ -24,30 +384,32 @@ Version 1.2
* Fix non-elevated combat camera.
* Fix camera snap when disengaging the look-around mode.
* Fix TR4 mapper not being visible.
### New features
* Improve head-on wall collision.
* Overhaul pushables:
- Separate climbable and non-climbable pushable object slots.
- Add new pushable OCB to manipulate pushable properties.
- Add new animations for pushing pushables off edgees (TR1-3 and TR4-5 versions).
- Fix pushables not working with raising blocks.
- Fix miscellaneous pushable bugs.
- Separate climbable and non-climbable pushable object slots.
- Add new pushable OCB to manipulate pushable properties.
- Add new animations for pushing pushables off edgees (TR1-3 and TR4-5 versions).
- Fix pushables not working with raising blocks.
- Fix miscellaneous pushable bugs.
* Overhaul look-around feature:
- Allow for more consistent and wider viewing angles while crawling, crouching, and hanging.
- Improve look camera movement and control.
- Re-enable looking while performing up jump, backward jump, or backward crawl.
- Add functionality to rotate slowly when holding Walk while using binoculars or lasersight.
- Allow for more consistent and wider viewing angles while crawling, crouching, and hanging.
- Improve look camera movement and control.
- Re-enable looking while performing up jump, backward jump, or backward crawl.
- Add functionality to rotate slowly when holding Walk while using binoculars or lasersight.
* Add target highlighter system with toggle in Sound and Gameplay settings.
* Add sprint slide state 191.
* Add swinging blade.
* Add crumbling platform and add new OCBs for behaviour:
- OCB 0: Default behaviour. When the player steps on the platform, it will shake and crumble after 1.2 seconds.
- OCB > 0: When the player steps on the platform, it will crumble after the number of frames set in the OCB.
- A positive value results in activation via player collision.
- A negative value requires a trigger to activate.
- OCB 0: Default behaviour. When the player steps on the platform, it will shake and crumble after 1.2 seconds.
- OCB > 0: When the player steps on the platform, it will crumble after the number of frames set in the OCB.
- A positive value results in activation via player collision.
- A negative value requires a trigger to activate.
* Add basic mouse input handling. Allows for binding of mouse inputs in control settings.
* Add settings for Mouse Sensitivity and Mouse Smoothing (not used in-game yet).
Lua API changes:
### Lua API changes
* Split and organize functions in `Misc` namespace to appropriate new namespaces.
* Make Vec2 and Vec3 objects float-based instead of integer-based.
* Add DisplaySprite object.
@ -73,9 +435,9 @@ Lua API changes:
* Add DisplayString::SetScale() function to resize text.
* Add DisplayString::GetScale() function to get text scale.
Version 1.1.0
==============
## [Version 1.1.0](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.8) - 2023-07-29
### Bug fixes
* Fix enemies shooting Lara through static meshes and moveables.
* Fix skeletons and mummies not being affected by explosive weapons.
* Fix crash on loading if static meshes with IDs above maximum are present.
@ -105,6 +467,8 @@ Version 1.1.0
* Fix room collector freezing game on some occasions.
* Fix incorrect culling for scaled static meshes.
* Fix normal mapping.
### New features
* Add ability to save screenshot in the "Screenshots" subfolder by pressing the "Print screen" key.
* Implement separate audio track channel for playing voiceovers with subtitles in .srt format.
* Don't stop ambience when Lara dies.
@ -122,15 +486,15 @@ Version 1.1.0
* Add TR1 skateboard kid.
* Add TR1 Kold.
Lua API changes:
### Lua API changes
* Add soundtrack functions:
- Misc::GetAudioTrackLoudness() for getting current loudness of a given track type.
- Misc::IsAudioTrackPlaying() for checking if a given track type is playing.
- Misc::GetCurrentSubtitle() for getting current subtitle string for the voice track.
Version 1.0.9
=============
## [Version 1.0.9](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.7) - 2023-06-03
### Bug fixes
* Fix cold bar triggered in non-water rooms.
* Fix spiky wall speed value and change it via OCB number or Lua (Moveable::SetItemFlags[0]).
* Fix bats emitter crashing the game if little beetle object does not exist in wad.
@ -151,6 +515,8 @@ Version 1.0.9
* Fix grenade firing angle.
* Fix rendering for static meshes with custom blending modes and alpha transparency.
* Fix inconsistent multiline string spacing on different display modes.
### New features
* Remove search object 4 hardcoded meshswap activated with a flipmap.
* Add TR1 cowboy.
* Add TR3 wall mounted blade.
@ -176,7 +542,7 @@ Version 1.0.9
* Add "Reset to defaults" entry to controls menu and automatically bind XBOX gamepad profile if connected.
* Add 64-bit executable and place both 32-bit and 64-bit versions into /Bin subdirectory.
Lua API changes:
### Lua API changes
* Add Vec2 class.
* Add function String::SetTranslated().
* Add function Misc::IsStringDisplaying().
@ -186,9 +552,9 @@ Lua API changes:
- PRESAVE, POSTSAVE
- PRELOAD, POSTLOAD
Version 1.0.8
=============
## [Version 1.0.8](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.6) - 2023-04-10
### Bug fixes
* Fix bubbles phasing through ceilings.
* Fix object camera not clearing at level end.
* Fix double breath sound effect when coming up for air.
@ -207,6 +573,8 @@ Version 1.0.8
- Imp is also scared of of the player if holding a lit torch.
- Please note you must use the patched version found here: https://github.com/TombEngine/Resources/blob/main/Wad2%20Objects/tr5_Imp.wad2
* Fix and improve wraith tails.
### New features/Amedments
* Add dedicated WRAITH_TRAP object with enhanced effects.
- OCB 0: Effect disabled.
- OCB 1: Effect enabled.
@ -227,13 +595,13 @@ Version 1.0.8
* Restored inventory compass.
* Allow dynamic segment count for hair object.
Lua API changes:
### Lua API changes
* Add function Misc::IsSoundPlaying()
* Add function DisplayString::SetFlags()
Version 1.0.7
=============
## [Version 1.0.7](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.5) - 2023-02-26
### Bug fixes
* Fix spark particles not being cleared on level reload.
* Fix visible but inactive enemies (e.g. Shiva or Xian guardians) taking damage.
* Fix blockable LOT type enemies (e.g. T-Rex and Shiva) not being able to step up 1 click or drop 2 clicks.
@ -254,6 +622,8 @@ Version 1.0.7
- Killing move for spear used wrong value.
* Fix TR3 big gun spawning rocket with 0 life which caused an immediate explosion.
* Fix TR3 Tony and add boss effect for him.
### New features
* Add TR3 civvy.
* Add TR3 electric cleaner.
* Add TR3 Sophia Leigh with following OCBs:
@ -271,14 +641,14 @@ Version 1.0.7
* Prevent Lara from drawing weapons during parallel bar swinging.
* Further renderer performance optimizations and bugfixes.
Lua API changes:
### Lua API changes
* Fix Camera:SetPosition not updating camera position when it is played simultaneously.
* Add Moveable:GetAirborne and Moveable:SetAirborne.
* Add Moveable:GetLocationAI and Moveable:SetLocationAI.
Version 1.0.6
=============
## [Version 1.0.6](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.4) - 2023-01-29
### Bug fixes
* Fix major pathfinding bug which could have caused lots of issues with enemy behaviour.
* Fix potential random crashes due to incorrect rendering behaviour.
* Fix savegame crash for disabled enemies with partially set activation mask.
@ -304,6 +674,8 @@ Version 1.0.6
* Fix grenade launcher super ammo emitting too many fragments.
* Fix grenade and rocket launcher lighting.
* Fix ceiling trapdoor and floor trapdoor that Lara couldn't open manually.
### New features
* Make enemies drop pickups at first available bounding box corner point, not centerpoint.
* Restore original volumetric explosion effects.
* Add TR3 lizard and Puna.
@ -315,7 +687,7 @@ Version 1.0.6
* Lua Moveable functions Enable and Disable now correctly trigger and antitrigger the moveable.
* Improve level loading speed a lot.
Lua API changes:
### Lua API changes
* Moveable:SetVisible has been added. MakeInvisible is now an alias for SetVisible(false).
* Moveable:MeshIsVisible is now GetMeshVisible.
* Moveable:SetMeshVisible has been added to replace ShowMesh/HideMesh.
@ -327,9 +699,9 @@ Lua API changes:
* Add new function Misc::GetCameraType()
* Add new functions Moveable:GetAirborne() and Moveable:SetAirborne(bool input)
Version 1.0.5
=============
## [Version 1.0.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.3) - 2022-12-30
### Bug fixes
* Fix combined items not existing in inventory upon game reload.
* Fix classic rollingball not behaving properly in rooms beyond the distance of 32 blocks.
* Fix rollingball not killing Lara under certain movement angles.
@ -354,6 +726,8 @@ Version 1.0.5
* Fix rare crash when smash item is inside a wall and add warning log for the scenario.
* Fix bone rotations of some entities.
* Fix Lara's animation for cog switch release.
### New features
* Added new OCB to cog switch object:
- Use OCB 0 to have the traditional behaviour.
- Use any other OCB to can use the Cog Switch without need of any door linked.
@ -362,12 +736,12 @@ Version 1.0.5
* Draw real mesh for darts.
* Added warning log when one slot requires another slot which is missing.
Lua API changes:
### Lua API changes
* Add new Room class and several methods for it.
Version 1.0.4
=============
## [Version 1.0.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.2) - 2022-12-16
### New features
* Add generic assignable effects for moveables - fire, sparks, smoke and laser / electric ignite.
* Add ability to burn enemies with FLAME_EMITTER_1 and death blocks.
* Add wireframe mode and other visual debug information (switch by F10/F11 debug page scroll hotkeys).
@ -389,6 +763,8 @@ Version 1.0.4
* SAS enhancements:
- Fix grenade shooting.
- Fix AI_MODIFY and AI_GUARD behaviour.
### Bug fixes
* Fix choppy camera movement in several cases.
* Fix Lara's vertical position when shimmying around steep slope corners.
* Fix legacy pickup triggers not working in certain cases.
@ -417,7 +793,7 @@ Version 1.0.4
* Fix current soundtrack fading into silence if incoming one doesn't exist.
* Fix crash if there is an attempt to display a string with missing characters.
Lua API changes:
### Lua API changes
* Add new Volume class and several methods for it.
* Add new Moveable functions: GetEffect, SetEffect and SetCustomEffect (for colored fire).
* Add new Lara functions: GetTarget, GetVehicle and TorchIsLit.
@ -435,9 +811,9 @@ Lua API changes:
* Fix Rotation class using integers under the hood which prevented using fractional rotation values.
* Fix distance tests failing on a very high distances.
Version 1.0.3
=============
## [Version 1.0.3](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.1) - 2022-11-18
### New features
* Add ledge jumps (Lara object must be updated with new animations to make it work).
* Allow any object slot to be used as a meshswap.
* Add OCB 1 for rollingball to make it silent.
@ -450,6 +826,8 @@ Version 1.0.3
* Improve game and inventory input handling.
* Adjust sprint jump timing.
* Backport DAMOCLES_SWORD from TR1.
### Bug fixes
* Fix going into inventory and load/save dialogs during fade-ins and fade-outs.
* Fix savegames not preserving save number and game timer.
* Fix dodgy weapon lock angle constraints.
@ -481,7 +859,7 @@ Version 1.0.3
* Fix SAS_DRAG_BLOKE object interaction.
* Fix KILLER_STATUE not triggering.
Lua API changes:
### Lua API changes
* A new class has been added, LaraObject, for Lara-specific functions. The built-in "Lara" variable now uses this class.
* Add functions for Lara object:
- GetPoison / SetPoison
@ -501,9 +879,9 @@ Lua API changes:
* Add SetTotalSecretCount option to gameflow script to set overall amount of secrets.
* Raised the maximum value on Moveable.SetHP to 32767 (its internal numeric maximum).
Version 1.0.2
=============
## [Version 1.0.2](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6) - 2022-09-16
### New features
* Fix removing Pistols with TakeItem and SetItemCount.
* Allow saving and loading of Vec3s in LevelVars and GameVars.
* Support volume triggers made with node editor.
@ -527,7 +905,8 @@ Version 1.0.2
- 5 for valve turn
- 6 for hole switch
- any other OCBs play corresponding switch on anim or OCB+1 switch off anim.
### Bug fixes
* Fix incorrect pole mounting.
* Fix zeroed forward velocity upon landing.
* Fix incorrect behaviour when falling on statics from the top after monkeyswing.
@ -541,7 +920,7 @@ Version 1.0.2
* Fix stargate blades needlessly pushing the player around while hardly doing any damage.
* Fix weapon hotkeys and add missing crossbow hotkey.
Lua API changes:
### Lua API changes
* Util.ShortenTENCalls no longer needs to be called; it is now automatic for both level scripts and Gameflow.lua.
* Flow.InvID has been removed; any function taking a pickup (e.g. GiveItem) now takes an Objects.ObjID instead.
* Add Enable, Disable, GetActive, Get/SetSolid functions for static meshes.
@ -555,12 +934,14 @@ Lua API changes:
* Rework GiveItem, TakeItem, and SetItemCount (e.g. SetItemCount with a value of -1 can give infinite ammo/consumables).
Version 1.0.1
=============
## [Version 1.0.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.5.2) - 2022-08-16
### New features
* Added antialiasing support.
* Added static mesh scaling support.
* Added free rotation for teeth spikes instead of using OCB codes.
### Bug fixes
* Fix some issues with shimmying between diagonal ledges and walls.
* Fix rope transparency.
* Fix objects disappearing under certain angles at the edges of the screen.
@ -578,6 +959,8 @@ Version 1.0.1
* Fix falling through twoblock platform on room number change.
* Fix falling block breaking too early if placed on a vertical portal.
* Fix crashes when loading image files are missing.
### Amendments
* Disable trigger check for puzzle holes.
* Clear locusts and other swarm enemies on level reload.
* Enhance cobra AI and fix targeting.
@ -593,7 +976,6 @@ Version 1.0.1
* EventSequence.lua has been added and documented.
Version 1.0
===========
## [Version 1.0](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.5.1) - 2022-08-06
First beta release.

82
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,82 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
TombEngine Discord Server.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

401
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,401 @@
What you will read under has been agreed by the current active team in order to keep a good flow of work and proper management. You are welcome to put suggestions in the comments in order to discuss it with the TEN team.
# Contributor General rules
**1. Developer**
- If you wish to contribute to the TEN project, you must read and follow the coding convention used by the team, available here: https://github.com/MontyTRC89/TombEngine/blob/master/CONTRIBUTING.md#coding-conventions
- Do not work directly on master nor any branch which may be developed by several members. Create your branch, and name it with a clear description of the feature/set of features you are working on. You may regularly merge/rebase master or the active development branch with yours.
- Be respectful to other developers. Disagreements may occur on code style, changes, and should be discussed directly with the person who pushed or merged the commit/branch. If you are unsure of what some parts of the code means, or why a decision was made, contact the team as a whole so they can be explained to you.
- Do not make a pull request until your branch and features are extensively tested by the QA team. You are invited to test your feature set as much as you can too.
- If you are allowed to merge PRs, do not do such until code has been properly reviewed, preferably by more than one person.
- Merge PRs using **squash and merge** mode to preserve main branch history in original state. For squash merge commit, leave list of commits in commit description (GitHub does it automatically in both desktop client and web interface).
- Do not revert active development branch to a previous commit unless you are absolutely sure there will be no regressions. Do not revert parts of code to a previous state for the same reasons. In both of those cases, the entire team needs to be aware of it and agree on the decision.
- There are **two key branches**: `master` and `develop`. `master` is to be used for releases, and nothing else. `develop` is based on `master` and is used for active development, **Every PR should use it as its base**. To keep in sync, for release period they are to be merged in the following order: `develop` -> `master`.
**2. Tester**
- Be respectful with the developer you are testing the feature of.
- New features or bug fixes should be tested with several use cases, if not all possible use cases you can think of, of this feature. This avoids going back to it in the future because of missed test cases.
- Pay attention to any regressions. Developer may have touched part of the code that could break other linked or unlinked features. Features or bug fixes can't be considered done if there are any regression elsewhere.
## Issues handling
**1. General**
- Preferably, write one issue for one specific bug. List of bugs should stay rare, unless they are closely related to each other.
- Follow the progress of features/fixes on the project board.
- Do not assign a dev to an issue. They will do so themselves
- Carefully choose the labels you add to the issue
**2. Developer**
- Assign yourself to an issue you want to work on available in the To do column of the project board. Add this feature to the "In progress" column. Make the team know you are working on it both by adding a comment to it and via Discord, on the strategy channel of Tomb Engine.
- Regularly update the status of the task by adding comments. If you are stuck, let other people know if they can help.
- Do not pass an issue as done unless fixes or features are fully tested and merged with active dev branch
**3. Tester**
- Give the Developer as much detail as you can about the bug you're writing for, and an easy way to reproduce it.
- Update and comment the issue with the tests you've done and your findings after fixes.
# Coding Conventions
## General Rules
* **Do not silently comment out any lines or change operators or conditional statements**, even for testing purposes. Should you comment out certain code, also leave a comment beginning with `TODO` prefix, and date-author signature, like this:
``` c
// TODO: This code caused NPCs to run into walls, so it is disabled for now. -- Lwmte, 21.08.19
// GetBoundsAccurate(blah);
```
* **Do not modify existing code without a clear understanding of what it does.** Even if you think you are fixing bugs or restoring original behavior by reverting the code, consult the TombEngine discord before committing any changes. It is possible you may ruin the work of other people since the original Core Design codebase is very fragile and it is very easy to falsely identify code as bugged.
* **Do not introduce quick hacks to fix issues**. Hacks like `if (room == 314)` or `if (direction == WEST)` should be left in the history of bad Core Design coding practices.
* If code remains unimplemented or you need to visit another part of the code for a considerable amount of time to implement missing functionality for that first part of code, **leave a comment** prefixed `FIXME` and with a date-author signature, describing missing functionality that you are about to add:
``` c
// FIXME: I need to precisely calculate floor height, but for now let's keep original. -- Lwmte, 21.08.19
foo = GetFloor(blah);
```
* Make sure that **any new code is warning-free** before committing. Exceptions to this guideline include cases where fixing warnings would require you to make many changes outside of the new code, where the warning originates from a 3rd-party library, or where fixing the warning would make the code significantly less readable or performant.
* Avoid using magic numbers. Use constants or enums instead.
* Use **American English**.
## Parenthesis and new lines
Please do not write like this:
``` c
if (blah) {
foo;
foo_foo;
} else {
bar;
bar_bar; }
```
Write like this instead:
``` c
if (blah)
{
foo;
foo_foo;
}
else
{
bar;
bar_bar;
}
```
However, if you have only one line enclosed (i. e. only `foo`), and there are no following `else` or `else if` conditions, you may omit brackets and do it like this:
``` c
if (blah)
foo;
```
For one-line if statements, if the condition itself is multi-line, brackets should be added anyway to maintain readability:
```
if (item.RoomNumber == 123 || IsHeld(In::Action) &&
LaraHasEatenSandwich == SANDWICH_BACON)
{
CurrentLevel = 4;
}
```
Same rule applies to loops.
Avoid multiple nested conditional statements. Where possible, do an early exit from the code using the opposite statement instead of enclosing the statement body in an extra set of brackets. For example, instead of this:
``` c
if (blah)
{
if (foo)
bar;
}
```
Write this:
``` c
if (!blah || !foo)
return;
bar;
```
## Spacing
Don't condense arguments, loop statements, or any other code which requires splitting with commas/semicolons or operators like this:
``` c
void SomeLoop(int blah,int blah2)
{
for(int i=0;i<4;i++)
{
bar =blah;
foo= blah2;
}
}
```
Instead, separate everything with spaces **on the both sides** of operator, and **after** the comma or semicolon, like this:
``` c
void SomeLoop(int blah, int blah2)
{
for (int i = 0; i < 4; i++)
{
bar = blah;
foo = blah2;
}
}
```
The same rule applies to any code body, not only function declarations or loop statements.
## Tabulation and indents
When you have numerous assignment operations in a row, and/or their names are somewhat of equal length, and their data types are similar, align "left" and "right" parts of the assignment using tabs most of the way and spaces the rest of the way, like this:
``` c
float foo = 1.0;
float bar_bar = 1.0;
float foo_o_o = 1.0;
```
Or:
``` c
bar = foo-foo-foo;
foo_o = bar;
bar_r_r = foo;
```
In case you have pointers defined along with "normal" variables, the asterisk symbol must be placed instead of the last tab's space symbol (this also applies for class declarations and/or implementations), like this:
``` c
bar = foo_foo;
*foo = &bar_bar;
```
Of course, if one's left part is way longer than another one's left part, there's no need for such alignment, so you can leave it like this:
``` c
*foo_foo = &bar_foo;
bar->foo_foo_foo.blah_blah_bar_foo = 1.0;
foo_bar = 1.0;
```
In a switch case, each case body must be one tab further case's own label. Like this:
``` c
switch (blah)
{
case foo:
foo - foo - foo;
foo - foo - foo - foo;
break;
case bar:
bar - bar;
bar - bar - bar;
break;
}
```
If you need to enclose a particular case into a bracket block, put the body one tab further:
``` c
switch (blah)
{
case foo:
{
float bar;
bar = foo - foo - foo;
foo - foo - foo - foo = bar;
break;
}
case bar:
bar - bar;
bar - bar - bar;
break;
}
```
## Code splitting
If a code block becomes too large, it is recommended to split it into several "sub-blocks" with empty lines, taking each sub-block's meaning into account, like this:
``` c
foo = foo_foo + foo_foo_foo;
if (foo)
foo - foo - foo - foo - foo;
foo - foo = foo;
bar = (bar - bar > bar) ? (bar - bar) : 0;
bar - bar - bar = bar - bar - bar;
```
Conditional statements, if there are many, should be grouped according to their meaning. That is, if you are doing early exit from the function because of different conditions, group them as such:
``` c
if (coll.Floor > CLICK(1) && coll.Ceiling < CLICK(3) && bounds.Y1 > WALL_SIZE)
return false;
if (enemy.health <= 0 || lara.health <= 0 || collidedItems[0] == nullptr)
return false;
```
However, if there are few conditional statements, you can group them together. The rule of thumb is if there are more than three conditional statements and two of them are of a different kind, split them. This variant is allowed:
``` c
if (coll.Floor > CLICK(2) && coll.Ceiling < CLICK(4) || lara.health <= 0)
return false;
```
Sometimes IDA decompiled output generates "pascal-styled" or "basic-styled" variable declarations at the beginning of the function or code block. Like this:
``` c
int foo, bar = 0;
float blah;
...
blah = foo;
foo = bar;
...
```
**Please**, get rid of this style everywhere you see it and declare variables in the place narrowest to its actual usage, like this:
``` c
int foo = foo;
float blah = bar;
...
```
Let's cite [Google Style Guide](https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Local_Variables) here:
> Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
## Naming
* Use `auto` where possible and if the original type name is longer than `auto`. E.g. there is no point in changing `bool enabled = true` to `auto enabled = true`. Remember that C++ `auto` **is not similar to C#** `var`, and for referencing existing value with `auto`, you should add `&` to it in the end, e.g. `auto& item = g_Level.Items[itemIndex];` Also, for underlying pointer types, please write `auto*` instead of `auto`, even if it seems redundant.
* Avoid using Hungarian notation for function names. Currently, inconsistent notation indicates an unrefactored coding style, because the original code used inconsistent naming, like `SOUND_Stop()`, `have_i_got_object()` or `gar_SuperpackYXZ()`. These should eventually be eradicated along the course of code restyling. For new function names, `PascalCase` should be used.
* For global struct or class members or methods, `PascalCase` should be used. For local variable names, `camelCase` should be used. For global variables, which are temporary until full refactoring occurs, an exclusive case of Hungarian notation with the `g_` prefix (e.g. `g_Foo`) is permitted.
* Functions designed to take an enum argument should take the enum type itself instead of an int or short. `PassInfo(InfoEnum type)` is more readable and error-proof than `PassInfo(int type)`.
* Functions designed to return boolean value (0 or 1) should define return type as bool. Please don't use `int` return type and don't write `return 0` or `return 1` in bool return functions, use `return false` or `return true`.
* Use the following convention for Pointers and References: `Type* somePointer` / `Type& someReference`. Do not write this: `Type * somePointer` / `Type & someReference` or `Type *somePointer` / `Type &someReference`. Pointers and references are distinct types, hence why the notation for them should have the token on the side of the type.
* Avoid unscoped enum types. For scoped `enum class` types, use `PascalCase` without including enum prefix:
``` c
enum class WeatherType
{
None,
Rain,
Snow,
Cats,
Dogs
};
```
`ENUM_ALL_CAPS` primarily indicates old C-styled Core notation. For C-styled (unscoped) enum values themselves, `ALL_CAPS` may be used for now, along with enum prefix:
``` c
enum LaraWeaponType
{
WEAPON_NONE,
WEAPON_PISTOLS,
WEAPON_REVOLVER
};
```
## Data types
Avoid using `_t` data types, such as `uint8_t`, `int8_t`, `int16_t` etc. Use `unsigned char`, `signed char`, `short` etc. instead. If new variables or fields are introduced, prefer longer and more contemporary data types over deprecated ones. That is, if integer value is used, use `int` instead of `char` or `short`. If potentially fractional value is used (such as coordinates which are eventually multiplied or divided or transformed otherwise), prefer `float` over `int`.
For legacy functions and code paths, preserving original data types may be necessary. Special case are angle values - original games used weird `signed short` angle convention. So extra caution must be taken when writing code which operates on native TR angles, and it should always be kept in variables of `signed short` data type.
Prefer using references over pointers, both in function body and as arguments. When using references or pointers, prefix with `const` for read-only safety if the variable is not being written to.
## Casting
Prefer using C-styled casting instead of C++-styled casting where it is safe. While using it, avoid separating casting operator and variable with space:
`bar = (int)foo;`
For expressions, you can enclose expression into brackets to cast it:
`bar = int(foo + blah * 4);`
Using C++-styled casting is allowed when C-styled casting provides undefined or unacceptable behaviour.
## Includes
For header files from project itself, always use includes with quotes, not with brackets. Also avoid using Windows-specific `\` backslash symbols to specify subpaths, only use normal `/` slashes. Also please include full path to a header file and order includes alphabetically:
```c
#include "Game/effects/lightning.h"
#include "Specific/phd_math.h"
```
Includes with brackets are only allowed when using external libraries:
```c
#include <algorithm>
#include "Game/collision/collide.h"
```
## Namespaces
Don't shorten `std` namespace types and methods by using `using` directive. This is bad: `auto x = vector<int>();`
Leave them as is. This is good: `auto x = std::vector<int>();`
## Comments
Use `//`-styled comments where possible.
Only use `/* */` style in case you are about to temporarily comment certain block for testing purposes or when writing a comment that will serve as the source for generated documentation.
Use a `NOTE: ` prefix in your comment if you want to highlight something particularly noteworthy:
```c
// NOTE: Will not work for bones at ends of hierarchies.
float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex)
{
const auto& object = Objects[objectID];
if (object.nmeshes == boneIndex)
return 0.0f;
auto nextBoneOffset = GetJointOffset(objectID, boneIndex + 1);
return nextBoneOffset.Length();
}
```
Use a `FAILSAFE: ` prefix in your comment if you want to highlight a particularly quirky solution without an obvious and clear purpose:
```c
if (portalRoomNumber != NO_VALUE &&
rayRoomNumber != portalRoomNumber) // FAILSAFE: Prevent infinite loop if room portal leads back to itself.
{
player.Explode();
}
```
## Branches and pull requests
Make sure that epic branches (tens or hundreds of files changed due to renames, namespace wrappings, etc) **are focused on a single feature or task**. Don't jump in to others epic branches with another round of your epic changes. It masks bugs and makes review process very cumbersome.
Avoid making new branches based on unapproved epic PRs which are in the process of review. It may render your work useless if parent epic branch is unapproved and scrapped.

11
Documentation/compile.bat Normal file
View file

@ -0,0 +1,11 @@
@echo off
setlocal
set DOC_DIR=.\doc
set LDOC_DIR=.\compiler\ldoc
set LUA_PATH=.\compiler\?.lua
set LUA_CPATH=.\compiler\?.dll
rmdir /s /q %DOC_DIR%
mkdir %DOC_DIR%
.\compiler\lua.exe %LDOC_DIR%\\ldoc.lua %*
del output.xml
exit /b %ERRORLEVEL%

View file

@ -0,0 +1,22 @@
LDoc License
-----------
Copyright (C) 2011 Steve Donovan.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,66 @@
package = "ldoc"
version = "scm-3"
source = {
dir="LDoc",
url = "git+https://github.com/stevedonovan/LDoc.git"
}
description = {
summary = "A Lua Documentation Tool",
detailed = [[
LDoc is a LuaDoc-compatible documentation generator which can also
process C extension source. Markdown may be optionally used to
render comments, as well as integrated readme documentation and
pretty-printed example files
]],
homepage='https://github.com/lunarmodules/LDoc',
maintainer='steve.j.donovan@gmail.com',
license = "MIT/X11",
}
dependencies = {
"penlight","markdown"
}
build = {
type = "builtin",
modules = {
["ldoc.tools"] = "ldoc/tools.lua",
["ldoc.lang"] = "ldoc/lang.lua",
["ldoc.parse"] = "ldoc/parse.lua",
["ldoc.html"] = "ldoc/html.lua",
["ldoc.lexer"] = "ldoc/lexer.lua",
["ldoc.markup"] = "ldoc/markup.lua",
["ldoc.prettify"] = "ldoc/prettify.lua",
["ldoc.markdown"] = "ldoc/markdown.lua",
["ldoc.doc"] = "ldoc/doc.lua",
["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua",
["ldoc.html.ldoc_md_ltp"] = "ldoc/html/ldoc_md_ltp.lua",
["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua",
["ldoc.html._code_css"] = "ldoc/html/_code_css.lua",
["ldoc.html._reset_css"] = "ldoc/html/_reset_css.lua",
["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua",
["ldoc.html.ldoc_pale_css"] = "ldoc/html/ldoc_pale_css.lua",
["ldoc.html.ldoc_new_css"] = "ldoc/html/ldoc_new_css.lua",
["ldoc.html.ldoc_fixed_css"] = "ldoc/html/ldoc_fixed_css.lua",
["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua",
["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua",
["ldoc.builtin.global"] = "ldoc/builtin/global.lua",
["ldoc.builtin.debug"] = "ldoc/builtin/debug.lua",
["ldoc.builtin.io"] = "ldoc/builtin/io.lua",
["ldoc.builtin.lfs"] = "ldoc/builtin/lfs.lua",
["ldoc.builtin.lpeg"] = "ldoc/builtin/lpeg.lua",
["ldoc.builtin.math"] = "ldoc/builtin/math.lua",
["ldoc.builtin.os"] = "ldoc/builtin/os.lua",
["ldoc.builtin.package"] = "ldoc/builtin/package.lua",
["ldoc.builtin.string"] = "ldoc/builtin/string.lua",
["ldoc.builtin.table"] = "ldoc/builtin/table.lua",
},
copy_directories = {'doc','tests'},
install = {
bin = {
ldoc = "ldoc.lua"
}
}
}

View file

@ -0,0 +1,866 @@
#!/usr/bin/env lua
---------------
-- ## ldoc, a Lua documentation generator.
--
-- Compatible with luadoc-style annotations, but providing
-- easier customization options.
--
-- C/C++ support for Lua extensions is provided.
--
-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.4.3.zip)
--
-- [Github Page](https://github.com/stevedonovan/ldoc)
--
-- @author Steve Donovan
-- @copyright 2011
-- @license MIT/X11
-- @script ldoc
local class = require 'pl.class'
local app = require 'pl.app'
local path = require 'pl.path'
local dir = require 'pl.dir'
local utils = require 'pl.utils'
local List = require 'pl.List'
local stringx = require 'pl.stringx'
local tablex = require 'pl.tablex'
-- Penlight compatibility
utils.unpack = utils.unpack or unpack or table.unpack
local lapp = require 'pl.lapp'
local version = '1.4.6'
-- so we can find our private modules
app.require_here()
--- @usage
local usage = [[
ldoc, a documentation generator for Lua, v]]..version..[[
Invocation:
ldoc [options] <file>
ldoc --version
Options:
-d,--dir (default doc) output directory
-o,--output (default 'index') output name
-v,--verbose verbose
-a,--all show local functions, etc, in docs
-q,--quiet suppress output
-m,--module module docs as text
-s,--style (default !) directory for style sheet (ldoc.css)
-l,--template (default !) directory for template (ldoc.ltp)
-p,--project (default ldoc) project name
-t,--title (default Reference) page title
-f,--format (default plain) formatting - can be markdown, discount or plain
-b,--package (default .) top-level package basename (needed for module(...))
-x,--ext (default html) output file extension
-c,--config (default config.ld) configuration name
-u,--unqualified don't show package name in sidebar links
-i,--ignore ignore any 'no doc comment or no module' warnings
-X,--not_luadoc break LuaDoc compatibility. Descriptions may continue after tags.
-D,--define (default none) set a flag to be used in config.ld
-C,--colon use colon style
-N,--no_args_infer don't infer arguments from source
-B,--boilerplate ignore first comment in source files
-M,--merge allow module merging
-S,--simple no return or params, no summary
-O,--one one-column output layout
-V,--version show version information
--date (default system) use this date in generated doc
--dump debug output dump
--filter (default none) filter output as Lua data (e.g pl.pretty.dump)
--tags (default none) show all references to given tags, comma-separated
--fatalwarnings non-zero exit status on any warning
--testing reproducible build; no date or version on output
--icon (default none) an image that will be displayed under the project name on all pages
<file> (string) source file or directory containing source
`ldoc .` reads options from an `config.ld` file in same directory;
`ldoc -c path/to/myconfig.ld <file>` reads options from `path/to/myconfig.ld`
and processes <file> if 'file' was not defined in the ld file.
]]
local args = lapp(usage)
local lfs = require 'lfs'
local doc = require 'ldoc.doc'
local lang = require 'ldoc.lang'
local tools = require 'ldoc.tools'
local global = require 'ldoc.builtin.globals'
local markup = require 'ldoc.markup'
local parse = require 'ldoc.parse'
local KindMap = tools.KindMap
local Item,File = doc.Item,doc.File
local quit = utils.quit
if args.version then
print('LDoc v' .. version)
os.exit(0)
end
local ModuleMap = class(KindMap)
doc.ModuleMap = ModuleMap
function ModuleMap:_init ()
self.klass = ModuleMap
self.fieldname = 'section'
end
local ProjectMap = class(KindMap)
ProjectMap.project_level = true
function ProjectMap:_init ()
self.klass = ProjectMap
self.fieldname = 'type'
end
local lua, cc = lang.lua, lang.cc
local file_types = {
['.lua'] = lua,
['.ldoc'] = lua,
['.luadoc'] = lua,
['.c'] = cc,
['.h'] = cc,
['.cpp'] = cc,
['.cxx'] = cc,
['.C'] = cc,
['.mm'] = cc,
['.cs'] = cc,
['.moon'] = lang.moon,
}
------- ldoc external API ------------
-- the ldoc table represents the API available in `config.ld`.
local ldoc = { charset = 'UTF-8', version = version }
local known_types, kind_names = {}
local function lookup (itype,igroup,isubgroup)
local kn = kind_names[itype]
known_types[itype] = true
if kn then
if type(kn) == 'string' then
igroup = kn
else
igroup = kn[1]
isubgroup = kn[2]
end
end
return itype, igroup, isubgroup
end
local function setup_kinds ()
kind_names = ldoc.kind_names or {}
ModuleMap:add_kind(lookup('function','Functions','Parameters'))
ModuleMap:add_kind(lookup('table','Tables','Fields'))
ModuleMap:add_kind(lookup('field','Fields'))
ModuleMap:add_kind(lookup('type','Types'))
ModuleMap:add_kind(lookup('lfunction','Local Functions','Parameters'))
ModuleMap:add_kind(lookup('annotation','Issues'))
ProjectMap:add_kind(lookup('module','Modules'))
ProjectMap:add_kind(lookup('script','Scripts'))
ProjectMap:add_kind(lookup('classmod','Classes'))
ProjectMap:add_kind(lookup('topic','Topics'))
ProjectMap:add_kind(lookup('example','Examples'))
ProjectMap:add_kind(lookup('file','Source'))
for k in pairs(kind_names) do
if not known_types[k] then
quit("unknown item type "..tools.quote(k).." in kind_names")
end
end
end
-- hacky way for doc module to be passed options...
doc.ldoc = ldoc
-- if the corresponding argument was the default, then any ldoc field overrides
local function override (field,defval)
defval = defval or false
if args[field] == defval and ldoc[field] ~= nil then args[field] = ldoc[field] end
end
-- aliases to existing tags can be defined. E.g. just 'p' for 'param'
function ldoc.alias (a,tag)
doc.add_alias(a,tag)
end
-- standard aliases --
ldoc.alias('tparam',{'param',modifiers={type="$1"}})
ldoc.alias('treturn',{'return',modifiers={type="$1"}})
ldoc.alias('tfield',{'field',modifiers={type="$1"}})
function ldoc.tparam_alias (name,type)
type = type or name
ldoc.alias(name,{'param',modifiers={type=type}})
end
ldoc.alias ('error',doc.error_macro)
ldoc.tparam_alias 'string'
ldoc.tparam_alias 'number'
ldoc.tparam_alias 'int'
ldoc.tparam_alias 'bool'
ldoc.tparam_alias 'func'
ldoc.tparam_alias 'tab'
ldoc.tparam_alias 'thread'
function ldoc.add_language_extension(ext, lang)
lang = (lang=='c' and cc) or (lang=='lua' and lua) or quit('unknown language')
if ext:sub(1,1) ~= '.' then ext = '.'..ext end
file_types[ext] = lang
end
function ldoc.add_section (name, title, subname)
ModuleMap:add_kind(name,title,subname)
end
-- new tags can be added, which can be on a project level.
function ldoc.new_type (tag, header, project_level,subfield)
doc.add_tag(tag,doc.TAG_TYPE,project_level)
if project_level then
ProjectMap:add_kind(tag,header,subfield)
else
ModuleMap:add_kind(tag,header,subfield)
end
end
function ldoc.manual_url (url)
global.set_manual_url(url)
end
function ldoc.custom_see_handler(pat, handler)
doc.add_custom_see_handler(pat, handler)
end
local ldoc_contents = {
'alias','add_language_extension','custom_tags','new_type','add_section', 'tparam_alias',
'file','project','title','package', 'icon','format','output','dir','ext', 'topics',
'one','style','template','description','examples', 'pretty', 'charset', 'plain',
'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars',
'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups',
'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler',
'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles',
'unqualified', 'custom_display_name_handler', 'kind_names', 'custom_references',
'dont_escape_underscore','global_lookup','prettify_files','convert_opt', 'user_keywords',
'postprocess_html',
'custom_css','version',
'no_args_infer',
'keep_menu_order'
}
ldoc_contents = tablex.makeset(ldoc_contents)
local function loadstr (ldoc,txt)
local chunk, err
-- Penlight's Lua 5.2 compatibility has wobbled over the years...
if not rawget(_G,'loadin') then -- Penlight 0.9.5
-- Penlight 0.9.7; no more global load() override
local load = load or utils.load
chunk,err = load(txt,'config',nil,ldoc)
else
-- luacheck: push ignore 113
chunk,err = loadin(ldoc,txt)
-- luacheck: pop
end
return chunk, err
end
-- any file called 'config.ld' found in the source tree will be
-- handled specially. It will be loaded using 'ldoc' as the environment.
local function read_ldoc_config (fname)
local directory = path.dirname(fname)
if directory == '' then
directory = '.'
end
local chunk, err, _
if args.filter == 'none' then
print('reading configuration from '..fname)
end
local txt,not_found = utils.readfile(fname)
if txt then
chunk, err = loadstr(ldoc,txt)
if chunk then
if args.define ~= 'none' then ldoc[args.define] = true end
_,err = pcall(chunk)
end
end
if err then quit('error loading config file '..fname..': '..err) end
for k in pairs(ldoc) do
if not ldoc_contents[k] then
quit("this config file field/function is unrecognized: "..k)
end
end
return directory, not_found
end
local quote = tools.quote
--- processing command line and preparing for output ---
local file_list = List()
File.list = file_list
local config_dir
local ldoc_dir = arg[0]:gsub('[^/\\]+$','')
local doc_path = ldoc_dir..'/ldoc/builtin/?.lua'
-- ldoc -m is expecting a Lua package; this converts this to a file path
if args.module then
-- first check if we've been given a global Lua lib function
if args.file:match '^%a+$' and global.functions[args.file] then
args.file = 'global.'..args.file
end
local fullpath,mod,_ = tools.lookup_existing_module_or_function (args.file, doc_path)
if not fullpath then
quit(mod)
else
args.file = fullpath
args.module = mod
end
end
local abspath = tools.abspath
-- a special case: 'ldoc .' can get all its parameters from config.ld
if args.file == '.' then
local err
config_dir,err = read_ldoc_config(args.config)
if err then quit("no "..quote(args.config).." found") end
local config_path = path.dirname(args.config)
if config_path ~= '' then
print('changing to directory',config_path)
lfs.chdir(config_path)
end
args.file = ldoc.file or '.'
if args.file == '.' then
args.file = lfs.currentdir()
elseif type(args.file) == 'table' then
for i,f in ipairs(args.file) do
args.file[i] = abspath(f)
end
else
args.file = abspath(args.file)
end
else
-- user-provided config file
if args.config ~= 'config.ld' then
local err
config_dir,err = read_ldoc_config(args.config)
if err then quit("no "..quote(args.config).." found") end
end
-- with user-provided file
if args.file == nil then
lapp.error('missing required parameter: file')
end
args.file = abspath(args.file)
end
if type(ldoc.custom_tags) == 'table' then -- custom tags
for i, custom in ipairs(ldoc.custom_tags) do
if type(custom) == 'string' then
custom = {custom}
ldoc.custom_tags[i] = custom
end
doc.add_tag(custom[1], 'ML')
end
end -- custom tags
local source_dir = args.file
if type(source_dir) == 'table' then
source_dir = source_dir[1]
end
if type(source_dir) == 'string' and path.isfile(source_dir) then
source_dir = path.splitpath(source_dir)
end
source_dir = source_dir:gsub('[/\\]%.$','')
---------- specifying the package for inferring module names --------
-- If you use module(...), or forget to explicitly use @module, then
-- ldoc has to infer the module name. There are three sensible values for
-- `args.package`:
--
-- * '.' the actual source is in an immediate subdir of the path given
-- * '..' the path given points to the source directory
-- * 'NAME' explicitly give the base module package name
--
override ('package','.')
local function setup_package_base()
if ldoc.package then args.package = ldoc.package end
if args.package == '.' then
args.package = source_dir
elseif args.package == '..' then
args.package = path.splitpath(source_dir)
elseif not args.package:find '[\\/]' then
local subdir,dir = path.splitpath(source_dir)
if dir == args.package then
args.package = subdir
elseif path.isdir(path.join(source_dir,args.package)) then
args.package = source_dir
else
quit("args.package is not the name of the source directory")
end
end
end
--------- processing files ---------------------
-- ldoc may be given a file, or a directory. `args.file` may also be specified in config.ld
-- where it is a list of files or directories. If specified on the command-line, we have
-- to find an optional associated config.ld, if not already loaded.
if ldoc.ignore then args.ignore = true end
local function process_file (f, flist)
local ext = path.extension(f)
local ftype = file_types[ext]
if ftype then
if args.verbose then print(f) end
ftype.extra = ldoc.parse_extra or {}
local F,err = parse.file(f,ftype,args)
if err then
if F then
F:warning("internal LDoc error")
end
quit(err)
end
flist:append(F)
end
end
local process_file_list = tools.process_file_list
setup_package_base()
override 'no_args_infer'
override 'colon'
override 'merge'
override 'not_luadoc'
override 'module_file'
override 'boilerplate'
override 'all'
setup_kinds()
-- LDoc is doing plain ole C, don't want random Lua references!
if ldoc.parse_extra and ldoc.parse_extra.C then
ldoc.no_lua_ref = true
end
if ldoc.merge_error_groups == nil then
ldoc.merge_error_groups = 'Error Message'
end
-- ldoc.module_file establishes a partial ordering where the
-- master module files are processed first.
local function reorder_module_file ()
if args.module_file then
local mf = {}
for mname, f in pairs(args.module_file) do
local fullpath = abspath(f)
mf[fullpath] = true
end
return function(x,y)
return mf[x] and not mf[y]
end
end
end
-- process files, optionally in order that respects master module files
local function process_all_files(files)
local sortfn = reorder_module_file()
local files = tools.expand_file_list(files,'*.*')
if sortfn then files:sort(sortfn) end
for f in files:iter() do
process_file(f, file_list)
end
if #file_list == 0 then quit "no source files found" end
end
if type(args.file) == 'table' then
-- this can only be set from config file so we can assume config is already read
process_all_files(args.file)
elseif path.isdir(args.file) then
-- use any configuration file we find, if not already specified
if not config_dir then
local files = List(dir.getallfiles(args.file,'*.*'))
local config_files = files:filter(function(f)
return path.basename(f) == args.config
end)
if #config_files > 0 then
config_dir = read_ldoc_config(config_files[1])
if #config_files > 1 then
print('warning: other config files found: '..config_files[2])
end
end
end
process_all_files({args.file})
elseif path.isfile(args.file) then
-- a single file may be accompanied by a config.ld in the same dir
if not config_dir then
config_dir = path.dirname(args.file)
if config_dir == '' then config_dir = '.' end
local config = path.join(config_dir,args.config)
if path.isfile(config) then
read_ldoc_config(config)
end
end
process_file(args.file, file_list)
if #file_list == 0 then quit "unsupported file extension" end
else
quit ("file or directory does not exist: "..quote(args.file))
end
-- create the function that renders text (descriptions and summaries)
-- (this also will initialize the code prettifier used)
override ('format','plain')
override 'pretty'
ldoc.markup = markup.create(ldoc, args.format, args.pretty, ldoc.user_keywords)
------ 'Special' Project-level entities ---------------------------------------
-- Examples and Topics do not contain code to be processed for doc comments.
-- Instead, they are intended to be rendered nicely as-is, whether as pretty-lua
-- or as Markdown text. Treating them as 'modules' does stretch the meaning of
-- of the term, but allows them to be treated much as modules or scripts.
-- They define an item 'body' field (containing the file's text) and a 'postprocess'
-- field which is used later to convert them into HTML. They may contain @{ref}s.
local function add_special_project_entity (f,tags,process)
local F = File(f)
tags.name = path.basename(f)
local text = utils.readfile(f)
local item = F:new_item(tags,1)
if process then
text = process(F, text)
end
F:finish()
file_list:append(F)
item.body = text
return item, F
end
local function prettify_source_files(files,class,linemap)
local prettify = require 'ldoc.prettify'
process_file_list (files, '*.*', function(f)
local ext = path.extension(f)
local ftype = file_types[ext]
if ftype then
local item = add_special_project_entity(f,{
class = class,
})
-- wrap prettify for this example so it knows which file to blame
-- if there's a problem
local lang = ext:sub(2)
item.postprocess = function(code)
return '<h2>'..path.basename(f)..'</h2>\n' ..
prettify.lua(lang,f,code,0,true,linemap and linemap[f])
end
end
end)
end
if type(ldoc.examples) == 'string' then
ldoc.examples = {ldoc.examples}
end
if type(ldoc.examples) == 'table' then
prettify_source_files(ldoc.examples,"example")
end
ldoc.is_file_prettified = {}
if ldoc.prettify_files then
local files = List()
local linemap = {}
for F in file_list:iter() do
files:append(F.filename)
local mod = F.modules[1]
if mod then
local ls = List()
for item in mod.items:iter() do
ls:append(item.lineno)
end
linemap[F.filename] = ls
end
end
if type(ldoc.prettify_files) == 'table' then
files = tools.expand_file_list(ldoc.prettify_files, '*.*')
elseif type(ldoc.prettify_files) == 'string' then
-- the gotcha is that if the person has a folder called 'show', only the contents
-- of that directory will be converted. So, we warn of this amibiguity
if ldoc.prettify_files == 'show' then
-- just fall through with all module files collected above
if path.exists 'show' then
print("Notice: if you only want to prettify files in `show`, then set prettify_files to `show/`")
end
else
files = tools.expand_file_list({ldoc.prettify_files}, '*.*')
end
end
ldoc.is_file_prettified = tablex.makeset(files)
prettify_source_files(files,"file",linemap)
end
if args.simple then
ldoc.no_return_or_parms=true
ldoc.no_summary=true
end
ldoc.readme = ldoc.readme or ldoc.topics
if type(ldoc.readme) == 'string' then
ldoc.readme = {ldoc.readme}
end
if type(ldoc.readme) == 'table' then
process_file_list(ldoc.readme, '*.md', function(f)
local item, F = add_special_project_entity(f,{
class = 'topic'
}, markup.add_sections)
-- add_sections above has created sections corresponding to the 2nd level
-- headers in the readme, which are attached to the File. So
-- we pass the File to the postprocesser, which will insert the section markers
-- and resolve inline @ references.
if ldoc.use_markdown_titles then
item.display_name = F.display_name
end
item.postprocess = function(txt) return ldoc.markup(txt,F) end
end)
end
-- extract modules from the file objects, resolve references and sort appropriately ---
local first_module
local project = ProjectMap()
local module_list = List()
module_list.by_name = {}
local modcount = 0
for F in file_list:iter() do
for mod in F.modules:iter() do
if not first_module then first_module = mod end
if doc.code_tag(mod.type) then modcount = modcount + 1 end
module_list:append(mod)
module_list.by_name[mod.name] = mod
end
end
local handle = io.open("output.xml", "w")
io.output(handle)
for mod in module_list:iter() do
mod:dumpToXML()
end
io.close(handle)
for mod in module_list:iter() do
if not args.module then -- no point if we're just showing docs on the console
mod:resolve_references(module_list)
end
project:add(mod,module_list)
end
if ldoc.sort_modules then
table.sort(module_list,function(m1,m2)
return m1.name < m2.name
end)
end
ldoc.single = modcount == 1 and first_module or nil
--do return end
-------- three ways to dump the object graph after processing -----
-- ldoc -m will give a quick & dirty dump of the module's documentation;
-- using -v will make it more verbose
if args.module then
if #module_list == 0 then quit("no modules found") end
if args.module == true then
file_list[1]:dump(args.verbose)
else
local M,name = module_list[1], args.module
local fun = M.items.by_name[name]
if not fun then
fun = M.items.by_name[M.mod_name..':'..name]
end
if not fun then quit(quote(name).." is not part of "..quote(args.file)) end
fun:dump(true)
end
return
end
-- ldoc --dump will do the same as -m, except for the currently specified files
if args.dump then
for mod in module_list:iter() do
mod:dump(true)
end
os.exit()
end
if args.tags ~= 'none' then
local tagset = {}
for t in stringx.split(args.tags,','):iter() do
tagset[t] = true
end
for mod in module_list:iter() do
mod:dump_tags(tagset)
end
os.exit()
end
-- ldoc --filter mod.name will load the module `mod` and pass the object graph
-- to the function `name`. As a special case --filter dump will use pl.pretty.dump.
if args.filter ~= 'none' then
doc.filter_objects_through_function(args.filter, module_list)
os.exit()
end
-- can specify format, output, dir and ext in config.ld
override ('output','index')
override ('dir','doc')
override ('ext','html')
override 'one'
-- handling styling and templates --
ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp'
-- special case: user wants to generate a .md file from a .lua file
if args.ext == 'md' then
if #module_list ~= 1 then
quit("can currently only generate Markdown output from one module only")
end
if not ldoc.template or ldoc.template == '!' then
ldoc.template = '!md'
end
args.output = module_list[1].name
args.dir = '.'
ldoc.template_escape = '>'
ldoc.style = false
args.ext = '.md'
end
local function match_bang (s)
if type(s) ~= 'string' then return end
return s:match '^!(.*)'
end
local function style_dir (sname)
local style = ldoc[sname]
local dir
if style==false and sname == 'style' then
args.style = false
ldoc.css = false
end
if style then
if style == true then
dir = config_dir
elseif type(style) == 'string' and (path.isdir(style) or match_bang(style)) then
dir = style
else
quit(quote(tostring(style)).." is not a directory")
end
args[sname] = dir
end
end
-- the directories for template and stylesheet can be specified
-- either by command-line '--template','--style' arguments or by 'template and
-- 'style' fields in config.ld.
-- The assumption here is that if these variables are simply true then the directory
-- containing config.ld contains a ldoc.css and a ldoc.ltp respectively. Otherwise
-- they must be a valid subdirectory.
style_dir 'style'
style_dir 'template'
if not args.ext:find '^%.' then
args.ext = '.'..args.ext
end
if args.one then
ldoc.style = '!one'
end
local builtin_style, builtin_template = match_bang(args.style),match_bang(args.template)
if builtin_style or builtin_template then
-- '!' here means 'use built-in templates'
local user = path.expanduser('~'):gsub('[/\\: ]','_')
local tmpdir = path.join(path.is_windows and os.getenv('TMP') or (os.getenv('TMPDIR') or '/tmp'),'ldoc'..user)
if not path.isdir(tmpdir) then
lfs.mkdir(tmpdir)
end
local function tmpwrite (name)
local ok,text = pcall(require,'ldoc.html.'..name:gsub('%.','_'))
if not ok then
quit("cannot find builtin template "..name.." ("..text..")")
end
if not utils.writefile(path.join(tmpdir,name),text) then
quit("cannot write to temp directory "..tmpdir)
end
end
if builtin_style then
if builtin_style ~= '' then
ldoc.css = 'ldoc_'..builtin_style..'.css'
end
tmpwrite(ldoc.css)
args.style = tmpdir
end
if builtin_template then
if builtin_template ~= '' then
ldoc.templ = 'ldoc_'..builtin_template..'.ltp'
end
tmpwrite(ldoc.templ)
args.template = tmpdir
end
end
-- default icon to nil
if args.icon == 'none' then args.icon = nil end
ldoc.log = print
ldoc.kinds = project
ldoc.modules = module_list
ldoc.title = ldoc.title or args.title
ldoc.project = ldoc.project or args.project
ldoc.package = args.package:match '%a+' and args.package or nil
ldoc.icon = ldoc.icon or args.icon
local source_date_epoch = os.getenv("SOURCE_DATE_EPOCH")
if args.testing then
ldoc.updatetime = "2015-01-01 12:00:00"
ldoc.version = 'TESTING'
elseif source_date_epoch == nil then
if args.date == 'system' then
ldoc.updatetime = os.date("%Y-%m-%d %H:%M:%S")
else
ldoc.updatetime = args.date
end
else
ldoc.updatetime = os.date("!%Y-%m-%d %H:%M:%S",source_date_epoch)
end
local html = require 'ldoc.html'
html.generate_output(ldoc, args, project)
if args.verbose then
print 'modules'
for k in pairs(module_list.by_name) do print(k) end
end
if args.fatalwarnings and Item.had_warning then
os.exit(1)
end

View file

@ -0,0 +1,3 @@
tabsize=3
indent.size=3
use.tabs=0

View file

@ -0,0 +1,50 @@
--- creating and controlling coroutines.
-- @module coroutine
local coroutine = {}
---
-- Creates a new coroutine, with body `f`. `f` must be a Lua
-- function. Returns this new coroutine, an object with type `"thread"`.
function coroutine.create(f) end
---
-- Starts or continues the execution of coroutine `co`. The first time
-- you resume a coroutine, it starts running its body. The values
-- ... are passed as the arguments to the body function. If the coroutine
-- has yielded, `resume` restarts it; the values ... are passed
-- as the results from the yield.
-- If the coroutine runs without any errors, `resume` returns true plus any
-- values passed to `yield` (if the coroutine yields) or any values returned
-- by the body function (if the coroutine terminates). If there is any error,
-- `resume` returns false plus the error message.
function coroutine.resume(co , ...) end
---
-- Returns the running coroutine. Or nil when called by the main thread.
function coroutine.running() end
---
-- Returns the status of coroutine `co`. Result is a string: `"running"`, if
-- the coroutine is running (that is, it called `status`); `"suspended"`, if
-- the coroutine is suspended in a call to `yield`, or if it has not started
-- running yet; `"normal"` if the coroutine is active but not running (that
-- is, it has resumed another coroutine); and `"dead"` if the coroutine has
-- finished its body function, or if it has stopped with an error.
function coroutine.status(co) end
---
-- Creates a new coroutine, with body `f`. `f` must be a Lua
-- function. Returns a function that resumes the coroutine each time it is
-- called. Any arguments passed to the function behave as the extra arguments to
-- `resume`. Returns the same values returned by `resume`, except the first
-- boolean. In case of error, propagates the error.
function coroutine.wrap(f) end
---
-- Suspends the execution of the calling coroutine. The coroutine cannot
-- be running a C function, a metamethod, or an iterator. Any arguments to
-- `yield` are passed as extra results to `resume`.
function coroutine.yield(...) end
return coroutine

View file

@ -0,0 +1,124 @@
--- getting runtime debug information.
-- @module debug
local debug = {}
---
-- Enters an interactive mode with the user, running each string that
-- the user enters. Using simple commands and other debug facilities,
-- the user can inspect global and local variables, change their values,
-- evaluate expressions, and so on. A line containing only the word `cont`
-- finishes this function, so that the caller continues its execution.
-- Note that commands for `debug.debug` are not lexically nested within any
-- function, and so have no direct access to local variables.
function debug.debug() end
---
-- Returns the environment of object `o`.
function debug.getfenv(o) end
---
-- Returns the current hook settings of the thread, as three values: the
-- current hook function, the current hook mask, and the current hook count
-- (as set by the `debug.sethook` function).
function debug.gethook(thread) end
---
-- Returns a table with information about a function. You can give the
-- function directly, or you can give a number as the value of `function`,
-- which means the function running at level `function` of the call stack
-- of the given thread: level 0 is the current function (`getinfo` itself);
-- level 1 is the function that called `getinfo`; and so on. If `function`
-- is a number larger than the number of active functions, then `getinfo`
-- returns nil.
--
-- `thread` and `what` are optional.
--
-- The returned table can contain all the fields returned by `lua_getinfo`,
-- with the string `what` describing which fields to fill in. The default for
-- `what` is to get all information available, except the table of valid
-- lines. If present, the option '`f`' adds a field named `func` with
-- the function itself. If present, the option '`L`' adds a field named
-- `activelines` with the table of valid lines.
-- For instance, the expression `debug.getinfo(1,"n").name` returns a table
-- with a name for the current function, if a reasonable name can be found,
-- and the expression `debug.getinfo(print)` returns a table with all available
-- information about the `print` function.
function debug.getinfo(thread, func , what) end
---
-- This function returns the name and the value of the local variable with
-- index `loc` of the function at level `level` of the stack. (The first
-- parameter or local variable has index 1, and so on, until the last active
-- local variable.) The function returns nil if there is no local variable
-- with the given index, and raises an error when called with a `level` out
-- of range. (You can call `debug.getinfo` to check whether the level is valid.)
-- Variable names starting with '`(`' (open parentheses) represent internal
-- variables (loop control variables, temporaries, and C function locals).
function debug.getlocal(thread, level, loc) end
---
-- Returns the metatable of the given `object` or nil if it does not have
-- a metatable.
function debug.getmetatable(object) end
---
-- Returns the registry table (see §3.5).
function debug.getregistry() end
---
-- This function returns the name and the value of the upvalue with index
-- `up` of the function `func`. The function returns nil if there is no
-- upvalue with the given index.
function debug.getupvalue(func, up) end
---
-- Sets the environment of the given `object` to the given `table`. Returns
-- `object`.
function debug.setfenv(object, table) end
---
-- Sets the given function as a hook. The string `mask` and the number
-- `count` describe when the hook will be called. The string mask may have
-- the following characters, with the given meaning:
--
-- * `"c"`: the hook is called every time Lua calls a function;
-- * `"r"`: the hook is called every time Lua returns from a function;
-- * `"l"`: the hook is called every time Lua enters a new line of code.
--
-- With a `count` different from zero, the hook is called after every `count`
-- instructions.
--
-- When called without arguments, `debug.sethook` turns off the hook.
--
-- When the hook is called, its first parameter is a string describing
-- the event that has triggered its call: `"call"`, `"return"` (or `"tail
-- return"`, when simulating a return from a tail call), `"line"`, and
-- `"count"`. For line events, the hook also gets the new line number as its
-- second parameter. Inside a hook, you can call `getinfo` with level 2 to
-- get more information about the running function (level 0 is the `getinfo`
-- function, and level 1 is the hook function), unless the event is `"tail
-- return"`. In this case, Lua is only simulating the return, and a call to
-- `getinfo` will return invalid data.
function debug.sethook(thread, hook, mask , count) end
---
-- This function assigns the value `value` to the local variable with
-- index `loc` of the function at level `level` of the stack. The function
-- returns nil if there is no local variable with the given index, and raises
-- an error when called with a `level` out of range. (You can call `getinfo`
-- to check whether the level is valid.) Otherwise, it returns the name of
-- the local variable.
function debug.setlocal(thread, level, loc, value) end
---
-- Sets the metatable for the given `object` to the given `table` (which
-- can be nil).
function debug.setmetatable(object, table) end
---
-- This function assigns the value `value` to the upvalue with index `up`
-- of the function `func`. The function returns nil if there is no upvalue
-- with the given index. Otherwise, it returns the name of the upvalue.
function debug.setupvalue(func, up, value) end
return debug

View file

@ -0,0 +1,243 @@
--- Lua global functions.
module 'global'
-- luacheck: ignore 121
---
-- Issues an error when its argument `v` is false.
-- That is, nil or false. otherwise, returns all its arguments.
-- `message` is an error when absent, it defaults to "assertion failed!"
function assert(v , message) end
---
-- This function is a generic interface to the garbage collector. It
-- performs different functions according to its first argument, `opt`:
--
-- * "stop": stops the garbage collector.
-- * "restart": restarts the garbage collector.
-- * "collect": performs a full garbage-collection cycle.
-- * "count": returns the total memory in use by Lua (in Kbytes).
-- * "step": performs a garbage-collection step. The step "size" is controlled
-- by `arg` (larger values mean more steps) in a non-specified way. If you
-- want to control the step size you must experimentally tune the value of
-- * "arg". Returns true if the step finished a collection cycle.
-- * "setpause": sets `arg` as the new value for the *pause* of the collector
-- (see 2.10). Returns the previous value for *pause*.
-- * "setstepmul": sets `arg` as the new value for the *step multiplier*
-- of the collector (see 2.10). Returns the previous value for *step*.
--
function collectgarbage(opt , arg) end
---
-- Opens the named file and executes its contents as a Lua chunk. When
-- called without arguments,
-- `dofile` executes the contents of the standard input (`stdin`). Returns
-- all values returned by the chunk. In case of errors, `dofile` propagates
-- the error to its caller (that is, `dofile` does not run in protected mode).
function dofile(filename) end
---
-- Terminates the last protected function called.
-- Returns `message` as the error message.
-- Function `error` never returns.
-- Usually, `error` adds some information about the error position at the
-- beginning of the message. The `level` argument specifies how to get the
-- error position. With level 1 (the default), the error position is where the
-- `error` function was called. Level 2 points the error to where the function
-- that called `error` was called; and so on. Passing a level 0 avoids the
-- addition of error position information to the message.
function error(message , level) end
---
-- A global variable (not a function) that holds the global environment
-- (that is, `_G._G = _G`). Lua itself does not use this variable; changing
-- its value does not affect any environment, nor vice-versa. (Set `__ENV`
-- to change environments in functions)
-- @table _G
---
-- If `object` does not have a metatable, returns nil. Otherwise, if the
-- object's metatable has a `"__metatable"` field, returns the associated
-- value. Otherwise, returns the metatable of the given object.
function getmetatable(object) end
---
-- For iterating over sequences. Returns three values: an iterator function, the table `t`, and 0,
-- so that the construction
-- for i,v in ipairs(t) do *body* end
-- will iterate over the pairs (`1,t[1]`), (`2,t[2]`), ..., up to the
-- first integer key absent from the table.
function ipairs(t) end
---
-- Loads a chunk.
-- If `ld` is a string, the chunk is this string.
-- If `ld` is a function, load calls it repeatedly to get the chunk pieces. Each call to `ld` must return a
-- string that concatenates with previous results. A return of an empty string, nil, or no value
-- signals the end of the chunk.
-- If there are no syntactic errors, returns the compiled chunk as a function;
-- otherwise, returns nil plus the error message.
-- If the resulting function has upvalues, the first upvalue is set to the value of the global environment or to `env`,
-- if that parameter is given. When loading main chunks, the first upvalue will be the`_ENV` variable (see 2.2).
-- `source` is used as the source of the chunk for error messages and debug information (see 4.9).
-- When absent, it defaults to `ld`, if `ld` is a string, or to "=(load)" otherwise.
-- The string `mode` controls whether the chunk can be text or binary (that is, a precompiled chunk).
-- It may be the string "b" (only binary chunks), "t" (only text chunks), or "bt" (both binary and text).
-- The default is "bt"
function load (ld , source , mode , env) end
---
-- Similar to `load`, but gets the chunk from file `filename`. Or from the
-- standard input, if no file name is given.
function loadfile (filename , mode , env) end
---
-- Allows a program to traverse all fields of a table. Its first argument is
-- a table and its second argument is an index in this table. `next` returns
-- the next index of the table and its associated value.
--
-- When called with nil
-- as its second argument, `next` returns an initial index and its associated
-- value. When called with the last index, or with nil in an empty table, `next`
-- returns nil.
--
-- If the second argument is absent, then it is interpreted as
-- nil. In particular, you can use `next(t)` to check whether a table is empty.
-- The order in which the indices are enumerated is not specified, *even for
-- numeric indices*. (To traverse a table in numeric order, use a numerical
-- for or the `ipairs` function.)
--
-- The behavior of `next` is *undefined* if, during the traversal, you assign
-- any value to a non-existent field in the table. You may however modify
-- existing fields. In particular, you may clear existing fields.
function next(table , index) end
---
-- For iterating over all key-value pairs of a table.
-- Returns three values: the `next` function, the table `t`, and nil,
-- so that the construction
-- for k,v in pairs(t) do *body* end
-- will iterate over all key-value pairs of table `t`.
-- See function `next` for the caveats of modifying the table during its
-- traversal.
function pairs(t) end
---
-- Calls function `f` with the given arguments in *protected mode*. This
-- means that any error inside `f` is not propagated; instead, `pcall` catches
-- the error and returns a status code. Its first result is the status code (a
-- boolean), which is true if the call succeeds without errors. In such case,
-- `pcall` also returns all results from the call, after this first result. In
-- case of any error, `pcall` returns false plus the error message.
function pcall(f, arg1, ...) end
---
--Prints any number of values to `stdout`.
-- Uses the `tostring` function to convert them to strings. `print` is not
-- intended for formatted output, but only as a quick way to show a value,
-- typically for debugging. For formatted output, use `string.format`.
function print(...) end
---
-- Checks whether `v1` is equal to `v2`. Does not invoke any
-- metamethod. Returns a boolean.
function rawequal(v1, v2) end
---
-- Gets the real value of `table[index]`. Does not invoke any
-- metamethod. `table` must be a table; `index` may be any value.
function rawget(table, index) end
---
-- Sets the real value of `table[index]` to `value`. Does not invoke any
-- metamethod. `table` must be a table, `index` any value different from nil,
-- and `value` any Lua value.
-- This function returns `table`.
function rawset(table, index, value) end
---
-- Returns all arguments after argument number
-- `index`. Otherwise, `index` must be the string `"#"`, and `select` returns
-- the total number of extra arguments it received.
function select(index, ...) end
---
-- Sets the metatable for the given table. (You cannot change the metatable
-- of other types from Lua, only from C.) If `metatable` is nil, removes the
-- metatable of the given table. If the original metatable has a `"__metatable"`
-- field, raises an error.
-- This function returns `table`.
function setmetatable(table, metatable) end
---
-- Tries to convert its argument to a number. If the argument is already
-- a number or a string convertible to a number, then `tonumber` returns this
-- number; otherwise, it returns nil.
-- An optional argument specifies the base to interpret the numeral. The base
-- may be any integer between 2 and 36, inclusive. In bases above 10, the
-- letter '`A`' (in either upper or lower case) represents 10, '`B`' represents
-- 11, and so forth, with '`Z`' representing 35. In base 10 (the default),
-- the number can have a decimal part, as well as an optional exponent part
-- (see 2.1). In other bases, only unsigned integers are accepted.
function tonumber(e , base) end
---
-- Converts any value to a string in a reasonable format.
-- For complete control of how numbers are converted, use `string.format`.
-- If the metatable of `e` has a `"__tostring"` field, then `tostring` calls
-- the corresponding value with `e` as argument, and uses the result of the
-- call as its result.
function tostring(e) end
---
-- Returns the type of its only argument, coded as a string. The possible
-- results of this function are "
-- `nil`" (a string, not the value nil), "`number`", "`string`", "`boolean`",
-- "`table`", "`function`", "`thread`", and "`userdata`".
function type(v) end
---
-- A global variable (not a function) that holds a string containing the
-- current interpreter version. The current contents of this variable is
-- "`Lua 5.1`".
-- @table _VERSION
---
-- This function is similar to `pcall`, except that you can set a new
-- error handler.
-- `xpcall` calls function `f` in protected mode, using `err` as the error
-- handler. Any error inside `f` is not propagated; instead, `xpcall` catches
-- the error, calls the `err` function with the original error object, and
-- returns a status code. Its first result is the status code (a boolean),
-- which is true if the call succeeds without errors. In this case, `xpcall`
-- also returns all results from the call, after this first result. In case
-- of any error, `xpcall` returns false plus the result from `err`.
function xpcall(f, err) end
---
-- Loads the given module. The function starts by looking into the
-- `package.loaded` table to determine whether `modname` is already
-- loaded. If it is, then `require` returns the value stored at
-- `package.loaded[modname]`. Otherwise, it tries to find a *loader* for
-- the module.
-- To find a loader, `require` is guided by the `package.loaders` array. By
-- changing this array, we can change how `require` looks for a module. The
-- following explanation is based on the default configuration for
-- `package.loaders`.
-- First `require` queries `package.preload[modname]`. If it has a value,
-- this value (which should be a function) is the loader. Otherwise `require`
-- searches for a Lua loader using the path stored in `package.path`. If
-- that also fails, it searches for a C loader using the path stored in
-- `package.cpath`. If that also fails, it tries an *all-in-one* loader (see
-- `package.loaders`).
-- Once a loader is found, `require` calls the loader with a single argument,
-- `modname`. If the loader returns any value, `require` assigns the returned
-- value to `package.loaded[modname]`. If the loader returns no value and
-- has not assigned any value to `package.loaded[modname]`, then `require`
-- assigns true to this entry. In any case, `require` returns the final value
-- of `package.loaded[modname]`.
-- If there is any error loading or running the module, or if it cannot find
-- any loader for the module, then `require` signals an error.
function require(modname) end

View file

@ -0,0 +1,186 @@
-------
-- global functions and tables
local tools = require 'ldoc.tools'
local globals = {}
local lua52 = _VERSION:match '5.2'
local lua53 = _VERSION:match '5.3'
local lua54 = _VERSION:match '5.4'
globals.functions = {
assert = true,
collectgarbage = true,
dofile = true,
error = true,
getmetatable = true,
setmetatable = true,
pairs = true,
ipairs = true,
load = true,
loadfile = true,
loadstring = true,
next = true,
pcall = true,
print = true,
rawequal = true,
rawget = true,
rawset = true,
select = true,
tonumber = true,
tostring = true,
type = true,
xpcall = true,
module = true,
require = true,
}
local functions = globals.functions
if lua54 then
functions.warn = true
functions.rawlen = true
elseif lua52 or lua53 then
functions.rawlen = true
else
functions.setfenv = true
functions.getfenv = true
functions.unpack = true
end
local manual, fun_ref
function globals.set_manual_url(url)
manual = url .. '#'
fun_ref = manual..'pdf-'
end
if lua54 then
globals.tables = {
io = '6.8',
package = '6.3',
math = '6.7',
os = '6.9',
string = '6.4',
table = '6.6',
coroutine = '6.2',
debug = '6.10'
}
globals.set_manual_url 'https://www.lua.org/manual/5.4/manual.html'
elseif lua53 then
globals.tables = {
io = '6.8',
package = '6.3',
math = '6.7',
os = '6.9',
string = '6.4',
table = '6.6',
coroutine = '6.2',
debug = '6.10'
}
globals.set_manual_url 'https://www.lua.org/manual/5.3/manual.html'
elseif lua52 then
globals.tables = {
io = '6.8',
package = '6.3',
math = '6.6',
os = '6.9',
string = '6.4',
table = '6.5',
coroutine = '6.2',
debug = '6.10'
}
globals.set_manual_url 'https://www.lua.org/manual/5.2/manual.html'
else
globals.tables = {
io = '5.7',
package = '5.3',
math = '5.6',
os = '5.8',
string = '5.4',
table = '5.5',
coroutine = '5.2',
debug = '5.9'
}
globals.set_manual_url 'https://www.lua.org/manual/5.1/manual.html'
end
local file_methods = {
close = true,
flush = true,
lines = true,
read = true,
seek = true,
setvbuf = true,
write = true,
}
-- external libs tracked by LDoc using LDoc style
local xlibs = {
lfs='lfs.html', lpeg='lpeg.html',
}
local xlib_url = 'http://stevedonovan.github.io/lua-stdlibs/modules/'
local tables = globals.tables
local function function_ref (name,tbl)
local href
if not tbl then -- can only be a standard Lua global function
if globals.functions[name] then
return {href = fun_ref..name, label = name}
else
return nil
end
end
if tbl == 'file' then -- special case: file objects!
if not file_methods[name] then
return nil
end
name = 'file:'..name
href = fun_ref..name
elseif tables[tbl] then -- function inside standard Lua table
local t = rawget(_G,tbl) -- do a quick sanity check
if not rawget(t,name) then
return nil
end
name = tbl..'.'..name
href = fun_ref..name
elseif xlibs[tbl] then -- in external libs, use LDoc style
local t = require('ldoc.builtin.'..tbl)
if not rawget(t,name) then
return nil
end
href = xlib_url..xlibs[tbl]..'#'..name
name = tbl..'.'..name
else
return nil
end
return {href = href, label = name}
end
local function module_ref (tbl)
local href
if tables[tbl] ~= nil then -- standard Lua table
href = manual..tables[tbl]
elseif xlibs[tbl] then -- external lib
href = xlib_url..xlibs[tbl]
else
return nil
end
return {href = href, label = tbl}
end
function globals.lua_manual_ref (name)
local tbl,fname = tools.split_dotted_name(name)
local ref
if not tbl then -- plain symbol
ref = function_ref(name)
if ref then return ref end
ref = module_ref(name)
if ref then return ref end
else
ref = function_ref(fname,tbl)
if ref then return ref end
end
return nil
end
return globals

View file

@ -0,0 +1,162 @@
--- Reading and Writing Files.
-- @module io
local io = {}
-- luacheck: ignore 241
local file = {}
---
-- Equivalent to `file:close()`. Without a `file`, closes the default
-- output file.
function io.close(file) end
---
-- Equivalent to `file:flush` over the default output file.
function io.flush() end
---
-- When called with a file name, it opens the named file (in text mode),
-- and sets its handle as the default input file. When called with a file
-- handle, it simply sets this file handle as the default input file. When
-- called without parameters, it returns the current default input file.
-- In case of errors this function raises the error, instead of returning an
-- error code.
function io.input(file) end
---
-- Opens the given file name in read mode and returns an iterator function
-- that, each time it is called, returns a new line from the file. Therefore,
-- the construction
-- for line in io.lines(filename) do *body* end
-- will iterate over all lines of the file. When the iterator function detects
-- the end of file, it returns nil (to finish the loop) and automatically
-- closes the file.
-- The call `io.lines()` (with no file name) is equivalent to
-- `io.input():lines()`; that is, it iterates over the lines of the default
-- input file. In this case it does not close the file when the loop ends.
function io.lines(filename) end
---
-- This function opens a file, in the mode specified in the string `mode`. It
-- returns a new file handle, or, in case of errors, nil plus an error message.
-- The `mode` string can be any of the following:
-- "r": read mode (the default);
-- "w": write mode;
-- "a": append mode;
-- "r+": update mode, all previous data is preserved;
-- "w+": update mode, all previous data is erased;
-- "a+": append update mode, previous data is preserved, writing is only
-- allowed at the end of file.
-- The `mode` string can also have a '`b`' at the end, which is needed in
-- some systems to open the file in binary mode. This string is exactly what
-- is used in the standard C function `fopen`.
function io.open(filename , mode) end
---
-- Similar to `io.input`, but operates over the default output file.
function io.output(file) end
---
-- Starts program `prog` in a separated process and returns a file handle
-- that you can use to read data from this program (if `mode` is `"r"`,
-- the default) or to write data to this program (if `mode` is `"w"`).
-- This function is system dependent and is not available on all platforms.
function io.popen(prog , mode) end
---
-- Equivalent to `io.input():read`.
function io.read(...) end
-- * `io.stderr`: Standard error.
-- * `io.stdin`: Standard in.
-- * `io.stdout`: Standard out.
---
-- Returns a handle for a temporary file. This file is opened in update
-- mode and it is automatically removed when the program ends.
function io.tmpfile() end
---
-- Checks whether `obj` is a valid file handle. Returns the string `"file"`
-- if `obj` is an open file handle, `"closed file"` if `obj` is a closed file
-- handle, or nil if `obj` is not a file handle.
function io.type(obj) end
---
-- Equivalent to `io.output():write`.
function io.write(...) end
---
-- Closes `file`. Note that files are automatically closed when their
-- handles are garbage collected, but that takes an unpredictable amount of
-- time to happen.
function file:close() end
---
-- Saves any written data to `file`.
function file:flush() end
---
-- Returns an iterator function that, each time it is called, returns a
-- new line from the file. Therefore, the construction
-- for line in file:lines() do *body* end
-- will iterate over all lines of the file. (Unlike `io.lines`, this function
-- does not close the file when the loop ends.)
function file:lines() end
---
-- Reads the file `file`, according to the given formats, which specify
-- what to read. For each format, the function returns a string (or a number)
-- with the characters read, or nil if it cannot read data with the specified
-- format. When called without formats, it uses a default format that reads
-- the entire next line (see below).
-- The available formats are
-- "*n": reads a number; this is the only format that returns a number
-- instead of a string.
-- "*a": reads the whole file, starting at the current position. On end of
-- file, it returns the empty string.
-- "*l": reads the next line (skipping the end of line), returning nil on
-- end of file. This is the default format.
-- *number*: reads a string with up to this number of characters, returning
-- nil on end of file. If number is zero, it reads nothing and returns an
-- empty string, or nil on end of file.
function file:read(...) end
---
-- Sets and gets the file position, measured from the beginning of the
-- file, to the position given by `offset` plus a base specified by the string
-- `whence`, as follows:
-- "set": base is position 0 (beginning of the file);
-- "cur": base is current position;
-- "end": base is end of file;
-- In case of success, function `seek` returns the final file position,
-- measured in bytes from the beginning of the file. If this function fails,
-- it returns nil, plus a string describing the error.
-- The default value for `whence` is `"cur"`, and for `offset` is 0. Therefore,
-- the call `file:seek()` returns the current file position, without changing
-- it; the call `file:seek("set")` sets the position to the beginning of the
-- file (and returns 0); and the call `file:seek("end")` sets the position
-- to the end of the file, and returns its size.
function file:seek(whence , offset) end
---
-- Sets the buffering mode for an output file. There are three available
-- modes:
--
-- * "no": no buffering; the result of any output operation appears immediately.
-- * "full": full buffering; output operation is performed only when the
-- buffer is full (or when you explicitly `flush` the file (see `io.flush`)).
-- * "line": line buffering; output is buffered until a newline is output or
-- there is any input from some special files (such as a terminal device).
-- For the last two cases, `size` specifies the size of the buffer, in
-- bytes. The default is an appropriate size.
function file:setvbuf(mode , size) end
---
-- Writes the value of each of its arguments to the `file`. The arguments
-- must be strings or numbers. To write other values, use `tostring` or
-- `string.format` before `write`.
function file:write(...) end
return io

View file

@ -0,0 +1,125 @@
--- File and Directory manipulation
-- @module lfs
local lfs = {}
---
-- Returns a table with the file attributes corresponding to filepath (or nil
-- followed by an error message in case of error). If the second optional
-- argument is given, then only the value of the named attribute is returned
-- (this use is equivalent to lfs.attributes(filepath).aname, but the table is
-- not created and only one attribute is retrieved from the O.S.). The
-- attributes are described as follows; attribute mode is a string, all the
-- others are numbers, and the time related attributes use the same time
-- reference of os.time:
--
-- - dev: on Unix systems, this represents the device that the inode resides on.
-- On Windows systems, represents the drive number of the disk containing
-- the file
-- - ino: on Unix systems, this represents the inode number. On Windows systems
-- this has no meaning
-- - mode: string representing the associated protection mode (the values could
-- be file, directory, link, socket, named pipe, char device, block
-- device or other)
-- - nlink: number of hard links to the file
-- - uid: user-id of owner (Unix only, always 0 on Windows)
-- - gid: group-id of owner (Unix only, always 0 on Windows)
-- - rdev: on Unix systems, represents the device type, for special file inodes.
-- On Windows systems represents the same as dev
-- - access: time of last access
-- - modification: time of last data modification
-- - change: time of last file status change
-- - size: file size, in bytes
-- - blocks: block allocated for file; (Unix only)
-- - blksize: optimal file system I/O blocksize; (Unix only)
-- This function uses stat internally thus if the given filepath is a symbolic
-- link, it is followed (if it points to another link the chain is followed
-- recursively) and the information is about the file it refers to. To obtain
-- information about the link itself, see function lfs.symlinkattributes.
function lfs.attributes(filepath , aname) end
---
-- Changes the current working directory to the given path.
-- Returns true in case of success or nil plus an error string.
function lfs.chdir(path) end
---
-- Creates a lockfile (called lockfile.lfs) in path if it does not exist and
-- returns the lock. If the lock already exists checks it it's stale, using the
-- second parameter (default for the second parameter is INT_MAX, which in
-- practice means the lock will never be stale. To free the the lock call
-- lock:free().
-- In case of any errors it returns nil and the error message. In particular,
-- if the lock exists and is not stale it returns the "File exists" message.
function lfs.lock_dir(path, seconds_stale) end
---
-- Returns a string with the current working directory or nil plus an error
-- string.
function lfs.currentdir() end
---
-- Lua iterator over the entries of a given directory. Each time the iterator is
-- called with dir_obj it returns a directory entry's name as a string, or nil
-- if there are no more entries. You can also iterate by calling `dir_obj:next()`,
-- and explicitly close the directory before the iteration finished with
-- `dir_obj:close()`. Raises an error if path is not a directory.
function lfs.dir(path) end
---
-- Locks a file or a part of it. This function works on open files; the file
-- handle should be specified as the first argument. The string mode could be
-- either r (for a read/shared lock) or w (for a write/exclusive lock). The
-- optional arguments start and length can be used to specify a starting point
-- and its length; both should be numbers.
-- Returns true if the operation was successful; in case of error, it returns
-- nil plus an error string.
function lfs.lock(filehandle, mode, start, length) end
---
-- Creates a new directory. The argument is the name of the new directory.
-- Returns true if the operation was successful; in case of error, it returns
-- nil plus an error string.
function lfs.mkdir(dirname) end
---
-- Removes an existing directory. The argument is the name of the directory.
-- Returns true if the operation was successful; in case of error, it returns
-- nil plus an error string.
function lfs.rmdir(dirname) end
---
-- Sets the writing mode for a file. The mode string can be either binary or
-- text. Returns the previous mode string for the file. This function is only
-- available in Windows, so you may want to make sure that lfs.setmode exists
-- before using it.
function lfs.setmode(file, mode) end
---
-- Identical to lfs.attributes except that it obtains information about the link
-- itself (not the file it refers to). This function is not available in Windows
-- so you may want to make sure that lfs.symlinkattributes exists before using
-- it.
function lfs.symlinkattributes(filepath , aname) end
---
-- Set access and modification times of a file. This function is a bind to utime
-- function. The first argument is the filename, the second argument (atime) is
-- the access time, and the third argument (mtime) is the modification time.
-- Both times are provided in seconds (which should be generated with Lua
-- standard function os.time). If the modification time is omitted, the access
-- time provided is used; if both times are omitted, the current time is used.
-- Returns true if the operation was successful; in case of error, it returns
-- nil plus an error string.
function lfs.touch(filepath , atime , mtime) end
---
-- Unlocks a file or a part of it. This function works on open files; the file
-- handle should be specified as the first argument. The optional arguments
-- start and length can be used to specify a starting point and its length; both
-- should be numbers.
-- Returns true if the operation was successful; in case of error, it returns
-- nil plus an error string.
function lfs.unlock(filehandle, start, length) end
return lfs

View file

@ -0,0 +1,214 @@
--- LPeg PEG pattern matching.
-- @module lpeg
local lpeg = {}
---
-- The matching function. It attempts to match the given pattern against the
-- subject string. If the match succeeds, returns the index in the subject of
-- the first character after the match, or the captured values (if the pattern
-- captured any value).
--
-- An optional numeric argument init makes the match starts at that position in
-- the subject string. As usual in Lua libraries, a negative value counts from
-- the end.
--
-- Unlike typical pattern-matching functions, match works only in anchored mode;
-- that is, it tries to match the pattern with a prefix of the given subject
-- string (at position init), not with an arbitrary substring of the subject.
-- So, if we want to find a pattern anywhere in a string, we must either write a
-- loop in Lua or write a pattern that matches anywhere. This second approach is
-- easy and quite efficient; see examples.
function lpeg.match(pattern, subject , init) end
---
-- If the given value is a pattern, returns the string "pattern". Otherwise
-- returns nil.
function lpeg.type(value) end
---
-- Returns a string with the running version of LPeg.
function lpeg.version() end
---
-- Sets the maximum size for the backtrack stack used by LPeg to track calls and
-- choices. Most well-written patterns need little backtrack levels and
-- therefore you seldom need to change this maximum; but a few useful patterns
-- may need more space. Before changing this maximum you should try to rewrite
-- your pattern to avoid the need for extra space.
function lpeg.setmaxstack(max) end
---
-- Converts the given value into a proper pattern, according to the following
-- rules:
-- * If the argument is a pattern, it is returned unmodified.
-- * If the argument is a string, it is translated to a pattern that matches
-- literally the string.
-- * If the argument is a non-negative number n, the result is a pattern that
-- matches exactly n characters.
-- * If the argument is a negative number -n, the result is a pattern that
-- succeeds only if the input string does not have n characters: lpeg.P(-n)
-- is equivalent to -lpeg.P(n) (see the unary minus operation).
-- * If the argument is a boolean, the result is a pattern that always
-- succeeds or always fails (according to the boolean value), without
-- consuming any input.
-- * If the argument is a table, it is interpreted as a grammar (see
-- Grammars).
-- * If the argument is a function, returns a pattern equivalent to a
-- match-time capture over the empty string.
function lpeg.P(value) end
---
-- Returns a pattern that matches any single character belonging to one of the
-- given ranges. Each range is a string xy of length 2, representing all
-- characters with code between the codes of x and y (both inclusive).
-- As an example, the pattern `lpeg.R("09")` matches any digit, and `lpeg.R("az",
-- "AZ")` matches any ASCII letter.
function lpeg.R(range) end
---
-- Returns a pattern that matches any single character that appears in the given
-- string. (The S stands for Set.)
-- As an example, the pattern lpeg.S("+-*/") matches any arithmetic operator.
-- Note that, if s is a character (that is, a string of length 1), then
-- lpeg.P(s) is equivalent to lpeg.S(s) which is equivalent to lpeg.R(s..s).
-- Note also that both lpeg.S("") and lpeg.R() are patterns that always fail.
function lpeg.S(string) end
---
-- This operation creates a non-terminal (a variable) for a grammar. The created
-- non-terminal refers to the rule indexed by v in the enclosing grammar. (See
-- Grammars for details.)
function lpeg.V(v) end
---
-- Returns a table with patterns for matching some character classes according
-- to the current locale. The table has fields:
--
-- * alnum
-- * alpha
-- * cntrl
-- * digit
-- * graph
-- * lower
-- * print
-- * punct
-- * space
-- * upper
-- * xdigit
--
-- each one containing a
-- correspondent pattern. Each pattern matches any single character that belongs
-- to its class.
--
-- If called with an argument table, then it creates those fields inside the
-- given table and returns that table.
function lpeg.locale(table) end
---
-- Creates a simple capture, which captures the substring of the subject that
-- matches patt. The captured value is a string. If patt has other captures,
-- their values are returned after this one.
function lpeg.C(patt) end
---
-- Creates an argument capture. This pattern matches the empty string and
-- produces the value given as the nth extra argument given in the call to
-- lpeg.match.
function lpeg.Carg(n) end
---
-- Creates a back capture. This pattern matches the empty string and produces
-- the values produced by the most recent group capture named name.
-- Most recent means the last complete outermost group capture with the given
-- name. A Complete capture means that the entire pattern corresponding to the
-- capture has matched. An Outermost capture means that the capture is not
-- inside another complete capture.
function lpeg.Cb(name) end
---
-- Creates a constant capture. This pattern matches the empty string and
-- produces all given values as its captured values.
function lpeg.Cc(...) end
---
-- Creates a fold capture. If patt produces a list of captures C1 C2 ... Cn,
-- this capture will produce the value func(...func(func(C1, C2), C3)..., Cn),
-- that is, it will fold (or accumulate, or reduce) the captures from patt using
-- function func.
--
-- This capture assumes that patt should produce at least one capture with at
-- least one value (of any type), which becomes the initial value of an
-- accumulator. (If you need a specific initial value, you may prefix a constant
-- capture to patt.) For each subsequent capture LPeg calls func with this
-- accumulator as the first argument and all values produced by the capture as
-- extra arguments; the value returned by this call becomes the new value for
-- the accumulator. The final value of the accumulator becomes the captured
-- value.
--
-- As an example, the following pattern matches a list of numbers separated by
-- commas and returns their addition:
--
-- -- matches a numeral and captures its value
-- number = lpeg.R"09"^1 / tonumber
-- -- matches a list of numbers, captures their values
-- list = number * ("," * number)^0
-- -- auxiliary function to add two numbers
-- function add (acc, newvalue) return acc + newvalue end
-- -- folds the list of numbers adding them
-- sum = lpeg.Cf(list, add)
-- -- example of use
-- print(sum:match("10,30,43")) --> 83
--
function lpeg.Cf(patt, func) end
---
-- Creates a group capture. It groups all values returned by patt into a single
-- capture. The group may be anonymous (if no name is given) or named with the
-- given name.
-- An anonymous group serves to join values from several captures into a single
-- capture. A named group has a different behavior. In most situations, a named
-- group returns no values at all. Its values are only relevant for a following
-- back capture or when used inside a table capture.
function lpeg.Cg(patt , name) end
---
-- Creates a position capture. It matches the empty string and captures the
-- position in the subject where the match occurs. The captured value is a
-- number.
function lpeg.Cp() end
---
-- Creates a substitution capture, which captures the substring of the subject
-- that matches patt, with substitutions. For any capture inside patt with a
-- value, the substring that matched the capture is replaced by the capture
-- value (which should be a string). The final captured value is the string
-- resulting from all replacements.
function lpeg.Cs(patt) end
---
-- Creates a table capture. This capture creates a table and puts all values
-- from all anonymous captures made by patt inside this table in successive
-- integer keys, starting at 1. Moreover, for each named capture group created
-- by patt, the first value of the group is put into the table with the group
-- name as its key. The captured value is only the table.
function lpeg.Ct(patt) end
---
-- Creates a match-time capture. Unlike all other captures, this one is
-- evaluated immediately when a match occurs. It forces the immediate evaluation
-- of all its nested captures and then calls func.
-- The given function gets as arguments the entire subject, the current position
-- (after the match of patt), plus any capture values produced by patt.
-- The first value returned by function defines how the match happens. If the
-- call returns a number, the match succeeds and the returned number becomes the
-- new current position. (Assuming a subject s and current position i, the
-- returned number must be in the range [i, len(s) + 1].) If the call returns
-- true, the match succeeds without consuming any input. (So, to return true is
-- equivalent to return i.) If the call returns false, nil, or no value, the
-- match fails.
-- Any extra values returned by the function become the values produced by the
-- capture.
function lpeg.Cmt(patt, func) end
return lpeg

View file

@ -0,0 +1,144 @@
--- standard mathematical functions.
-- @module math
local math = {}
---
-- Returns the absolute value of `x`.
function math.abs(x) end
---
-- Returns the arc cosine of `x` (in radians).
function math.acos(x) end
---
-- Returns the arc sine of `x` (in radians).
function math.asin(x) end
---
-- Returns the arc tangent of `x` (in radians).
function math.atan(x) end
---
-- Returns the arc tangent of `y/x` (in radians), but uses the signs
-- of both parameters to find the quadrant of the result. (It also handles
-- correctly the case of `x` being zero.)
function math.atan2(y, x) end
---
-- Returns the smallest integer larger than or equal to `x`.
function math.ceil(x) end
---
-- Returns the cosine of `x` (assumed to be in radians).
function math.cos(x) end
---
-- Returns the hyperbolic cosine of `x`.
function math.cosh(x) end
---
-- Returns the angle `x` (given in radians) in degrees.
function math.deg(x) end
---
-- Returns the value *e^x*.
function math.exp(x) end
---
-- Returns the largest integer smaller than or equal to `x`.
function math.floor(x) end
---
-- Returns the remainder of the division of `x` by `y` that rounds the
-- quotient towards zero.
function math.fmod(x, y) end
---
-- Returns `m` and `e` such that *x = m2^e*, `e` is an integer and the
-- absolute value of `m` is in the range *[0.5, 1)* (or zero when `x` is zero).
function math.frexp(x) end
---
-- The value `HUGE_VAL`, a value larger than or equal to any other
-- numerical value.
-- function math.huge end
-- * `math.HUGE_VAL`: math.HUGE_VAL
---
-- Returns *m2^e* (`e` should be an integer).
function math.ldexp(m, e) end
---
-- Returns the natural logarithm of `x`.
function math.log(x) end
---
-- Returns the base-10 logarithm of `x`.
function math.log10(x) end
---
-- Returns the maximum value among its arguments.
function math.max(x, ...) end
---
-- Returns the minimum value among its arguments.
function math.min(x, ...) end
---
-- Returns two numbers, the integral part of `x` and the fractional part of
-- `x`.
function math.modf(x) end
---
-- The value of *pi*.
-- function math.pi end
-- * `math.pi`: math.pi
---
-- Returns *x^y*. (You can also use the expression `x^y` to compute this
-- value.)
function math.pow(x, y) end
---
-- Returns the angle `x` (given in degrees) in radians.
function math.rad(x) end
---
-- This function is an interface to the simple pseudo-random generator
-- function `rand` provided by ANSI C. (No guarantees can be given for its
-- statistical properties.)
-- When called without arguments, returns a uniform pseudo-random real
-- number in the range *[0,1)*. When called with an integer number `m`,
-- `math.random` returns a uniform pseudo-random integer in the range *[1,
-- m]*. When called with two integer numbers `m` and `n`, `math.random`
-- returns a uniform pseudo-random integer in the range *[m, n]*.
function math.random(m , n) end
---
-- Sets `x` as the "seed" for the pseudo-random generator: equal seeds
-- produce equal sequences of numbers.
function math.randomseed(x) end
---
-- Returns the sine of `x` (assumed to be in radians).
function math.sin(x) end
---
-- Returns the hyperbolic sine of `x`.
function math.sinh(x) end
---
-- Returns the square root of `x`. (You can also use the expression `x^0.5`
-- to compute this value.)
function math.sqrt(x) end
---
-- Returns the tangent of `x` (assumed to be in radians).
function math.tan(x) end
---
-- Returns the hyperbolic tangent of `x`.
function math.tanh(x) end
return math

View file

@ -0,0 +1,112 @@
--- Operating System facilities like date, time and program execution.
-- @module os
local os = {}
---
-- Returns an approximation of the amount in seconds of CPU time used by
-- the program.
function os.clock() end
---
-- Returns a string or a table containing date and time, formatted according
-- to the given string `format`.
--
-- If the `time` argument is present, this is the time to be formatted
-- (see the `os.time` function for a description of this value). Otherwise,
-- `date` formats the current time.
--
-- If `format` starts with '`!`', then the date is formatted in Coordinated
-- Universal Time. After this optional character, if `format` is the string
-- "`*t`", then `date` returns a table with the following fields:
--
-- * `year` (four digits)
-- * `month` (1--12)
-- * `day` (1--31)
-- * `hour` (0--23)
-- * `min` (0--59)
-- * `sec` (0--61)
-- * `wday` (weekday, Sunday is 1)
-- * `yday` (day of the year)
-- * `isdst` (daylight saving flag, a boolean).
--
-- If `format` is not "`*t`", then `date` returns the date as a string,
-- formatted according to the same rules as the C function `strftime`.
-- When called without arguments, `date` returns a reasonable date and time
-- representation that depends on the host system and on the current locale
-- (that is, `os.date()` is equivalent to `os.date("%c")`).
function os.date(format , time) end
---
-- Returns the number of seconds from time `t1` to time `t2`. In POSIX,
-- Windows, and some other systems, this value is exactly `t2`*-*`t1`.
function os.difftime(t2, t1) end
---
-- This function is equivalent to the C function `system`. It passes
-- `command` to be executed by an operating system shell. It returns a status
-- code, which is system-dependent. If `command` is absent, then it returns
-- nonzero if a shell is available and zero otherwise.
function os.execute(command) end
---
-- Calls the C function `exit`, with an optional `code`, to terminate the
-- host program. The default value for `code` is the success code.
function os.exit(code) end
---
-- Returns the value of the process environment variable `varname`, or
-- nil if the variable is not defined.
function os.getenv(varname) end
---
-- Deletes the file or directory with the given name. Directories must be
-- empty to be removed. If this function fails, it returns nil, plus a string
-- describing the error.
function os.remove(filename) end
---
-- Renames file or directory named `oldname` to `newname`. If this function
-- fails, it returns nil, plus a string describing the error.
function os.rename(oldname, newname) end
---
-- Sets the current locale of the program. `locale` is a string specifying
-- a locale; `category` is an optional string describing which category to
-- change: `"all"`, `"collate"`, `"ctype"`, `"monetary"`, `"numeric"`, or
-- `"time"`; the default category is `"all"`. The function returns the name
-- of the new locale, or nil if the request cannot be honored.
-- If `locale` is the empty string, the current locale is set to an
-- implementation-defined native locale. If `locale` is the string "`C`",
-- the current locale is set to the standard C locale.
-- When called with nil as the first argument, this function only returns
-- the name of the current locale for the given category.
function os.setlocale(locale , category) end
---
-- Returns the current time when called without arguments, or a time
-- representing the date and time specified by the given table. This table
-- must have fields `year`, `month`, and `day`, and may have fields `hour`,
-- `min`, `sec`, and `isdst` (for a description of these fields, see the
-- `os.date` function).
-- The returned value is a number, whose meaning depends on your system. In
-- POSIX, Windows, and some other systems, this number counts the number
-- of seconds since some given start time (the "epoch"). In other systems,
-- the meaning is not specified, and the number returned by `time` can be
-- used only as an argument to `date` and `difftime`.
function os.time(table) end
---
-- Returns a string with a file name that can be used for a temporary
-- file. The file must be explicitly opened before its use and explicitly
-- removed when no longer needed.
-- On some systems (POSIX), this function also creates a file with that
-- name, to avoid security risks. (Someone else might create the file with
-- wrong permissions in the time between getting the name and creating the
-- file.) You still have to open the file to use it and to remove it (even
-- if you do not use it).
-- When possible, you may prefer to use `io.tmpfile`, which automatically
-- removes the file when the program ends.
function os.tmpname() end
return os

View file

@ -0,0 +1,97 @@
--- controlling how `require` finds packages.
-- @module package
local package = {}
---
-- The path used by `require` to search for a C loader.
-- Lua initializes the C path `package.cpath` in the same way it initializes
-- the Lua path `package.path`, using the environment variable `LUA_CPATH`
-- or a default path defined in `luaconf.h`.
-- function package.cpath end
-- * `package.cpath`: package.cpath
---
-- A table used by `require` to control which modules are already
-- loaded. When you require a module `modname` and `package.loaded[modname]`
-- is not false, `require` simply returns the value stored there.
-- function package.loaded end
-- * `package.loaded`: package.loaded
---
-- A table used by `require` to control how to load modules.
-- Each entry in this table is a *searcher function*. When looking for a module,
-- `require` calls each of these searchers in ascending order, with the module
-- name (the argument given to `require`) as its sole parameter. The function
-- can return another function (the module *loader*) or a string explaining
-- why it did not find that module (or nil if it has nothing to say). Lua
-- initializes this table with four functions.
-- The first searcher simply looks for a loader in the `package.preload` table.
-- The second searcher looks for a loader as a Lua library, using the path
-- stored at `package.path`. A path is a sequence of *templates* separated by
-- semicolons. For each template, the searcher will change each interrogation
-- mark in the template by `filename`, which is the module name with each dot
-- replaced by a "directory separator" (such as "`/`" in Unix); then it will
-- try to open the resulting file name. So, for instance, if the Lua path is
-- the string
-- "./?.lua;./?.lc;/usr/local/?/init.lua"
-- the search for a Lua file for module `foo` will try to open the files
-- `./foo.lua`, `./foo.lc`, and `/usr/local/foo/init.lua`, in that order.
-- The third searcher looks for a loader as a C library, using the path given
-- by the variable `package.cpath`. For instance, if the C path is the string
-- "./?.so;./?.dll;/usr/local/?/init.so"
-- the searcher for module `foo` will try to open the files `./foo.so`,
-- `./foo.dll`, and `/usr/local/foo/init.so`, in that order. Once it finds
-- a C library, this searcher first uses a dynamic link facility to link the
-- application with the library. Then it tries to find a C function inside the
-- library to be used as the loader. The name of this C function is the string
-- "`luaopen_`" concatenated with a copy of the module name where each dot
-- is replaced by an underscore. Moreover, if the module name has a hyphen,
-- its prefix up to (and including) the first hyphen is removed. For instance,
-- if the module name is `a.v1-b.c`, the function name will be `luaopen_b_c`.
-- The fourth searcher tries an *all-in-one loader*. It searches the C
-- path for a library for the root name of the given module. For instance,
-- when requiring `a.b.c`, it will search for a C library for `a`. If found,
-- it looks into it for an open function for the submodule; in our example,
-- that would be `luaopen_a_b_c`. With this facility, a package can pack
-- several C submodules into one single library, with each submodule keeping
-- its original open function.
-- function package.loaders end
-- * `package.loaders`: package.loaders
---
-- Dynamically links the host program with the C library `libname`. Inside
-- this library, looks for a function `funcname` and returns this function as a
-- C function. (So, `funcname` must follow the protocol (see `lua_CFunction`)).
-- This is a low-level function. It completely bypasses the package and module
-- system. Unlike `require`, it does not perform any path searching and does
-- not automatically adds extensions. `libname` must be the complete file name
-- of the C library, including if necessary a path and extension. `funcname`
-- must be the exact name exported by the C library (which may depend on the
-- C compiler and linker used).
-- This function is not supported by ANSI C. As such, it is only available
-- on some platforms (Windows, Linux, Mac OS X, Solaris, BSD, plus other Unix
-- systems that support the `dlfcn` standard).
function package.loadlib(libname, funcname) end
---
-- The path used by `require` to search for a Lua loader.
-- At start-up, Lua initializes this variable with the value of the environment
-- variable `LUA_PATH` or with a default path defined in `luaconf.h`, if
-- the environment variable is not defined. Any "`;;`" in the value of the
-- environment variable is replaced by the default path.
-- function package.path end
-- * `package.path`: package.path
---
-- A table to store loaders for specific modules (see `require`).
-- function package.preload end
-- * `package.preload`: package.preload
---
-- Sets a metatable for `module` with its `__index` field referring to the
-- global environment, so that this module inherits values from the global
-- environment. To be used as an option to function `module`.
function package.seeall(module) end
return package

View file

@ -0,0 +1,191 @@
--- string operations like searching and matching.
-- @module string
local string = {}
---
-- Returns the internal numerical codes of the characters `s[i]`, `s[i+1]`,
-- ..., `s[j]`. The default value for `i` is 1; the default value for `j`
-- is `i`.
-- Note that numerical codes are not necessarily portable across platforms.
function string.byte(s , i , j) end
---
-- Receives zero or more integers. Returns a string with length equal to
-- the number of arguments, in which each character has the internal numerical
-- code equal to its corresponding argument.
-- Note that numerical codes are not necessarily portable across platforms.
function string.char(...) end
---
-- Returns a string containing a binary representation of the given
-- function, so that a later `loadstring` on this string returns a copy of
-- the function. `function` must be a Lua function without upvalues.
function string.dump(func) end
---
-- Looks for the first match of `pattern` in the string `s`. If it finds a
-- match, then `find` returns the indices of `s` where this occurrence starts
-- and ends; otherwise, it returns nil. A third, optional numerical argument
-- `init` specifies where to start the search; its default value is 1 and
-- can be negative. A value of true as a fourth, optional argument `plain`
-- turns off the pattern matching facilities, so the function does a plain
-- "find substring" operation, with no characters in `pattern` being considered
-- "magic". Note that if `plain` is given, then `init` must be given as well.
-- If the pattern has captures, then in a successful match the captured values
-- are also returned, after the two indices.
function string.find(s, pattern , init , plain) end
---
-- Returns a formatted version of its variable number of arguments following
-- the description given in its first argument (which must be a string). The
-- format string follows the same rules as the `printf` family of standard C
-- functions. The only differences are that the options/modifiers `*`, `l`,
-- `L`, `n`, `p`, and `h` are not supported and that there is an extra option,
-- `q`. The `q` option formats a string in a form suitable to be safely read
-- back by the Lua interpreter: the string is written between double quotes,
-- and all double quotes, newlines, embedded zeros, and backslashes in the
-- string are correctly escaped when written. For instance, the call
--
-- string.format('%q', 'a string with "quotes" and \n new line')
--
-- will produce the string:
--
-- "a string with \"quotes\" and \
-- new line"
--
-- The options `c`, `d`, `E`, `e`, `f`, `g`, `G`, `i`, `o`, `u`, `X`, and
-- `x` all expect a number as argument, whereas `q` and `s` expect a string.
-- This function does not accept string values containing embedded zeros,
-- except as arguments to the `q` option.
function string.format(formatstring, ...) end
---
-- Returns an iterator function that, each time it is called, returns the
-- next captures from `pattern` over string `s`. If `pattern` specifies no
-- captures, then the whole match is produced in each call.
-- As an example, the following loop
--
-- s = "hello world from Lua"
-- for w in string.gmatch(s, "%a+") do
-- print(w)
-- end
--
-- will iterate over all the words from string `s`, printing one per line. The
-- next example collects all pairs `key=value` from the given string into
-- a table:
--
-- t = {}
-- s = "from=world, to=Lua"
-- for k, v in string.gmatch(s, "(%w+)=(%w+)") do
-- t[k] = v
-- end
--
-- For this function, a '`^`' at the start of a pattern does not work as an
-- anchor, as this would prevent the iteration.
function string.gmatch(s, pattern) end
---
-- Returns a copy of `s` in which all (or the first `n`, if given)
-- occurrences of the `pattern` have been replaced by a replacement string
-- specified by `repl`, which can be a string, a table, or a function. `gsub`
-- also returns, as its second value, the total number of matches that occurred.
--
-- If `repl` is a string, then its value is used for replacement. The character
-- `%` works as an escape character: any sequence in `repl` of the form `%n`,
-- with *n* between 1 and 9, stands for the value of the *n*-th captured
-- substring (see below). The sequence `%0` stands for the whole match. The
-- sequence `%%` stands for a single `%`.
--
-- If `repl` is a table, then the table is queried for every match, using
-- the first capture as the key; if the pattern specifies no captures, then
-- the whole match is used as the key.
--
-- If `repl` is a function, then this function is called every time a match
-- occurs, with all captured substrings passed as arguments, in order; if
-- the pattern specifies no captures, then the whole match is passed as a
-- sole argument.
--
-- If the value returned by the table query or by the function call is a
-- string or a number, then it is used as the replacement string; otherwise,
-- if it is false or nil, then there is no replacement (that is, the original
-- match is kept in the string).
--
-- Here are some examples:
-- x = string.gsub("hello world", "(%w+)", "%1 %1")
-- --> x="hello hello world world"
-- x = string.gsub("hello world", "%w+", "%0 %0", 1)
-- --> x="hello hello world"
-- x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
-- --> x="world hello Lua from"
-- x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
-- --> x="home = /home/roberto, user = roberto"
-- x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
-- return loadstring(s)()
-- end)
-- --> x="4+5 = 9"
-- local t = {name="lua", version="5.1"}
-- x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
-- --> x="lua-5.1.tar.gz"
function string.gsub(s, pattern, repl , n) end
---
-- Receives a string and returns its length. The empty string `""` has
-- length 0. Embedded zeros are counted, so `"a\000bc\000"` has length 5.
function string.len(s) end
---
-- Receives a string and returns a copy of this string with all uppercase
-- letters changed to lowercase. All other characters are left unchanged. The
-- definition of what an uppercase letter is depends on the current locale.
function string.lower(s) end
---
-- Looks for the first *match* of `pattern` in the string `s`. If it
-- finds one, then `match` returns the captures from the pattern; otherwise
-- it returns nil. If `pattern` specifies no captures, then the whole match
-- is returned. A third, optional numerical argument `init` specifies where
-- to start the search; its default value is 1 and can be negative.
function string.match(s, pattern , init) end
---
-- Returns a string that is the concatenation of `n` copies of the string
-- `s`.
function string.rep(s, n) end
---
-- Returns a string that is the string `s` reversed.
function string.reverse(s) end
---
-- Returns the substring of `s` that starts at `i` and continues until
-- `j`; `i` and `j` can be negative. If `j` is absent, then it is assumed to
-- be equal to -1 (which is the same as the string length). In particular,
-- the call `string.sub(s,1,j)` returns a prefix of `s` with length `j`, and
-- `string.sub(s, -i)` returns a suffix of `s` with length `i`.
function string.sub(s, i , j) end
---
-- Receives a string and returns a copy of this string with all lowercase
-- letters changed to uppercase. All other characters are left unchanged. The
-- definition of what a lowercase letter is depends on the current locale.
function string.upper(s) end
---
-- (5.3) Returns a binary string containing the values v1, v2, etc. packed (that is, serialized in binary form)
--- according to the format string fmt (see 6.4.2).
function string.pack (fmt, v1, v2, ...) end
---
-- (5.3) Returns the size of a string resulting from string.pack with the given format.
-- The format string cannot have the variable-length options 's' or 'z' (see 6.4.2).
function string.packsize (fmt) end
---
-- (5.3) Returns the values packed in string s (see string.pack) according to the format string fmt (see 6.4.2).
-- An optional pos marks where to start reading in s (default is 1)
-- After the read values, this function also returns the index of the first unread byte in s.
function string.unpack (fmt, s , pos) end
return string

View file

@ -0,0 +1,52 @@
--- manipulating Lua tables.
-- @module table
local table = {}
---
-- Given an array where all elements are strings or numbers, returns
-- `table[i]..sep..table[i+1] ... sep..table[j]`. The default value for
-- `sep` is the empty string, the default for `i` is 1, and the default for
-- `j` is the length of the table. If `i` is greater than `j`, returns the
-- empty string.
function table.concat(table , sep , i , j) end
---
-- Inserts element `value` at position `pos` in `table`, shifting up
-- other elements to open space, if necessary. The default value for `pos` is
-- `n+1`, where `n` is the length of the table (see §2.5.5), so that a call
-- `table.insert(t,x)` inserts `x` at the end of table `t`.
function table.insert(table, pos, value) end
---
-- Removes from `table` the element at position `pos`, shifting down other
-- elements to close the space, if necessary. Returns the value of the removed
-- element. The default value for `pos` is `n`, where `n` is the length of the
-- table, so that a call `table.remove(t)` removes the last element of table
-- `t`.
function table.remove(table , pos) end
---
-- Returns a new table with all parameters stored into keys 1, 2, etc. and with a field "n" with
-- the total number of parameters. Note that the resulting table may not be a sequence.
function table.pack (...) end
---
-- Sorts table elements in a given order,
-- *in-place*, from `table[1]` to `table[n]`, where `n` is the length of the
-- table. If `comp` is given, then it must be a function that receives two
-- table elements, and returns true when the first is less than the second
-- (so that `not comp(a[i+1],a[i])` will be true after the sort). If `comp`
-- is not given, then the '<' operator will be used.
function table.sort(table , comp) end
-- luacheck: ignore 121
---
-- Returns the elements from the given table. This function is equivalent to
-- return list[i], list[i+1], ..., list[j]
-- except that the above code can be written only for a fixed number of
-- elements. By default, `i` is 1 and `j` is the length of the list, as
-- defined by the length operator (see §2.5.5).
function unpack(list , i , j) end
return table

View file

@ -0,0 +1,48 @@
--- This library provides basic support for UTF-8 encoding.
-- @module utf8
local utf8 = {}
---
-- Receives zero or more integers, converts each one to its corresponding UTF-8 byte sequence and returns
-- a string with the concatenation of all these sequences.
function utf8.char (...) end
---
-- The pattern "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" , which matches exactly one
-- UTF-8 byte sequence, assuming that the subject is a valid UTF-8 string.
-- @field charpattern
---
-- Iterate over all characters in string.
--
-- for p, c in utf8.codes(s) do body end
--
-- will iterate over all characters in string s, with p being the position (in bytes) and c the code point
-- of each character. It raises an error if it meets any invalid byte sequence.
function utf8.codes (s) end
---
-- Returns the codepoints (as integers) from all characters in s that start between byte position i and j (both included).
-- The default for i is 1 and for j is i. It raises an error if it meets any invalid byte sequence.
function utf8.codepoint (s , i , j) end
---
-- Returns the number of UTF-8 characters in string s that start between positions i and j (both inclusive).
-- The default for i is 1 and for j is -1. If it finds any invalid byte sequence, returns a false value plus
-- the position of the first invalid byte.
function utf8.len (s , i , j) end
---
-- Returns the position (in bytes) where the encoding of the n-th character of s (counting from position i) starts.
-- A negative n gets characters before position i. The default for i is 1 when n is non-negative
-- and #s + 1 otherwise, so that utf8.offset(s, -n) gets the offset of the n-th character from the end
-- of the string.
-- If the specified character is neither in the subject nor right after its end, the function returns nil.
--
-- As a special case, when n is 0 the function returns the start of the encoding of the character that contains the i-th byte of s.
--
-- This function assumes that s is a valid UTF-8 string.
function utf8.offset (s, n , i) end
return utf8

View file

@ -0,0 +1,11 @@
project = 'Lua'
description = 'Lua Standard Libraries'
full_description = [[
These are the built-in libraries of Lua 5.1
Plus documentation for lpeg and luafilesystem.
]]
file = {'builtin',exclude = {'builtin/globals.lua'}}
no_summary = true
no_return_or_parms = true
format = 'discount'

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,402 @@
------ generating HTML output ---------
-- Although this can be generalized for outputting any format, since the template
-- is language-agnostic, this implementation concentrates on HTML.
-- This does the actual generation of HTML, and provides support functions in the ldoc
-- table for the template
--
-- A fair amount of the complexity comes from operating in two basic modes; first, where
-- there is a number of modules (classic LuaDoc) or otherwise, where there is only one
-- module and the index contains the documentation for that module.
--
-- Like LuaDoc, LDoc puts similar kinds of documentation files in their own directories.
-- So module docs go into 'modules/', scripts go into 'scripts/', and so forth. LDoc
-- generalizes the idea of these project-level categories and in fact custom categories
-- can be created (refered to as 'kinds' in the code)
local List = require 'pl.List'
local utils = require 'pl.utils'
local path = require 'pl.path'
local stringx = require 'pl.stringx'
local template = require 'pl.template'
local tablex = require 'pl.tablex'
local OrderedMap = require 'pl.OrderedMap'
local tools = require 'ldoc.tools'
local markup = require 'ldoc.markup'
local prettify = require 'ldoc.prettify'
local doc = require 'ldoc.doc'
local unpack = utils.unpack
local html = {}
local quit = utils.quit
local function cleanup_whitespaces(text)
local lines = stringx.splitlines(text)
for i = 1, #lines do
lines[i] = stringx.rstrip(lines[i])
end
lines[#lines + 1] = "" -- Little trick: file should end with newline
return table.concat(lines, "\n")
end
local function get_module_info(m)
local info = OrderedMap()
for tag in doc.module_info_tags() do
local val = m.tags[tag]
if type(val)=='table' then
val = table.concat(val,',')
end
tag = stringx.title(tag)
info:set(tag,val)
end
if #info:keys() > 0 then
return info
end
end
local escape_table = { ["'"] = "&apos;", ["\""] = "&quot;", ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" }
function html.generate_output(ldoc, args, project)
local check_directory, check_file, writefile = tools.check_directory, tools.check_file, tools.writefile
local original_ldoc
local function save_and_set_ldoc (set)
if not set then return end
if not original_ldoc then
original_ldoc = tablex.copy(ldoc)
end
for s in set:iter() do
local var,val = s:match('([^=]+)=(.+)')
local num = tonumber(val)
if num then val = num
elseif val == 'true' then val = true
elseif val == 'false' then val = false
end
print('setting',var,val)
ldoc[var] = val
end
end
local function restore_ldoc ()
if original_ldoc then
ldoc = original_ldoc
end
end
function ldoc.escape(str)
return (str:gsub("['&<>\"]", escape_table))
end
function ldoc.prettify(str)
return prettify.code('lua','usage',str,0,false)
end
-- Item descriptions come from combining the summary and description fields
function ldoc.descript(item)
return tools.join(' ', item.summary, item.description)
end
function ldoc.module_name (mod)
local name = mod.name
if args.unqualified and (mod.type == 'module' or mod.type == 'classmod') then -- leave out package
name = name:gsub('^.-%.','')
elseif mod.type == 'topic' then
if mod.display_name then
name = mod.display_name
else -- leave out md extension
name = name:gsub('%..*$','')
end
end
return name
end
-- this generates the internal module/function references
function ldoc.href(see)
if see.href then -- explict reference, e.g. to Lua manual
return see.href
elseif doc.Module:class_of(see) then
return ldoc.ref_to_module(see)
else
return ldoc.ref_to_module(see.mod)..'#'..see.name
end
end
-- this is either called from the 'root' (index or single module) or
-- from the 'modules' etc directories. If we are in one of those directories,
-- then linking to another kind is `../kind/name`; to the same kind is just `name`.
-- If we are in the root, then it is `kind/name`.
function ldoc.ref_to_module (mod)
local base = "" -- default: same directory
mod = mod or ldoc.module
local kind, module = mod.kind, ldoc.module
local name = mod.name -- default: name of module
if not ldoc.single then
if module then -- we are in kind/
if module.type ~= type then -- cross ref to ../kind/
base = "../"..kind.."/"
end
else -- we are in root: index
base = kind..'/'
end
else -- single module
if mod == ldoc.single then
name = ldoc.output
if not ldoc.root then base = '../' end
elseif ldoc.root then -- ref to other kinds (like examples)
base = kind..'/'
else
if module.type ~= type then -- cross ref to ../kind/
base = "../"..kind.."/"
end
end
end
return base..name..'.html'
end
function ldoc.include_file (file)
local text,_ = utils.readfile(file)
if not text then quit("unable to include "..file)
else
return text
end
end
-- these references are never from the index...?
function ldoc.source_ref (fun)
local modname = fun.module.name
local pack,name = tools.split_dotted_name(modname)
if not pack then
name = modname
end
return (ldoc.single and "" or "../").."source/"..name..'.lua.html#'..fun.lineno
end
function ldoc.use_li(ls)
if #ls > 1 then return '<li>','</li>' else return '','' end
end
function ldoc.default_display_name(item)
-- Project-level items:
if doc.project_level(item.type) then
return ldoc.module_name(item)
end
-- Module-level items:
local name = item.display_name or item.name
if item.type == 'function' or item.type == 'lfunction' then
if not ldoc.no_space_before_args then
name = name..' '
end
return name..item.args
else
return name
end
end
function ldoc.display_name(item)
if ldoc.custom_display_name_handler then
return ldoc.custom_display_name_handler(item, ldoc.default_display_name)
else
return ldoc.default_display_name(item)
end
end
function ldoc.no_spaces(s)
s = s:gsub('%s*$','')
return (s:gsub('%W','_'))
end
function ldoc.module_typename(m)
return doc.presentation_name(m.type)
end
function ldoc.is_list (t)
return type(t) == 'table' and t.append
end
function ldoc.strip_header (s)
if not s then return s end
return s:gsub('^%s*#+%s+','')
end
function ldoc.typename (tp)
if not tp or tp == '' or tp:match '^@' then return '' end
local optional
-- ?<type> is short for ?nil|<type>
if tp:match("^%?") and not tp:match '|' then
tp = '?|'..tp:sub(2)
end
local tp2 = tp:match("%?|?(.*)")
if tp2 then
optional = true
tp = tp2
end
local types = {}
for name in tp:gmatch("[^|]+") do
local sym = name:match '([%w%.%:]+)'
local ref,_ = markup.process_reference(sym,true)
if ref then
if ref.label and sym == name then
name = ref.label
end
local shortName = name:match("%.(%w*)$")
if shortName then
name = shortName
end
types[#types+1] = ('<a class="type" href="%s">%s</a>'):format(ldoc.href(ref),name)
else
types[#types+1] = '<span class="type">'..name..'</span>'
end
end
local names = table.concat(types, ", ", 1, math.max(#types-1, 1))
if #types > 1 then names = names.." or "..types[#types] end
if optional then
if names ~= '' then
if #types == 1 then names = "optional "..names end
else
names = "optional"
end
end
return names
end
-- the somewhat tangled logic that controls whether a type appears in the
-- navigation sidebar. (At least it's no longer in the template ;))
function ldoc.allowed_in_contents(type,module)
local allowed = true
if ldoc.kinds_allowed then
allowed = ldoc.kinds_allowed[type]
elseif ldoc.prettify_files and type == 'file' then
allowed = ldoc.prettify_files == 'show' or (module and module.type == 'file')
end
return allowed
end
local function set_charset (ldoc,m)
m = m or ldoc.module
ldoc.doc_charset = (m and m.tags.charset) or ldoc.charset
end
local module_template,_ = utils.readfile (path.join(args.template,ldoc.templ))
if not module_template then
quit("template not found at '"..args.template.."' Use -l to specify directory containing ldoc.ltp")
end
-- Runs a template on a module to generate HTML page.
local function templatize(template_str, ldoc, module)
local out, err = template.substitute(template_str, {
ldoc = ldoc,
module = module,
_escape = ldoc.template_escape
})
if not out then
quit(("template failed for %s: %s"):format(
module and module.name or ldoc.output or "index",
err))
end
if ldoc.postprocess_html then
out = ldoc.postprocess_html(out, module)
end
return cleanup_whitespaces(out)
end
local css, custom_css = ldoc.css, ldoc.custom_css
ldoc.output = args.output
ldoc.ipairs = ipairs
ldoc.pairs = pairs
ldoc.print = print
-- Bang out the index.
-- in single mode there is one module and the 'index' is the
-- documentation for that module.
ldoc.module = ldoc.single
if ldoc.single and args.one then
ldoc.kinds_allowed = {module = true, topic = true}
ldoc.one = true
end
ldoc.root = true
if ldoc.module then
ldoc.module.info = get_module_info(ldoc.module)
ldoc.module.ldoc = ldoc
save_and_set_ldoc(ldoc.module.tags.set)
end
set_charset(ldoc)
local out = templatize(module_template, ldoc, ldoc.module)
ldoc.root = false
restore_ldoc()
check_directory(args.dir) -- make sure output directory is ok
-- project icon
if ldoc.icon then
local dir_data = args.dir .. '/data'
if not path.isdir(dir_data) then
-- luacheck: push ignore lfs
lfs.mkdir(dir_data)
-- luacheck: pop
end
local file = require 'pl.file'
file.copy(ldoc.icon, dir_data)
end
args.dir = args.dir .. path.sep
if css then -- has CSS been copied?
check_file(args.dir..css, path.join(args.style,css))
end
if custom_css then -- has custom CSS been copied?
check_file(args.dir..custom_css, custom_css)
end
-- write out the module index
out = cleanup_whitespaces(out)
writefile(args.dir..args.output..args.ext,out)
-- in single mode, we exclude any modules since the module has been done;
-- ext step is then only for putting out any examples or topics
local mods = List()
for kind, modules in project() do
local lkind = kind:lower()
if not ldoc.single or ldoc.single and lkind ~= 'modules' then
mods:append {kind, lkind, modules}
end
end
-- write out the per-module documentation
-- note that we reset the internal ordering of the 'kinds' so that
-- e.g. when reading a topic the other Topics will be listed first.
if css then
ldoc.css = '../'..css
end
if custom_css then
ldoc.custom_css = '../'..custom_css
end
for m in mods:iter() do
local kind, lkind, modules = unpack(m)
check_directory(args.dir..lkind)
if not ldoc.keep_menu_order then
project:put_kind_first(kind)
end
for m in modules() do
ldoc.module = m
ldoc.body = m.body
m.ldoc = ldoc
if m.tags.set then
save_and_set_ldoc(m.tags.set)
end
set_charset(ldoc)
m.info = get_module_info(m)
if ldoc.body and m.postprocess then
ldoc.body = m.postprocess(ldoc.body)
end
local out = templatize(module_template, ldoc, m)
writefile(args.dir..lkind..'/'..m.name..args.ext,out)
restore_ldoc()
end
end
if not args.quiet then print('output written to '..tools.abspath(args.dir)) end
end
return html

View file

@ -0,0 +1,19 @@
return [[
/* styles for prettification of source */
pre .comment { color: #558817; }
pre .constant { color: #a8660d; }
pre .escape { color: #844631; }
pre .keyword { color: #aa5050; font-weight: bold; }
pre .library { color: #0e7c6b; }
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
pre .string { color: #8080ff; }
pre .number { color: #f8660d; }
pre .operator { color: #2239a8; font-weight: bold; }
pre .preprocessor, pre .prepro { color: #a33243; }
pre .global { color: #800080; }
pre .user-keyword { color: #800080; }
pre .prompt { color: #558817; }
pre .url { color: #272fc2; text-decoration: underline; }
]]

View file

@ -0,0 +1,66 @@
return [[
/* BEGIN RESET
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.8.2r1
*/
html {
color: #000;
background: #FFF;
}
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
fieldset,img {
border: 0;
}
address,caption,cite,code,dfn,em,strong,th,var,optgroup {
font-style: inherit;
font-weight: inherit;
}
del,ins {
text-decoration: none;
}
li {
margin-left: 20px;
}
caption,th {
text-align: left;
}
h1,h2,h3,h4,h5,h6 {
font-size: 100%;
font-weight: bold;
}
q:before,q:after {
content: '';
}
abbr,acronym {
border: 0;
font-variant: normal;
}
sup {
vertical-align: baseline;
}
sub {
vertical-align: baseline;
}
legend {
color: #000;
}
input,button,textarea,select,optgroup,option {
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-weight: inherit;
}
input,button,textarea,select {*font-size:100%;
}
/* END RESET */
]]

View file

@ -0,0 +1,225 @@
return require('ldoc.html._reset_css') .. [[
body {
margin-left: 1em;
margin-right: 1em;
font-family: arial, helvetica, geneva, sans-serif;
background-color: #ffffff; margin: 0px;
}
code, tt { font-family: monospace; font-size: 1.1em; }
span.parameter { font-family:monospace; }
span.parameter:after { content:":"; }
span.types:before { content:"("; }
span.types:after { content:")"; }
.type { font-weight: bold; font-style:italic }
body, p, td, th { font-size: .95em; line-height: 1.2em;}
p, ul { margin: 10px 0 0 0px;}
strong { font-weight: bold;}
em { font-style: italic;}
h1 {
font-size: 1.5em;
margin: 20px 0 20px 0;
}
h2, h3, h4 { margin: 15px 0 10px 0; }
h2 { font-size: 1.25em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.06em; }
a:link { font-weight: bold; color: #004080; text-decoration: none; }
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
a:link:hover { text-decoration: underline; }
hr {
color:#cccccc;
background: #00007f;
height: 1px;
}
blockquote { margin-left: 3em; }
ul { list-style-type: disc; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
}
pre {
background-color: rgb(245, 245, 245);
border: 1px solid #C0C0C0; /* silver */
padding: 10px;
margin: 10px 0 10px 0;
overflow: auto;
font-family: "Andale Mono", monospace;
}
pre.example {
font-size: .85em;
}
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
#container {
margin-left: 1em;
margin-right: 1em;
background-color: #f0f0f0;
}
#product {
text-align: center;
border-bottom: 1px solid #cccccc;
background-color: #ffffff;
}
#product big {
font-size: 2em;
}
#main {
background-color: #f0f0f0;
border-left: 2px solid #cccccc;
}
#navigation {
float: left;
width: 14em;
vertical-align: top;
background-color: #f0f0f0;
overflow: visible;
}
#navigation h2 {
background-color:#e7e7e7;
font-size:1.1em;
color:#000000;
text-align: left;
padding:0.2em;
border-top:1px solid #dddddd;
border-bottom:1px solid #dddddd;
}
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 14em;
padding: 1em;
width: 700px;
border-left: 2px solid #cccccc;
border-right: 2px solid #cccccc;
background-color: #ffffff;
}
#about {
clear: both;
padding: 5px;
border-top: 2px solid #cccccc;
background-color: #ffffff;
}
@media print {
body {
font: 12pt "Times New Roman", "TimeNR", Times, serif;
}
a { font-weight: bold; color: #004080; text-decoration: underline; }
#main {
background-color: #ffffff;
border-left: 0px;
}
#container {
margin-left: 2%;
margin-right: 2%;
background-color: #ffffff;
}
#content {
padding: 1em;
background-color: #ffffff;
}
#navigation {
display: none;
}
pre.example {
font-family: "Andale Mono", monospace;
font-size: 10pt;
page-break-inside: avoid;
}
}
table.module_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.module_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.module_list td.name { background-color: #f0f0f0; min-width: 200px; }
table.module_list td.summary { width: 100%; }
table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.function_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.function_list td.name { background-color: #f0f0f0; min-width: 200px; }
table.function_list td.summary { width: 100%; }
ul.nowrap {
overflow:auto;
white-space:nowrap;
}
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
dl.table h3, dl.function h3 {font-size: .95em;}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* make the target distinct; helps when we're navigating to a function */
a:target + * {
background-color: #FF9;
}
]]
.. require('ldoc.html._code_css')

View file

@ -0,0 +1,233 @@
return require('ldoc.html._reset_css') .. [[
body {
margin-left: 1em;
margin-right: 1em;
font-family: arial, helvetica, geneva, sans-serif;
background-color: #ffffff; margin: 0px;
}
code, tt { font-family: monospace; font-size: 1.1em; }
span.parameter { font-family:monospace; }
span.parameter:after { content:":"; }
span.types:before { content:"("; }
span.types:after { content:")"; }
.type { font-weight: bold; font-style:italic }
body, p, td, th { font-size: .95em; line-height: 1.2em;}
p, ul { margin: 10px 0 0 0px;}
strong { font-weight: bold;}
em { font-style: italic;}
h1 {
font-size: 1.5em;
margin: 0 0 20px 0;
}
h2, h3, h4 { margin: 15px 0 10px 0; }
h2 { font-size: 1.25em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.06em; }
a:link { font-weight: bold; color: #004080; text-decoration: none; }
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
a:link:hover { text-decoration: underline; }
hr {
color:#cccccc;
background: #00007f;
height: 1px;
}
blockquote { margin-left: 3em; }
ul { list-style-type: disc; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
}
pre {
background-color: rgb(245, 245, 245);
border: 1px solid #C0C0C0; /* silver */
padding: 10px;
margin: 10px 0 10px 0;
overflow: auto;
font-family: "Andale Mono", monospace;
}
pre.example {
font-size: .85em;
}
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
#container {
margin-left: 1em;
margin-right: 1em;
background-color: #ffffff;
}
#product {
text-align: center;
border-bottom: 1px solid #cccccc;
background-color: #ffffff;
}
#product big {
font-size: 2em;
}
#main {
background-color:#FFFFFF; // #f0f0f0;
border-left: 1px solid #cccccc;
}
#navigation {
position: fixed;
top: 0;
left: 0;
float: left;
width: 14em;
vertical-align: top;
background-color:#FFFFFF; // #f0f0f0;
border-right: 2px solid #cccccc;
overflow: visible;
overflow-y: scroll;
height: 100%;
padding-left: 1em;
}
#navigation h2 {
background-color:#FFFFFF;//:#e7e7e7;
font-size:1.1em;
color:#000000;
text-align: left;
padding:0.2em;
border-bottom:1px solid #dddddd;
}
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 14em;
padding: 1em;
padding-left: 2em;
width: 700px;
border-left: 2px solid #cccccc;
// border-right: 2px solid #cccccc;
background-color: #ffffff;
}
#about {
clear: both;
padding-left: 1em;
margin-left: 14em; // avoid the damn sidebar!
border-top: 2px solid #cccccc;
border-left: 2px solid #cccccc;
background-color: #ffffff;
}
@media print {
body {
font: 12pt "Times New Roman", "TimeNR", Times, serif;
}
a { font-weight: bold; color: #004080; text-decoration: underline; }
#main {
background-color: #ffffff;
border-left: 0px;
}
#container {
margin-left: 2%;
margin-right: 2%;
background-color: #ffffff;
}
#content {
padding: 1em;
background-color: #ffffff;
}
#navigation {
display: none;
}
pre.example {
font-family: "Andale Mono", monospace;
font-size: 10pt;
page-break-inside: avoid;
}
}
table.module_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.module_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; }
table.module_list td.summary { width: 100%; }
table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.function_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; }
table.function_list td.summary { width: 100%; }
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
dl.table h3, dl.function h3 {font-size: .95em;}
ul.nowrap {
overflow:auto;
whitespace:nowrap;
}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* make the target distinct; helps when we're navigating to a function */
a:target + * {
background-color: #FF9;
}
]]
.. require('ldoc.html._code_css')

View file

@ -0,0 +1,324 @@
return [==[
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=$(ldoc.doc_charset)"/>
<head>
<title>$(ldoc.title)</title>
<link rel="stylesheet" href="$(ldoc.css)" type="text/css" />
# if ldoc.custom_css then -- add custom CSS file if configured.
<link rel="stylesheet" href="$(ldoc.custom_css)" type="text/css" />
# end
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
# local no_spaces = ldoc.no_spaces
# local use_li = ldoc.use_li
# local display_name = ldoc.display_name
# local iter = ldoc.modules.iter
# local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end
# local nowrap = ldoc.wrap and '' or 'nowrap'
<!-- Menu -->
<div id="navigation">
<br/>
<h1>$(ldoc.project)</h1>
# if ldoc.icon then
# if module then
<img src="../data/$(ldoc.icon)" />
# else
<img src="data/$(ldoc.icon)" />
# end
# end
# if not ldoc.single and module then -- reference back to project index
<ul>
<li><a href="../$(ldoc.output).html">Index</a></li>
</ul>
# end
# --------- contents of module -------------
# if module and not ldoc.no_summary and #module.items > 0 then
<h2>Contents</h2>
<ul>
# for kind,items in module.kinds() do
<li><a href="#$(no_spaces(kind))">$(kind)</a></li>
# end
</ul>
# end
# if ldoc.no_summary and module and not ldoc.one then -- bang out the functions on the side
# for kind, items in module.kinds() do
<h2>$(kind)</h2>
<ul class="nowrap">
# for item in items() do
<li><a href="#$(item.name)">$(display_name(item))</a></li>
# end
</ul>
# end
# end
# -------- contents of project ----------
# local this_mod = module and module.name
# for kind, mods, type in ldoc.kinds() do
# if ldoc.allowed_in_contents(type,module) then
<h2>$(kind)</h2>
<ul class="$(kind=='Topics' and '' or 'nowrap')">
# for mod in mods() do local name = display_name(mod)
# if mod.name == this_mod then
<li><strong>$(name)</strong></li>
# else
<li><a href="$(ldoc.ref_to_module(mod))">$(name)</a></li>
# end
# end
# end
</ul>
# end
</div>
<div id="content">
# if ldoc.body then -- verbatim HTML as contents; 'non-code' entries
$(ldoc.body)
# elseif module then -- module documentation
<h1>$(ldoc.module_typename(module)) <code>$(module.name)</code></h1>
<p>$(M(module.summary,module))</p>
<p>$(M(module.description,module))</p>
# if module.tags.include then
$(M(ldoc.include_file(module.tags.include)))
# end
# if module.see then
# local li,il = use_li(module.see)
<h3>See also:</h3>
<ul>
# for see in iter(module.see) do
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
# end -- for
</ul>
# end -- if see
# if module.usage then
# local li,il = use_li(module.usage)
<h3>Usage:</h3>
<ul>
# for usage in iter(module.usage) do
$(li)<pre class="example">$(ldoc.escape(usage))</pre>$(il)
# end -- for
</ul>
# end -- if usage
# if module.info then
<h3>Info:</h3>
<ul>
# for tag, value in module.info:iter() do
<li><strong>$(tag)</strong>: $(M(value,module))</li>
# end
</ul>
# end -- if module.info
# if not ldoc.no_summary then
# -- bang out the tables of item types for this module (e.g Functions, Tables, etc)
# for kind,items in module.kinds() do
<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2>
<table class="function_list">
# for item in items() do
<tr>
<td class="name" $(nowrap)><a href="#$(item.name)">$(display_name(item))</a></td>
<td class="summary">$(M(item.summary,item))</td>
</tr>
# end -- for items
</table>
#end -- for kinds
<br/>
<br/>
#end -- if not no_summary
# --- currently works for both Functions and Tables. The params field either contains
# --- function parameters or table fields.
# local show_return = not ldoc.no_return_or_parms
# local show_parms = show_return
# for kind, items in module.kinds() do
# local kitem = module.kinds:get_item(kind)
# local has_description = kitem and ldoc.descript(kitem) ~= ""
<h2 class="section-header $(has_description and 'has-description')"><a name="$(no_spaces(kind))"></a>$(kind)</h2>
$(M(module.kinds:get_section_description(kind),nil))
# if kitem then
# if has_description then
<div class="section-description">
$(M(ldoc.descript(kitem),kitem))
</div>
# end
# if kitem.usage then
<h3>Usage:</h3>
<pre class="example">$(ldoc.prettify(kitem.usage[1]))</pre>
# end
# end
<dl class="function">
# for item in items() do
<dt>
<a name = "$(item.name)"></a>
<strong>$(display_name(item))</strong>
# if ldoc.prettify_files and ldoc.is_file_prettified[item.module.file.filename] then
<a style="float:right;" href="$(ldoc.source_ref(item))">line $(item.lineno)</a>
# end
</dt>
<dd>
$(M(ldoc.descript(item),item))
# if ldoc.custom_tags then
# for custom in iter(ldoc.custom_tags) do
# local tag = item.tags[custom[1]]
# if tag and not custom.hidden then
# local li,il = use_li(tag)
<h3>$(custom.title or custom[1]):</h3>
<ul>
# for value in iter(tag) do
$(li)$(custom.format and custom.format(value) or M(value))$(il)
# end -- for
# end -- if tag
</ul>
# end -- iter tags
# end
# if show_parms and item.params and #item.params > 0 then
# local subnames = module.kinds:type_of(item).subnames
# if subnames then
<h3>$(subnames):</h3>
# end
<ul>
# for parm in iter(item.params) do
# local param,sublist = item:subparam(parm)
# if sublist then
<li><span class="parameter">$(sublist)</span>$(M(item.params.map[sublist],item))
<ul>
# end
# for p in iter(param) do
# local name,tp,def = item:display_name_of(p), ldoc.typename(item:type_of_param(p)), item:default_of_param(p)
<li><span class="parameter">$(name)</span>
# if tp ~= '' then
<span class="types">$(tp)</span>
# end
$(M(item.params.map[p],item))
# if def == true then
(<em>optional</em>)
# elseif def then
(<em>default</em> $(def))
# end
# if item:readonly(p) then
<em>readonly</em>
# end
</li>
# end
# if sublist then
</li></ul>
# end
# end -- for
</ul>
# end -- if params
# if show_return and item.retgroups then local groups = item.retgroups
<h3>Returns:</h3>
# for i,group in ldoc.ipairs(groups) do local li,il = use_li(group)
<ol>
# for r in group:iter() do local type, ctypes = item:return_type(r); local rt = ldoc.typename(type)
$(li)
# if rt ~= '' then
<span class="types">$(rt)</span>
# end
$(M(r.text,item))$(il)
# if ctypes then
<ul>
# for c in ctypes:iter() do
<li><span class="parameter">$(c.name)</span>
<span class="types">$(ldoc.typename(c.type))</span>
$(M(c.comment,item))</li>
# end
</ul>
# end -- if ctypes
# end -- for r
</ol>
# if i < #groups then
<h3>Or</h3>
# end
# end -- for group
# end -- if returns
# if show_return and item.raise then
<h3>Raises:</h3>
$(M(item.raise,item))
# end
# if item.see then
# local li,il = use_li(item.see)
<h3>See also:</h3>
<ul>
# for see in iter(item.see) do
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
# end -- for
</ul>
# end -- if see
# if item.usage then
# local li,il = use_li(item.usage)
<h3>Usage:</h3>
<ul>
# for usage in iter(item.usage) do
$(li)<pre class="example">$(ldoc.prettify(usage))</pre>$(il)
# end -- for
</ul>
# end -- if usage
</dd>
# end -- for items
</dl>
# end -- for kinds
# else -- if module; project-level contents
# if ldoc.description then
<h2>$(M(ldoc.description,nil))</h2>
# end
# if ldoc.full_description then
<p>$(M(ldoc.full_description,nil))</p>
# end
# for kind, mods in ldoc.kinds() do
<h2>$(kind)</h2>
# kind = kind:lower()
<table class="module_list">
# for m in mods() do
<tr>
<td class="name" $(nowrap)><a href="$(no_spaces(kind))/$(m.name).html">$(m.name)</a></td>
<td class="summary">$(M(ldoc.strip_header(m.summary),m))</td>
</tr>
# end -- for modules
</table>
# end -- for kinds
# end -- if module
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc $(ldoc.version)</a></i>
<i style="float:right;">Last updated $(ldoc.updatetime) </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>
]==]

View file

@ -0,0 +1,17 @@
return [[
> local lev = ldoc.level or 2
> local lev1,lev2 = ('#'):rep(lev),('#'):rep(lev+1)
> for kind, items in module.kinds() do
> local kitem = module.kinds:get_item(kind)
> if kitem then
$(lev1) $(ldoc.descript(kitem))
> end
> for item in items() do
$(lev2) $(ldoc.display_name(item))
$(ldoc.descript(item))
> end
> end
]]

View file

@ -0,0 +1,292 @@
return [[
body {
color: #47555c;
font-size: 16px;
font-family: "Open Sans", sans-serif;
margin: 0;
background: #eff4ff;
}
a:link { color: #008fee; }
a:visited { color: #008fee; }
a:hover { color: #22a7ff; }
h1 { font-size:26px; font-weight: normal; }
h2 { font-size:22px; font-weight: normal; }
h3 { font-size:18px; font-weight: normal; }
h4 { font-size:16px; font-weight: bold; }
hr {
height: 1px;
background: #c1cce4;
border: 0px;
margin: 15px 0;
}
code, tt {
font-family: monospace;
}
span.parameter {
font-family: monospace;
font-weight: bold;
color: rgb(99, 115, 131);
}
span.parameter:after {
content:":";
}
span.types:before {
content:"(";
}
span.types:after {
content:")";
}
.type {
font-weight: bold; font-style:italic
}
p.name {
font-family: "Andale Mono", monospace;
}
#navigation {
float: left;
background-color: white;
border-right: 1px solid #d3dbec;
border-bottom: 1px solid #d3dbec;
width: 14em;
vertical-align: top;
overflow: visible;
}
#navigation br {
display: none;
}
#navigation h1 {
background-color: white;
border-bottom: 1px solid #d3dbec;
padding: 15px;
margin-top: 0px;
margin-bottom: 0px;
}
#navigation h2 {
font-size: 18px;
background-color: white;
border-bottom: 1px solid #d3dbec;
padding-left: 15px;
padding-right: 15px;
padding-top: 10px;
padding-bottom: 10px;
margin-top: 30px;
margin-bottom: 0px;
}
#content h1 {
background-color: #2c3e67;
color: white;
padding: 15px;
margin: 0px;
}
#content h2 {
background-color: #6c7ea7;
color: white;
padding: 15px;
padding-top: 15px;
padding-bottom: 15px;
margin-top: 0px;
}
#content h2 a {
background-color: #6c7ea7;
color: white;
text-decoration: none;
}
#content h2 a:hover {
text-decoration: underline;
}
#content h3 {
font-style: italic;
padding-top: 15px;
padding-bottom: 4px;
margin-right: 15px;
margin-left: 15px;
margin-bottom: 5px;
border-bottom: solid 1px #bcd;
}
#content h4 {
margin-right: 15px;
margin-left: 15px;
border-bottom: solid 1px #bcd;
}
#content pre {
margin: 15px;
}
pre {
background-color: rgb(50, 55, 68);
color: white;
border-radius: 3px;
/* border: 1px solid #C0C0C0; /* silver */
padding: 15px;
overflow: auto;
font-family: "Andale Mono", monospace;
}
#content ul pre.example {
margin-left: 0px;
}
table.index {
/* border: 1px #00007f; */
}
table.index td { text-align: left; vertical-align: top; }
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 14em;
}
#content p {
padding-left: 15px;
padding-right: 15px;
}
#content table {
padding-left: 15px;
padding-right: 15px;
background-color: white;
}
#content p, #content table, #content ol, #content ul, #content dl {
max-width: 900px;
}
#about {
padding: 15px;
padding-left: 16em;
background-color: white;
border-top: 1px solid #d3dbec;
border-bottom: 1px solid #d3dbec;
}
table.module_list, table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
margin: 15px;
}
table.module_list td, table.function_list td {
border-width: 1px;
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
padding-bottom: 5px;
border: solid 1px rgb(193, 204, 228);
}
table.module_list td.name, table.function_list td.name {
background-color: white; min-width: 200px; border-right-width: 0px;
}
table.module_list td.summary, table.function_list td.summary {
background-color: white; width: 100%; border-left-width: 0px;
}
dl.function {
margin-right: 15px;
margin-left: 15px;
border-bottom: solid 1px rgb(193, 204, 228);
border-left: solid 1px rgb(193, 204, 228);
border-right: solid 1px rgb(193, 204, 228);
background-color: white;
}
dl.function dt {
color: rgb(99, 123, 188);
font-family: monospace;
border-top: solid 1px rgb(193, 204, 228);
padding: 15px;
}
dl.function dd {
margin-left: 15px;
margin-right: 15px;
margin-top: 5px;
margin-bottom: 15px;
}
#content dl.function dd h3 {
margin-top: 0px;
margin-left: 0px;
padding-left: 0px;
font-size: 16px;
color: rgb(128, 128, 128);
border-bottom: solid 1px #def;
}
#content dl.function dd ul, #content dl.function dd ol {
padding: 0px;
padding-left: 15px;
list-style-type: none;
}
ul.nowrap {
overflow:auto;
white-space:nowrap;
}
.section-description {
padding-left: 15px;
padding-right: 15px;
}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* make the target distinct; helps when we're navigating to a function */
a:target + * {
background-color: #FF9;
}
/* styles for prettification of source */
pre .comment { color: #bbccaa; }
pre .constant { color: #a8660d; }
pre .escape { color: #844631; }
pre .keyword { color: #ffc090; font-weight: bold; }
pre .library { color: #0e7c6b; }
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
pre .string { color: #8080ff; }
pre .number { color: #f8660d; }
pre .operator { color: #2239a8; font-weight: bold; }
pre .preprocessor, pre .prepro { color: #a33243; }
pre .global { color: #c040c0; }
pre .user-keyword { color: #800080; }
pre .prompt { color: #558817; }
pre .url { color: #272fc2; text-decoration: underline; }
]]

View file

@ -0,0 +1,202 @@
return require('ldoc.html._reset_css') .. [[
body {
margin-left: 1em;
margin-right: 1em;
font-family: arial, helvetica, geneva, sans-serif;
background-color: #ffffff; margin: 0px;
}
code, tt { font-family: monospace; font-size: 1.1em; }
body, p, td, th { font-size: .95em; line-height: 1.2em;}
p, ul { margin: 10px 0 0 10px;}
strong { font-weight: bold;}
em { font-style: italic;}
h1 {
font-size: 1.5em;
margin: 0 0 20px 0;
}
h2, h3, h4 { margin: 15px 0 10px 0; }
h2 { font-size: 1.25em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.06em; }
a:link { font-weight: bold; color: #004080; text-decoration: none; }
a:visited { font-weight: bold; color: #2808FF; text-decoration: none; }
a:link:hover { text-decoration: underline; }
hr {
color:#cccccc;
background: #00007f;
height: 1px;
}
blockquote { margin-left: 3em; }
ul { list-style-type: disc; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
}
pre {
background-color: rgb(245, 245, 245);
border: 1px solid #C0C0C0; /* silver */
padding: 10px;
margin: 10px 0 10px 0;
overflow: auto;
font-family: "Andale Mono", monospace;
}
pre.example {
font-size: .85em;
}
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
#container {
margin-left: 1em;
margin-right: 1em;
background-color: #f5f5f5;
}
#product {
text-align: center;
border-bottom: 1px solid #cccccc;
background-color: #ffffff;
}
#product big {
font-size: 2em;
}
#main {
border-left: 2px solid #cccccc;
border-right: 2px solid #cccccc;
}
#navigation {
float: top;
vertical-align: top;
background-color: #f5f5f5;
overflow: visible;
}
#navigation h2 {
background-color:#e7e7e7;
font-size:1.1em;
color:#000000;
text-align: left;
padding:0.2em;
border-top:1px solid #dddddd;
border-bottom:1px solid #dddddd;
}
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
padding: 1em;
background-color: #ffffff;
}
#about {
clear: both;
padding: 5px;
border-top: 2px solid #cccccc;
background-color: #ffffff;
}
@media print {
body {
font: 12pt "Times New Roman", "TimeNR", Times, serif;
}
a { font-weight: bold; color: #004080; text-decoration: underline; }
#main {
background-color: #ffffff;
border-left: 0px;
}
#container {
margin-left: 2%;
margin-right: 2%;
background-color: #ffffff;
}
#content {
padding: 1em;
background-color: #ffffff;
}
#navigation {
display: none;
}
pre.example {
font-family: "Andale Mono", monospace;
font-size: 10pt;
page-break-inside: avoid;
}
}
table.module_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.module_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.module_list td.name { background-color: #f5f5f5; }
table.module_list td.summary { width: 100%; }
table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.function_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.function_list td.name {
background-color: #f5f5f5;
white-space: normal; /* voids the "nowrap" in HTML */
}
table.function_list td.summary { width: 100%; }
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
dl.table h3, dl.function h3 {font-size: .95em;}
]]
.. require('ldoc.html._code_css')

View file

@ -0,0 +1,225 @@
return require('ldoc.html._reset_css') .. [[
body {
margin-left: 1em;
margin-right: 1em;
font-family: arial, helvetica, geneva, sans-serif;
background-color: #ffffff; margin: 0px;
}
code, tt { font-family: monospace; font-size: 1.1em; }
span.parameter { font-family:monospace; }
span.parameter:after { content:":"; }
span.types:before { content:"("; }
span.types:after { content:")"; }
.type { font-weight: bold; font-style:italic }
body, p, td, th { font-size: .95em; line-height: 1.2em;}
p, ul { margin: 10px 0 0 0px;}
strong { font-weight: bold;}
em { font-style: italic;}
h1 {
font-size: 1.5em;
margin: 0 0 20px 0;
}
h2, h3, h4 { margin: 15px 0 10px 0; }
h2 { font-size: 1.25em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.06em; }
a:link { font-weight: bold; color: #004080; text-decoration: none; }
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
a:link:hover { text-decoration: underline; }
hr {
color:#cccccc;
background: #00007f;
height: 1px;
}
blockquote { margin-left: 3em; }
ul { list-style-type: disc; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
}
pre {
background-color: rgb(245, 245, 245);
border: 1px solid #C0C0C0; /* silver */
padding: 10px;
margin: 10px 0 10px 0;
overflow: auto;
font-family: "Andale Mono", monospace;
}
pre.example {
font-size: .85em;
}
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
#container {
margin-left: 1em;
margin-right: 1em;
background-color: #ffffff;
}
#product {
text-align: center;
border-bottom: 1px solid #cccccc;
background-color: #ffffff;
}
#product big {
font-size: 2em;
}
#main {
background-color:#FFFFFF; // #f0f0f0;
//border-left: 2px solid #cccccc;
}
#navigation {
float: left;
width: 14em;
vertical-align: top;
background-color:#FFFFFF; // #f0f0f0;
border-right: 2px solid #cccccc;
overflow: visible;
}
#navigation h2 {
background-color:#FFFFFF;//:#e7e7e7;
font-size:1.1em;
color:#000000;
text-align: left;
padding:0.2em;
//border-top:1px solid #dddddd;
border-bottom:1px solid #dddddd;
}
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 14em;
padding: 1em;
width: 700px;
border-left: 2px solid #cccccc;
// border-right: 2px solid #cccccc;
background-color: #ffffff;
}
#about {
clear: both;
padding: 5px;
border-top: 2px solid #cccccc;
background-color: #ffffff;
}
@media print {
body {
font: 12pt "Times New Roman", "TimeNR", Times, serif;
}
a { font-weight: bold; color: #004080; text-decoration: underline; }
#main {
background-color: #ffffff;
border-left: 0px;
}
#container {
margin-left: 2%;
margin-right: 2%;
background-color: #ffffff;
}
#content {
padding: 1em;
background-color: #ffffff;
}
#navigation {
display: none;
}
pre.example {
font-family: "Andale Mono", monospace;
font-size: 10pt;
page-break-inside: avoid;
}
}
table.module_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.module_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; }
table.module_list td.summary { width: 100%; }
table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.function_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; }
table.function_list td.summary { width: 100%; }
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
dl.table h3, dl.function h3 {font-size: .95em;}
ul.nowrap {
overflow:auto;
whitespace:nowrap;
}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* make the target distinct; helps when we're navigating to a function */
a:target + * {
background-color: #FF9;
}
]]
.. require('ldoc.html._code_css')

View file

@ -0,0 +1,379 @@
------------
-- Language-dependent parsing of code.
-- This encapsulates the different strategies needed for parsing C and Lua
-- source code.
local class = require 'pl.class'
local utils = require 'pl.utils'
local List = require 'pl.List'
local tools = require 'ldoc.tools'
local lexer = require 'ldoc.lexer'
local quit = utils.quit
local tnext = lexer.skipws
local Lang = class()
function Lang:trim_comment (s)
return s:gsub(self.line_comment,'')
end
function Lang:start_comment (v)
local line = v:match (self.start_comment_)
if line and self.end_comment_ and v:match (self.end_comment_) then
return nil
end
local block = v:match(self.block_comment)
return line or block, block
end
function Lang:empty_comment (v)
return v:match(self.empty_comment_)
end
function Lang:grab_block_comment(v,tok)
v = v:gsub(self.block_comment,'')
return tools.grab_block_comment(v,tok,self.end_comment)
end
function Lang:find_module(tok,t,v)
return '...',t,v
end
function Lang:item_follows(t,v)
return false
end
function Lang:finalize()
self.empty_comment_ = self.start_comment_..'%s*$'
end
function Lang:search_for_token (tok,type,value,t,v)
while t and not (t == type and v == value) do
if t == 'comment' and self:start_comment(v) then return nil,t,v end
t,v = tnext(tok)
end
return t ~= nil,t,v
end
function Lang:parse_extra (tags,tok)
end
function Lang:is_module_modifier ()
return false
end
function Lang:parse_module_modifier (tags, tok)
return nil, "@usage or @exports deduction not implemented for this language"
end
local Lua = class(Lang)
function Lua:_init()
self.line_comment = '^%-%-+' -- used for stripping
self.start_comment_ = '^%-%-%-+' -- used for doc comment line start
self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments
self.end_comment_ = '[^%-]%-%-+[^-]*\n$' ---- exclude --- this kind of comment ---
self.method_call = ':'
self:finalize()
end
function Lua.lexer(fname)
local f,e = io.open(fname)
if not f then quit(e) end
return lexer.lua(f,{}),f
end
function Lua:grab_block_comment(v,tok)
local equals = v:match('^%-%-%[(=*)%[')
if not equals then return v end
v = v:gsub(self.block_comment,'')
return tools.grab_block_comment(v,tok,'%]'..equals..'%]')
end
-- luacheck: push ignore 312
function Lua:parse_module_call(tok,t,v)
t,v = tnext(tok)
if t == '(' then t,v = tnext(tok) end
if t == 'string' then -- explicit name, cool
return v,t,v
elseif t == '...' then -- we have to guess!
return '...',t,v
end
end
-- luacheck: pop
-- If a module name was not provided, then we look for an explicit module()
-- call. However, we should not try too hard; if we hit a doc comment then
-- we should go back and process it. Likewise, module(...) also means
-- that we must infer the module name.
function Lua:find_module(tok,t,v)
local res
res,t,v = self:search_for_token(tok,'iden','module',t,v)
if not res then return nil,t,v end
return self:parse_module_call(tok,t,v)
end
local function parse_lua_parameters (tags,tok)
tags.formal_args = tools.get_parameters(tok)
tags:add('class','function')
end
local function parse_lua_function_header (tags,tok)
if not tags.name then
tags:add('name',tools.get_fun_name(tok))
end
if not tags.name then return 'function has no name' end
parse_lua_parameters(tags,tok)
end
local function parse_lua_table (tags,tok)
tags.formal_args = tools.get_parameters(tok,'}',function(s)
return s == ',' or s == ';'
end)
end
--------------- function and variable inferrence -----------
-- After a doc comment, there may be a local followed by:
-- [1] (l)function: function NAME
-- [2] (l)function: NAME = function
-- [3] table: NAME = {
-- [4] field: NAME = <anything else> (this is a module-level field)
--
-- Depending on the case successfully detected, returns a function which
-- will be called later to fill in inferred item tags
function Lua:item_follows(t,v,tok)
local parser, case
local is_local = t == 'keyword' and v == 'local'
if is_local then t,v = tnext(tok) end
if t == 'keyword' and v == 'function' then -- case [1]
case = 1
parser = parse_lua_function_header
elseif t == 'iden' then
local name,t,_ = tools.get_fun_name(tok,v)
if t ~= '=' then return nil,"not 'name = function,table or value'" end
t,v = tnext(tok)
if t == 'keyword' and v == 'function' then -- case [2]
tnext(tok) -- skip '('
case = 2
parser = function(tags,tok)
tags:add('name',name)
parse_lua_parameters(tags,tok)
end
elseif t == '{' then -- case [3]
case = 3
parser = function(tags,tok)
tags:add('class','table')
tags:add('name',name)
parse_lua_table (tags,tok)
end
else -- case [4]
case = 4
parser = function(tags)
tags:add('class','field')
tags:add('name',name)
end
end
elseif t == 'keyword' and v == 'return' then
t, v = tnext(tok)
if t == 'keyword' and v == 'function' then
-- return function(a, b, c)
tnext(tok) -- skip '('
case = 2
parser = parse_lua_parameters
elseif t == '{' then
-- return {...}
case = 5
parser = function(tags,tok)
tags:add('class','table')
parse_lua_table(tags,tok)
end
else
return nil,'not returning function or table'
end
else
return nil,"not 'name=value' or 'return value'"
end
return parser, is_local, case
end
-- we only call the function returned by the item_follows above if there
-- is not already a name and a type.
-- Otherwise, this is called. Currrently only tries to fill in the fields
-- of a table from a table definition as identified above
function Lua:parse_extra (tags,tok,case)
if tags.class == 'table' and not tags.field and case == 3 then
parse_lua_table(tags,tok)
end
end
-- For Lua, a --- @usage comment means that a long
-- string containing the usage follows, which we
-- use to update the module usage tag. Likewise, the @export
-- tag alone in a doc comment refers to the following returned
-- Lua table of functions
function Lua:is_module_modifier (tags)
return tags.summary == '' and (tags.usage or tags.export)
end
-- Allow for private name convention.
function Lua:is_private_var (name)
return name:match '^_' or name:match '_$'
end
function Lua:parse_module_modifier (tags, tok, F)
if tags.usage then
if tags.class ~= 'field' then return nil,"cannot deduce @usage" end
local t1= tnext(tok)
if t1 ~= '[' then return nil, t1..' '..': not a long string' end
local _, v = tools.grab_block_comment('',tok,'%]%]')
return true, v, 'usage'
elseif tags.export then
if tags.class ~= 'table' then return nil, "cannot deduce @export" end
for f in tags.formal_args:iter() do
if not self:is_private_var(f) then
F:export_item(f)
end
end
return true
end
end
-- note a difference here: we scan C/C++ code in full-text mode, not line by line.
-- This is because we can't detect multiline comments in line mode.
-- Note: this applies to C/C++ code used to generate _Lua_ documentation!
local CC = class(Lang)
function CC:_init()
self.line_comment = '^//+'
self.start_comment_ = '^///+'
self.block_comment = '^/%*%*+'
self.method_call = ':'
self:finalize()
end
function CC.lexer(f)
local err
f,err = utils.readfile(f)
if not f then quit(err) end
return lexer.cpp(f,{},nil,true)
end
function CC:grab_block_comment(v,tok)
v = v:gsub(self.block_comment,''):gsub('\n%s*%*','\n')
return 'comment',v:sub(1,-3)
end
--- here the argument name is always last, and the type is composed of any tokens before
function CC:extract_arg (tl,idx)
idx = idx or 1
local res = List()
for i = idx,#tl-1 do
res:append(tl[i][2])
end
local type = res:join ' '
return tl[#tl][2], type
end
function CC:item_follows (t,v,tok)
if not self.extra.C then
return false
end
if t == 'iden' or t == 'keyword' then --
local _
if v == self.extra.export then -- this is not part of the return type!
_,v = tnext(tok)
end
-- types may have multiple tokens: example, const char *bonzo(...)
local return_type, name = v
_,v = tnext(tok)
name = v
t,v = tnext(tok)
while t ~= '(' do
return_type = return_type .. ' ' .. name
name = v
t,v = tnext(tok)
end
--print ('got',name,t,v,return_type)
return function(tags,tok)
if not tags.name then
tags:add('name',name)
end
tags:add('class','function')
if t == '(' then
tags.formal_args,t,_ = tools.get_parameters(tok,')',',',self)
if return_type ~= 'void' then
tags.formal_args.return_type = return_type
end
end
end
end
return false
end
local Moon = class(Lua)
function Moon:_init()
self.line_comment = '^%-%-+' -- used for stripping
self.start_comment_ = '^%s*%-%-%-+' -- used for doc comment line start
self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments
self.end_comment_ = '[^%-]%-%-+\n$' ---- exclude --- this kind of comment ---
self.method_call = '\\'
self:finalize()
end
--- much like Lua, BUT auto-assign parameters start with @
function Moon:extract_arg (tl,idx)
idx = idx or 1
local auto_assign = tl[idx][1] == '@'
if auto_assign then idx = idx + 1 end
local res = tl[idx][2]
return res
end
function Moon:item_follows (t,v,tok)
if t == '.' then -- enclosed in with statement
t,v = tnext(tok)
end
if t == 'iden' then
local name,t,v = tools.get_fun_name(tok,v,'')
if name == 'class' then
local _
name,_,_ = tools.get_fun_name(tok,v,'')
-- class!
return function(tags,tok)
tags:add('class','type')
tags:add('name',name)
end
elseif t == '=' or t == ':' then -- function/method
local _
t,_ = tnext(tok)
return function(tags,tok)
if not tags.name then
tags:add('name',name)
end
if t == '(' then
tags.formal_args,t,_ = tools.get_parameters(tok,')',',',self)
else
tags.formal_args = List()
end
t,_ = tnext(tok)
tags:add('class','function')
if t ~= '>' then
tags.static = true
end
end
else
return nil, "expecting '=' or ':'"
end
end
end
return { lua = Lua(), cc = CC(), moon = Moon() }

View file

@ -0,0 +1,504 @@
--- Lexical scanner for creating a sequence of tokens from text. <br>
-- <p><code>lexer.scan(s)</code> returns an iterator over all tokens found in the
-- string <code>s</code>. This iterator returns two values, a token type string
-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the
-- token.
-- <p>
-- Versions specialized for Lua and C are available; these also handle block comments
-- and classify keywords as 'keyword' tokens. For example:
-- <pre class=example>
-- > s = 'for i=1,n do'
-- > for t,v in lexer.lua(s) do print(t,v) end
-- keyword for
-- iden i
-- = =
-- number 1
-- , ,
-- iden n
-- keyword do
-- </pre>
--
-- Based on pl.lexer from Penlight
local strfind = string.find
local strsub = string.sub
local append = table.insert
local function assert_arg(idx,val,tp)
if type(val) ~= tp then
error("argument "..idx.." must be "..tp, 2)
end
end
local lexer = {}
local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+'
local NUMBER2 = '^[%+%-]?%d+%.?%d*'
local NUMBER3 = '^0x[%da-fA-F]+'
local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+'
local NUMBER5 = '^%d+%.?%d*'
local IDEN = '^[%a_][%w_]*'
local WSPACE = '^%s+'
local STRING1 = [[^'.-[^\\]']]
local STRING2 = [[^".-[^\\]"]]
local STRING3 = "^((['\"])%2)" -- empty string
local PREPRO = '^#.-[^\\]\n'
local plain_matches,lua_matches,cpp_matches,cpp_matches_no_string,lua_keyword,cpp_keyword
local function tdump(tok)
return tok,tok
end
local function ndump(tok,options)
if options and options.number then
tok = tonumber(tok)
end
return "number",tok
end
-- regular strings, single or double quotes; usually we want them
-- without the quotes
local function sdump(tok,options)
if options and options.string then
tok = tok:sub(2,-2)
end
return "string",tok
end
-- strings enclosed in back ticks
local function bdump(tok,options)
if options and options.string then
tok = tok:sub(2,-2)
end
return "backtick",tok
end
-- long Lua strings need extra work to get rid of the quotes
local function sdump_l(tok,options)
if options and options.string then
tok = tok:sub(3,-3)
end
return "string",tok
end
local function chdump(tok,options)
if options and options.string then
tok = tok:sub(2,-2)
end
return "char",tok
end
local function cdump(tok)
return 'comment',tok
end
local function wsdump (tok)
return "space",tok
end
local function pdump (tok)
return 'prepro',tok
end
local function plain_vdump(tok)
return "iden",tok
end
local function lua_vdump(tok)
if lua_keyword[tok] then
return "keyword",tok
else
return "iden",tok
end
end
local function cpp_vdump(tok)
if cpp_keyword[tok] then
return "keyword",tok
else
return "iden",tok
end
end
local function count_lines(line, text)
local index, limit = 1, #text
while index <= limit do
local start, stop = text:find('\r\n', index, true)
if not start then
start, stop = text:find('[\r\n\f]', index)
if not start then break end
end
index = stop + 1
line = line + 1
end
return line
end
local multiline = { comment = true, space = true }
--- create a plain token iterator from a string or file-like object.
-- @param s the string
-- @param matches an optional match table (set of pattern-action pairs)
-- @param filter a table of token types to exclude, by default {space=true}
-- @param options a table of options; by default, {number=true,string=true},
-- which means convert numbers and strip string quotes.
function lexer.scan (s,matches,filter,options)
--assert_arg(1,s,'string')
local file = type(s) ~= 'string' and s
filter = filter or {space=true}
options = options or {number=true,string=true}
if filter then
if filter.space then filter[wsdump] = true end
if filter.comments then
filter[cdump] = true
end
end
if not matches then
if not plain_matches then
plain_matches = {
{WSPACE,wsdump},
{NUMBER3,ndump},
{IDEN,plain_vdump},
{NUMBER1,ndump},
{NUMBER2,ndump},
{STRING3,sdump},
{STRING1,sdump},
{STRING2,sdump},
{'^.',tdump}
}
end
matches = plain_matches
end
local i1,i2,tok,pat,fun
local line = 1
if file then
s = file:read()
if not s then return nil end -- empty file
if s:match '^\239\187' then -- UTF-8 BOM Abomination
s = s:sub(4)
end
s = s ..'\n'
end
local sz = #s
local idx = 1
if sz == 0 then return nil end -- empty file
local res = {}
local mt = {}
mt.__index = mt
setmetatable(res,mt)
function mt.lineno() return line end
function mt.getline()
if idx < sz then
tok = strsub(s,idx,-2)
idx = sz + 1
line = line + 1
return tok
else
idx = sz + 1
line = line + 1
return file:read()
end
end
function mt.next (tok)
local t,v = tok()
while t == 'space' do
t,v = tok()
end
return t,v
end
function mt.__call ()
if not s then return end
while true do
for _,m in ipairs(matches) do
pat,fun = m[1],m[2]
if fun == nil then error("no match for "..pat) end
i1,i2 = strfind(s,pat,idx)
if i1 then
tok = strsub(s,i1,i2)
idx = i2 + 1
if not (filter and filter[fun]) then
lexer.finished = idx > sz
local t,v = fun(tok,options)
if not file and multiline[t] then
line = count_lines(line,v)
end
return t,v
end
end
end
if idx > sz then
if file then
line = line + 1
s = file:read()
if not s then return end
s = s .. '\n'
idx ,sz = 1,#s
else
return
end
end
end
end
return res
end
--- get everything in a stream upto a newline.
-- @param tok a token stream
-- @return a string
function lexer.getline (tok)
return tok:getline()
end
--- get current line number. <br>
-- Only available if the input source is a file-like object.
-- @param tok a token stream
-- @return the line number and current column
function lexer.lineno (tok)
return tok:lineno()
end
--- get the Lua keywords as a set-like table.
-- So <code>res["and"]</code> etc would be <code>true</code>.
-- @return a table
function lexer.get_keywords ()
if not lua_keyword then
lua_keyword = {
["and"] = true, ["break"] = true, ["do"] = true,
["else"] = true, ["elseif"] = true, ["end"] = true,
["false"] = true, ["for"] = true, ["function"] = true,
["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true,
["not"] = true, ["or"] = true, ["repeat"] = true,
["return"] = true, ["then"] = true, ["true"] = true,
["until"] = true, ["while"] = true
}
end
return lua_keyword
end
--- create a Lua token iterator from a string or file-like object.
-- Will return the token type and value.
-- @param s the string
-- @param filter a table of token types to exclude, by default {space=true,comments=true}
-- @param options a table of options; by default, {number=true,string=true},
-- which means convert numbers and strip string quotes.
function lexer.lua(s,filter,options)
filter = filter or {space=true,comments=true}
lexer.get_keywords()
if not lua_matches then
lua_matches = {
{WSPACE,wsdump},
{NUMBER3,ndump},
{IDEN,lua_vdump},
{NUMBER4,ndump},
{NUMBER5,ndump},
{STRING3,sdump},
{STRING1,sdump},
{STRING2,sdump},
{'^`[^`]+`',bdump},
{'^%-%-%[(=*)%[.-%]%1%]',cdump},
{'^%-%-.-\n',cdump},
{'^%-%-.-$',cdump},
{'^%[(=*)%[.-%]%1%]',sdump_l},
{'^==',tdump},
{'^~=',tdump},
{'^<=',tdump},
{'^>=',tdump},
{'^%.%.%.',tdump},
{'^%.%.',tdump},
{'^.',tdump}
}
end
return lexer.scan(s,lua_matches,filter,options)
end
--- create a C/C++ token iterator from a string or file-like object.
-- Will return the token type type and value.
-- @param s the string
-- @param filter a table of token types to exclude, by default {space=true,comments=true}
-- @param options a table of options; by default, {number=true,string=true},
-- which means convert numbers and strip string quotes.
function lexer.cpp(s,filter,options,no_string)
filter = filter or {comments=true}
if not cpp_keyword then
cpp_keyword = {
["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true,
["else"] = true, ["continue"] = true, ["struct"] = true,
["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true,
["private"] = true, ["protected"] = true, ["goto"] = true,
["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true,
["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true,
["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true,
["double"] = true, ["while"] = true, ["new"] = true,
["namespace"] = true, ["try"] = true, ["catch"] = true,
["switch"] = true, ["case"] = true, ["extern"] = true,
["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true,
["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true,
}
end
if not cpp_matches then
cpp_matches = {
{WSPACE,wsdump},
{PREPRO,pdump},
{NUMBER3,ndump},
{IDEN,cpp_vdump},
{NUMBER4,ndump},
{NUMBER5,ndump},
{STRING3,sdump},
{STRING1,chdump},
{STRING2,sdump},
{'^//.-\n',cdump},
{'^//.-$',cdump},
{'^/%*.-%*/',cdump},
{'^==',tdump},
{'^!=',tdump},
{'^<=',tdump},
{'^>=',tdump},
{'^->',tdump},
{'^&&',tdump},
{'^||',tdump},
{'^%+%+',tdump},
{'^%-%-',tdump},
{'^%+=',tdump},
{'^%-=',tdump},
{'^%*=',tdump},
{'^/=',tdump},
{'^|=',tdump},
{'^%^=',tdump},
{'^::',tdump},
{'^%.%.%.',tdump},
{'^.',tdump}
}
end
if not cpp_matches_no_string then
cpp_matches_no_string = {
{WSPACE,wsdump},
{PREPRO,pdump},
{NUMBER3,ndump},
{IDEN,cpp_vdump},
{NUMBER4,ndump},
{NUMBER5,ndump},
{'^//.-\n',cdump},
{'^/%*.-%*/',cdump},
{'^==',tdump},
{'^!=',tdump},
{'^<=',tdump},
{'^>=',tdump},
{'^->',tdump},
{'^&&',tdump},
{'^||',tdump},
{'^%+%+',tdump},
{'^%-%-',tdump},
{'^%+=',tdump},
{'^%-=',tdump},
{'^%*=',tdump},
{'^/=',tdump},
{'^|=',tdump},
{'^%^=',tdump},
{'^::',tdump},
{'^%.%.%.',tdump},
{'^.',tdump}
}
end
return lexer.scan(s,
not no_string and cpp_matches or cpp_matches_no_string,
filter,options)
end
--- get a list of parameters separated by a delimiter from a stream.
-- @param tok the token stream
-- @param endtoken end of list (default ')'). Can be '\n'
-- @param delim separator (default ',')
-- @return a list of token lists.
function lexer.get_separated_list(tok,endtoken,delim)
endtoken = endtoken or ')'
delim = delim or ','
local function tappend (tl,t,val)
val = val or t
append(tl,{t,val})
end
local is_end
if endtoken == '\n' then
is_end = function(t,val)
return t == 'space' and val:find '\n'
end
else
is_end = function (t)
return t == endtoken
end
end
local is_delim
if type(delim) == 'function' then
is_delim = delim
else
is_delim = function(t)
return t == delim
end
end
local parm_values = {}
local level = 1 -- used to count ( and )
local tl = {}
local token,value
while true do
token,value=tok()
if not token then return nil,'EOS' end -- end of stream is an error!
if is_end(token,value) and level == 1 then
if next(tl) then
append(parm_values,tl)
end
break
elseif token == '(' then
level = level + 1
tappend(tl,'(')
elseif token == ')' then
level = level - 1
if level == 0 then -- finished with parm list
append(parm_values,tl)
break
else
tappend(tl,')')
end
elseif level == 1 and is_delim(token) then
append(parm_values,tl) -- a new parm
tl = {}
else
tappend(tl,token,value)
end
end
return parm_values,{token,value}
end
--- get the next non-space token from the stream.
-- @param tok the token stream.
function lexer.skipws (tok)
return tok:next()
end
local skipws = lexer.skipws
--- get the next token, which must be of the expected type.
-- Throws an error if this type does not match!
-- @param tok the token stream
-- @param expected_type the token type
-- @param no_skip_ws whether we should skip whitespace
function lexer.expecting (tok,expected_type,no_skip_ws)
assert_arg(1,tok,'function')
assert_arg(2,expected_type,'string')
local t,v
if no_skip_ws then
t,v = tok()
else
t,v = skipws(tok)
end
if t ~= expected_type then error ("expecting "..expected_type,2) end
return v
end
return lexer

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,420 @@
--------------
-- Handling markup transformation.
-- Currently just does Markdown, but this is intended to
-- be the general module for managing other formats as well.
local doc = require 'ldoc.doc'
local utils = require 'pl.utils'
local stringx = require 'pl.stringx'
local prettify = require 'ldoc.prettify'
local concat = table.concat
local markup = {}
local backtick_references
-- inline <references> use same lookup as @see
local function resolve_inline_references (ldoc, txt, item, plain)
local do_escape = not plain and not ldoc.dont_escape_underscore
local res = (txt:gsub('@{([^}]-)}',function (name)
if name:match '^\\' then return '@{'..name:sub(2)..'}' end
local qname,label = utils.splitv(name,'%s*|')
if not qname then
qname = name
end
local ref, err
local custom_ref, refname = utils.splitv(qname,':')
if custom_ref and ldoc.custom_references then
custom_ref = ldoc.custom_references[custom_ref]
if custom_ref then
ref,err = custom_ref(refname)
end
end
if not ref then
ref,err = markup.process_reference(qname)
end
if not ref then
err = err .. ' ' .. qname
if item and item.warning then item:warning(err)
else
io.stderr:write('nofile error: ',err,'\n')
end
return '???'
end
if not label then
label = ref.label
end
if label and do_escape then -- a nastiness with markdown.lua and underscores
label = label:gsub('_','\\_')
end
local html = ldoc.href(ref) or '#'
label = ldoc.escape(label or qname)
local res = ('<a href="%s">%s</a>'):format(html,label)
return res
end))
if backtick_references then
res = res:gsub('`([^`]+)`',function(name)
local ref,_ = markup.process_reference(name)
local label = name
if name and do_escape then
label = name:gsub('_', '\\_')
end
label = ldoc.escape(label)
if ref then
return ('<a href="%s">%s</a>'):format(ldoc.href(ref),label)
else
return '<code>'..label..'</code>'
end
end)
end
return res
end
-- for readme text, the idea here is to create module sections at ## so that
-- they can appear in the contents list as a ToC.
function markup.add_sections(F, txt)
local sections, L, first = {}, 1, true
local title_pat
local lstrip = stringx.lstrip
for line in stringx.lines(txt) do
if first then
local level,header = line:match '^(#+)%s*(.+)'
if level then
level = level .. '#'
else
level = '##'
end
title_pat = '^'..level..'([^#]%s*.+)'
title_pat = lstrip(title_pat)
first = false
F.display_name = header
end
local title = line:match (title_pat)
if title then
--- Windows line endings are the cockroaches of text
title = title:gsub('\r$','')
-- Markdown allows trailing '#'...
title = title:gsub('%s*#+$','')
sections[L] = F:add_document_section(lstrip(title))
end
L = L + 1
end
F.sections = sections
return txt
end
local function indent_line (line)
line = line:gsub('\t',' ') -- support for barbarians ;)
local indent = #line:match '^%s*'
return indent,line
end
local function blank (line)
return not line:find '%S'
end
local global_context, local_context
-- before we pass Markdown documents to markdown/discount, we need to do three things:
-- - resolve any @{refs} and (optionally) `refs`
-- - any @lookup directives that set local context for ref lookup
-- - insert any section ids which were generated by add_sections above
-- - prettify any code blocks
local function process_multiline_markdown(ldoc, txt, F, filename, deflang)
local res, L, append = {}, 0, table.insert
local err_item = {
warning = function (self,msg)
io.stderr:write(filename..':'..L..': '..msg,'\n')
end
}
local get = stringx.lines(txt)
local getline = function()
L = L + 1
return get()
end
local function pretty_code (code, lang)
code = concat(code,'\n')
if code ~= '' then
local _
-- If we omit the following '\n', a '--' (or '//') comment on the
-- last line won't be recognized.
code, _ = prettify.code(lang,filename,code..'\n',L,false)
code = resolve_inline_references(ldoc, code, err_item,true)
append(res,'<pre>')
append(res, code)
append(res,'</pre>')
else
append(res,code)
end
end
local indent,start_indent
local_context = nil
local line = getline()
while line do
local name = line:match '^@lookup%s+(%S+)'
if name then
local_context = name .. '.'
line = getline()
end
local fence = line:match '^```(.*)'
if fence then
local plain = fence==''
line = getline()
local code = {}
while not line:match '^```' do
if not plain then
append(code, line)
else
append(res, ' '..line)
end
line = getline()
end
pretty_code (code,fence)
line = getline() -- skip fence
if not line then break end
end
indent, line = indent_line(line)
if indent >= 4 then -- indented code block
local code = {}
local plain
while indent >= 4 or blank(line) do
if not start_indent then
start_indent = indent
if line:match '^%s*@plain%s*$' then
plain = true
line = getline()
end
end
if not plain then
append(code,line:sub(start_indent + 1))
else
append(res,line)
end
line = getline()
if line == nil then break end
indent, line = indent_line(line)
end
start_indent = nil
while #code > 1 and blank(code[#code]) do -- trim blank lines.
table.remove(code)
end
pretty_code (code,deflang)
else
local section = F and F.sections[L]
if section then
append(res,('<a name="%s"></a>'):format(section))
end
line = resolve_inline_references(ldoc, line, err_item)
append(res,line)
line = getline()
end
end
res = concat(res,'\n')
return res
end
-- Handle markdown formatters
-- Try to get the one the user has asked for, but if it's not available,
-- try all the others we know about. If they don't work, fall back to text.
local function generic_formatter(format)
local ok, f = pcall(require, format)
return ok and f
end
local formatters =
{
markdown = function(format)
local ok, markdown = pcall(require, 'markdown')
if not ok then
print('format: using built-in markdown')
ok, markdown = pcall(require, 'ldoc.markdown')
end
return ok and markdown
end,
discount = function(format)
local ok, markdown = pcall(require, 'discount')
if ok then
-- luacheck: push ignore 542
if 'function' == type(markdown) then
-- lua-discount by A.S. Bradbury, https://luarocks.org/modules/luarocks/lua-discount
elseif 'table' == type(markdown) and ('function' == type(markdown.compile) or 'function' == type(markdown.to_html)) then
-- discount by Craig Barnes, https://luarocks.org/modules/craigb/discount
-- result of apt-get install lua-discount (links against libmarkdown2)
local mysterious_debian_variant = markdown.to_html ~= nil
markdown = markdown.compile or markdown.to_html
return function(text)
local result, errmsg = markdown(text)
if result then
if mysterious_debian_variant then
return result
else
return result.body
end
else
io.stderr:write('LDoc discount failed with error ',errmsg)
os.exit(1)
end
end
else
ok = false
end
-- luacheck: pop
end
if not ok then
print('format: using built-in markdown')
ok, markdown = pcall(require, 'ldoc.markdown')
end
return ok and markdown
end,
lunamark = function(format)
local ok, lunamark = pcall(require, format)
if ok then
local writer = lunamark.writer.html.new()
local parse = lunamark.reader.markdown.new(writer,
{ smart = true })
return function(text) return parse(text) end
end
end,
commonmark = function(format)
local ok, cmark = pcall(require, 'cmark')
if ok then
return function(text)
local doc = cmark.parse_document(text, string.len(text), cmark.OPT_DEFAULT)
return cmark.render_html(doc, cmark.OPT_DEFAULT)
end
end
end
}
local function get_formatter(format)
local used_format = format
local formatter = (formatters[format] or generic_formatter)(format)
if not formatter then -- try another equivalent processor
for name, f in pairs(formatters) do
formatter = f(name)
if formatter then
print('format: '..format..' not found, using '..name)
used_format = name
break
end
end
end
return formatter, used_format
end
local function text_processor(ldoc)
return function(txt,item)
if txt == nil then return '' end
-- hack to separate paragraphs with blank lines
txt = txt:gsub('\n\n','\n<p>')
return resolve_inline_references(ldoc, txt, item, true)
end
end
local plain_processor
local function markdown_processor(ldoc, formatter)
return function (txt,item,plain)
if txt == nil then return '' end
if plain then
if not plain_processor then
plain_processor = text_processor(ldoc)
end
return plain_processor(txt,item)
end
local is_file = utils.is_type(item,doc.File)
local is_module = not is_file and item and doc.project_level(item.type)
if is_file or is_module then
local deflang = 'lua'
if ldoc.parse_extra and ldoc.parse_extra.C then
deflang = 'c'
end
if is_module then
txt = process_multiline_markdown(ldoc, txt, nil, item.file.filename, deflang)
else
txt = process_multiline_markdown(ldoc, txt, item, item.filename, deflang)
end
else
txt = resolve_inline_references(ldoc, txt, item)
end
txt = formatter(txt)
-- We will add our own paragraph tags, if needed.
return (txt:gsub('^%s*<p>(.+)</p>%s*$','%1'))
end
end
local function get_processor(ldoc, format)
if format == 'plain' then return text_processor(ldoc) end
local formatter,actual_format = get_formatter(format)
if formatter then
markup.plain = false
-- AFAIK only markdown.lua has underscore-in-identifier problem...
if ldoc.dont_escape_underscore ~= nil then
ldoc.dont_escape_underscore = actual_format ~= 'markdown'
end
return markdown_processor(ldoc, formatter)
end
print('format: '..format..' not found, falling back to text')
return text_processor(ldoc)
end
function markup.create (ldoc, format, pretty, user_keywords)
local processor
markup.plain = true
if format == 'backtick' then
ldoc.backtick_references = true
format = 'plain'
end
backtick_references = ldoc.backtick_references
global_context = ldoc.package and ldoc.package .. '.'
prettify.set_prettifier(pretty)
prettify.set_user_keywords(user_keywords)
markup.process_reference = function(name,istype)
if local_context == 'none.' and not name:match '%.' then
return nil,'not found'
end
local mod = ldoc.single or ldoc.module or ldoc.modules[1]
local ref,err = mod:process_see_reference(name, ldoc.modules, istype)
if ref then return ref end
if global_context then
local qname = global_context .. name
ref = mod:process_see_reference(qname, ldoc.modules, istype)
if ref then return ref end
end
if local_context then
local qname = local_context .. name
ref = mod:process_see_reference(qname, ldoc.modules, istype)
if ref then return ref end
end
-- note that we'll return the original error!
return ref,err
end
markup.href = function(ref)
return ldoc.href(ref)
end
processor = get_processor(ldoc, format)
if not markup.plain and backtick_references == nil then
backtick_references = true
end
markup.resolve_inline_references = function(txt, errfn)
return resolve_inline_references(ldoc, txt, errfn, markup.plain)
end
markup.processor = processor
prettify.resolve_inline_references = function(txt, errfn)
return resolve_inline_references(ldoc, txt, errfn, true)
end
return processor
end
return markup

View file

@ -0,0 +1,430 @@
-- parsing code for doc comments
local utils = require 'pl.utils'
local List = require 'pl.List'
-- local Map = require 'pl.Map'
local stringio = require 'pl.stringio'
local lexer = require 'ldoc.lexer'
local tools = require 'ldoc.tools'
local doc = require 'ldoc.doc'
local Item,File = doc.Item,doc.File
local unpack = utils.unpack
------ Parsing the Source --------------
-- This uses the lexer from PL, but it should be possible to use Peter Odding's
-- excellent Lpeg based lexer instead.
local parse = {}
local tnext, append = lexer.skipws, table.insert
-- a pattern particular to LuaDoc tag lines: the line must begin with @TAG,
-- followed by the value, which may extend over several lines.
local luadoc_tag = '^%s*@(%w+)'
local luadoc_tag_value = luadoc_tag..'(.*)'
local luadoc_tag_mod_and_value = luadoc_tag..'%[([^%]]*)%](.*)'
-- assumes that the doc comment consists of distinct tag lines
local function parse_at_tags(text)
local lines = stringio.lines(text)
local preamble, line = tools.grab_while_not(lines,luadoc_tag)
local tag_items = {}
local follows
while line do
local tag, mod_string, rest = line :match(luadoc_tag_mod_and_value)
if not tag then tag, rest = line :match (luadoc_tag_value) end
local modifiers
if mod_string then
modifiers = { }
for x in mod_string :gmatch "[^,]+" do
local k, v = x :match "^([^=]+)=(.*)$"
if not k then k, v = x, true end -- wuz x, x
modifiers[k] = v
end
end
-- follows: end of current tag
-- line: beginning of next tag (for next iteration)
follows, line = tools.grab_while_not(lines,luadoc_tag)
append(tag_items,{tag, rest .. '\n' .. follows, modifiers})
end
return preamble,tag_items
end
--local colon_tag = '%s*(%a+):%s'
local colon_tag = '%s*(%S-):%s'
local colon_tag_value = colon_tag..'(.*)'
local function parse_colon_tags (text)
local lines = stringio.lines(text)
local preamble, line = tools.grab_while_not(lines,colon_tag)
local tag_items, follows = {}
while line do
local tag, rest = line:match(colon_tag_value)
follows, line = tools.grab_while_not(lines,colon_tag)
local value = rest .. '\n' .. follows
if tag:match '^[%?!]' then
tag = tag:gsub('^!','')
value = tag .. ' ' .. value
tag = 'tparam'
end
append(tag_items,{tag, value})
end
return preamble,tag_items
end
-- Tags are stored as an ordered multi map from strings to strings
-- If the same key is used, then the value becomes a list
local Tags = {}
Tags.__index = Tags
function Tags.new (t,name)
local class
if name then
class = t
t = {}
end
t._order = List()
local tags = setmetatable(t,Tags)
if name then
tags:add('class',class)
tags:add('name',name)
end
return tags
end
function Tags:add (tag,value,modifiers)
if modifiers then -- how modifiers are encoded
value = {value,modifiers=modifiers}
end
local ovalue = self:get(tag)
if ovalue then
ovalue:append(value)
value = ovalue
end
rawset(self,tag,value)
if not ovalue then
self._order:append(tag)
end
end
function Tags:get (tag)
local ovalue = rawget(self,tag)
if ovalue then -- previous value?
if getmetatable(ovalue) ~= List then
ovalue = List{ovalue}
end
return ovalue
end
end
function Tags:iter ()
return self._order:iter()
end
local function comment_contains_tags (comment,args)
return (args.colon and comment:find ': ') or (not args.colon and comment:find '@')
end
-- This takes the collected comment block, and uses the docstyle to
-- extract tags and values. Assume that the summary ends in a period or a question
-- mark, and everything else in the preamble is the description.
-- If a tag appears more than once, then its value becomes a list of strings.
-- Alias substitution and @TYPE NAME shortcutting is handled by Item.check_tag
local function extract_tags (s,args)
local preamble,tag_items
if s:match '^%s*$' then return {} end
if args.colon then --and s:match ':%s' and not s:match '@%a' then
preamble,tag_items = parse_colon_tags(s)
else
preamble,tag_items = parse_at_tags(s)
end
local strip = tools.strip
local summary, description = preamble:match('^(.-[%.?])(%s.+)')
if not summary then
-- perhaps the first sentence did not have a . or ? terminating it.
-- Then try split at linefeed
summary, description = preamble:match('^(.-\n\n)(.+)')
if not summary then
summary = preamble
end
end -- and strip(description) ?
local tags = Tags.new{summary=summary and strip(summary) or '',description=description or ''}
for _,item in ipairs(tag_items) do
local tag, value, modifiers = Item.check_tag(tags,unpack(item))
-- treat multiline values more gently..
if not value:match '\n[^\n]+\n' then
value = strip(value)
end
tags:add(tag,value,modifiers)
end
return tags --Map(tags)
end
-- parses a Lua or C file, looking for ldoc comments. These are like LuaDoc comments;
-- they start with multiple '-'. (Block commments are allowed)
-- If they don't define a name tag, then by default
-- it is assumed that a function definition follows. If it is the first comment
-- encountered, then ldoc looks for a call to module() to find the name of the
-- module if there isn't an explicit module name specified.
local function parse_file(fname, lang, package, args)
local F = File(fname)
local module_found, first_comment = false,true
local current_item, module_item
F.args = args
F.lang = lang
F.base = package
local tok,f = lang.lexer(fname)
if not tok then return nil end
local function lineno ()
return tok:lineno()
end
function F:warning (msg,kind,line)
line = line or lineno()
Item.had_warning = true
io.stderr:write(fname..':'..line..': '..msg,'\n')
end
function F:error (msg)
self:warning(msg,'error')
io.stderr:write('LDoc error\n')
os.exit(1)
end
local function add_module(tags,module_found,old_style)
tags:add('name',module_found)
tags:add('class','module')
local item = F:new_item(tags,lineno())
item.old_style = old_style
module_item = item
end
local mod
local t,v = tnext(tok)
-- with some coding styles first comment is standard boilerplate; option to ignore this.
if args.boilerplate and t == 'comment' then
-- hack to deal with boilerplate inside Lua block comments
if v:match '%s*%-%-%[%[' then lang:grab_block_comment(v,tok) end
t,v = tnext(tok)
end
if t == '#' then -- skip Lua shebang line, if present
while t and t ~= 'comment' do t,v = tnext(tok) end
if t == nil then
F:warning('empty file')
return nil
end
end
if lang.parse_module_call and t ~= 'comment' then
local prev_token
while t do
if prev_token ~= '.' and prev_token ~= ':' and t == 'iden' and v == 'module' then
break
end
prev_token = t
t, v = tnext(tok)
end
if not t then
if not args.ignore then
F:warning("no module() call found; no initial doc comment")
end
--return nil
else
mod,t,v = lang:parse_module_call(tok,t,v)
if mod and mod ~= '...' then
add_module(Tags.new{summary='(no description)'},mod,true)
first_comment = false
module_found = true
end
end
end
local ok, err = xpcall(function()
while t do
if t == 'comment' then
local comment = {}
local ldoc_comment,block = lang:start_comment(v)
if ldoc_comment and block then
t,v = lang:grab_block_comment(v,tok)
end
if lang:empty_comment(v) then -- ignore rest of empty start comments
t,v = tok()
if t == 'space' and not v:match '\n' then
t,v = tok()
end
end
while t and t == 'comment' do
v = lang:trim_comment(v)
append(comment,v)
t,v = tok()
if t == 'space' and not v:match '\n' then
t,v = tok()
end
end
if t == 'space' then t,v = tnext(tok) end
local item_follows, tags, is_local, case, parse_error
if ldoc_comment then
comment = table.concat(comment)
if comment:match '^%s*$' then
ldoc_comment = nil
end
end
if ldoc_comment then
if first_comment then
first_comment = false
else
item_follows, is_local, case = lang:item_follows(t,v,tok)
if not item_follows then
parse_error = is_local
is_local = false
end
end
if item_follows or comment_contains_tags(comment,args) then
tags = extract_tags(comment,args)
-- explicitly named @module (which is recommended)
if doc.project_level(tags.class) then
module_found = tags.name
-- might be a module returning a single function!
if tags.param or tags['return'] then
local parms, ret = tags.param, tags['return']
local name = tags.name
tags.param = nil
tags['return'] = nil
tags['class'] = nil
tags['name'] = nil
add_module(tags,name,false)
tags = {
summary = '',
name = 'returns...',
class = 'function',
['return'] = ret,
param = parms
}
end
end
doc.expand_annotation_item(tags,current_item)
-- if the item has an explicit name or defined meaning
-- then don't continue to do any code analysis!
-- Watch out for the case where there are field or param tags
-- but no class, since these will be fixed up later as module/class
-- entities
if (tags.field or tags.param) and not tags.class then
parse_error = false
end
if tags.name then
if not tags.class then
F:warning("no type specified, assuming function: '"..tags.name.."'")
tags:add('class','function')
end
item_follows, is_local, parse_error = false, false, false
elseif args.no_args_infer then
F:error("No name and type provided (no_args_infer)")
elseif lang:is_module_modifier (tags) then
if not item_follows then
F:warning("@usage or @export followed by unknown code")
break
end
item_follows(tags,tok)
local res, value, tagname = lang:parse_module_modifier(tags,tok,F)
if not res then F:warning(value); break
else
if tagname then
module_item:set_tag(tagname,value)
end
-- don't continue to make an item!
ldoc_comment = false
end
end
end
if parse_error then
F:warning('definition cannot be parsed - '..parse_error)
end
end
-- some hackery necessary to find the module() call
if not module_found and ldoc_comment then
local old_style
module_found,t,v = lang:find_module(tok,t,v)
-- right, we can add the module object ...
old_style = module_found ~= nil
if not module_found or module_found == '...' then
-- we have to guess the module name
module_found = tools.this_module_name(package,fname)
end
if not tags then tags = extract_tags(comment,args) end
add_module(tags,module_found,old_style)
tags = nil
if not t then
F:warning('contains no items','warning',1)
break;
end -- run out of file!
-- if we did bump into a doc comment, then we can continue parsing it
end
-- end of a block of document comments
if ldoc_comment and tags then
local line = lineno()
if t ~= nil then
if item_follows then -- parse the item definition
local err = item_follows(tags,tok)
if err then F:error(err) end
elseif parse_error then
F:warning('definition cannot be parsed - '..parse_error)
else
lang:parse_extra(tags,tok,case)
end
end
if is_local or tags['local'] then
tags:add('local',true)
end
-- support for standalone fields/properties of classes/modules
if (tags.field or tags.param) and not tags.class then
-- the hack is to take a subfield and pull out its name,
-- (see Tag:add above) but let the subfield itself go through
-- with any modifiers.
local fp = tags.field or tags.param
if type(fp) == 'table' then fp = fp[1] end
fp = tools.extract_identifier(fp)
tags:add('name',fp)
tags:add('class','field')
end
if tags.name then
current_item = F:new_item(tags,line)
current_item.inferred = item_follows ~= nil
if doc.project_level(tags.class) then
if module_item then
F:error("Module already declared!")
end
module_item = current_item
end
end
if not t then break end
end
end
if t ~= 'comment' then t,v = tok() end
end
end,debug.traceback)
if not ok then return F, err end
if f then f:close() end
return F
end
function parse.file(name,lang, args)
local F,err = parse_file(name,lang,args.package,args)
if err or not F then return F,err end
local ok,err = xpcall(function() F:finish() end,debug.traceback)
if not ok then return F,err end
return F
end
return parse

View file

@ -0,0 +1,131 @@
-- Making Lua source code look pretty.
-- A simple scanner based prettifier, which scans comments for @{ref} and code
-- for known modules and functions.
-- A module reference to an example `test-fun.lua` would look like
-- `@{example:test-fun}`.
local List = require 'pl.List'
local tablex = require 'pl.tablex'
local globals = require 'ldoc.builtin.globals'
local prettify = {}
local user_keywords = {}
local escaped_chars = {
['&'] = '&amp;',
['<'] = '&lt;',
['>'] = '&gt;',
}
local escape_pat = '[&<>]'
local function escape(str)
return (str:gsub(escape_pat,escaped_chars))
end
local function span(t,val)
return ('<span class="%s">%s</span>'):format(t,val)
end
local spans = {keyword=true,number=true,string=true,comment=true,global=true,backtick=true}
local cpp_lang = {C = true, c = true, cpp = true, cxx = true, h = true}
function prettify.lua (lang, fname, code, initial_lineno, pre, linenos)
local res, lexer = List(), require 'ldoc.lexer'
local tokenizer
local ik = 1
if not cpp_lang[lang] then
tokenizer = lexer.lua
else
tokenizer = lexer.cpp
end
if pre then
res:append '<pre>\n'
end
initial_lineno = initial_lineno or 0
local tok = tokenizer(code,{},{})
local error_reporter = {
warning = function (self,msg)
io.stderr:write(fname..':'..tok:lineno()+initial_lineno..': '..msg,'\n')
end
}
local last_t, last_val
local t,val = tok()
if not t then return nil,"empty file" end
while t do
val = escape(val)
if linenos and tok:lineno() == linenos[ik] then
res:append('<a id="'..linenos[ik]..'"></a>')
ik = ik + 1
end
if globals.functions[val] or globals.tables[val] then
t = 'global'
end
if user_keywords[val] then
res:append(span('user-keyword keyword-' .. val,val))
elseif spans[t] then
if t == 'comment' or t == 'backtick' then -- may contain @{ref} or `..`
val = prettify.resolve_inline_references(val,error_reporter)
end
res:append(span(t,val))
else
res:append(val)
end
last_t, last_val = t,val
t,val = tok()
end
if last_t == 'comment' then
res[#res] = span('comment',last_val:gsub('\r*\n$',''))
end
local last = res[#res]
if last:match '\n$' then
res[#res] = last:gsub('\n+','')
end
if pre then
res:append '</pre>\n'
end
return res:join ()
end
local lxsh
local lxsh_highlighers = {bib=true,c=true,lua=true,sh=true}
function prettify.code (lang,fname,code,initial_lineno,pre)
if not lxsh then
return prettify.lua (lang,fname, code, initial_lineno, pre)
else
if not lxsh_highlighers[lang] then
lang = 'lua'
end
code = lxsh.highlighters[lang](code, {
formatter = lxsh.formatters.html,
external = true
})
if not pre then
code = code:gsub("^<pre.->(.-)%s*</pre>$", '%1')
end
return code
end
end
function prettify.set_prettifier (pretty)
local ok
if pretty == 'lxsh' then
ok,lxsh = pcall(require,'lxsh')
if not ok then
print('pretty: '..pretty..' not found, using built-in Lua')
lxsh = nil
end
end
end
function prettify.set_user_keywords(keywords)
if keywords then
user_keywords = tablex.makeset(keywords)
end
end
return prettify

View file

@ -0,0 +1,544 @@
---------
-- General utility functions for ldoc
-- @module tools
local class = require 'pl.class'
local List = require 'pl.List'
local path = require 'pl.path'
local utils = require 'pl.utils'
local tablex = require 'pl.tablex'
local stringx = require 'pl.stringx'
local dir = require 'pl.dir'
local tools = {}
local M = tools
local append = table.insert
local lexer = require 'ldoc.lexer'
local quit = utils.quit
-- at rendering time, can access the ldoc table from any module item,
-- or the item itself if it's a module
function M.item_ldoc (item)
local mod = item and (item.module or item)
return mod and mod.ldoc
end
-- this constructs an iterator over a list of objects which returns only
-- those objects where a field has a certain value. It's used to iterate
-- only over functions or tables, etc. If the list of item has a module
-- with a context, then use that to pre-sort the fltered items.
-- (something rather similar exists in LuaDoc)
function M.type_iterator (list,field,value)
return function()
local fls = list:filter(function(item)
return item[field] == value
end)
local ldoc = M.item_ldoc(fls[1])
if ldoc and ldoc.sort then
fls:sort(function(ia,ib)
return ia.name < ib.name
end)
end
return fls:iter()
end
end
-- KindMap is used to iterate over a set of categories, called _kinds_,
-- and the associated iterator over all items in that category.
-- For instance, a module contains functions, tables, etc and we will
-- want to iterate over these categories in a specified order:
--
-- for kind, items in module.kinds() do
-- print('kind',kind)
-- for item in items() do print(item.name) end
-- end
--
-- The kind is typically used as a label or a Title, so for type 'function' the
-- kind is 'Functions' and so on.
local KindMap = class()
M.KindMap = KindMap
-- calling a KindMap returns an iterator. This returns the kind, the iterator
-- over the items of that type, and the actual type tag value.
function KindMap:__call ()
local i = 1
local klass = self.klass
return function()
local kind = klass.kinds[i]
if not kind then return nil end -- no more kinds
while not self[kind] do
i = i + 1
kind = klass.kinds[i]
if not kind then return nil end
end
i = i + 1
local type = klass.types_by_kind [kind].type
return kind, self[kind], type
end
end
function KindMap:put_kind_first (kind)
-- find this kind in our kind list
local kinds = self.klass.kinds
local idx = tablex.find(kinds,kind)
-- and swop with the start!
if idx then
kinds[1],kinds[idx] = kinds[idx],kinds[1]
end
end
function KindMap:type_of (item)
local klass = self.klass
local kind = klass.types_by_tag[item.type]
return klass.types_by_kind [kind]
end
function KindMap:get_section_description (kind)
return self.klass.descriptions[kind]
end
function KindMap:get_item (kind)
return self.klass.items_by_kind[kind]
end
-- called for each new item. It does not actually create separate lists,
-- (although that would not break the interface) but creates iterators
-- for that item type if not already created.
function KindMap:add (item,items,description)
local group = item[self.fieldname] -- which wd be item's type or section
local kname = self.klass.types_by_tag[group] -- the kind name
if not self[kname] then
self[kname] = M.type_iterator (items,self.fieldname,group)
self.klass.descriptions[kname] = description
end
item.kind = kname:lower()
end
-- KindMap has a 'class constructor' which is used to modify
-- any new base class.
function KindMap._class_init (klass)
klass.kinds = {} -- list in correct order of kinds
klass.types_by_tag = {} -- indexed by tag
klass.types_by_kind = {} -- indexed by kind
klass.descriptions = {} -- optional description for each kind
klass.items_by_kind = {} -- some kinds are items
end
function KindMap.add_kind (klass,tag,kind,subnames,item)
if not klass.types_by_kind[kind] then
klass.types_by_tag[tag] = kind
klass.types_by_kind[kind] = {type=tag,subnames=subnames}
if item then
klass.items_by_kind[kind] = item
end
append(klass.kinds,kind)
end
end
----- some useful utility functions ------
function M.module_basepath()
local lpath = List.split(package.path,';')
for p in lpath:iter() do
local p = path.dirname(p)
if path.isabs(p) then
return p
end
end
end
-- split a qualified name into the module part and the name part,
-- e.g 'pl.utils.split' becomes 'pl.utils' and 'split'. Also
-- must understand colon notation!
function M.split_dotted_name (s)
local s1,s2 = s:match '^(.+)[%.:](.+)$'
if s1 then -- we can split
return s1,s2
else
return nil
end
--~ local s1,s2 = path.splitext(s)
--~ if s2=='' then return nil
--~ else return s1,s2:sub(2)
--~ end
end
-- grab lines from a line iterator `iter` until the line matches the pattern.
-- Returns the joined lines and the line, which may be nil if we run out of
-- lines.
function M.grab_while_not(iter,pattern)
local line = iter()
local res = {}
while line and not line:match(pattern) do
append(res,line)
line = iter()
end
res = table.concat(res,'\n')
return res,line
end
function M.extract_identifier (value)
return value:match('([%.:%-_%w]+)(.*)$')
end
function M.identifier_list (ls)
local ns = List()
if type(ls) == 'string' then ls = List{ns} end
for s in ls:iter() do
if s:match ',' then
ns:extend(List.split(s,'[,%s]+'))
else
ns:append(s)
end
end
return ns
end
function M.strip (s)
return s:gsub('^%s+',''):gsub('%s+$','')
end
-- Joins strings using a separator.
--
-- Empty strings and nil arguments are ignored:
--
-- assert(join('+', 'one', '', 'two', nil, 'three') == 'one+two+three')
-- assert(join(' ', '', '') == '')
--
-- This is especially useful for the last case demonstrated above,
-- where "conventional" solutions (".." or table.concat) would result
-- in a spurious space.
function M.join(sep, ...)
local contents = {}
for i = 1, select('#', ...) do
local value = select(i, ...)
if value and value ~= "" then
contents[#contents + 1] = value
end
end
return table.concat(contents, sep)
end
function M.check_directory(d)
if not path.isdir(d) then
if not dir.makepath(d) then
quit("Could not create "..d.." directory")
end
end
end
function M.check_file (f,original)
if not path.exists(f)
or not path.exists(original)
or path.getmtime(original) > path.getmtime(f) then
local text,err = utils.readfile(original)
local _
if text then
_,err = utils.writefile(f,text)
end
if err then
quit("Could not copy "..original.." to "..f)
end
end
end
function M.writefile(name,text)
local f,err = io.open(name,"wb")
--~ local ok,err = utils.writefile(name,text)
if err then quit(err) end
f:write(text)
f:close()
end
function M.name_of (lpath)
local _
lpath,_ = path.splitext(lpath)
return lpath
end
function M.this_module_name (basename,fname)
if basename == '' then
return M.name_of(fname)
end
basename = path.abspath(basename)
if basename:sub(-1,-1) ~= path.sep then
basename = basename..path.sep
end
local lpath,cnt = fname:gsub('^'..utils.escape(basename),'')
--print('deduce',lpath,cnt,basename)
if cnt ~= 1 then quit("module(...) name deduction failed: base "..basename.." "..fname) end
lpath = lpath:gsub(path.sep,'.')
return (M.name_of(lpath):gsub('%.init$',''))
end
function M.find_existing_module (name, dname, searchfn)
local fullpath,lua = searchfn(name)
local mod = true
if not fullpath then -- maybe it's a function reference?
-- try again with the module part
local mpath,fname = M.split_dotted_name(name)
if mpath then
fullpath,lua = searchfn(mpath)
else
fullpath = nil
end
if not fullpath then
return nil, "module or function '"..dname.."' not found on module path"
else
mod = fname
end
end
if not lua then return nil, "module '"..name.."' is a binary extension" end
return fullpath, mod
end
function M.lookup_existing_module_or_function (name, docpath)
-- first look up on the Lua module path
local on_docpath
local fullpath, mod = M.find_existing_module(name,name,path.package_path)
-- no go; but see if we can find it on the doc path
if not fullpath then
fullpath, mod = M.find_existing_module("ldoc.builtin." .. name,name,path.package_path)
on_docpath = true
--~ fullpath, mod = M.find_existing_module(name, function(name)
--~ local fpath = package.searchpath(name,docpath)
--~ return fpath,true -- result must always be 'lua'!
--~ end)
end
return fullpath, mod, on_docpath -- `mod` can be the error message
end
--------- lexer tools -----
local tnext = lexer.skipws
local function type_of (tok) return tok and tok[1] or 'end' end
local function value_of (tok) return tok[2] end
-- This parses Lua formal argument lists. It will return a list of argument
-- names, which also has a comments field, which will contain any commments
-- following the arguments. ldoc will use these in addition to explicit
-- param tags.
function M.get_parameters (tok,endtoken,delim,lang)
tok = M.space_skip_getter(tok)
local args = List()
args.comments = {}
local ltl,tt = lexer.get_separated_list(tok,endtoken,delim)
if not ltl or not ltl[1] or #ltl[1] == 0 then return args end -- no arguments
local strip_comment, extract_arg
if lang then
strip_comment = utils.bind1(lang.trim_comment,lang)
extract_arg = utils.bind1(lang.extract_arg,lang)
else
strip_comment = function(text)
return text:match("%s*%-%-+%s*(.*)")
end
extract_arg = function(tl,idx)
idx = idx or 1
local res = value_of(tl[idx])
if res == '[' then -- we do allow array indices in tables now
res = '['..value_of(tl[idx + 1])..']'
end
return res
end
end
local function set_comment (idx,tok)
local text = stringx.rstrip(value_of(tok))
text = strip_comment(text)
local arg = args[idx]
local current_comment = args.comments[arg]
if current_comment then
text = current_comment .. " " .. text
end
args.comments[arg] = text
end
local function add_arg (tl,idx)
local name, type = extract_arg(tl,idx)
args:append(name)
if type then
if not args.types then args.types = List() end
args.types:append(type)
end
end
for i = 1,#ltl do
local tl = ltl[i] -- token list for argument
if #tl > 0 then
local j = 1
if type_of(tl[1]) == 'comment' then
-- the comments for the i-1 th arg are in the i th arg...
if i > 1 then
while type_of(tl[j]) == 'comment' do
set_comment(i-1,tl[j])
j = j + 1
end
else -- first comment however is for the function return comment!
args.return_comment = strip_comment(value_of(tl[i]))
j = j + 1
end
if #tl > 1 then
add_arg(tl,j)
end
else
add_arg(tl,1)
end
if i == #ltl and #tl > 1 then
while j <= #tl and type_of(tl[j]) ~= 'comment' do
j = j + 1
end
if j > #tl then break end -- was no comments!
while type_of(tl[j]) == 'comment' do
set_comment(i,tl[j])
j = j + 1
end
end
else
return nil,"empty argument"
end
end
-- we had argument comments
-- but the last one may be outside the parens! (Geoff style)
-- (only try this stunt if it's a function parameter list!)
if (not endtoken or endtoken == ')') and (#args > 0 or next(args.comments)) then
local n = #args
local last_arg = args[n]
if not args.comments[last_arg] then
while true do
tt = {tok()}
if type_of(tt) == 'comment' then
set_comment(n,tt)
else
break
end
end
end
end
-- return what token we ended on as well - can be token _past_ ')'
return args,tt[1],tt[2]
end
-- parse a Lua identifier - contains names separated by . and (optionally) :.
-- Set `colon` to be the secondary separator, '' for none.
function M.get_fun_name (tok,first,colon)
local res = {}
local t,name,sep,_
colon = colon or ':'
if not first then
t,name = tnext(tok)
else
t,name = 'iden',first
end
if t ~= 'iden' then return nil end
t,sep = tnext(tok)
while sep == '.' or sep == colon do
append(res,name)
append(res,sep)
_,name = tnext(tok)
t,sep = tnext(tok)
end
append(res,name)
return table.concat(res),t,sep
end
-- space-skipping version of token iterator
function M.space_skip_getter(tok)
return function ()
local t,v = tok()
while t and t == 'space' do
t,v = tok()
end
return t,v
end
end
function M.quote (s)
return "'"..s.."'"
end
-- The PL Lua lexer does not do block comments
-- when used in line-grabbing mode, so this function grabs each line
-- until we meet the end of the comment
function M.grab_block_comment (v,tok,patt)
local res = {v}
repeat
v = lexer.getline(tok)
if v:match (patt) then break end
append(res,v)
append(res,'\n')
until false
res = table.concat(res)
--print(res)
return 'comment',res
end
local prel = path.normcase('/[^/]-/%.%.')
function M.abspath (f)
local count
local res = path.normcase(path.abspath(f))
while true do
res,count = res:gsub(prel,'')
if count == 0 then break end
end
return res
end
function M.getallfiles(root,mask)
local res = List(dir.getallfiles(root,mask))
res:sort()
return res
end
function M.expand_file_list (list, mask)
local exclude_list = list.exclude and M.files_from_list(list.exclude, mask)
local files = List()
local function process (f)
f = M.abspath(f)
if not exclude_list or exclude_list and exclude_list:index(f) == nil then
files:append(f)
end
end
for _,f in ipairs(list) do
if path.isdir(f) then
local dfiles = M.getallfiles(f,mask)
for f in dfiles:iter() do
process(f)
end
elseif path.isfile(f) then
process(f)
else
quit("file or directory does not exist: "..M.quote(f))
end
end
return files
end
function M.process_file_list (list, mask, operation, ...)
local files = M.expand_file_list(list,mask)
for f in files:iter() do
operation(f,...)
end
end
function M.files_from_list (list, mask)
local excl = List()
M.process_file_list (list, mask, function(f)
excl:append(f)
end)
return excl
end
return tools

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,675 @@
--- Date and Date Format classes.
-- See @{05-dates.md|the Guide}.
--
-- NOTE: the date module is deprecated! see
-- https://github.com/lunarmodules/Penlight/issues/285
--
-- Dependencies: `pl.class`, `pl.stringx`, `pl.utils`
-- @classmod pl.Date
-- @pragma nostrip
local class = require 'pl.class'
local os_time, os_date = os.time, os.date
local stringx = require 'pl.stringx'
local utils = require 'pl.utils'
local assert_arg,assert_string = utils.assert_arg,utils.assert_string
utils.raise_deprecation {
source = "Penlight " .. utils._VERSION,
message = "the 'Date' module is deprecated, see https://github.com/lunarmodules/Penlight/issues/285",
version_removed = "2.0.0",
version_deprecated = "1.9.2",
}
local Date = class()
Date.Format = class()
--- Date constructor.
-- @param t this can be either
--
-- * `nil` or empty - use current date and time
-- * number - seconds since epoch (as returned by `os.time`). Resulting time is UTC
-- * `Date` - make a copy of this date
-- * table - table containing year, month, etc as for `os.time`. You may leave out year, month or day,
-- in which case current values will be used.
-- * year (will be followed by month, day etc)
--
-- @param ... true if Universal Coordinated Time, or two to five numbers: month,day,hour,min,sec
-- @function Date
function Date:_init(t,...)
local time
local nargs = select('#',...)
if nargs > 2 then
local extra = {...}
local year = t
t = {
year = year,
month = extra[1],
day = extra[2],
hour = extra[3],
min = extra[4],
sec = extra[5]
}
end
if nargs == 1 then
self.utc = select(1,...) == true
end
if t == nil or t == 'utc' then
time = os_time()
self.utc = t == 'utc'
elseif type(t) == 'number' then
time = t
if self.utc == nil then self.utc = true end
elseif type(t) == 'table' then
if getmetatable(t) == Date then -- copy ctor
time = t.time
self.utc = t.utc
else
if not (t.year and t.month) then
local lt = os_date('*t')
if not t.year and not t.month and not t.day then
t.year = lt.year
t.month = lt.month
t.day = lt.day
else
t.year = t.year or lt.year
t.month = t.month or (t.day and lt.month or 1)
t.day = t.day or 1
end
end
t.day = t.day or 1
time = os_time(t)
end
else
error("bad type for Date constructor: "..type(t),2)
end
self:set(time)
end
--- set the current time of this Date object.
-- @int t seconds since epoch
function Date:set(t)
self.time = t
if self.utc then
self.tab = os_date('!*t',t)
else
self.tab = os_date('*t',t)
end
end
--- get the time zone offset from UTC.
-- @int ts seconds ahead of UTC
function Date.tzone (ts)
if ts == nil then
ts = os_time()
elseif type(ts) == "table" then
if getmetatable(ts) == Date then
ts = ts.time
else
ts = Date(ts).time
end
end
local utc = os_date('!*t',ts)
local lcl = os_date('*t',ts)
lcl.isdst = false
return os.difftime(os_time(lcl), os_time(utc))
end
--- convert this date to UTC.
function Date:toUTC ()
local ndate = Date(self)
if not self.utc then
ndate.utc = true
ndate:set(ndate.time)
end
return ndate
end
--- convert this UTC date to local.
function Date:toLocal ()
local ndate = Date(self)
if self.utc then
ndate.utc = false
ndate:set(ndate.time)
--~ ndate:add { sec = Date.tzone(self) }
end
return ndate
end
--- set the year.
-- @int y Four-digit year
-- @class function
-- @name Date:year
--- set the month.
-- @int m month
-- @class function
-- @name Date:month
--- set the day.
-- @int d day
-- @class function
-- @name Date:day
--- set the hour.
-- @int h hour
-- @class function
-- @name Date:hour
--- set the minutes.
-- @int min minutes
-- @class function
-- @name Date:min
--- set the seconds.
-- @int sec seconds
-- @class function
-- @name Date:sec
--- set the day of year.
-- @class function
-- @int yday day of year
-- @name Date:yday
--- get the year.
-- @int y Four-digit year
-- @class function
-- @name Date:year
--- get the month.
-- @class function
-- @name Date:month
--- get the day.
-- @class function
-- @name Date:day
--- get the hour.
-- @class function
-- @name Date:hour
--- get the minutes.
-- @class function
-- @name Date:min
--- get the seconds.
-- @class function
-- @name Date:sec
--- get the day of year.
-- @class function
-- @name Date:yday
for _,c in ipairs{'year','month','day','hour','min','sec','yday'} do
Date[c] = function(self,val)
if val then
assert_arg(1,val,"number")
self.tab[c] = val
self:set(os_time(self.tab))
return self
else
return self.tab[c]
end
end
end
--- name of day of week.
-- @bool full abbreviated if true, full otherwise.
-- @ret string name
function Date:weekday_name(full)
return os_date(full and '%A' or '%a',self.time)
end
--- name of month.
-- @int full abbreviated if true, full otherwise.
-- @ret string name
function Date:month_name(full)
return os_date(full and '%B' or '%b',self.time)
end
--- is this day on a weekend?.
function Date:is_weekend()
return self.tab.wday == 1 or self.tab.wday == 7
end
--- add to a date object.
-- @param t a table containing one of the following keys and a value:
-- one of `year`,`month`,`day`,`hour`,`min`,`sec`
-- @return this date
function Date:add(t)
local old_dst = self.tab.isdst
local key,val = next(t)
self.tab[key] = self.tab[key] + val
self:set(os_time(self.tab))
if old_dst ~= self.tab.isdst then
self.tab.hour = self.tab.hour - (old_dst and 1 or -1)
self:set(os_time(self.tab))
end
return self
end
--- last day of the month.
-- @return int day
function Date:last_day()
local d = 28
local m = self.tab.month
while self.tab.month == m do
d = d + 1
self:add{day=1}
end
self:add{day=-1}
return self
end
--- difference between two Date objects.
-- @tparam Date other Date object
-- @treturn Date.Interval object
function Date:diff(other)
local dt = self.time - other.time
if dt < 0 then error("date difference is negative!",2) end
return Date.Interval(dt)
end
--- long numerical ISO data format version of this date.
function Date:__tostring()
local fmt = '%Y-%m-%dT%H:%M:%S'
if self.utc then
fmt = "!"..fmt
end
local t = os_date(fmt,self.time)
if self.utc then
return t .. 'Z'
else
local offs = self:tzone()
if offs == 0 then
return t .. 'Z'
end
local sign = offs > 0 and '+' or '-'
local h = math.ceil(offs/3600)
local m = (offs % 3600)/60
if m == 0 then
return t .. ('%s%02d'):format(sign,h)
else
return t .. ('%s%02d:%02d'):format(sign,h,m)
end
end
end
--- equality between Date objects.
function Date:__eq(other)
return self.time == other.time
end
--- ordering between Date objects.
function Date:__lt(other)
return self.time < other.time
end
--- difference between Date objects.
-- @function Date:__sub
Date.__sub = Date.diff
--- add a date and an interval.
-- @param other either a `Date.Interval` object or a table such as
-- passed to `Date:add`
function Date:__add(other)
local nd = Date(self)
if Date.Interval:class_of(other) then
other = {sec=other.time}
end
nd:add(other)
return nd
end
Date.Interval = class(Date)
---- Date.Interval constructor
-- @int t an interval in seconds
-- @function Date.Interval
function Date.Interval:_init(t)
self:set(t)
end
function Date.Interval:set(t)
self.time = t
self.tab = os_date('!*t',self.time)
end
local function ess(n)
if n > 1 then return 's '
else return ' '
end
end
--- If it's an interval then the format is '2 hours 29 sec' etc.
function Date.Interval:__tostring()
local t, res = self.tab, ''
local y,m,d = t.year - 1970, t.month - 1, t.day - 1
if y > 0 then res = res .. y .. ' year'..ess(y) end
if m > 0 then res = res .. m .. ' month'..ess(m) end
if d > 0 then res = res .. d .. ' day'..ess(d) end
if y == 0 and m == 0 then
local h = t.hour
if h > 0 then res = res .. h .. ' hour'..ess(h) end
if t.min > 0 then res = res .. t.min .. ' min ' end
if t.sec > 0 then res = res .. t.sec .. ' sec ' end
end
if res == '' then res = 'zero' end
return res
end
------------ Date.Format class: parsing and renderinig dates ------------
-- short field names, explicit os.date names, and a mask for allowed field repeats
local formats = {
d = {'day',{true,true}},
y = {'year',{false,true,false,true}},
m = {'month',{true,true}},
H = {'hour',{true,true}},
M = {'min',{true,true}},
S = {'sec',{true,true}},
}
--- Date.Format constructor.
-- @string fmt. A string where the following fields are significant:
--
-- * d day (either d or dd)
-- * y year (either yy or yyy)
-- * m month (either m or mm)
-- * H hour (either H or HH)
-- * M minute (either M or MM)
-- * S second (either S or SS)
--
-- Alternatively, if fmt is nil then this returns a flexible date parser
-- that tries various date/time schemes in turn:
--
-- * [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601), like `2010-05-10 12:35:23Z` or `2008-10-03T14:30+02`
-- * times like 15:30 or 8.05pm (assumed to be today's date)
-- * dates like 28/10/02 (European order!) or 5 Feb 2012
-- * month name like march or Mar (case-insensitive, first 3 letters); here the
-- day will be 1 and the year this current year
--
-- A date in format 3 can be optionally followed by a time in format 2.
-- Please see test-date.lua in the tests folder for more examples.
-- @usage df = Date.Format("yyyy-mm-dd HH:MM:SS")
-- @class function
-- @name Date.Format
function Date.Format:_init(fmt)
if not fmt then
self.fmt = '%Y-%m-%d %H:%M:%S'
self.outf = self.fmt
self.plain = true
return
end
local append = table.insert
local D,PLUS,OPENP,CLOSEP = '\001','\002','\003','\004'
local vars,used = {},{}
local patt,outf = {},{}
local i = 1
while i < #fmt do
local ch = fmt:sub(i,i)
local df = formats[ch]
if df then
if used[ch] then error("field appeared twice: "..ch,4) end
used[ch] = true
-- this field may be repeated
local _,inext = fmt:find(ch..'+',i+1)
local cnt = not _ and 1 or inext-i+1
if not df[2][cnt] then error("wrong number of fields: "..ch,4) end
-- single chars mean 'accept more than one digit'
local p = cnt==1 and (D..PLUS) or (D):rep(cnt)
append(patt,OPENP..p..CLOSEP)
append(vars,ch)
if ch == 'y' then
append(outf,cnt==2 and '%y' or '%Y')
else
append(outf,'%'..ch)
end
i = i + cnt
else
append(patt,ch)
append(outf,ch)
i = i + 1
end
end
-- escape any magic characters
fmt = utils.escape(table.concat(patt))
-- fmt = table.concat(patt):gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1')
-- replace markers with their magic equivalents
fmt = fmt:gsub(D,'%%d'):gsub(PLUS,'+'):gsub(OPENP,'('):gsub(CLOSEP,')')
self.fmt = fmt
self.outf = table.concat(outf)
self.vars = vars
end
local parse_date
--- parse a string into a Date object.
-- @string str a date string
-- @return date object
function Date.Format:parse(str)
assert_string(1,str)
if self.plain then
return parse_date(str,self.us)
end
local res = {str:match(self.fmt)}
if #res==0 then return nil, 'cannot parse '..str end
local tab = {}
for i,v in ipairs(self.vars) do
local name = formats[v][1] -- e.g. 'y' becomes 'year'
tab[name] = tonumber(res[i])
end
-- os.date() requires these fields; if not present, we assume
-- that the time set is for the current day.
if not (tab.year and tab.month and tab.day) then
local today = Date()
tab.year = tab.year or today:year()
tab.month = tab.month or today:month()
tab.day = tab.day or today:day()
end
local Y = tab.year
if Y < 100 then -- classic Y2K pivot
tab.year = Y + (Y < 35 and 2000 or 1999)
elseif not Y then
tab.year = 1970
end
return Date(tab)
end
--- convert a Date object into a string.
-- @param d a date object, or a time value as returned by @{os.time}
-- @return string
function Date.Format:tostring(d)
local tm
local fmt = self.outf
if type(d) == 'number' then
tm = d
else
tm = d.time
if d.utc then
fmt = '!'..fmt
end
end
return os_date(fmt,tm)
end
--- force US order in dates like 9/11/2001
function Date.Format:US_order(yesno)
self.us = yesno
end
--local months = {jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,dec=12}
local months
local parse_date_unsafe
local function create_months()
local ld, day1 = parse_date_unsafe '2000-12-31', {day=1}
months = {}
for i = 1,12 do
ld = ld:last_day()
ld:add(day1)
local mon = ld:month_name():lower()
months [mon] = i
end
end
--[[
Allowed patterns:
- [day] [monthname] [year] [time]
- [day]/[month][/year] [time]
]]
local function looks_like_a_month(w)
return w:match '^%a+,*$' ~= nil
end
local is_number = stringx.isdigit
local function tonum(s,l1,l2,kind)
kind = kind or ''
local n = tonumber(s)
if not n then error(("%snot a number: '%s'"):format(kind,s)) end
if n < l1 or n > l2 then
error(("%s out of range: %s is not between %d and %d"):format(kind,s,l1,l2))
end
return n
end
local function parse_iso_end(p,ns,sec)
-- may be fractional part of seconds
local _,nfrac,secfrac = p:find('^%.%d+',ns+1)
if secfrac then
sec = sec .. secfrac
p = p:sub(nfrac+1)
else
p = p:sub(ns+1)
end
-- ISO 8601 dates may end in Z (for UTC) or [+-][isotime]
-- (we're working with the date as lower case, hence 'z')
if p:match 'z$' then -- we're UTC!
return sec, {h=0,m=0}
end
p = p:gsub(':','') -- turn 00:30 to 0030
local _,_,sign,offs = p:find('^([%+%-])(%d+)')
if not sign then return sec, nil end -- not UTC
if #offs == 2 then offs = offs .. '00' end -- 01 to 0100
local tz = { h = tonumber(offs:sub(1,2)), m = tonumber(offs:sub(3,4)) }
if sign == '-' then tz.h = -tz.h; tz.m = -tz.m end
return sec, tz
end
function parse_date_unsafe (s,US)
s = s:gsub('T',' ') -- ISO 8601
local parts = stringx.split(s:lower())
local i,p = 1,parts[1]
local function nextp() i = i + 1; p = parts[i] end
local year,min,hour,sec,apm
local tz
local _,nxt,day, month = p:find '^(%d+)/(%d+)'
if day then
-- swop for US case
if US then
day, month = month, day
end
_,_,year = p:find('^/(%d+)',nxt+1)
nextp()
else -- ISO
year,month,day = p:match('^(%d+)%-(%d+)%-(%d+)')
if year then
nextp()
end
end
if p and not year and is_number(p) then -- has to be date
if #p < 4 then
day = p
nextp()
else -- unless it looks like a 24-hour time
year = true
end
end
if p and looks_like_a_month(p) then -- date followed by month
p = p:sub(1,3)
if not months then
create_months()
end
local mon = months[p]
if mon then
month = mon
else error("not a month: " .. p) end
nextp()
end
if p and not year and is_number(p) then
year = p
nextp()
end
if p then -- time is hh:mm[:ss], hhmm[ss] or H.M[am|pm]
_,nxt,hour,min = p:find '^(%d+):(%d+)'
local ns
if nxt then -- are there seconds?
_,ns,sec = p:find ('^:(%d+)',nxt+1)
--if ns then
sec,tz = parse_iso_end(p,ns or nxt,sec)
--end
else -- might be h.m
_,ns,hour,min = p:find '^(%d+)%.(%d+)'
if ns then
apm = p:match '[ap]m$'
else -- or hhmm[ss]
local hourmin
_,nxt,hourmin = p:find ('^(%d+)')
if nxt then
hour = hourmin:sub(1,2)
min = hourmin:sub(3,4)
sec = hourmin:sub(5,6)
if #sec == 0 then sec = nil end
sec,tz = parse_iso_end(p,nxt,sec)
end
end
end
end
local today
if year == true then year = nil end
if not (year and month and day) then
today = Date()
end
day = day and tonum(day,1,31,'day') or (month and 1 or today:day())
month = month and tonum(month,1,12,'month') or today:month()
year = year and tonumber(year) or today:year()
if year < 100 then -- two-digit year pivot around year < 2035
year = year + (year < 35 and 2000 or 1900)
end
hour = hour and tonum(hour,0,apm and 12 or 24,'hour') or 12
if apm == 'pm' then
hour = hour + 12
end
min = min and tonum(min,0,59) or 0
sec = sec and tonum(sec,0,60) or 0 --60 used to indicate leap second
local res = Date {year = year, month = month, day = day, hour = hour, min = min, sec = sec}
if tz then -- ISO 8601 UTC time
local corrected = false
if tz.h ~= 0 then res:add {hour = -tz.h}; corrected = true end
if tz.m ~= 0 then res:add {min = -tz.m}; corrected = true end
res.utc = true
-- we're in UTC, so let's go local...
if corrected then
res = res:toLocal()
end-- we're UTC!
end
return res
end
function parse_date (s)
local ok, d = pcall(parse_date_unsafe,s)
if not ok then -- error
d = d:gsub('.-:%d+: ','')
return nil, d
else
return d
end
end
return Date

View file

@ -0,0 +1,566 @@
--- Python-style list class.
--
-- **Please Note**: methods that change the list will return the list.
-- This is to allow for method chaining, but please note that `ls = ls:sort()`
-- does not mean that a new copy of the list is made. In-place (mutable) methods
-- are marked as returning 'the list' in this documentation.
--
-- See the Guide for further @{02-arrays.md.Python_style_Lists|discussion}
--
-- See <a href="http://www.python.org/doc/current/tut/tut.html">http://www.python.org/doc/current/tut/tut.html</a>, section 5.1
--
-- **Note**: The comments before some of the functions are from the Python docs
-- and contain Python code.
--
-- Written for Lua version Nick Trout 4.0; Redone for Lua 5.1, Steve Donovan.
--
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`
-- @classmod pl.List
-- @pragma nostrip
local tinsert,tremove,concat,tsort = table.insert,table.remove,table.concat,table.sort
local setmetatable, getmetatable,type,tostring,string = setmetatable,getmetatable,type,tostring,string
local tablex = require 'pl.tablex'
local filter,imap,imap2,reduce,transform,tremovevalues = tablex.filter,tablex.imap,tablex.imap2,tablex.reduce,tablex.transform,tablex.removevalues
local tsub = tablex.sub
local utils = require 'pl.utils'
local class = require 'pl.class'
local array_tostring,split,assert_arg,function_arg = utils.array_tostring,utils.split,utils.assert_arg,utils.function_arg
local normalize_slice = tablex._normalize_slice
-- metatable for our list and map objects has already been defined..
local Multimap = utils.stdmt.MultiMap
local List = utils.stdmt.List
local iter
class(nil,nil,List)
-- we want the result to be _covariant_, i.e. t must have type of obj if possible
local function makelist (t,obj)
local klass = List
if obj then
klass = getmetatable(obj)
end
return setmetatable(t,klass)
end
local function simple_table(t)
return type(t) == 'table' and not getmetatable(t) and #t > 0
end
function List._create (src)
if simple_table(src) then return src end
end
function List:_init (src)
if self == src then return end -- existing table used as self!
if src then
for v in iter(src) do
tinsert(self,v)
end
end
end
--- Create a new list. Can optionally pass a table;
-- passing another instance of List will cause a copy to be created;
-- this will return a plain table with an appropriate metatable.
-- we pass anything which isn't a simple table to iterate() to work out
-- an appropriate iterator
-- @see List.iterate
-- @param[opt] t An optional list-like table
-- @return a new List
-- @usage ls = List(); ls = List {1,2,3,4}
-- @function List.new
List.new = List
--- Make a copy of an existing list.
-- The difference from a plain 'copy constructor' is that this returns
-- the actual List subtype.
function List:clone()
local ls = makelist({},self)
ls:extend(self)
return ls
end
--- Add an item to the end of the list.
-- @param i An item
-- @return the list
function List:append(i)
tinsert(self,i)
return self
end
List.push = tinsert
--- Extend the list by appending all the items in the given list.
-- equivalent to 'a[len(a):] = L'.
-- @tparam List L Another List
-- @return the list
function List:extend(L)
assert_arg(1,L,'table')
for i = 1,#L do tinsert(self,L[i]) end
return self
end
--- Insert an item at a given position. i is the index of the
-- element before which to insert.
-- @int i index of element before whichh to insert
-- @param x A data item
-- @return the list
function List:insert(i, x)
assert_arg(1,i,'number')
tinsert(self,i,x)
return self
end
--- Insert an item at the begining of the list.
-- @param x a data item
-- @return the list
function List:put (x)
return self:insert(1,x)
end
--- Remove an element given its index.
-- (equivalent of Python's del s[i])
-- @int i the index
-- @return the list
function List:remove (i)
assert_arg(1,i,'number')
tremove(self,i)
return self
end
--- Remove the first item from the list whose value is given.
-- (This is called 'remove' in Python; renamed to avoid confusion
-- with table.remove)
-- Return nil if there is no such item.
-- @param x A data value
-- @return the list
function List:remove_value(x)
for i=1,#self do
if self[i]==x then tremove(self,i) return self end
end
return self
end
--- Remove the item at the given position in the list, and return it.
-- If no index is specified, a:pop() returns the last item in the list.
-- The item is also removed from the list.
-- @int[opt] i An index
-- @return the item
function List:pop(i)
if not i then i = #self end
assert_arg(1,i,'number')
return tremove(self,i)
end
List.get = List.pop
--- Return the index in the list of the first item whose value is given.
-- Return nil if there is no such item.
-- @function List:index
-- @param x A data value
-- @int[opt=1] idx where to start search
-- @return the index, or nil if not found.
local tfind = tablex.find
List.index = tfind
--- Does this list contain the value?
-- @param x A data value
-- @return true or false
function List:contains(x)
return tfind(self,x) and true or false
end
--- Return the number of times value appears in the list.
-- @param x A data value
-- @return number of times x appears
function List:count(x)
local cnt=0
for i=1,#self do
if self[i]==x then cnt=cnt+1 end
end
return cnt
end
--- Sort the items of the list, in place.
-- @func[opt='<'] cmp an optional comparison function
-- @return the list
function List:sort(cmp)
if cmp then cmp = function_arg(1,cmp) end
tsort(self,cmp)
return self
end
--- Return a sorted copy of this list.
-- @func[opt='<'] cmp an optional comparison function
-- @return a new list
function List:sorted(cmp)
return List(self):sort(cmp)
end
--- Reverse the elements of the list, in place.
-- @return the list
function List:reverse()
local t = self
local n = #t
for i = 1,n/2 do
t[i],t[n] = t[n],t[i]
n = n - 1
end
return self
end
--- Return the minimum and the maximum value of the list.
-- @return minimum value
-- @return maximum value
function List:minmax()
local vmin,vmax = 1e70,-1e70
for i = 1,#self do
local v = self[i]
if v < vmin then vmin = v end
if v > vmax then vmax = v end
end
return vmin,vmax
end
--- Emulate list slicing. like 'list[first:last]' in Python.
-- If first or last are negative then they are relative to the end of the list
-- eg. slice(-2) gives last 2 entries in a list, and
-- slice(-4,-2) gives from -4th to -2nd
-- @param first An index
-- @param last An index
-- @return a new List
function List:slice(first,last)
return tsub(self,first,last)
end
--- Empty the list.
-- @return the list
function List:clear()
for i=1,#self do tremove(self) end
return self
end
local eps = 1.0e-10
--- Emulate Python's range(x) function.
-- Include it in List table for tidiness
-- @int start A number
-- @int[opt] finish A number greater than start; if absent,
-- then start is 1 and finish is start
-- @int[opt=1] incr an increment (may be less than 1)
-- @return a List from start .. finish
-- @usage List.range(0,3) == List{0,1,2,3}
-- @usage List.range(4) = List{1,2,3,4}
-- @usage List.range(5,1,-1) == List{5,4,3,2,1}
function List.range(start,finish,incr)
if not finish then
finish = start
start = 1
end
if incr then
assert_arg(3,incr,'number')
if math.ceil(incr) ~= incr then finish = finish + eps end
else
incr = 1
end
assert_arg(1,start,'number')
assert_arg(2,finish,'number')
local t = List()
for i=start,finish,incr do tinsert(t,i) end
return t
end
--- list:len() is the same as #list.
function List:len()
return #self
end
-- Extended operations --
--- Remove a subrange of elements.
-- equivalent to 'del s[i1:i2]' in Python.
-- @int i1 start of range
-- @int i2 end of range
-- @return the list
function List:chop(i1,i2)
return tremovevalues(self,i1,i2)
end
--- Insert a sublist into a list
-- equivalent to 's[idx:idx] = list' in Python
-- @int idx index
-- @tparam List list list to insert
-- @return the list
-- @usage l = List{10,20}; l:splice(2,{21,22}); assert(l == List{10,21,22,20})
function List:splice(idx,list)
assert_arg(1,idx,'number')
idx = idx - 1
local i = 1
for v in iter(list) do
tinsert(self,i+idx,v)
i = i + 1
end
return self
end
--- General slice assignment s[i1:i2] = seq.
-- @int i1 start index
-- @int i2 end index
-- @tparam List seq a list
-- @return the list
function List:slice_assign(i1,i2,seq)
assert_arg(1,i1,'number')
assert_arg(1,i2,'number')
i1,i2 = normalize_slice(self,i1,i2)
if i2 >= i1 then self:chop(i1,i2) end
self:splice(i1,seq)
return self
end
--- Concatenation operator.
-- @within metamethods
-- @tparam List L another List
-- @return a new list consisting of the list with the elements of the new list appended
function List:__concat(L)
assert_arg(1,L,'table')
local ls = self:clone()
ls:extend(L)
return ls
end
--- Equality operator ==. True iff all elements of two lists are equal.
-- @within metamethods
-- @tparam List L another List
-- @return true or false
function List:__eq(L)
if #self ~= #L then return false end
for i = 1,#self do
if self[i] ~= L[i] then return false end
end
return true
end
--- Join the elements of a list using a delimiter.
-- This method uses tostring on all elements.
-- @string[opt=''] delim a delimiter string, can be empty.
-- @return a string
function List:join (delim)
delim = delim or ''
assert_arg(1,delim,'string')
return concat(array_tostring(self),delim)
end
--- Join a list of strings. <br>
-- Uses `table.concat` directly.
-- @function List:concat
-- @string[opt=''] delim a delimiter
-- @return a string
List.concat = concat
local function tostring_q(val)
local s = tostring(val)
if type(val) == 'string' then
s = '"'..s..'"'
end
return s
end
--- How our list should be rendered as a string. Uses join().
-- @within metamethods
-- @see List:join
function List:__tostring()
return '{'..self:join(',',tostring_q)..'}'
end
--- Call the function on each element of the list.
-- @func fun a function or callable object
-- @param ... optional values to pass to function
function List:foreach (fun,...)
fun = function_arg(1,fun)
for i = 1,#self do
fun(self[i],...)
end
end
local function lookup_fun (obj,name)
local f = obj[name]
if not f then error(type(obj).." does not have method "..name,3) end
return f
end
--- Call the named method on each element of the list.
-- @string name the method name
-- @param ... optional values to pass to function
function List:foreachm (name,...)
for i = 1,#self do
local obj = self[i]
local f = lookup_fun(obj,name)
f(obj,...)
end
end
--- Create a list of all elements which match a function.
-- @func fun a boolean function
-- @param[opt] arg optional argument to be passed as second argument of the predicate
-- @return a new filtered list.
function List:filter (fun,arg)
return makelist(filter(self,fun,arg),self)
end
--- Split a string using a delimiter.
-- @string s the string
-- @string[opt] delim the delimiter (default spaces)
-- @return a List of strings
-- @see pl.utils.split
function List.split (s,delim)
assert_arg(1,s,'string')
return makelist(split(s,delim))
end
--- Apply a function to all elements.
-- Any extra arguments will be passed to the function.
-- @func fun a function of at least one argument
-- @param ... arbitrary extra arguments.
-- @return a new list: {f(x) for x in self}
-- @usage List{'one','two'}:map(string.upper) == {'ONE','TWO'}
-- @see pl.tablex.imap
function List:map (fun,...)
return makelist(imap(fun,self,...),self)
end
--- Apply a function to all elements, in-place.
-- Any extra arguments are passed to the function.
-- @func fun A function that takes at least one argument
-- @param ... arbitrary extra arguments.
-- @return the list.
function List:transform (fun,...)
transform(fun,self,...)
return self
end
--- Apply a function to elements of two lists.
-- Any extra arguments will be passed to the function
-- @func fun a function of at least two arguments
-- @tparam List ls another list
-- @param ... arbitrary extra arguments.
-- @return a new list: {f(x,y) for x in self, for x in arg1}
-- @see pl.tablex.imap2
function List:map2 (fun,ls,...)
return makelist(imap2(fun,self,ls,...),self)
end
--- apply a named method to all elements.
-- Any extra arguments will be passed to the method.
-- @string name name of method
-- @param ... extra arguments
-- @return a new list of the results
-- @see pl.seq.mapmethod
function List:mapm (name,...)
local res = {}
for i = 1,#self do
local val = self[i]
local fn = lookup_fun(val,name)
res[i] = fn(val,...)
end
return makelist(res,self)
end
local function composite_call (method,f)
return function(self,...)
return self[method](self,f,...)
end
end
function List.default_map_with(T)
return function(self,name)
local m
if T then
local f = lookup_fun(T,name)
m = composite_call('map',f)
else
m = composite_call('mapn',name)
end
getmetatable(self)[name] = m -- and cache..
return m
end
end
List.default_map = List.default_map_with
--- 'reduce' a list using a binary function.
-- @func fun a function of two arguments
-- @return result of the function
-- @see pl.tablex.reduce
function List:reduce (fun)
return reduce(fun,self)
end
--- Partition a list using a classifier function.
-- The function may return nil, but this will be converted to the string key '<nil>'.
-- @func fun a function of at least one argument
-- @param ... will also be passed to the function
-- @treturn MultiMap a table where the keys are the returned values, and the values are Lists
-- of values where the function returned that key.
-- @see pl.MultiMap
function List:partition (fun,...)
fun = function_arg(1,fun)
local res = {}
for i = 1,#self do
local val = self[i]
local klass = fun(val,...)
if klass == nil then klass = '<nil>' end
if not res[klass] then res[klass] = List() end
res[klass]:append(val)
end
return setmetatable(res,Multimap)
end
--- return an iterator over all values.
function List:iter ()
return iter(self)
end
--- Create an iterator over a seqence.
-- This captures the Python concept of 'sequence'.
-- For tables, iterates over all values with integer indices.
-- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function
-- @usage for x in iterate {1,10,22,55} do io.write(x,',') end ==> 1,10,22,55
-- @usage for ch in iterate 'help' do do io.write(ch,' ') end ==> h e l p
function List.iterate(seq)
if type(seq) == 'string' then
local idx = 0
local n = #seq
local sub = string.sub
return function ()
idx = idx + 1
if idx > n then return nil
else
return sub(seq,idx,idx)
end
end
elseif type(seq) == 'table' then
local idx = 0
local n = #seq
return function()
idx = idx + 1
if idx > n then return nil
else
return seq[idx]
end
end
elseif type(seq) == 'function' then
return seq
elseif type(seq) == 'userdata' and io.type(seq) == 'file' then
return seq:lines()
end
end
iter = List.iterate
return List

View file

@ -0,0 +1,120 @@
--- A Map class.
--
-- > Map = require 'pl.Map'
-- > m = Map{one=1,two=2}
-- > m:update {three=3,four=4,two=20}
-- > = m == M{one=1,two=20,three=3,four=4}
-- true
--
-- Dependencies: `pl.utils`, `pl.class`, `pl.tablex`, `pl.pretty`
-- @classmod pl.Map
local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local stdmt = utils.stdmt
local deepcompare = tablex.deepcompare
local pretty_write = require 'pl.pretty' . write
local Map = stdmt.Map
local Set = stdmt.Set
local class = require 'pl.class'
-- the Map class ---------------------
class(nil,nil,Map)
function Map:_init (t)
local mt = getmetatable(t)
if mt == Set or mt == Map then
self:update(t)
else
return t -- otherwise assumed to be a map-like table
end
end
local function makelist(t)
return setmetatable(t, require('pl.List'))
end
--- list of keys.
Map.keys = tablex.keys
--- list of values.
Map.values = tablex.values
--- return an iterator over all key-value pairs.
function Map:iter ()
return pairs(self)
end
--- return a List of all key-value pairs, sorted by the keys.
function Map:items()
local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self))
ls:sort(function(t1,t2) return t1[1] < t2[1] end)
return ls
end
--- set a value in the map if it doesn't exist yet.
-- @param key the key
-- @param default value to set
-- @return the value stored in the map (existing value, or the new value)
function Map:setdefault(key, default)
local val = self[key]
if val ~= nil then
return val
end
self:set(key,default)
return default
end
--- size of map.
-- note: this is a relatively expensive operation!
-- @class function
-- @name Map:len
Map.len = tablex.size
--- put a value into the map.
-- This will remove the key if the value is `nil`
-- @param key the key
-- @param val the value
function Map:set (key,val)
self[key] = val
end
--- get a value from the map.
-- @param key the key
-- @return the value, or nil if not found.
function Map:get (key)
return rawget(self,key)
end
local index_by = tablex.index_by
--- get a list of values indexed by a list of keys.
-- @param keys a list-like table of keys
-- @return a new list
function Map:getvalues (keys)
return makelist(index_by(self,keys))
end
--- update the map using key/value pairs from another table.
-- @tab table
-- @function Map:update
Map.update = tablex.update
--- equality between maps.
-- @within metamethods
-- @tparam Map m another map.
function Map:__eq (m)
-- note we explicitly ask deepcompare _not_ to use __eq!
return deepcompare(self,m,true)
end
--- string representation of a map.
-- @within metamethods
function Map:__tostring ()
return pretty_write(self,'')
end
return Map

View file

@ -0,0 +1,54 @@
--- MultiMap, a Map which has multiple values per key.
--
-- Dependencies: `pl.utils`, `pl.class`, `pl.List`, `pl.Map`
-- @classmod pl.MultiMap
local utils = require 'pl.utils'
local class = require 'pl.class'
local List = require 'pl.List'
local Map = require 'pl.Map'
-- MultiMap is a standard MT
local MultiMap = utils.stdmt.MultiMap
class(Map,nil,MultiMap)
MultiMap._name = 'MultiMap'
function MultiMap:_init (t)
if not t then return end
self:update(t)
end
--- update a MultiMap using a table.
-- @param t either a Multimap or a map-like table.
-- @return the map
function MultiMap:update (t)
utils.assert_arg(1,t,'table')
if Map:class_of(t) then
for k,v in pairs(t) do
self[k] = List()
self[k]:append(v)
end
else
for k,v in pairs(t) do
self[k] = List(v)
end
end
end
--- add a new value to a key. Setting a nil value removes the key.
-- @param key the key
-- @param val the value
-- @return the map
function MultiMap:set (key,val)
if val == nil then
self[key] = nil
else
if not self[key] then
self[key] = List()
end
self[key]:append(val)
end
end
return MultiMap

View file

@ -0,0 +1,167 @@
--- OrderedMap, a map which preserves ordering.
--
-- Derived from `pl.Map`.
--
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.List`, `pl.Map`
-- @classmod pl.OrderedMap
local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local List = require 'pl.List'
local index_by,tsort,concat = tablex.index_by,table.sort,table.concat
local class = require 'pl.class'
local Map = require 'pl.Map'
local OrderedMap = class(Map)
OrderedMap._name = 'OrderedMap'
local rawset = rawset
--- construct an OrderedMap.
-- Will throw an error if the argument is bad.
-- @param t optional initialization table, same as for @{OrderedMap:update}
function OrderedMap:_init (t)
rawset(self,'_keys',List())
if t then
local map,err = self:update(t)
if not map then error(err,2) end
end
end
local assert_arg,raise = utils.assert_arg,utils.raise
--- update an OrderedMap using a table.
-- If the table is itself an OrderedMap, then its entries will be appended.
-- if it s a table of the form `{{key1=val1},{key2=val2},...}` these will be appended.
--
-- Otherwise, it is assumed to be a map-like table, and order of extra entries is arbitrary.
-- @tab t a table.
-- @return the map, or nil in case of error
-- @return the error message
function OrderedMap:update (t)
assert_arg(1,t,'table')
if OrderedMap:class_of(t) then
for k,v in t:iter() do
self:set(k,v)
end
elseif #t > 0 then -- an array must contain {key=val} tables
if type(t[1]) == 'table' then
for _,pair in ipairs(t) do
local key,value = next(pair)
if not key then return raise 'empty pair initialization table' end
self:set(key,value)
end
else
return raise 'cannot use an array to initialize an OrderedMap'
end
else
for k,v in pairs(t) do
self:set(k,v)
end
end
return self
end
--- set the key's value. This key will be appended at the end of the map.
--
-- If the value is nil, then the key is removed.
-- @param key the key
-- @param val the value
-- @return the map
function OrderedMap:set (key,val)
if rawget(self, key) == nil and val ~= nil then -- new key
self._keys:append(key) -- we keep in order
rawset(self,key,val) -- don't want to provoke __newindex!
else -- existing key-value pair
if val == nil then
self._keys:remove_value(key)
rawset(self,key,nil)
else
self[key] = val
end
end
return self
end
OrderedMap.__newindex = OrderedMap.set
--- insert a key/value pair before a given position.
-- Note: if the map already contains the key, then this effectively
-- moves the item to the new position by first removing at the old position.
-- Has no effect if the key does not exist and val is nil
-- @int pos a position starting at 1
-- @param key the key
-- @param val the value; if nil use the old value
function OrderedMap:insert (pos,key,val)
local oldval = self[key]
val = val or oldval
if oldval then
self._keys:remove_value(key)
end
if val then
self._keys:insert(pos,key)
rawset(self,key,val)
end
return self
end
--- return the keys in order.
-- (Not a copy!)
-- @return List
function OrderedMap:keys ()
return self._keys
end
--- return the values in order.
-- this is relatively expensive.
-- @return List
function OrderedMap:values ()
return List(index_by(self,self._keys))
end
--- sort the keys.
-- @func cmp a comparison function as for @{table.sort}
-- @return the map
function OrderedMap:sort (cmp)
tsort(self._keys,cmp)
return self
end
--- iterate over key-value pairs in order.
function OrderedMap:iter ()
local i = 0
local keys = self._keys
local idx
return function()
i = i + 1
if i > #keys then return nil end
idx = keys[i]
return idx,self[idx]
end
end
--- iterate over an ordered map (5.2).
-- @within metamethods
-- @function OrderedMap:__pairs
OrderedMap.__pairs = OrderedMap.iter
--- string representation of an ordered map.
-- @within metamethods
function OrderedMap:__tostring ()
local res = {}
for i,v in ipairs(self._keys) do
local val = self[v]
local vs = tostring(val)
if type(val) ~= 'number' then
vs = '"'..vs..'"'
end
res[i] = tostring(v)..'='..vs
end
return '{'..concat(res,',')..'}'
end
return OrderedMap

View file

@ -0,0 +1,222 @@
--- A Set class.
--
-- > Set = require 'pl.Set'
-- > = Set{'one','two'} == Set{'two','one'}
-- true
-- > fruit = Set{'apple','banana','orange'}
-- > = fruit['banana']
-- true
-- > = fruit['hazelnut']
-- nil
-- > colours = Set{'red','orange','green','blue'}
-- > = fruit,colours
-- [apple,orange,banana] [blue,green,orange,red]
-- > = fruit+colours
-- [blue,green,apple,red,orange,banana]
-- [orange]
-- > more_fruits = fruit + 'apricot'
-- > = fruit*colours
-- > = more_fruits, fruit
-- [banana,apricot,apple,orange] [banana,apple,orange]
--
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.Map`, (`pl.List` if __tostring is used)
-- @classmod pl.Set
local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local array_tostring, concat = utils.array_tostring, table.concat
local merge,difference = tablex.merge,tablex.difference
local Map = require 'pl.Map'
local class = require 'pl.class'
local stdmt = utils.stdmt
local Set = stdmt.Set
-- the Set class --------------------
class(Map,nil,Set)
-- note: Set has _no_ methods!
Set.__index = nil
local function makeset (t)
return setmetatable(t,Set)
end
--- create a set. <br>
-- @param t may be a Set, Map or list-like table.
-- @class function
-- @name Set
function Set:_init (t)
t = t or {}
local mt = getmetatable(t)
if mt == Set or mt == Map then
for k in pairs(t) do self[k] = true end
else
for _,v in ipairs(t) do self[v] = true end
end
end
--- string representation of a set.
-- @within metamethods
function Set:__tostring ()
return '['..concat(array_tostring(Set.values(self)),',')..']'
end
--- get a list of the values in a set.
-- @param self a Set
-- @function Set.values
-- @return a list
Set.values = Map.keys
--- map a function over the values of a set.
-- @param self a Set
-- @param fn a function
-- @param ... extra arguments to pass to the function.
-- @return a new set
function Set.map (self,fn,...)
fn = utils.function_arg(1,fn)
local res = {}
for k in pairs(self) do
res[fn(k,...)] = true
end
return makeset(res)
end
--- union of two sets (also +).
-- @param self a Set
-- @param set another set
-- @return a new set
function Set.union (self,set)
return merge(self,set,true)
end
--- modifies '+' operator to allow addition of non-Set elements
--- Preserves +/- semantics - does not modify first argument.
local function setadd(self,other)
local mt = getmetatable(other)
if mt == Set or mt == Map then
return Set.union(self,other)
else
local new = Set(self)
new[other] = true
return new
end
end
--- union of sets.
-- @within metamethods
-- @function Set.__add
Set.__add = setadd
--- intersection of two sets (also *).
-- @param self a Set
-- @param set another set
-- @return a new set
-- @usage
-- > s = Set{10,20,30}
-- > t = Set{20,30,40}
-- > = t
-- [20,30,40]
-- > = Set.intersection(s,t)
-- [30,20]
-- > = s*t
-- [30,20]
function Set.intersection (self,set)
return merge(self,set,false)
end
--- intersection of sets.
-- @within metamethods
-- @function Set.__mul
Set.__mul = Set.intersection
--- new set with elements in the set that are not in the other (also -).
-- @param self a Set
-- @param set another set
-- @return a new set
function Set.difference (self,set)
return difference(self,set,false)
end
--- modifies "-" operator to remove non-Set values from set.
--- Preserves +/- semantics - does not modify first argument.
local function setminus (self,other)
local mt = getmetatable(other)
if mt == Set or mt == Map then
return Set.difference(self,other)
else
local new = Set(self)
new[other] = nil
return new
end
end
--- difference of sets.
-- @within metamethods
-- @function Set.__sub
Set.__sub = setminus
-- a new set with elements in _either_ the set _or_ other but not both (also ^).
-- @param self a Set
-- @param set another set
-- @return a new set
function Set.symmetric_difference (self,set)
return difference(self,set,true)
end
--- symmetric difference of sets.
-- @within metamethods
-- @function Set.__pow
Set.__pow = Set.symmetric_difference
--- is the first set a subset of the second (also <)?.
-- @param self a Set
-- @param set another set
-- @return true or false
function Set.issubset (self,set)
for k in pairs(self) do
if not set[k] then return false end
end
return true
end
--- first set subset of second?
-- @within metamethods
-- @function Set.__lt
Set.__lt = Set.issubset
--- is the set empty?.
-- @param self a Set
-- @return true or false
function Set.isempty (self)
return next(self) == nil
end
--- are the sets disjoint? (no elements in common).
-- Uses naive definition, i.e. that intersection is empty
-- @param s1 a Set
-- @param s2 another set
-- @return true or false
function Set.isdisjoint (s1,s2)
return Set.isempty(Set.intersection(s1,s2))
end
--- size of this set (also # for 5.2).
-- @param s a Set
-- @return size
-- @function Set.len
Set.len = tablex.size
--- cardinality of set (5.2).
-- @within metamethods
-- @function Set.__len
Set.__len = Set.len
--- equality between sets.
-- @within metamethods
function Set.__eq (s1,s2)
return Set.issubset(s1,s2) and Set.issubset(s2,s1)
end
return Set

View file

@ -0,0 +1,309 @@
--- Application support functions.
-- See @{01-introduction.md.Application_Support|the Guide}
--
-- Dependencies: `pl.utils`, `pl.path`
-- @module pl.app
local io,package,require = _G.io, _G.package, _G.require
local utils = require 'pl.utils'
local path = require 'pl.path'
local app = {}
--- return the name of the current script running.
-- The name will be the name as passed on the command line
-- @return string filename
function app.script_name()
if _G.arg and _G.arg[0] then
return _G.arg[0]
end
return utils.raise("No script name found")
end
--- prefixes the current script's path to the Lua module path.
-- Applies to both the source and the binary module paths. It makes it easy for
-- the main file of a multi-file program to access its modules in the same directory.
-- `base` allows these modules to be put in a specified subdirectory, to allow for
-- cleaner deployment and resolve potential conflicts between a script name and its
-- library directory.
--
-- Note: the path is prefixed, so it is searched first when requiring modules.
-- @string base optional base directory (absolute, or relative path).
-- @bool nofollow always use the invocation's directory, even if the invoked file is a symlink
-- @treturn string the current script's path with a trailing slash
function app.require_here (base, nofollow)
local p = app.script_name()
if not path.isabs(p) then
p = path.join(path.currentdir(),p)
end
if not nofollow then
local t = path.link_attrib(p)
if t and t.mode == 'link' then
t = t.target
if not path.isabs(t) then
t = path.join(path.dirname(p), t)
end
p = t
end
end
p = path.normpath(path.dirname(p))
if p:sub(-1,-1) ~= path.sep then
p = p..path.sep
end
if base then
if path.is_windows then
base = base:gsub('/','\\')
end
if path.isabs(base) then
p = base .. path.sep
else
p = p..base..path.sep
end
end
local so_ext = path.is_windows and 'dll' or 'so'
local lsep = package.path:find '^;' and '' or ';'
local csep = package.cpath:find '^;' and '' or ';'
package.path = ('%s?.lua;%s?%sinit.lua%s%s'):format(p,p,path.sep,lsep,package.path)
package.cpath = ('%s?.%s%s%s'):format(p,so_ext,csep,package.cpath)
return p
end
--- return a suitable path for files private to this application.
-- These will look like '~/.SNAME/file', with '~' as with expanduser and
-- SNAME is the name of the script without .lua extension.
-- If the directory does not exist, it will be created.
-- @string file a filename (w/out path)
-- @return a full pathname, or nil
-- @return cannot create directory error
-- @usage
-- -- when run from a script called 'testapp' (on Windows):
-- local app = require 'pl.app'
-- print(app.appfile 'test.txt')
-- -- C:\Documents and Settings\steve\.testapp\test.txt
function app.appfile(file)
local sfullname, err = app.script_name()
if not sfullname then return utils.raise(err) end
local sname = path.basename(sfullname)
local name = path.splitext(sname)
local dir = path.join(path.expanduser('~'),'.'..name)
if not path.isdir(dir) then
local ret = path.mkdir(dir)
if not ret then return utils.raise('cannot create '..dir) end
end
return path.join(dir,file)
end
--- return string indicating operating system.
-- @return 'Windows','OSX' or whatever uname returns (e.g. 'Linux')
function app.platform()
if path.is_windows then
return 'Windows'
else
local f = io.popen('uname')
local res = f:read()
if res == 'Darwin' then res = 'OSX' end
f:close()
return res
end
end
--- return the full command-line used to invoke this script.
-- It will not include the scriptname itself, see `app.script_name`.
-- @return command-line
-- @return name of Lua program used
-- @usage
-- -- execute: lua -lluacov -e 'print(_VERSION)' myscript.lua
--
-- -- myscript.lua
-- print(require("pl.app").lua()) --> "lua -lluacov -e 'print(_VERSION)'", "lua"
function app.lua()
local args = _G.arg
if not args then
return utils.raise "not in a main program"
end
local cmd = {}
local i = -1
while true do
table.insert(cmd, 1, args[i])
if not args[i-1] then
return utils.quote_arg(cmd), args[i]
end
i = i - 1
end
end
--- parse command-line arguments into flags and parameters.
-- Understands GNU-style command-line flags; short (`-f`) and long (`--flag`).
--
-- These may be given a value with either '=' or ':' (`-k:2`,`--alpha=3.2`,`-n2`),
-- a number value can be given without a space. If the flag is marked
-- as having a value, then a space-separated value is also accepted (`-i hello`),
-- see the `flags_with_values` argument).
--
-- Multiple short args can be combined like so: ( `-abcd`).
--
-- When specifying the `flags_valid` parameter, its contents can also contain
-- aliasses, to convert short/long flags to the same output name. See the
-- example below.
--
-- Note: if a flag is repeated, the last value wins.
-- @tparam {string} args an array of strings (default is the global `arg`)
-- @tab flags_with_values any flags that take values, either list or hash
-- table e.g. `{ out=true }` or `{ "out" }`.
-- @tab flags_valid (optional) flags that are valid, either list or hashtable.
-- If not given, everything
-- will be accepted(everything in `flags_with_values` will automatically be allowed)
-- @return a table of flags (flag=value pairs)
-- @return an array of parameters
-- @raise if args is nil, then the global `args` must be available!
-- @usage
-- -- Simple form:
-- local flags, params = app.parse_args(nil,
-- { "hello", "world" }, -- list of flags taking values
-- { "l", "a", "b"}) -- list of allowed flags (value ones will be added)
--
-- -- More complex example using aliasses:
-- local valid = {
-- long = "l", -- if 'l' is specified, it is reported as 'long'
-- new = { "n", "old" }, -- here both 'n' and 'old' will go into 'new'
-- }
-- local values = {
-- "value", -- will automatically be added to the allowed set of flags
-- "new", -- will mark 'n' and 'old' as requiring a value as well
-- }
-- local flags, params = app.parse_args(nil, values, valid)
--
-- -- command: myapp.lua -l --old:hello --value world param1 param2
-- -- will yield:
-- flags = {
-- long = true, -- input from 'l'
-- new = "hello", -- input from 'old'
-- value = "world", -- allowed because it was in 'values', note: space separated!
-- }
-- params = {
-- [1] = "param1"
-- [2] = "param2"
-- }
function app.parse_args (args,flags_with_values, flags_valid)
if not args then
args = _G.arg
if not args then utils.raise "Not in a main program: 'arg' not found" end
end
local with_values = {}
for k,v in pairs(flags_with_values or {}) do
if type(k) == "number" then
k = v
end
with_values[k] = true
end
local valid
if not flags_valid then
-- if no allowed flags provided, we create a table that always returns
-- the keyname, no matter what you look up
valid = setmetatable({},{ __index = function(_, key) return key end })
else
valid = {}
for k,aliasses in pairs(flags_valid) do
if type(k) == "number" then -- array/list entry
k = aliasses
end
if type(aliasses) == "string" then -- single alias
aliasses = { aliasses }
end
if type(aliasses) == "table" then -- list of aliasses
-- it's the alternate name, so add the proper mappings
for i, alias in ipairs(aliasses) do
valid[alias] = k
end
end
valid[k] = k
end
do
local new_with_values = {} -- needed to prevent "invalid key to 'next'" error
for k,v in pairs(with_values) do
if not valid[k] then
valid[k] = k -- add the with_value entry as a valid one
new_with_values[k] = true
else
new_with_values[valid[k]] = true --set, but by its alias
end
end
with_values = new_with_values
end
end
-- now check that all flags with values are reported as such under all
-- of their aliasses
for k, main_alias in pairs(valid) do
if with_values[main_alias] then
with_values[k] = true
end
end
local _args = {}
local flags = {}
local i = 1
while i <= #args do
local a = args[i]
local v = a:match('^-(.+)')
local is_long
if not v then
-- we have a parameter
_args[#_args+1] = a
else
-- it's a flag
if v:find '^-' then
is_long = true
v = v:sub(2)
end
if with_values[v] then
if i == #args or args[i+1]:find '^-' then
return utils.raise ("no value for '"..v.."'")
end
flags[valid[v]] = args[i+1]
i = i + 1
else
-- a value can also be indicated with = or :
local var,val = utils.splitv (v,'[=:]', false, 2)
var = var or v
val = val or true
if not is_long then
if #var > 1 then
if var:find '.%d+' then -- short flag, number value
val = var:sub(2)
var = var:sub(1,1)
else -- multiple short flags
for i = 1,#var do
local f = var:sub(i,i)
if not valid[f] then
return utils.raise("unknown flag '"..f.."'")
else
f = valid[f]
end
flags[f] = true
end
val = nil -- prevents use of var as a flag below
end
else -- single short flag (can have value, defaults to true)
val = val or true
end
end
if val then
if not valid[var] then
return utils.raise("unknown flag '"..var.."'")
else
var = valid[var]
end
flags[var] = val
end
end
end
i = i + 1
end
return flags,_args
end
return app

View file

@ -0,0 +1,585 @@
--- Operations on two-dimensional arrays.
-- See @{02-arrays.md.Operations_on_two_dimensional_tables|The Guide}
--
-- The size of the arrays is determined by using the length operator `#` hence
-- the module is not `nil` safe, and the usual precautions apply.
--
-- Note: all functions taking `i1,j1,i2,j2` as arguments will normalize the
-- arguments using `default_range`.
--
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.types`
-- @module pl.array2d
local tonumber,tostring,io,ipairs,string,table =
_G.tonumber,_G.tostring,_G.io,_G.ipairs,_G.string,_G.table
local setmetatable,getmetatable = setmetatable,getmetatable
local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local types = require 'pl.types'
local imap,tmap,reduce,keys,tmap2,tset,index_by = tablex.imap,tablex.map,tablex.reduce,tablex.keys,tablex.map2,tablex.set,tablex.index_by
local remove = table.remove
local splitv,fprintf,assert_arg = utils.splitv,utils.fprintf,utils.assert_arg
local byte = string.byte
local stdout = io.stdout
local min = math.min
local array2d = {}
local function obj (int,out)
local mt = getmetatable(int)
if mt then
setmetatable(out,mt)
end
return out
end
local function makelist (res)
return setmetatable(res, require('pl.List'))
end
--- return the row and column size.
-- Size is calculated using the Lua length operator #, so usual precautions
-- regarding `nil` values apply.
-- @array2d a a 2d array
-- @treturn int number of rows (`#a`)
-- @treturn int number of cols (`#a[1]`)
function array2d.size (a)
assert_arg(1,a,'table')
return #a,#a[1]
end
do
local function index (t,k)
return t[k]
end
--- extract a column from the 2D array.
-- @array2d a 2d array
-- @param j column index
-- @return 1d array
function array2d.column (a,j)
assert_arg(1,a,'table')
return makelist(imap(index,a,j))
end
end
local column = array2d.column
--- extract a row from the 2D array.
-- Added in line with `column`, for read-only purposes directly
-- accessing a[i] is more performant.
-- @array2d a 2d array
-- @param i row index
-- @return 1d array (copy of the row)
function array2d.row(a,i)
assert_arg(1,a,'table')
local row = a[i]
local r = {}
for n,v in ipairs(row) do
r[n] = v
end
return makelist(r)
end
--- map a function over a 2D array
-- @func f a function of at least one argument
-- @array2d a 2d array
-- @param arg an optional extra argument to be passed to the function.
-- @return 2d array
function array2d.map (f,a,arg)
assert_arg(2,a,'table')
f = utils.function_arg(1,f)
return obj(a,imap(function(row) return imap(f,row,arg) end, a))
end
--- reduce the rows using a function.
-- @func f a binary function
-- @array2d a 2d array
-- @return 1d array
-- @see pl.tablex.reduce
function array2d.reduce_rows (f,a)
assert_arg(1,a,'table')
return tmap(function(row) return reduce(f,row) end, a)
end
--- reduce the columns using a function.
-- @func f a binary function
-- @array2d a 2d array
-- @return 1d array
-- @see pl.tablex.reduce
function array2d.reduce_cols (f,a)
assert_arg(1,a,'table')
return tmap(function(c) return reduce(f,column(a,c)) end, keys(a[1]))
end
--- reduce a 2D array into a scalar, using two operations.
-- @func opc operation to reduce the final result
-- @func opr operation to reduce the rows
-- @param a 2D array
function array2d.reduce2 (opc,opr,a)
assert_arg(3,a,'table')
local tmp = array2d.reduce_rows(opr,a)
return reduce(opc,tmp)
end
--- map a function over two arrays.
-- They can be both or either 2D arrays
-- @func f function of at least two arguments
-- @int ad order of first array (`1` if `a` is a list/array, `2` if it is a 2d array)
-- @int bd order of second array (`1` if `b` is a list/array, `2` if it is a 2d array)
-- @tab a 1d or 2d array
-- @tab b 1d or 2d array
-- @param arg optional extra argument to pass to function
-- @return 2D array, unless both arrays are 1D
function array2d.map2 (f,ad,bd,a,b,arg)
assert_arg(1,a,'table')
assert_arg(2,b,'table')
f = utils.function_arg(1,f)
if ad == 1 and bd == 2 then
return imap(function(row)
return tmap2(f,a,row,arg)
end, b)
elseif ad == 2 and bd == 1 then
return imap(function(row)
return tmap2(f,row,b,arg)
end, a)
elseif ad == 1 and bd == 1 then
return tmap2(f,a,b)
elseif ad == 2 and bd == 2 then
return tmap2(function(rowa,rowb)
return tmap2(f,rowa,rowb,arg)
end, a,b)
end
end
--- cartesian product of two 1d arrays.
-- @func f a function of 2 arguments
-- @array t1 a 1d table
-- @array t2 a 1d table
-- @return 2d table
-- @usage product('..',{1,2},{'a','b'}) == {{'1a','2a'},{'1b','2b'}}
function array2d.product (f,t1,t2)
f = utils.function_arg(1,f)
assert_arg(2,t1,'table')
assert_arg(3,t2,'table')
local res = {}
for i,v in ipairs(t2) do
res[i] = tmap(f,t1,v)
end
return res
end
--- flatten a 2D array.
-- (this goes over columns first.)
-- @array2d t 2d table
-- @return a 1d table
-- @usage flatten {{1,2},{3,4},{5,6}} == {1,2,3,4,5,6}
function array2d.flatten (t)
local res = {}
local k = 1
local rows, cols = array2d.size(t)
for r = 1, rows do
local row = t[r]
for c = 1, cols do
res[k] = row[c]
k = k + 1
end
end
return makelist(res)
end
--- reshape a 2D array. Reshape the aray by specifying a new nr of rows.
-- @array2d t 2d array
-- @int nrows new number of rows
-- @bool co use column-order (Fortran-style) (default false)
-- @return a new 2d array
function array2d.reshape (t,nrows,co)
local nr,nc = array2d.size(t)
local ncols = nr*nc / nrows
local res = {}
local ir,ic = 1,1
for i = 1,nrows do
local row = {}
for j = 1,ncols do
row[j] = t[ir][ic]
if not co then
ic = ic + 1
if ic > nc then
ir = ir + 1
ic = 1
end
else
ir = ir + 1
if ir > nr then
ic = ic + 1
ir = 1
end
end
end
res[i] = row
end
return obj(t,res)
end
--- transpose a 2D array.
-- @array2d t 2d array
-- @return a new 2d array
function array2d.transpose(t)
assert_arg(1,t,'table')
local _, c = array2d.size(t)
return array2d.reshape(t,c,true)
end
--- swap two rows of an array.
-- @array2d t a 2d array
-- @int i1 a row index
-- @int i2 a row index
-- @return t (same, modified 2d array)
function array2d.swap_rows (t,i1,i2)
assert_arg(1,t,'table')
t[i1],t[i2] = t[i2],t[i1]
return t
end
--- swap two columns of an array.
-- @array2d t a 2d array
-- @int j1 a column index
-- @int j2 a column index
-- @return t (same, modified 2d array)
function array2d.swap_cols (t,j1,j2)
assert_arg(1,t,'table')
for _, row in ipairs(t) do
row[j1],row[j2] = row[j2],row[j1]
end
return t
end
--- extract the specified rows.
-- @array2d t 2d array
-- @tparam {int} ridx a table of row indices
-- @return a new 2d array with the extracted rows
function array2d.extract_rows (t,ridx)
return obj(t,index_by(t,ridx))
end
--- extract the specified columns.
-- @array2d t 2d array
-- @tparam {int} cidx a table of column indices
-- @return a new 2d array with the extracted colums
function array2d.extract_cols (t,cidx)
assert_arg(1,t,'table')
local res = {}
for i = 1,#t do
res[i] = index_by(t[i],cidx)
end
return obj(t,res)
end
--- remove a row from an array.
-- @function array2d.remove_row
-- @array2d t a 2d array
-- @int i a row index
array2d.remove_row = remove
--- remove a column from an array.
-- @array2d t a 2d array
-- @int j a column index
function array2d.remove_col (t,j)
assert_arg(1,t,'table')
for i = 1,#t do
remove(t[i],j)
end
end
do
local function _parse (s)
local r, c = s:match 'R(%d+)C(%d+)'
if r then
r,c = tonumber(r),tonumber(c)
return r,c
end
c,r = s:match '(%a+)(%d+)'
if c then
local cv = 0
for i = 1, #c do
cv = cv * 26 + byte(c:sub(i,i)) - byte 'A' + 1
end
return tonumber(r), cv
end
error('bad cell specifier: '..s)
end
--- parse a spreadsheet range or cell.
-- The range/cell can be specified either as 'A1:B2' or 'R1C1:R2C2' or for
-- single cells as 'A1' or 'R1C1'.
-- @string s a range (case insensitive).
-- @treturn int start row
-- @treturn int start col
-- @treturn int end row (or `nil` if the range was a single cell)
-- @treturn int end col (or `nil` if the range was a single cell)
function array2d.parse_range (s)
assert_arg(1,s,'string')
s = s:upper()
if s:find ':' then
local start,finish = splitv(s,':')
local i1,j1 = _parse(start)
local i2,j2 = _parse(finish)
return i1,j1,i2,j2
else -- single value
local i,j = _parse(s)
return i,j
end
end
end
--- get a slice of a 2D array.
-- Same as `slice`.
-- @see slice
function array2d.range (...)
return array2d.slice(...)
end
local default_range do
local function norm_value(v, max)
if not v then return v end
if v < 0 then
v = max + v + 1
end
if v < 1 then v = 1 end
if v > max then v = max end
return v
end
--- normalizes coordinates to valid positive entries and defaults.
-- Negative indices will be counted from the end, too low, or too high
-- will be limited by the array sizes.
-- @array2d t a 2D array
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
-- @tparam[opt=1] int j1 start col
-- @tparam[opt=N] int i2 end row
-- @tparam[opt=M] int j2 end col
-- @see parse_range
-- @return i1, j1, i2, j2
function array2d.default_range (t,i1,j1,i2,j2)
if (type(i1) == 'string') and not (j1 or i2 or j2) then
i1, j1, i2, j2 = array2d.parse_range(i1)
end
local nr, nc = array2d.size(t)
i1 = norm_value(i1 or 1, nr)
j1 = norm_value(j1 or 1, nc)
i2 = norm_value(i2 or nr, nr)
j2 = norm_value(j2 or nc, nc)
return i1,j1,i2,j2
end
default_range = array2d.default_range
end
--- get a slice of a 2D array. Note that if the specified range has
-- a 1D result, the rank of the result will be 1.
-- @array2d t a 2D array
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
-- @tparam[opt=1] int j1 start col
-- @tparam[opt=N] int i2 end row
-- @tparam[opt=M] int j2 end col
-- @see parse_range
-- @return an array, 2D in general but 1D in special cases.
function array2d.slice (t,i1,j1,i2,j2)
assert_arg(1,t,'table')
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
local res = {}
for i = i1,i2 do
local val
local row = t[i]
if j1 == j2 then
val = row[j1]
else
val = {}
for j = j1,j2 do
val[#val+1] = row[j]
end
end
res[#res+1] = val
end
if i1 == i2 then res = res[1] end
return obj(t,res)
end
--- set a specified range of an array to a value.
-- @array2d t a 2D array
-- @param value the value (may be a function, called as `val(i,j)`)
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
-- @tparam[opt=1] int j1 start col
-- @tparam[opt=N] int i2 end row
-- @tparam[opt=M] int j2 end col
-- @see parse_range
-- @see tablex.set
function array2d.set (t,value,i1,j1,i2,j2)
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
local i = i1
if types.is_callable(value) then
local old_f = value
value = function(j)
return old_f(i,j)
end
end
while i <= i2 do
tset(t[i],value,j1,j2)
i = i + 1
end
end
--- write a 2D array to a file.
-- @array2d t a 2D array
-- @param f a file object (default stdout)
-- @string fmt a format string (default is just to use tostring)
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
-- @tparam[opt=1] int j1 start col
-- @tparam[opt=N] int i2 end row
-- @tparam[opt=M] int j2 end col
-- @see parse_range
function array2d.write (t,f,fmt,i1,j1,i2,j2)
assert_arg(1,t,'table')
f = f or stdout
local rowop
if fmt then
rowop = function(row,j) fprintf(f,fmt,row[j]) end
else
rowop = function(row,j) f:write(tostring(row[j]),' ') end
end
local function newline()
f:write '\n'
end
array2d.forall(t,rowop,newline,i1,j1,i2,j2)
end
--- perform an operation for all values in a 2D array.
-- @array2d t 2D array
-- @func row_op function to call on each value; `row_op(row,j)`
-- @func end_row_op function to call at end of each row; `end_row_op(i)`
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
-- @tparam[opt=1] int j1 start col
-- @tparam[opt=N] int i2 end row
-- @tparam[opt=M] int j2 end col
-- @see parse_range
function array2d.forall (t,row_op,end_row_op,i1,j1,i2,j2)
assert_arg(1,t,'table')
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
for i = i1,i2 do
local row = t[i]
for j = j1,j2 do
row_op(row,j)
end
if end_row_op then end_row_op(i) end
end
end
---- move a block from the destination to the source.
-- @array2d dest a 2D array
-- @int di start row in dest
-- @int dj start col in dest
-- @array2d src a 2D array
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
-- @tparam[opt=1] int j1 start col
-- @tparam[opt=N] int i2 end row
-- @tparam[opt=M] int j2 end col
-- @see parse_range
function array2d.move (dest,di,dj,src,i1,j1,i2,j2)
assert_arg(1,dest,'table')
assert_arg(4,src,'table')
i1,j1,i2,j2 = default_range(src,i1,j1,i2,j2)
local nr,nc = array2d.size(dest)
i2, j2 = min(nr,i2), min(nc,j2)
--i1, j1 = max(1,i1), max(1,j1)
dj = dj - 1
for i = i1,i2 do
local drow, srow = dest[i+di-1], src[i]
for j = j1,j2 do
drow[j+dj] = srow[j]
end
end
end
--- iterate over all elements in a 2D array, with optional indices.
-- @array2d a 2D array
-- @bool indices with indices (default false)
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
-- @tparam[opt=1] int j1 start col
-- @tparam[opt=N] int i2 end row
-- @tparam[opt=M] int j2 end col
-- @see parse_range
-- @return either `value` or `i,j,value` depending on the value of `indices`
function array2d.iter(a,indices,i1,j1,i2,j2)
assert_arg(1,a,'table')
i1,j1,i2,j2 = default_range(a,i1,j1,i2,j2)
local i,j = i1,j1-1
local row = a[i]
return function()
j = j + 1
if j > j2 then
j = j1
i = i + 1
row = a[i]
if i > i2 then
return nil
end
end
if indices then
return i,j,row[j]
else
return row[j]
end
end
end
--- iterate over all columns.
-- @array2d a a 2D array
-- @return column, column-index
function array2d.columns(a)
assert_arg(1,a,'table')
local n = #a[1]
local i = 0
return function()
i = i + 1
if i > n then return nil end
return column(a,i), i
end
end
--- iterate over all rows.
-- Returns a copy of the row, for read-only purposes directly iterating
-- is more performant; `ipairs(a)`
-- @array2d a a 2D array
-- @return row, row-index
function array2d.rows(a)
assert_arg(1,a,'table')
local n = #a
local i = 0
return function()
i = i + 1
if i > n then return nil end
return array2d.row(a,i), i
end
end
--- new array of specified dimensions
-- @int rows number of rows
-- @int cols number of cols
-- @param val initial value; if it's a function then use `val(i,j)`
-- @return new 2d array
function array2d.new(rows,cols,val)
local res = {}
local fun = types.is_callable(val)
for i = 1,rows do
local row = {}
if fun then
for j = 1,cols do row[j] = val(i,j) end
else
for j = 1,cols do row[j] = val end
end
res[i] = row
end
return res
end
return array2d

View file

@ -0,0 +1,265 @@
--- Provides a reuseable and convenient framework for creating classes in Lua.
-- Two possible notations:
--
-- B = class(A)
-- class.B(A)
--
-- The latter form creates a named class within the current environment. Note
-- that this implicitly brings in `pl.utils` as a dependency.
--
-- See the Guide for further @{01-introduction.md.Simplifying_Object_Oriented_Programming_in_Lua|discussion}
-- @module pl.class
local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type =
_G.error, _G.getmetatable, _G.io, _G.pairs, _G.rawget, _G.rawset, _G.setmetatable, _G.tostring, _G.type
local compat
-- this trickery is necessary to prevent the inheritance of 'super' and
-- the resulting recursive call problems.
local function call_ctor (c,obj,...)
local init = rawget(c,'_init')
local parent_with_init = rawget(c,'_parent_with_init')
if parent_with_init then
if not init then -- inheriting an init
init = rawget(parent_with_init, '_init')
parent_with_init = rawget(parent_with_init, '_parent_with_init')
end
if parent_with_init then -- super() points to one above whereever _init came from
rawset(obj,'super',function(obj,...)
call_ctor(parent_with_init,obj,...)
end)
end
else
-- Without this, calling super() where none exists will sometimes loop and stack overflow
rawset(obj,'super',nil)
end
local res = init(obj,...)
if parent_with_init then -- If this execution of call_ctor set a super, unset it
rawset(obj,'super',nil)
end
return res
end
--- initializes an __instance__ upon creation.
-- @function class:_init
-- @param ... parameters passed to the constructor
-- @usage local Cat = class()
-- function Cat:_init(name)
-- --self:super(name) -- call the ancestor initializer if needed
-- self.name = name
-- end
--
-- local pussycat = Cat("pussycat")
-- print(pussycat.name) --> pussycat
--- checks whether an __instance__ is derived from some class.
-- Works the other way around as `class_of`. It has two ways of using;
-- 1) call with a class to check against, 2) call without params.
-- @function instance:is_a
-- @param some_class class to check against, or `nil` to return the class
-- @return `true` if `instance` is derived from `some_class`, or if `some_class == nil` then
-- it returns the class table of the instance
-- @usage local pussycat = Lion() -- assuming Lion derives from Cat
-- if pussycat:is_a(Cat) then
-- -- it's true, it is a Lion, but also a Cat
-- end
--
-- if pussycat:is_a() == Lion then
-- -- It's true
-- end
local function is_a(self,klass)
if klass == nil then
-- no class provided, so return the class this instance is derived from
return getmetatable(self)
end
local m = getmetatable(self)
if not m then return false end --*can't be an object!
while m do
if m == klass then return true end
m = rawget(m,'_base')
end
return false
end
--- checks whether an __instance__ is derived from some class.
-- Works the other way around as `is_a`.
-- @function some_class:class_of
-- @param some_instance instance to check against
-- @return `true` if `some_instance` is derived from `some_class`
-- @usage local pussycat = Lion() -- assuming Lion derives from Cat
-- if Cat:class_of(pussycat) then
-- -- it's true
-- end
local function class_of(klass,obj)
if type(klass) ~= 'table' or not rawget(klass,'is_a') then return false end
return klass.is_a(obj,klass)
end
--- cast an object to another class.
-- It is not clever (or safe!) so use carefully.
-- @param some_instance the object to be changed
-- @function some_class:cast
local function cast (klass, obj)
return setmetatable(obj,klass)
end
local function _class_tostring (obj)
local mt = obj._class
local name = rawget(mt,'_name')
setmetatable(obj,nil)
local str = tostring(obj)
setmetatable(obj,mt)
if name then str = name ..str:gsub('table','') end
return str
end
local function tupdate(td,ts,dont_override)
for k,v in pairs(ts) do
if not dont_override or td[k] == nil then
td[k] = v
end
end
end
local function _class(base,c_arg,c)
-- the class `c` will be the metatable for all its objects,
-- and they will look up their methods in it.
local mt = {} -- a metatable for the class to support __call and _handler
-- can define class by passing it a plain table of methods
local plain = type(base) == 'table' and not getmetatable(base)
if plain then
c = base
base = c._base
else
c = c or {}
end
if type(base) == 'table' then
-- our new class is a shallow copy of the base class!
-- but be careful not to wipe out any methods we have been given at this point!
tupdate(c,base,plain)
c._base = base
-- inherit the 'not found' handler, if present
if rawget(c,'_handler') then mt.__index = c._handler end
elseif base ~= nil then
error("must derive from a table type",3)
end
c.__index = c
setmetatable(c,mt)
if not plain then
if base and rawget(base,'_init') then c._parent_with_init = base end -- For super and inherited init
c._init = nil
end
if base and rawget(base,'_class_init') then
base._class_init(c,c_arg)
end
-- expose a ctor which can be called by <classname>(<args>)
mt.__call = function(class_tbl,...)
local obj
if rawget(c,'_create') then obj = c._create(...) end
if not obj then obj = {} end
setmetatable(obj,c)
if rawget(c,'_init') or rawget(c,'_parent_with_init') then -- constructor exists
local res = call_ctor(c,obj,...)
if res then -- _if_ a ctor returns a value, it becomes the object...
obj = res
setmetatable(obj,c)
end
end
if base and rawget(base,'_post_init') then
base._post_init(obj)
end
return obj
end
-- Call Class.catch to set a handler for methods/properties not found in the class!
c.catch = function(self, handler)
if type(self) == "function" then
-- called using . instead of :
handler = self
end
c._handler = handler
mt.__index = handler
end
c.is_a = is_a
c.class_of = class_of
c.cast = cast
c._class = c
if not rawget(c,'__tostring') then
c.__tostring = _class_tostring
end
return c
end
--- create a new class, derived from a given base class.
-- Supporting two class creation syntaxes:
-- either `Name = class(base)` or `class.Name(base)`.
-- The first form returns the class directly and does not set its `_name`.
-- The second form creates a variable `Name` in the current environment set
-- to the class, and also sets `_name`.
-- @function class
-- @param base optional base class
-- @param c_arg optional parameter to class constructor
-- @param c optional table to be used as class
local class
class = setmetatable({},{
__call = function(fun,...)
return _class(...)
end,
__index = function(tbl,key)
if key == 'class' then
io.stderr:write('require("pl.class").class is deprecated. Use require("pl.class")\n')
return class
end
compat = compat or require 'pl.compat'
local env = compat.getfenv(2)
return function(...)
local c = _class(...)
c._name = key
rawset(env,key,c)
return c
end
end
})
class.properties = class()
function class.properties._class_init(klass)
klass.__index = function(t,key)
-- normal class lookup!
local v = klass[key]
if v then return v end
-- is it a getter?
v = rawget(klass,'get_'..key)
if v then
return v(t)
end
-- is it a field?
return rawget(t,'_'..key)
end
klass.__newindex = function (t,key,value)
-- if there's a setter, use that, otherwise directly set table
local p = 'set_'..key
local setter = klass[p]
if setter then
setter(t,value)
else
rawset(t,key,value)
end
end
end
return class

View file

@ -0,0 +1,252 @@
----------------
--- Lua 5.1/5.2/5.3 compatibility.
-- Injects `table.pack`, `table.unpack`, and `package.searchpath` in the global
-- environment, to make sure they are available for Lua 5.1 and LuaJIT.
--
-- All other functions are exported as usual in the returned module table.
--
-- NOTE: everything in this module is also available in `pl.utils`.
-- @module pl.compat
local compat = {}
--- boolean flag this is Lua 5.1 (or LuaJIT).
-- @field lua51
compat.lua51 = _VERSION == 'Lua 5.1'
--- boolean flag this is LuaJIT.
-- @field jit
compat.jit = (tostring(assert):match('builtin') ~= nil)
--- boolean flag this is LuaJIT with 5.2 compatibility compiled in.
-- @field jit52
if compat.jit then
-- 'goto' is a keyword when 52 compatibility is enabled in LuaJit
compat.jit52 = not loadstring("local goto = 1")
end
--- the directory separator character for the current platform.
-- @field dir_separator
compat.dir_separator = _G.package.config:sub(1,1)
--- boolean flag this is a Windows platform.
-- @field is_windows
compat.is_windows = compat.dir_separator == '\\'
--- execute a shell command, in a compatible and platform independent way.
-- This is a compatibility function that returns the same for Lua 5.1 and
-- Lua 5.2+.
--
-- NOTE: Windows systems can use signed 32bit integer exitcodes. Posix systems
-- only use exitcodes 0-255, anything else is undefined.
--
-- NOTE2: In Lua 5.2 and 5.3 a Windows exitcode of -1 would not properly be
-- returned, this function will return it properly for all versions.
-- @param cmd a shell command
-- @return true if successful
-- @return actual return code
function compat.execute(cmd)
local res1,res2,res3 = os.execute(cmd)
if res2 == "No error" and res3 == 0 and compat.is_windows then
-- os.execute bug in Lua 5.2/5.3 not reporting -1 properly on Windows
-- this was fixed in 5.4
res3 = -1
end
if compat.lua51 and not compat.jit52 then
if compat.is_windows then
return res1==0,res1
else
res1 = res1 > 255 and res1 / 256 or res1
return res1==0,res1
end
else
if compat.is_windows then
return res3==0,res3
else
return not not res1,res3
end
end
end
----------------
-- Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way).
-- @param ld code string or loader
-- @param[opt] source name of chunk for errors
-- @param[opt] mode 'b', 't' or 'bt'
-- @param[opt] env environment to load the chunk in
-- @function compat.load
---------------
-- Get environment of a function (in a Lua 5.1 compatible way).
-- Not 100% compatible, so with Lua 5.2 it may return nil for a function with no
-- global references!
-- Based on code by [Sergey Rozhenko](http://lua-users.org/lists/lua-l/2010-06/msg00313.html)
-- @param f a function or a call stack reference
-- @function compat.getfenv
---------------
-- Set environment of a function (in a Lua 5.1 compatible way).
-- @param f a function or a call stack reference
-- @param env a table that becomes the new environment of `f`
-- @function compat.setfenv
if compat.lua51 then -- define Lua 5.2 style load()
if not compat.jit then -- but LuaJIT's load _is_ compatible
local lua51_load = load
function compat.load(str,src,mode,env)
local chunk,err
if type(str) == 'string' then
if str:byte(1) == 27 and not (mode or 'bt'):find 'b' then
return nil,"attempt to load a binary chunk"
end
chunk,err = loadstring(str,src)
else
chunk,err = lua51_load(str,src)
end
if chunk and env then setfenv(chunk,env) end
return chunk,err
end
else
compat.load = load
end
compat.setfenv, compat.getfenv = setfenv, getfenv
else
compat.load = load
-- setfenv/getfenv replacements for Lua 5.2
-- by Sergey Rozhenko
-- http://lua-users.org/lists/lua-l/2010-06/msg00313.html
-- Roberto Ierusalimschy notes that it is possible for getfenv to return nil
-- in the case of a function with no globals:
-- http://lua-users.org/lists/lua-l/2010-06/msg00315.html
function compat.setfenv(f, t)
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
local name
local up = 0
repeat
up = up + 1
name = debug.getupvalue(f, up)
until name == '_ENV' or name == nil
if name then
debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue
debug.setupvalue(f, up, t)
end
if f ~= 0 then return f end
end
function compat.getfenv(f)
local f = f or 0
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
local name, val
local up = 0
repeat
up = up + 1
name, val = debug.getupvalue(f, up)
until name == '_ENV' or name == nil
return val
end
end
--- Global exported functions (for Lua 5.1 & LuaJIT)
-- @section lua52
--- pack an argument list into a table.
-- @param ... any arguments
-- @return a table with field n set to the length
-- @function table.pack
if not table.pack then
function table.pack (...) -- luacheck: ignore
return {n=select('#',...); ...}
end
end
--- unpack a table and return the elements.
--
-- NOTE: this version does NOT honor the n field, and hence it is not nil-safe.
-- See `utils.unpack` for a version that is nil-safe.
-- @param t table to unpack
-- @param[opt] i index from which to start unpacking, defaults to 1
-- @param[opt] j index of the last element to unpack, defaults to #t
-- @return multiple return values from the table
-- @function table.unpack
-- @see utils.unpack
if not table.unpack then
table.unpack = unpack -- luacheck: ignore
end
--- return the full path where a file name would be matched.
-- This function was introduced in Lua 5.2, so this compatibility version
-- will be injected in Lua 5.1 engines.
-- @string name file name, possibly dotted
-- @string path a path-template in the same form as package.path or package.cpath
-- @string[opt] sep template separate character to be replaced by path separator. Default: "."
-- @string[opt] rep the path separator to use, defaults to system separator. Default; "/" on Unixes, "\" on Windows.
-- @see path.package_path
-- @function package.searchpath
-- @return on success: path of the file
-- @return on failure: nil, error string listing paths tried
if not package.searchpath then
function package.searchpath (name,path,sep,rep) -- luacheck: ignore
if type(name) ~= "string" then
error(("bad argument #1 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
end
if type(path) ~= "string" then
error(("bad argument #2 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
end
if sep ~= nil and type(sep) ~= "string" then
error(("bad argument #3 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
end
if rep ~= nil and type(rep) ~= "string" then
error(("bad argument #4 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
end
sep = sep or "."
rep = rep or compat.dir_separator
do
local s, e = name:find(sep, nil, true)
while s do
name = name:sub(1, s-1) .. rep .. name:sub(e+1, -1)
s, e = name:find(sep, s + #rep + 1, true)
end
end
local tried = {}
for m in path:gmatch('[^;]+') do
local nm = m:gsub('?', name)
tried[#tried+1] = nm
local f = io.open(nm,'r')
if f then f:close(); return nm end
end
return nil, "\tno file '" .. table.concat(tried, "'\n\tno file '") .. "'"
end
end
--- Global exported functions (for Lua < 5.4)
-- @section lua54
--- raise a warning message.
-- This functions mimics the `warn` function added in Lua 5.4.
-- @function warn
-- @param ... any arguments
if not rawget(_G, "warn") then
local enabled = false
local function warn(arg1, ...)
if type(arg1) == "string" and arg1:sub(1, 1) == "@" then
-- control message
if arg1 == "@on" then
enabled = true
return
end
if arg1 == "@off" then
enabled = false
return
end
return -- ignore unknown control messages
end
if enabled then
io.stderr:write("Lua warning: ", arg1, ...)
io.stderr:write("\n")
end
end
-- use rawset to bypass OpenResty's protection of global scope
rawset(_G, "warn", warn)
end
return compat

View file

@ -0,0 +1,285 @@
--- List comprehensions implemented in Lua.
--
-- See the [wiki page](http://lua-users.org/wiki/ListComprehensions)
--
-- local C= require 'pl.comprehension' . new()
--
-- C ('x for x=1,10') ()
-- ==> {1,2,3,4,5,6,7,8,9,10}
-- C 'x^2 for x=1,4' ()
-- ==> {1,4,9,16}
-- C '{x,x^2} for x=1,4' ()
-- ==> {{1,1},{2,4},{3,9},{4,16}}
-- C '2*x for x' {1,2,3}
-- ==> {2,4,6}
-- dbl = C '2*x for x'
-- dbl {10,20,30}
-- ==> {20,40,60}
-- C 'x for x if x % 2 == 0' {1,2,3,4,5}
-- ==> {2,4}
-- C '{x,y} for x = 1,2 for y = 1,2' ()
-- ==> {{1,1},{1,2},{2,1},{2,2}}
-- C '{x,y} for x for y' ({1,2},{10,20})
-- ==> {{1,10},{1,20},{2,10},{2,20}}
-- assert(C 'sum(x^2 for x)' {2,3,4} == 2^2+3^2+4^2)
--
-- (c) 2008 David Manura. Licensed under the same terms as Lua (MIT license).
--
-- Dependencies: `pl.utils`, `pl.luabalanced`
--
-- See @{07-functional.md.List_Comprehensions|the Guide}
-- @module pl.comprehension
local utils = require 'pl.utils'
local status,lb = pcall(require, "pl.luabalanced")
if not status then
lb = require 'luabalanced'
end
local math_max = math.max
local table_concat = table.concat
-- fold operations
-- http://en.wikipedia.org/wiki/Fold_(higher-order_function)
local ops = {
list = {init=' {} ', accum=' __result[#__result+1] = (%s) '},
table = {init=' {} ', accum=' local __k, __v = %s __result[__k] = __v '},
sum = {init=' 0 ', accum=' __result = __result + (%s) '},
min = {init=' nil ', accum=' local __tmp = %s ' ..
' if __result then if __tmp < __result then ' ..
'__result = __tmp end else __result = __tmp end '},
max = {init=' nil ', accum=' local __tmp = %s ' ..
' if __result then if __tmp > __result then ' ..
'__result = __tmp end else __result = __tmp end '},
}
-- Parses comprehension string expr.
-- Returns output expression list <out> string, array of for types
-- ('=', 'in' or nil) <fortypes>, array of input variable name
-- strings <invarlists>, array of input variable value strings
-- <invallists>, array of predicate expression strings <preds>,
-- operation name string <opname>, and number of placeholder
-- parameters <max_param>.
--
-- The is equivalent to the mathematical set-builder notation:
--
-- <opname> { <out> | <invarlist> in <invallist> , <preds> }
--
-- @usage "x^2 for x" -- array values
-- @usage "x^2 for x=1,10,2" -- numeric for
-- @usage "k^v for k,v in pairs(_1)" -- iterator for
-- @usage "(x+y)^2 for x for y if x > y" -- nested
--
local function parse_comprehension(expr)
local pos = 1
-- extract opname (if exists)
local opname
local tok, post = expr:match('^%s*([%a_][%w_]*)%s*%(()', pos)
local pose = #expr + 1
if tok then
local tok2, posb = lb.match_bracketed(expr, post-1)
assert(tok2, 'syntax error')
if expr:match('^%s*$', posb) then
opname = tok
pose = posb - 1
pos = post
end
end
opname = opname or "list"
-- extract out expression list
local out; out, pos = lb.match_explist(expr, pos)
assert(out, "syntax error: missing expression list")
out = table_concat(out, ', ')
-- extract "for" clauses
local fortypes = {}
local invarlists = {}
local invallists = {}
while 1 do
local post = expr:match('^%s*for%s+()', pos)
if not post then break end
pos = post
-- extract input vars
local iv; iv, pos = lb.match_namelist(expr, pos)
assert(#iv > 0, 'syntax error: zero variables')
for _,ident in ipairs(iv) do
assert(not ident:match'^__',
"identifier " .. ident .. " may not contain __ prefix")
end
invarlists[#invarlists+1] = iv
-- extract '=' or 'in' (optional)
local fortype, post = expr:match('^(=)%s*()', pos)
if not fortype then fortype, post = expr:match('^(in)%s+()', pos) end
if fortype then
pos = post
-- extract input value range
local il; il, pos = lb.match_explist(expr, pos)
assert(#il > 0, 'syntax error: zero expressions')
assert(fortype ~= '=' or #il == 2 or #il == 3,
'syntax error: numeric for requires 2 or three expressions')
fortypes[#invarlists] = fortype
invallists[#invarlists] = il
else
fortypes[#invarlists] = false
invallists[#invarlists] = false
end
end
assert(#invarlists > 0, 'syntax error: missing "for" clause')
-- extract "if" clauses
local preds = {}
while 1 do
local post = expr:match('^%s*if%s+()', pos)
if not post then break end
pos = post
local pred; pred, pos = lb.match_expression(expr, pos)
assert(pred, 'syntax error: predicated expression not found')
preds[#preds+1] = pred
end
-- extract number of parameter variables (name matching "_%d+")
local stmp = ''; lb.gsub(expr, function(u, sin) -- strip comments/strings
if u == 'e' then stmp = stmp .. ' ' .. sin .. ' ' end
end)
local max_param = 0; stmp:gsub('[%a_][%w_]*', function(s)
local s = s:match('^_(%d+)$')
if s then max_param = math_max(max_param, tonumber(s)) end
end)
if pos ~= pose then
assert(false, "syntax error: unrecognized " .. expr:sub(pos))
end
--DEBUG:
--print('----\n', string.format("%q", expr), string.format("%q", out), opname)
--for k,v in ipairs(invarlists) do print(k,v, invallists[k]) end
--for k,v in ipairs(preds) do print(k,v) end
return out, fortypes, invarlists, invallists, preds, opname, max_param
end
-- Create Lua code string representing comprehension.
-- Arguments are in the form returned by parse_comprehension.
local function code_comprehension(
out, fortypes, invarlists, invallists, preds, opname, max_param
)
local op = assert(ops[opname])
local code = op.accum:gsub('%%s', out)
for i=#preds,1,-1 do local pred = preds[i]
code = ' if ' .. pred .. ' then ' .. code .. ' end '
end
for i=#invarlists,1,-1 do
if not fortypes[i] then
local arrayname = '__in' .. i
local idx = '__idx' .. i
code =
' for ' .. idx .. ' = 1, #' .. arrayname .. ' do ' ..
' local ' .. invarlists[i][1] .. ' = ' .. arrayname .. '['..idx..'] ' ..
code .. ' end '
else
code =
' for ' ..
table_concat(invarlists[i], ', ') ..
' ' .. fortypes[i] .. ' ' ..
table_concat(invallists[i], ', ') ..
' do ' .. code .. ' end '
end
end
code = ' local __result = ( ' .. op.init .. ' ) ' .. code
return code
end
-- Convert code string represented by code_comprehension
-- into Lua function. Also must pass ninputs = #invarlists,
-- max_param, and invallists (from parse_comprehension).
-- Uses environment env.
local function wrap_comprehension(code, ninputs, max_param, invallists, env)
assert(ninputs > 0)
local ts = {}
for i=1,max_param do
ts[#ts+1] = '_' .. i
end
for i=1,ninputs do
if not invallists[i] then
local name = '__in' .. i
ts[#ts+1] = name
end
end
if #ts > 0 then
code = ' local ' .. table_concat(ts, ', ') .. ' = ... ' .. code
end
code = code .. ' return __result '
--print('DEBUG:', code)
local f, err = utils.load(code,'tmp','t',env)
if not f then assert(false, err .. ' with generated code ' .. code) end
return f
end
-- Build Lua function from comprehension string.
-- Uses environment env.
local function build_comprehension(expr, env)
local out, fortypes, invarlists, invallists, preds, opname, max_param
= parse_comprehension(expr)
local code = code_comprehension(
out, fortypes, invarlists, invallists, preds, opname, max_param)
local f = wrap_comprehension(code, #invarlists, max_param, invallists, env)
return f
end
-- Creates new comprehension cache.
-- Any list comprehension function created are set to the environment
-- env (defaults to caller of new).
local function new(env)
-- Note: using a single global comprehension cache would have had
-- security implications (e.g. retrieving cached functions created
-- in other environments).
-- The cache lookup function could have instead been written to retrieve
-- the caller's environment, lookup up the cache private to that
-- environment, and then looked up the function in that cache.
-- That would avoid the need for this <new> call to
-- explicitly manage caches; however, that might also have an undue
-- performance penalty.
if not env then
env = utils.getfenv(2)
end
local mt = {}
local cache = setmetatable({}, mt)
-- Index operator builds, caches, and returns Lua function
-- corresponding to comprehension expression string.
--
-- Example: f = comprehension['x^2 for x']
--
function mt:__index(expr)
local f = build_comprehension(expr, env)
self[expr] = f -- cache
return f
end
-- Convenience syntax.
-- Allows comprehension 'x^2 for x' instead of comprehension['x^2 for x'].
mt.__call = mt.__index
cache.new = new
return cache
end
local comprehension = {}
comprehension.new = new
return comprehension

View file

@ -0,0 +1,207 @@
--- Reads configuration files into a Lua table.
-- Understands INI files, classic Unix config files, and simple
-- delimited columns of values. See @{06-data.md.Reading_Configuration_Files|the Guide}
--
-- # test.config
-- # Read timeout in seconds
-- read.timeout=10
-- # Write timeout in seconds
-- write.timeout=5
-- #acceptable ports
-- ports = 1002,1003,1004
--
-- -- readconfig.lua
-- local config = require 'config'
-- local t = config.read 'test.config'
-- print(pretty.write(t))
--
-- ### output #####
-- {
-- ports = {
-- 1002,
-- 1003,
-- 1004
-- },
-- write_timeout = 5,
-- read_timeout = 10
-- }
--
-- @module pl.config
local type,tonumber,ipairs,io, table = _G.type,_G.tonumber,_G.ipairs,_G.io,_G.table
local function split(s,re)
local res = {}
local t_insert = table.insert
re = '[^'..re..']+'
for k in s:gmatch(re) do t_insert(res,k) end
return res
end
local function strip(s)
return s:gsub('^%s+',''):gsub('%s+$','')
end
local function strip_quotes (s)
return s:gsub("['\"](.*)['\"]",'%1')
end
local config = {}
--- like io.lines(), but allows for lines to be continued with '\'.
-- @param file a file-like object (anything where read() returns the next line) or a filename.
-- Defaults to stardard input.
-- @return an iterator over the lines, or nil
-- @return error 'not a file-like object' or 'file is nil'
function config.lines(file)
local f,openf,err
local line = ''
if type(file) == 'string' then
f,err = io.open(file,'r')
if not f then return nil,err end
openf = true
else
f = file or io.stdin
if not file.read then return nil, 'not a file-like object' end
end
if not f then return nil, 'file is nil' end
return function()
local l = f:read()
while l do
-- only for non-blank lines that don't begin with either ';' or '#'
if l:match '%S' and not l:match '^%s*[;#]' then
-- does the line end with '\'?
local i = l:find '\\%s*$'
if i then -- if so,
line = line..l:sub(1,i-1)
elseif line == '' then
return l
else
l = line..l
line = ''
return l
end
end
l = f:read()
end
if openf then f:close() end
end
end
--- read a configuration file into a table
-- @param file either a file-like object or a string, which must be a filename
-- @tab[opt] cnfg a configuration table that may contain these fields:
--
-- * `smart` try to deduce what kind of config file we have (default false)
-- * `variabilize` make names into valid Lua identifiers (default true)
-- * `convert_numbers` try to convert values into numbers (default true)
-- * `trim_space` ensure that there is no starting or trailing whitespace with values (default true)
-- * `trim_quotes` remove quotes from strings (default false)
-- * `list_delim` delimiter to use when separating columns (default ',')
-- * `keysep` separator between key and value pairs (default '=')
--
-- @return a table containing items, or `nil`
-- @return error message (same as @{config.lines}
function config.read(file,cnfg)
local auto
local iter,err = config.lines(file)
if not iter then return nil,err end
local line = iter()
cnfg = cnfg or {}
if cnfg.smart then
auto = true
if line:match '^[^=]+=' then
cnfg.keysep = '='
elseif line:match '^[^:]+:' then
cnfg.keysep = ':'
cnfg.list_delim = ':'
elseif line:match '^%S+%s+' then
cnfg.keysep = ' '
-- more than two columns assume that it's a space-delimited list
-- cf /etc/fstab with /etc/ssh/ssh_config
if line:match '^%S+%s+%S+%s+%S+' then
cnfg.list_delim = ' '
end
cnfg.variabilize = false
end
end
local function check_cnfg (var,def)
local val = cnfg[var]
if val == nil then return def else return val end
end
local initial_digits = '^[%d%+%-]'
local t = {}
local top_t = t
local variabilize = check_cnfg ('variabilize',true)
local list_delim = check_cnfg('list_delim',',')
local convert_numbers = check_cnfg('convert_numbers',true)
local convert_boolean = check_cnfg('convert_boolean',false)
local trim_space = check_cnfg('trim_space',true)
local trim_quotes = check_cnfg('trim_quotes',false)
local ignore_assign = check_cnfg('ignore_assign',false)
local keysep = check_cnfg('keysep','=')
local keypat = keysep == ' ' and '%s+' or '%s*'..keysep..'%s*'
if list_delim == ' ' then list_delim = '%s+' end
local function process_name(key)
if variabilize then
key = key:gsub('[^%w]','_')
end
return key
end
local function process_value(value)
if list_delim and value:find(list_delim) then
value = split(value,list_delim)
for i,v in ipairs(value) do
value[i] = process_value(v)
end
elseif convert_numbers and value:find(initial_digits) then
local val = tonumber(value)
if not val and value:match ' kB$' then
value = value:gsub(' kB','')
val = tonumber(value)
end
if val then value = val end
elseif convert_boolean and value == 'true' then
return true
elseif convert_boolean and value == 'false' then
return false
end
if type(value) == 'string' then
if trim_space then value = strip(value) end
if not trim_quotes and auto and value:match '^"' then
trim_quotes = true
end
if trim_quotes then value = strip_quotes(value) end
end
return value
end
while line do
if line:find('^%[') then -- section!
local section = process_name(line:match('%[([^%]]+)%]'))
t = top_t
t[section] = {}
t = t[section]
else
line = line:gsub('^%s*','')
local i1,i2 = line:find(keypat)
if i1 and not ignore_assign then -- key,value assignment
local key = process_name(line:sub(1,i1-1))
local value = process_value(line:sub(i2+1))
t[key] = value
else -- a plain list of values...
t[#t+1] = process_value(line)
end
end
line = iter()
end
return top_t
end
return config

View file

@ -0,0 +1,654 @@
--- Reading and querying simple tabular data.
--
-- data.read 'test.txt'
-- ==> {{10,20},{2,5},{40,50},fieldnames={'x','y'},delim=','}
--
-- Provides a way of creating basic SQL-like queries.
--
-- require 'pl'
-- local d = data.read('xyz.txt')
-- local q = d:select('x,y,z where x > 3 and z < 2 sort by y')
-- for x,y,z in q do
-- print(x,y,z)
-- end
--
-- See @{06-data.md.Reading_Columnar_Data|the Guide}
--
-- Dependencies: `pl.utils`, `pl.array2d` (fallback methods)
-- @module pl.data
local utils = require 'pl.utils'
local _DEBUG = rawget(_G,'_DEBUG')
local patterns,function_arg,usplit,array_tostring = utils.patterns,utils.function_arg,utils.split,utils.array_tostring
local append,concat = table.insert,table.concat
local gsub = string.gsub
local io = io
local _G,print,type,tonumber,ipairs,setmetatable = _G,print,type,tonumber,ipairs,setmetatable
local data = {}
local parse_select
local function rstrip(s)
return (s:gsub('%s+$',''))
end
local function strip (s)
return (rstrip(s):gsub('^%s*',''))
end
-- This gives `l` the standard List metatable,
-- pulling in the List module.
local function makelist(l)
return setmetatable(l, require('pl.List'))
end
local function map(fun,t)
local res = {}
for i = 1,#t do
res[i] = fun(t[i])
end
return res
end
local function split(line,delim,csv,n)
local massage
-- CSV fields may be double-quoted and may contain commas!
if csv and line:match '"' then
line = line:gsub('"([^"]+)"',function(str)
local s,cnt = str:gsub(',','\001')
if cnt > 0 then massage = true end
return s
end)
if massage then
massage = function(s) return (s:gsub('\001',',')) end
end
end
local res = (usplit(line,delim,false,n))
if csv then
-- restore CSV commas-in-fields
if massage then res = map(massage,res) end
-- in CSV mode trailiing commas are significant!
if line:match ',$' then append(res,'') end
end
return makelist(res)
end
local function find(t,v)
for i = 1,#t do
if v == t[i] then return i end
end
end
local DataMT = {
column_by_name = function(self,name)
if type(name) == 'number' then
name = '$'..name
end
local arr = {}
for res in data.query(self,name) do
append(arr,res)
end
return makelist(arr)
end,
copy_select = function(self,condn)
condn = parse_select(condn,self)
local iter = data.query(self,condn)
local res = {}
local row = makelist{iter()}
while #row > 0 do
append(res,row)
row = makelist{iter()}
end
res.delim = self.delim
return data.new(res,split(condn.fields,','))
end,
column_names = function(self)
return self.fieldnames
end,
}
local array2d
DataMT.__index = function(self,name)
local f = DataMT[name]
if f then return f end
if not array2d then
array2d = require 'pl.array2d'
end
return array2d[name]
end
--- return a particular column as a list of values (method).
-- @param name either name of column, or numerical index.
-- @function Data.column_by_name
--- return a query iterator on this data (method).
-- @string condn the query expression
-- @function Data.select
-- @see data.query
--- return a row iterator on this data (method).
-- @string condn the query expression
-- @function Data.select_row
--- return a new data object based on this query (method).
-- @string condn the query expression
-- @function Data.copy_select
--- return the field names of this data object (method).
-- @function Data.column_names
--- write out a row (method).
-- @param f file-like object
-- @function Data.write_row
--- write data out to file (method).
-- @param f file-like object
-- @function Data.write
-- [guessing delimiter] We check for comma, tab and spaces in that order.
-- [issue] any other delimiters to be checked?
local delims = {',', '\t', ' ', ';'}
local function guess_delim (line)
if line=='' then return ' ' end
for _,delim in ipairs(delims) do
if line:find(delim) then
return delim == ' ' and '%s+' or delim
end
end
return ' '
end
-- [file parameter] If it's a string, we try open as a filename. If nil, then
-- either stdin or stdout depending on the mode. Otherwise, check if this is
-- a file-like object (implements read or write depending)
local function open_file (f,mode)
local opened, err
local reading = mode == 'r'
if type(f) == 'string' then
if f == 'stdin' then
f = io.stdin
elseif f == 'stdout' then
f = io.stdout
else
f,err = io.open(f,mode)
if not f then return nil,err end
opened = true
end
end
if f and ((reading and not f.read) or (not reading and not f.write)) then
return nil, "not a file-like object"
end
return f,nil,opened
end
--- read a delimited file in a Lua table.
-- By default, attempts to treat first line as separated list of fieldnames.
-- @param file a filename or a file-like object
-- @tab cnfg parsing options
-- @string cnfg.delim a string pattern to split fields
-- @array cnfg.fieldnames (i.e. don't read from first line)
-- @bool cnfg.no_convert (default is to try conversion on first data line)
-- @tab cnfg.convert table of custom conversion functions with column keys
-- @int cnfg.numfields indices of columns known to be numbers
-- @bool cnfg.last_field_collect only split as many fields as fieldnames.
-- @int cnfg.thousands_dot thousands separator in Excel CSV is '.'
-- @bool cnfg.csv fields may be double-quoted and contain commas;
-- Also, empty fields are considered to be equivalent to zero.
-- @return `data` object, or `nil`
-- @return error message. May be a file error, 'not a file-like object'
-- or a conversion error
function data.read(file,cnfg)
local count,line
local D = {}
if not cnfg then cnfg = {} end
local f,err,opened = open_file(file,'r')
if not f then return nil, err end
local thousands_dot = cnfg.thousands_dot
local csv = cnfg.csv
if csv then cnfg.delim = ',' end
-- note that using dot as the thousands separator (@thousands_dot)
-- requires a special conversion function! For CSV, _empty fields_ are
-- considered to default to numerial zeroes.
local tonumber = tonumber
local function try_number(x)
if thousands_dot then x = x:gsub('%.(...)','%1') end
if csv and x == '' then x = '0' end
local v = tonumber(x)
if v == nil then return nil,"not a number" end
return v
end
count = 1
line = f:read()
if not line then return nil, "empty file" end
-- first question: what is the delimiter?
D.delim = cnfg.delim and cnfg.delim or guess_delim(line)
local delim = D.delim
local conversion
local numfields = {}
local function append_conversion (idx,conv)
conversion = conversion or {}
append(numfields,idx)
append(conversion,conv)
end
if cnfg.numfields then
for _,n in ipairs(cnfg.numfields) do append_conversion(n,try_number) end
end
-- some space-delimited data starts with a space. This should not be a column,
-- although it certainly would be for comma-separated, etc.
local stripper
if delim == '%s+' and line:find(delim) == 1 then
stripper = function(s) return s:gsub('^%s+','') end
line = stripper(line)
end
-- first line will usually be field names. Unless fieldnames are specified,
-- we check if it contains purely numerical values for the case of reading
-- plain data files.
if not cnfg.fieldnames then
local fields,nums
fields = split(line,delim,csv)
if not cnfg.convert then
nums = map(tonumber,fields)
if #nums == #fields then -- they're ALL numbers!
append(D,nums) -- add the first converted row
-- and specify conversions for subsequent rows
for i = 1,#nums do append_conversion(i,try_number) end
else -- we'll try to check numbers just now..
nums = nil
end
else -- [explicit column conversions] (any deduced number conversions will be added)
for idx,conv in pairs(cnfg.convert) do append_conversion(idx,conv) end
end
if nums == nil then
cnfg.fieldnames = fields
end
line = f:read()
count = count + 1
if stripper then line = stripper(line) end
elseif type(cnfg.fieldnames) == 'string' then
cnfg.fieldnames = split(cnfg.fieldnames,delim,csv)
end
local nfields
-- at this point, the column headers have been read in. If the first
-- row consisted of numbers, it has already been added to the dataset.
if cnfg.fieldnames then
D.fieldnames = cnfg.fieldnames
-- [collecting end field] If @last_field_collect then we'll
-- only split as many fields as there are fieldnames
if cnfg.last_field_collect then
nfields = #D.fieldnames
end
-- [implicit column conversion] unless @no_convert, we need the numerical field indices
-- of the first data row. These can also be specified explicitly by @numfields.
if not cnfg.no_convert then
local fields = split(line,D.delim,csv,nfields)
for i = 1,#fields do
if not find(numfields,i) and try_number(fields[i]) then
append_conversion(i,try_number)
end
end
end
end
-- keep going until finished
while line do
if not line:find ('^%s*$') then -- [blank lines] ignore them!
if stripper then line = stripper(line) end
local fields = split(line,delim,csv,nfields)
if conversion then -- there were field conversions...
for k = 1,#numfields do
local i,conv = numfields[k],conversion[k]
local val,err = conv(fields[i])
if val == nil then
return nil, err..": "..fields[i].." at line "..count
else
fields[i] = val
end
end
end
append(D,fields)
end
line = f:read()
count = count + 1
end
if opened then f:close() end
if delim == '%s+' then D.delim = ' ' end
if not D.fieldnames then D.fieldnames = {} end
return data.new(D)
end
local function write_row (data,f,row,delim)
data.temp = array_tostring(row,data.temp)
f:write(concat(data.temp,delim),'\n')
end
function DataMT:write_row(f,row)
write_row(self,f,row,self.delim)
end
--- write 2D data to a file.
-- Does not assume that the data has actually been
-- generated with `new` or `read`.
-- @param data 2D array
-- @param file filename or file-like object
-- @tparam[opt] {string} fieldnames list of fields (optional)
-- @string[opt='\t'] delim delimiter (default tab)
-- @return true or nil, error
function data.write (data,file,fieldnames,delim)
local f,err,opened = open_file(file,'w')
if not f then return nil, err end
if not fieldnames then
fieldnames = data.fieldnames
end
delim = delim or '\t'
if fieldnames and #fieldnames > 0 then
f:write(concat(fieldnames,delim),'\n')
end
for i = 1,#data do
write_row(data,f,data[i],delim)
end
if opened then f:close() end
return true
end
function DataMT:write(file)
data.write(self,file,self.fieldnames,self.delim)
end
local function massage_fieldnames (fields,copy)
-- fieldnames must be valid Lua identifiers; ignore any surrounding padding
-- but keep the original fieldnames...
for i = 1,#fields do
local f = strip(fields[i])
copy[i] = f
fields[i] = f:gsub('%W','_')
end
end
--- create a new dataset from a table of rows.
-- Can specify the fieldnames, else the table must have a field called
-- 'fieldnames', which is either a string of delimiter-separated names,
-- or a table of names. <br>
-- If the table does not have a field called 'delim', then an attempt will be
-- made to guess it from the fieldnames string, defaults otherwise to tab.
-- @param d the table.
-- @tparam[opt] {string} fieldnames optional fieldnames
-- @return the table.
function data.new (d,fieldnames)
d.fieldnames = d.fieldnames or fieldnames or ''
if not d.delim and type(d.fieldnames) == 'string' then
d.delim = guess_delim(d.fieldnames)
d.fieldnames = split(d.fieldnames,d.delim)
end
d.fieldnames = makelist(d.fieldnames)
d.original_fieldnames = {}
massage_fieldnames(d.fieldnames,d.original_fieldnames)
setmetatable(d,DataMT)
-- a query with just the fieldname will return a sequence
-- of values, which seq.copy turns into a table.
return d
end
local sorted_query = [[
return function (t)
local i = 0
local v
local ls = {}
for i,v in ipairs(t) do
if CONDITION then
ls[#ls+1] = v
end
end
table.sort(ls,function(v1,v2)
return SORT_EXPR
end)
local n = #ls
return function()
i = i + 1
v = ls[i]
if i > n then return end
return FIELDLIST
end
end
]]
-- question: is this optimized case actually worth the extra code?
local simple_query = [[
return function (t)
local n = #t
local i = 0
local v
return function()
repeat
i = i + 1
v = t[i]
until i > n or CONDITION
if i > n then return end
return FIELDLIST
end
end
]]
local function is_string (s)
return type(s) == 'string'
end
local field_error
local function fieldnames_as_string (data)
return concat(data.fieldnames,',')
end
local function massage_fields(data,f)
local idx
if f:find '^%d+$' then
idx = tonumber(f)
else
idx = find(data.fieldnames,f)
end
if idx then
return 'v['..idx..']'
else
field_error = f..' not found in '..fieldnames_as_string(data)
return f
end
end
local function process_select (data,parms)
--- preparing fields ----
field_error = nil
local fields = parms.fields
local numfields = fields:find '%$' or #data.fieldnames == 0
if fields:find '^%s*%*%s*' then
if not numfields then
fields = fieldnames_as_string(data)
else
local ncol = #data[1]
fields = {}
for i = 1,ncol do append(fields,'$'..i) end
fields = concat(fields,',')
end
end
local idpat = patterns.IDEN
if numfields then
idpat = '%$(%d+)'
else
-- massage field names to replace non-identifier chars
fields = rstrip(fields):gsub('[^,%w]','_')
end
local massage_fields = utils.bind1(massage_fields,data)
local ret = gsub(fields,idpat,massage_fields)
if field_error then return nil,field_error end
parms.fields = fields
parms.proc_fields = ret
parms.where = parms.where or 'true'
if is_string(parms.where) then
parms.where = gsub(parms.where,idpat,massage_fields)
field_error = nil
end
return true
end
parse_select = function(s,data)
local endp
local parms = {}
local w1,w2 = s:find('where ')
local s1,s2 = s:find('sort by ')
if w1 then -- where clause!
endp = (s1 or 0)-1
parms.where = s:sub(w2+1,endp)
end
if s1 then -- sort by clause (must be last!)
parms.sort_by = s:sub(s2+1)
end
endp = (w1 or s1 or 0)-1
parms.fields = s:sub(1,endp)
local status,err = process_select(data,parms)
if not status then return nil,err
else return parms end
end
--- create a query iterator from a select string.
-- Select string has this format: <br>
-- FIELDLIST [ where LUA-CONDN [ sort by FIELD] ]<br>
-- FIELDLIST is a comma-separated list of valid fields, or '*'. <br> <br>
-- The condition can also be a table, with fields 'fields' (comma-sep string or
-- table), 'sort_by' (string) and 'where' (Lua expression string or function)
-- @param data table produced by read
-- @param condn select string or table
-- @param context a list of tables to be searched when resolving functions
-- @param return_row if true, wrap the results in a row table
-- @return an iterator over the specified fields, or nil
-- @return an error message
function data.query(data,condn,context,return_row)
local err
if is_string(condn) then
condn,err = parse_select(condn,data)
if not condn then return nil,err end
elseif type(condn) == 'table' then
if type(condn.fields) == 'table' then
condn.fields = concat(condn.fields,',')
end
if not condn.proc_fields then
local status,err = process_select(data,condn)
if not status then return nil,err end
end
else
return nil, "condition must be a string or a table"
end
local query
if condn.sort_by then -- use sorted_query
query = sorted_query
else
query = simple_query
end
local fields = condn.proc_fields or condn.fields
if return_row then
fields = '{'..fields..'}'
end
query = query:gsub('FIELDLIST',fields)
if is_string(condn.where) then
query = query:gsub('CONDITION',condn.where)
condn.where = nil
else
query = query:gsub('CONDITION','_condn(v)')
condn.where = function_arg(0,condn.where,'condition.where must be callable')
end
if condn.sort_by then
local expr,sort_var,sort_dir
local sort_by = condn.sort_by
local i1,i2 = sort_by:find('%s+')
if i1 then
sort_var,sort_dir = sort_by:sub(1,i1-1),sort_by:sub(i2+1)
else
sort_var = sort_by
sort_dir = 'asc'
end
if sort_var:match '^%$' then sort_var = sort_var:sub(2) end
sort_var = massage_fields(data,sort_var)
if field_error then return nil,field_error end
if sort_dir == 'asc' then
sort_dir = '<'
else
sort_dir = '>'
end
expr = ('%s %s %s'):format(sort_var:gsub('v','v1'),sort_dir,sort_var:gsub('v','v2'))
query = query:gsub('SORT_EXPR',expr)
end
if condn.where then
query = 'return function(_condn) '..query..' end'
end
if _DEBUG then print(query) end
local fn,err = utils.load(query,'tmp')
if not fn then return nil,err end
fn = fn() -- get the function
if condn.where then
fn = fn(condn.where)
end
local qfun = fn(data)
if context then
-- [specifying context for condition] @context is a list of tables which are
-- 'injected'into the condition's custom context
append(context,_G)
local lookup = {}
utils.setfenv(qfun,lookup)
setmetatable(lookup,{
__index = function(tbl,key)
-- _G.print(tbl,key)
for k,t in ipairs(context) do
if t[key] then return t[key] end
end
end
})
end
return qfun
end
DataMT.select = data.query
DataMT.select_row = function(d,condn,context)
return data.query(d,condn,context,true)
end
--- Filter input using a query.
-- @string Q a query string
-- @param infile filename or file-like object
-- @param outfile filename or file-like object
-- @bool dont_fail true if you want to return an error, not just fail
function data.filter (Q,infile,outfile,dont_fail)
local d = data.read(infile or 'stdin')
local out = open_file(outfile or 'stdout')
local iter,err = d:select(Q)
local delim = d.delim
if not iter then
err = 'error: '..err
if dont_fail then
return nil,err
else
utils.quit(1,err)
end
end
while true do
local res = {iter()}
if #res == 0 then break end
out:write(concat(res,delim),'\n')
end
end
return data

View file

@ -0,0 +1,527 @@
--- Listing files in directories and creating/removing directory paths.
--
-- Dependencies: `pl.utils`, `pl.path`
--
-- Soft Dependencies: `alien`, `ffi` (either are used on Windows for copying/moving files)
-- @module pl.dir
local utils = require 'pl.utils'
local path = require 'pl.path'
local is_windows = path.is_windows
local ldir = path.dir
local mkdir = path.mkdir
local rmdir = path.rmdir
local sub = string.sub
local os,pcall,ipairs,pairs,require,setmetatable = os,pcall,ipairs,pairs,require,setmetatable
local remove = os.remove
local append = table.insert
local assert_arg,assert_string,raise = utils.assert_arg,utils.assert_string,utils.raise
local exists, isdir = path.exists, path.isdir
local sep = path.sep
local dir = {}
local function makelist(l)
return setmetatable(l, require('pl.List'))
end
local function assert_dir (n,val)
assert_arg(n,val,'string',path.isdir,'not a directory',4)
end
local function filemask(mask)
mask = utils.escape(path.normcase(mask))
return '^'..mask:gsub('%%%*','.*'):gsub('%%%?','.')..'$'
end
--- Test whether a file name matches a shell pattern.
-- Both parameters are case-normalized if operating system is
-- case-insensitive.
-- @string filename A file name.
-- @string pattern A shell pattern. The only special characters are
-- `'*'` and `'?'`: `'*'` matches any sequence of characters and
-- `'?'` matches any single character.
-- @treturn bool
-- @raise dir and mask must be strings
function dir.fnmatch(filename,pattern)
assert_string(1,filename)
assert_string(2,pattern)
return path.normcase(filename):find(filemask(pattern)) ~= nil
end
--- Return a list of all file names within an array which match a pattern.
-- @tab filenames An array containing file names.
-- @string pattern A shell pattern (see `fnmatch`).
-- @treturn List(string) List of matching file names.
-- @raise dir and mask must be strings
function dir.filter(filenames,pattern)
assert_arg(1,filenames,'table')
assert_string(2,pattern)
local res = {}
local mask = filemask(pattern)
for i,f in ipairs(filenames) do
if path.normcase(f):find(mask) then append(res,f) end
end
return makelist(res)
end
local function _listfiles(dirname,filemode,match)
local res = {}
local check = utils.choose(filemode,path.isfile,path.isdir)
if not dirname then dirname = '.' end
for f in ldir(dirname) do
if f ~= '.' and f ~= '..' then
local p = path.join(dirname,f)
if check(p) and (not match or match(f)) then
append(res,p)
end
end
end
return makelist(res)
end
--- return a list of all files in a directory which match a shell pattern.
-- @string[opt='.'] dirname A directory.
-- @string[opt] mask A shell pattern (see `fnmatch`). If not given, all files are returned.
-- @treturn {string} list of files
-- @raise dirname and mask must be strings
function dir.getfiles(dirname,mask)
dirname = dirname or '.'
assert_dir(1,dirname)
if mask then assert_string(2,mask) end
local match
if mask then
mask = filemask(mask)
match = function(f)
return path.normcase(f):find(mask)
end
end
return _listfiles(dirname,true,match)
end
--- return a list of all subdirectories of the directory.
-- @string[opt='.'] dirname A directory.
-- @treturn {string} a list of directories
-- @raise dir must be a valid directory
function dir.getdirectories(dirname)
dirname = dirname or '.'
assert_dir(1,dirname)
return _listfiles(dirname,false)
end
local alien,ffi,ffi_checked,CopyFile,MoveFile,GetLastError,win32_errors,cmd_tmpfile
local function execute_command(cmd,parms)
if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end
local err = path.is_windows and ' > ' or ' 2> '
cmd = cmd..' '..parms..err..utils.quote_arg(cmd_tmpfile)
local ret = utils.execute(cmd)
if not ret then
local err = (utils.readfile(cmd_tmpfile):gsub('\n(.*)',''))
remove(cmd_tmpfile)
return false,err
else
remove(cmd_tmpfile)
return true
end
end
local function find_ffi_copyfile ()
if not ffi_checked then
ffi_checked = true
local res
res,alien = pcall(require,'alien')
if not res then
alien = nil
res, ffi = pcall(require,'ffi')
end
if not res then
ffi = nil
return
end
else
return
end
if alien then
-- register the Win32 CopyFile and MoveFile functions
local kernel = alien.load('kernel32.dll')
CopyFile = kernel.CopyFileA
CopyFile:types{'string','string','int',ret='int',abi='stdcall'}
MoveFile = kernel.MoveFileA
MoveFile:types{'string','string',ret='int',abi='stdcall'}
GetLastError = kernel.GetLastError
GetLastError:types{ret ='int', abi='stdcall'}
elseif ffi then
ffi.cdef [[
int CopyFileA(const char *src, const char *dest, int iovr);
int MoveFileA(const char *src, const char *dest);
int GetLastError();
]]
CopyFile = ffi.C.CopyFileA
MoveFile = ffi.C.MoveFileA
GetLastError = ffi.C.GetLastError
end
win32_errors = {
ERROR_FILE_NOT_FOUND = 2,
ERROR_PATH_NOT_FOUND = 3,
ERROR_ACCESS_DENIED = 5,
ERROR_WRITE_PROTECT = 19,
ERROR_BAD_UNIT = 20,
ERROR_NOT_READY = 21,
ERROR_WRITE_FAULT = 29,
ERROR_READ_FAULT = 30,
ERROR_SHARING_VIOLATION = 32,
ERROR_LOCK_VIOLATION = 33,
ERROR_HANDLE_DISK_FULL = 39,
ERROR_BAD_NETPATH = 53,
ERROR_NETWORK_BUSY = 54,
ERROR_DEV_NOT_EXIST = 55,
ERROR_FILE_EXISTS = 80,
ERROR_OPEN_FAILED = 110,
ERROR_INVALID_NAME = 123,
ERROR_BAD_PATHNAME = 161,
ERROR_ALREADY_EXISTS = 183,
}
end
local function two_arguments (f1,f2)
return utils.quote_arg(f1)..' '..utils.quote_arg(f2)
end
local function file_op (is_copy,src,dest,flag)
if flag == 1 and path.exists(dest) then
return false,"cannot overwrite destination"
end
if is_windows then
-- if we haven't tried to load Alien/LuaJIT FFI before, then do so
find_ffi_copyfile()
-- fallback if there's no Alien, just use DOS commands *shudder*
-- 'rename' involves a copy and then deleting the source.
if not CopyFile then
if path.is_windows then
src = src:gsub("/","\\")
dest = dest:gsub("/","\\")
end
local res, err = execute_command('copy',two_arguments(src,dest))
if not res then return false,err end
if not is_copy then
return execute_command('del',utils.quote_arg(src))
end
return true
else
if path.isdir(dest) then
dest = path.join(dest,path.basename(src))
end
local ret
if is_copy then ret = CopyFile(src,dest,flag)
else ret = MoveFile(src,dest) end
if ret == 0 then
local err = GetLastError()
for name,value in pairs(win32_errors) do
if value == err then return false,name end
end
return false,"Error #"..err
else return true
end
end
else -- for Unix, just use cp for now
return execute_command(is_copy and 'cp' or 'mv',
two_arguments(src,dest))
end
end
--- copy a file.
-- @string src source file
-- @string dest destination file or directory
-- @bool flag true if you want to force the copy (default)
-- @treturn bool operation succeeded
-- @raise src and dest must be strings
function dir.copyfile (src,dest,flag)
assert_string(1,src)
assert_string(2,dest)
flag = flag==nil or flag
return file_op(true,src,dest,flag and 0 or 1)
end
--- move a file.
-- @string src source file
-- @string dest destination file or directory
-- @treturn bool operation succeeded
-- @raise src and dest must be strings
function dir.movefile (src,dest)
assert_string(1,src)
assert_string(2,dest)
return file_op(false,src,dest,0)
end
local function _dirfiles(dirname,attrib)
local dirs = {}
local files = {}
for f in ldir(dirname) do
if f ~= '.' and f ~= '..' then
local p = path.join(dirname,f)
local mode = attrib(p,'mode')
if mode=='directory' then
append(dirs,f)
else
append(files,f)
end
end
end
return makelist(dirs), makelist(files)
end
--- return an iterator which walks through a directory tree starting at root.
-- The iterator returns (root,dirs,files)
-- Note that dirs and files are lists of names (i.e. you must say path.join(root,d)
-- to get the actual full path)
-- If bottom_up is false (or not present), then the entries at the current level are returned
-- before we go deeper. This means that you can modify the returned list of directories before
-- continuing.
-- This is a clone of os.walk from the Python libraries.
-- @string root A starting directory
-- @bool bottom_up False if we start listing entries immediately.
-- @bool follow_links follow symbolic links
-- @return an iterator returning root,dirs,files
-- @raise root must be a directory
function dir.walk(root,bottom_up,follow_links)
assert_dir(1,root)
local attrib
if path.is_windows or not follow_links then
attrib = path.attrib
else
attrib = path.link_attrib
end
local to_scan = { root }
local to_return = {}
local iter = function()
while #to_scan > 0 do
local current_root = table.remove(to_scan)
local dirs,files = _dirfiles(current_root, attrib)
for _, d in ipairs(dirs) do
table.insert(to_scan, current_root..path.sep..d)
end
if not bottom_up then
return current_root, dirs, files
else
table.insert(to_return, { current_root, dirs, files })
end
end
if #to_return > 0 then
return utils.unpack(table.remove(to_return))
end
end
return iter
end
--- remove a whole directory tree.
-- Symlinks in the tree will be deleted without following them.
-- @string fullpath A directory path (must be an actual directory, not a symlink)
-- @return true or nil
-- @return error if failed
-- @raise fullpath must be a string
function dir.rmtree(fullpath)
assert_dir(1,fullpath)
if path.islink(fullpath) then return false,'will not follow symlink' end
for root,dirs,files in dir.walk(fullpath,true) do
if path.islink(root) then
-- sub dir is a link, remove link, do not follow
if is_windows then
-- Windows requires using "rmdir". Deleting the link like a file
-- will instead delete all files from the target directory!!
local res, err = rmdir(root)
if not res then return nil,err .. ": " .. root end
else
local res, err = remove(root)
if not res then return nil,err .. ": " .. root end
end
else
for i,f in ipairs(files) do
local res, err = remove(path.join(root,f))
if not res then return nil,err .. ": " .. path.join(root,f) end
end
local res, err = rmdir(root)
if not res then return nil,err .. ": " .. root end
end
end
return true
end
do
local dirpat
if path.is_windows then
dirpat = '(.+)\\[^\\]+$'
else
dirpat = '(.+)/[^/]+$'
end
local _makepath
function _makepath(p)
-- windows root drive case
if p:find '^%a:[\\]*$' then
return true
end
if not path.isdir(p) then
local subp = p:match(dirpat)
if subp then
local ok, err = _makepath(subp)
if not ok then return nil, err end
end
return mkdir(p)
else
return true
end
end
--- create a directory path.
-- This will create subdirectories as necessary!
-- @string p A directory path
-- @return true on success, nil + errormsg on failure
-- @raise failure to create
function dir.makepath (p)
assert_string(1,p)
if path.is_windows then
p = p:gsub("/", "\\")
end
return _makepath(path.abspath(p))
end
end
--- clone a directory tree. Will always try to create a new directory structure
-- if necessary.
-- @string path1 the base path of the source tree
-- @string path2 the new base path for the destination
-- @func file_fun an optional function to apply on all files
-- @bool verbose an optional boolean to control the verbosity of the output.
-- It can also be a logging function that behaves like print()
-- @return true, or nil
-- @return error message, or list of failed directory creations
-- @return list of failed file operations
-- @raise path1 and path2 must be strings
-- @usage clonetree('.','../backup',copyfile)
function dir.clonetree (path1,path2,file_fun,verbose)
assert_string(1,path1)
assert_string(2,path2)
if verbose == true then verbose = print end
local abspath,normcase,isdir,join = path.abspath,path.normcase,path.isdir,path.join
local faildirs,failfiles = {},{}
if not isdir(path1) then return raise 'source is not a valid directory' end
path1 = abspath(normcase(path1))
path2 = abspath(normcase(path2))
if verbose then verbose('normalized:',path1,path2) end
-- particularly NB that the new path isn't fully contained in the old path
if path1 == path2 then return raise "paths are the same" end
local _,i2 = path2:find(path1,1,true)
if i2 == #path1 and path2:sub(i2+1,i2+1) == path.sep then
return raise 'destination is a subdirectory of the source'
end
local cp = path.common_prefix (path1,path2)
local idx = #cp
if idx == 0 then -- no common path, but watch out for Windows paths!
if path1:sub(2,2) == ':' then idx = 3 end
end
for root,dirs,files in dir.walk(path1) do
local opath = path2..root:sub(idx)
if verbose then verbose('paths:',opath,root) end
if not isdir(opath) then
local ret = dir.makepath(opath)
if not ret then append(faildirs,opath) end
if verbose then verbose('creating:',opath,ret) end
end
if file_fun then
for i,f in ipairs(files) do
local p1 = join(root,f)
local p2 = join(opath,f)
local ret = file_fun(p1,p2)
if not ret then append(failfiles,p2) end
if verbose then
verbose('files:',p1,p2,ret)
end
end
end
end
return true,faildirs,failfiles
end
-- each entry of the stack is an array with three items:
-- 1. the name of the directory
-- 2. the lfs iterator function
-- 3. the lfs iterator userdata
local function treeiter(iterstack)
local diriter = iterstack[#iterstack]
if not diriter then
return -- done
end
local dirname = diriter[1]
local entry = diriter[2](diriter[3])
if not entry then
table.remove(iterstack)
return treeiter(iterstack) -- tail-call to try next
end
if entry ~= "." and entry ~= ".." then
entry = dirname .. sep .. entry
if exists(entry) then -- Just in case a symlink is broken.
local is_dir = isdir(entry)
if is_dir then
table.insert(iterstack, { entry, ldir(entry) })
end
return entry, is_dir
end
end
return treeiter(iterstack) -- tail-call to try next
end
--- return an iterator over all entries in a directory tree
-- @string d a directory
-- @return an iterator giving pathname and mode (true for dir, false otherwise)
-- @raise d must be a non-empty string
function dir.dirtree( d )
assert( d and d ~= "", "directory parameter is missing or empty" )
local last = sub ( d, -1 )
if last == sep or last == '/' then
d = sub( d, 1, -2 )
end
local iterstack = { {d, ldir(d)} }
return treeiter, iterstack
end
--- Recursively returns all the file starting at 'path'. It can optionally take a shell pattern and
-- only returns files that match 'shell_pattern'. If a pattern is given it will do a case insensitive search.
-- @string[opt='.'] start_path A directory.
-- @string[opt='*'] shell_pattern A shell pattern (see `fnmatch`).
-- @treturn List(string) containing all the files found recursively starting at 'path' and filtered by 'shell_pattern'.
-- @raise start_path must be a directory
function dir.getallfiles( start_path, shell_pattern )
start_path = start_path or '.'
assert_dir(1,start_path)
shell_pattern = shell_pattern or "*"
local files = {}
local normcase = path.normcase
for filename, mode in dir.dirtree( start_path ) do
if not mode then
local mask = filemask( shell_pattern )
if normcase(filename):find( mask ) then
files[#files + 1] = filename
end
end
end
return makelist(files)
end
return dir

View file

@ -0,0 +1,55 @@
--- File manipulation functions: reading, writing, moving and copying.
--
-- This module wraps a number of functions from other modules into a
-- file related module for convenience.
--
-- Dependencies: `pl.utils`, `pl.dir`, `pl.path`
-- @module pl.file
local os = os
local utils = require 'pl.utils'
local dir = require 'pl.dir'
local path = require 'pl.path'
local file = {}
--- return the contents of a file as a string.
-- This function is a copy of `utils.readfile`.
-- @function file.read
file.read = utils.readfile
--- write a string to a file.
-- This function is a copy of `utils.writefile`.
-- @function file.write
file.write = utils.writefile
--- copy a file.
-- This function is a copy of `dir.copyfile`.
-- @function file.copy
file.copy = dir.copyfile
--- move a file.
-- This function is a copy of `dir.movefile`.
-- @function file.move
file.move = dir.movefile
--- Return the time of last access as the number of seconds since the epoch.
-- This function is a copy of `path.getatime`.
-- @function file.access_time
file.access_time = path.getatime
---Return when the file was created.
-- This function is a copy of `path.getctime`.
-- @function file.creation_time
file.creation_time = path.getctime
--- Return the time of last modification.
-- This function is a copy of `path.getmtime`.
-- @function file.modified_time
file.modified_time = path.getmtime
--- Delete a file.
-- This function is a copy of `os.remove`.
-- @function file.delete
file.delete = os.remove
return file

View file

@ -0,0 +1,393 @@
--- Functional helpers like composition, binding and placeholder expressions.
-- Placeholder expressions are useful for short anonymous functions, and were
-- inspired by the Boost Lambda library.
--
-- > utils.import 'pl.func'
-- > ls = List{10,20,30}
-- > = ls:map(_1+1)
-- {11,21,31}
--
-- They can also be used to _bind_ particular arguments of a function.
--
-- > p = bind(print,'start>',_0)
-- > p(10,20,30)
-- > start> 10 20 30
--
-- See @{07-functional.md.Creating_Functions_from_Functions|the Guide}
--
-- Dependencies: `pl.utils`, `pl.tablex`
-- @module pl.func
local type,setmetatable,getmetatable,rawset = type,setmetatable,getmetatable,rawset
local concat,append = table.concat,table.insert
local tostring = tostring
local utils = require 'pl.utils'
local pairs,rawget,unpack,pack = pairs,rawget,utils.unpack,utils.pack
local tablex = require 'pl.tablex'
local map = tablex.map
local _DEBUG = rawget(_G,'_DEBUG')
local assert_arg = utils.assert_arg
local func = {}
-- metatable for Placeholder Expressions (PE)
local _PEMT = {}
local function P (t)
setmetatable(t,_PEMT)
return t
end
func.PE = P
local function isPE (obj)
return getmetatable(obj) == _PEMT
end
func.isPE = isPE
-- construct a placeholder variable (e.g _1 and _2)
local function PH (idx)
return P {op='X',repr='_'..idx, index=idx}
end
-- construct a constant placeholder variable (e.g _C1 and _C2)
local function CPH (idx)
return P {op='X',repr='_C'..idx, index=idx}
end
func._1,func._2,func._3,func._4,func._5 = PH(1),PH(2),PH(3),PH(4),PH(5)
func._0 = P{op='X',repr='...',index=0}
function func.Var (name)
local ls = utils.split(name,'[%s,]+')
local res = {}
for i = 1, #ls do
append(res,P{op='X',repr=ls[i],index=0})
end
return unpack(res)
end
function func._ (value)
return P{op='X',repr=value,index='wrap'}
end
local repr
func.Nil = func.Var 'nil'
function _PEMT.__index(obj,key)
return P{op='[]',obj,key}
end
function _PEMT.__call(fun,...)
return P{op='()',fun,...}
end
function _PEMT.__tostring (e)
return repr(e)
end
function _PEMT.__unm(arg)
return P{op='unm',arg}
end
function func.Not (arg)
return P{op='not',arg}
end
function func.Len (arg)
return P{op='#',arg}
end
local function binreg(context,t)
for name,op in pairs(t) do
rawset(context,name,function(x,y)
return P{op=op,x,y}
end)
end
end
local function import_name (name,fun,context)
rawset(context,name,function(...)
return P{op='()',fun,...}
end)
end
local imported_functions = {}
local function is_global_table (n)
return type(_G[n]) == 'table'
end
--- wrap a table of functions. This makes them available for use in
-- placeholder expressions.
-- @string tname a table name
-- @tab context context to put results, defaults to environment of caller
function func.import(tname,context)
assert_arg(1,tname,'string',is_global_table,'arg# 1: not a name of a global table')
local t = _G[tname]
context = context or _G
for name,fun in pairs(t) do
import_name(name,fun,context)
imported_functions[fun] = name
end
end
--- register a function for use in placeholder expressions.
-- @func fun a function
-- @string[opt] name an optional name
-- @return a placeholder functiond
function func.register (fun,name)
assert_arg(1,fun,'function')
if name then
assert_arg(2,name,'string')
imported_functions[fun] = name
end
return function(...)
return P{op='()',fun,...}
end
end
function func.lookup_imported_name (fun)
return imported_functions[fun]
end
local function _arg(...) return ... end
function func.Args (...)
return P{op='()',_arg,...}
end
-- binary operators with their precedences (see Lua manual)
-- precedences might be incremented by one before use depending on
-- left- or right-associativity, space them out
local binary_operators = {
['or'] = 0,
['and'] = 2,
['=='] = 4, ['~='] = 4, ['<'] = 4, ['>'] = 4, ['<='] = 4, ['>='] = 4,
['..'] = 6,
['+'] = 8, ['-'] = 8,
['*'] = 10, ['/'] = 10, ['%'] = 10,
['^'] = 14
}
-- unary operators with their precedences
local unary_operators = {
['not'] = 12, ['#'] = 12, ['unm'] = 12
}
-- comparisons (as prefix functions)
binreg (func,{And='and',Or='or',Eq='==',Lt='<',Gt='>',Le='<=',Ge='>='})
-- standard binary operators (as metamethods)
binreg (_PEMT,{__add='+',__sub='-',__mul='*',__div='/',__mod='%',__pow='^',__concat='..'})
binreg (_PEMT,{__eq='=='})
--- all elements of a table except the first.
-- @tab ls a list-like table.
function func.tail (ls)
assert_arg(1,ls,'table')
local res = {}
for i = 2,#ls do
append(res,ls[i])
end
return res
end
--- create a string representation of a placeholder expression.
-- @param e a placeholder expression
-- @param lastpred not used
function repr (e,lastpred)
local tail = func.tail
if isPE(e) then
local pred = binary_operators[e.op] or unary_operators[e.op]
if pred then
-- binary or unary operator
local s
if binary_operators[e.op] then
local left_pred = pred
local right_pred = pred
if e.op == '..' or e.op == '^' then
left_pred = left_pred + 1
else
right_pred = right_pred + 1
end
local left_arg = repr(e[1], left_pred)
local right_arg = repr(e[2], right_pred)
s = left_arg..' '..e.op..' '..right_arg
else
local op = e.op == 'unm' and '-' or e.op
s = op..' '..repr(e[1], pred)
end
if lastpred and lastpred > pred then
s = '('..s..')'
end
return s
else -- either postfix, or a placeholder
local ls = map(repr,e)
if e.op == '[]' then
return ls[1]..'['..ls[2]..']'
elseif e.op == '()' then
local fn
if ls[1] ~= nil then -- was _args, undeclared!
fn = ls[1]
else
fn = ''
end
return fn..'('..concat(tail(ls),',')..')'
else
return e.repr
end
end
elseif type(e) == 'string' then
return '"'..e..'"'
elseif type(e) == 'function' then
local name = func.lookup_imported_name(e)
if name then return name else return tostring(e) end
else
return tostring(e) --should not really get here!
end
end
func.repr = repr
-- collect all the non-PE values in this PE into vlist, and replace each occurence
-- with a constant PH (_C1, etc). Return the maximum placeholder index found.
local collect_values
function collect_values (e,vlist)
if isPE(e) then
if e.op ~= 'X' then
local m = 0
for i = 1,#e do
local subx = e[i]
local pe = isPE(subx)
if pe then
if subx.op == 'X' and subx.index == 'wrap' then
subx = subx.repr
pe = false
else
m = math.max(m,collect_values(subx,vlist))
end
end
if not pe then
append(vlist,subx)
e[i] = CPH(#vlist)
end
end
return m
else -- was a placeholder, it has an index...
return e.index
end
else -- plain value has no placeholder dependence
return 0
end
end
func.collect_values = collect_values
--- instantiate a PE into an actual function. First we find the largest placeholder used,
-- e.g. _2; from this a list of the formal parameters can be build. Then we collect and replace
-- any non-PE values from the PE, and build up a constant binding list.
-- Finally, the expression can be compiled, and e.__PE_function is set.
-- @param e a placeholder expression
-- @return a function
function func.instantiate (e)
local consts,values,parms = {},{},{}
local rep, err, fun
local n = func.collect_values(e,values)
for i = 1,#values do
append(consts,'_C'..i)
if _DEBUG then print(i,values[i]) end
end
for i =1,n do
append(parms,'_'..i)
end
consts = concat(consts,',')
parms = concat(parms,',')
rep = repr(e)
local fstr = ('return function(%s) return function(%s) return %s end end'):format(consts,parms,rep)
if _DEBUG then print(fstr) end
fun,err = utils.load(fstr,'fun')
if not fun then return nil,err end
fun = fun() -- get wrapper
fun = fun(unpack(values)) -- call wrapper (values could be empty)
e.__PE_function = fun
return fun
end
--- instantiate a PE unless it has already been done.
-- @param e a placeholder expression
-- @return the function
function func.I(e)
if rawget(e,'__PE_function') then
return e.__PE_function
else return func.instantiate(e)
end
end
utils.add_function_factory(_PEMT,func.I)
--- bind the first parameter of the function to a value.
-- @function func.bind1
-- @func fn a function of one or more arguments
-- @param p a value
-- @return a function of one less argument
-- @usage (bind1(math.max,10))(20) == math.max(10,20)
func.bind1 = utils.bind1
func.curry = func.bind1
--- create a function which chains two functions.
-- @func f a function of at least one argument
-- @func g a function of at least one argument
-- @return a function
-- @usage printf = compose(io.write,string.format)
function func.compose (f,g)
return function(...) return f(g(...)) end
end
--- bind the arguments of a function to given values.
-- `bind(fn,v,_2)` is equivalent to `bind1(fn,v)`.
-- @func fn a function of at least one argument
-- @param ... values or placeholder variables
-- @return a function
-- @usage (bind(f,_1,a))(b) == f(a,b)
-- @usage (bind(f,_2,_1))(a,b) == f(b,a)
function func.bind(fn,...)
local args = pack(...)
local holders,parms,bvalues,values = {},{},{'fn'},{}
local nv,maxplace,varargs = 1,0,false
for i = 1,args.n do
local a = args[i]
if isPE(a) and a.op == 'X' then
append(holders,a.repr)
maxplace = math.max(maxplace,a.index)
if a.index == 0 then varargs = true end
else
local v = '_v'..nv
append(bvalues,v)
append(holders,v)
append(values,a)
nv = nv + 1
end
end
for np = 1,maxplace do
append(parms,'_'..np)
end
if varargs then append(parms,'...') end
bvalues = concat(bvalues,',')
parms = concat(parms,',')
holders = concat(holders,',')
local fstr = ([[
return function (%s)
return function(%s) return fn(%s) end
end
]]):format(bvalues,parms,holders)
if _DEBUG then print(fstr) end
local res = utils.load(fstr)
res = res()
return res(fn,unpack(values))
end
return func

View file

@ -0,0 +1,91 @@
--------------
-- PL loader, for loading all PL libraries, only on demand.
-- Whenever a module is implicitly accesssed, the table will have the module automatically injected.
-- (e.g. `_ENV.tablex`)
-- then that module is dynamically loaded. The submodules are all brought into
-- the table that is provided as the argument, or returned in a new table.
-- If a table is provided, that table's metatable is clobbered, but the values are not.
-- This module returns a single function, which is passed the environment.
-- If this is `true`, then return a 'shadow table' as the module
-- See @{01-introduction.md.To_Inject_or_not_to_Inject_|the Guide}
-- @module pl.import_into
return function(env)
local mod
if env == true then
mod = {}
env = {}
end
local env = env or {}
local modules = {
utils = true,path=true,dir=true,tablex=true,stringio=true,sip=true,
input=true,seq=true,lexer=true,stringx=true,
config=true,pretty=true,data=true,func=true,text=true,
operator=true,lapp=true,array2d=true,
comprehension=true,xml=true,types=true,
test = true, app = true, file = true, class = true,
luabalanced = true, permute = true, template = true,
url = true, compat = true,
-- classes --
List = true, Map = true, Set = true,
OrderedMap = true, MultiMap = true, Date = true,
}
rawset(env,'utils',require 'pl.utils')
for name,klass in pairs(env.utils.stdmt) do
klass.__index = function(t,key)
return require ('pl.'..name)[key]
end;
end
-- ensure that we play nice with libraries that also attach a metatable
-- to the global table; always forward to a custom __index if we don't
-- match
local _hook,_prev_index
local gmt = {}
local prevenvmt = getmetatable(env)
if prevenvmt then
_prev_index = prevenvmt.__index
if prevenvmt.__newindex then
gmt.__newindex = prevenvmt.__newindex
end
end
function gmt.hook(handler)
_hook = handler
end
function gmt.__index(t,name)
local found = modules[name]
-- either true, or the name of the module containing this class.
-- either way, we load the required module and make it globally available.
if found then
-- e..g pretty.dump causes pl.pretty to become available as 'pretty'
rawset(env,name,require('pl.'..name))
return env[name]
else
local res
if _hook then
res = _hook(t,name)
if res then return res end
end
if _prev_index then
return _prev_index(t,name)
end
end
end
if mod then
function gmt.__newindex(t,name,value)
mod[name] = value
rawset(t,name,value)
end
end
setmetatable(env,gmt)
return env,mod or env
end

View file

@ -0,0 +1,11 @@
--------------
-- Entry point for loading all PL libraries only on demand, into the global space.
-- Requiring 'pl' means that whenever a module is implicitly accesssed
-- (e.g. `utils.split`)
-- then that module is dynamically loaded. The submodules are all brought into
-- the global space.
--Updated to use @{pl.import_into}
-- @module pl
require'pl.import_into'(_G)
if rawget(_G,'PENLIGHT_STRICT') then require 'pl.strict' end

View file

@ -0,0 +1,171 @@
--- Iterators for extracting words or numbers from an input source.
--
-- require 'pl'
-- local total,n = seq.sum(input.numbers())
-- print('average',total/n)
--
-- _source_ is defined as a string or a file-like object (i.e. has a read() method which returns the next line)
--
-- See @{06-data.md.Reading_Unstructured_Text_Data|here}
--
-- Dependencies: `pl.utils`
-- @module pl.input
local strfind = string.find
local strsub = string.sub
local strmatch = string.match
local utils = require 'pl.utils'
local unpack = utils.unpack
local pairs,type,tonumber = pairs,type,tonumber
local patterns = utils.patterns
local io = io
local input = {}
--- create an iterator over all tokens.
-- based on allwords from PiL, 7.1
-- @func getter any function that returns a line of text
-- @string pattern
-- @string[opt] fn Optionally can pass a function to process each token as it's found.
-- @return an iterator
function input.alltokens (getter,pattern,fn)
local line = getter() -- current line
local pos = 1 -- current position in the line
return function () -- iterator function
while line do -- repeat while there are lines
local s, e = strfind(line, pattern, pos)
if s then -- found a word?
pos = e + 1 -- next position is after this token
local res = strsub(line, s, e) -- return the token
if fn then res = fn(res) end
return res
else
line = getter() -- token not found; try next line
pos = 1 -- restart from first position
end
end
return nil -- no more lines: end of traversal
end
end
local alltokens = input.alltokens
-- question: shd this _split_ a string containing line feeds?
--- create a function which grabs the next value from a source. If the source is a string, then the getter
-- will return the string and thereafter return nil. If not specified then the source is assumed to be stdin.
-- @param f a string or a file-like object (i.e. has a read() method which returns the next line)
-- @return a getter function
function input.create_getter(f)
if f then
if type(f) == 'string' then
local ls = utils.split(f,'\n')
local i,n = 0,#ls
return function()
i = i + 1
if i > n then return nil end
return ls[i]
end
else
-- anything that supports the read() method!
if not f.read then error('not a file-like object') end
return function() return f:read() end
end
else
return io.read -- i.e. just read from stdin
end
end
--- generate a sequence of numbers from a source.
-- @param f A source
-- @return An iterator
function input.numbers(f)
return alltokens(input.create_getter(f),
'('..patterns.FLOAT..')',tonumber)
end
--- generate a sequence of words from a source.
-- @param f A source
-- @return An iterator
function input.words(f)
return alltokens(input.create_getter(f),"%w+")
end
local function apply_tonumber (no_fail,...)
local args = {...}
for i = 1,#args do
local n = tonumber(args[i])
if n == nil then
if not no_fail then return nil,args[i] end
else
args[i] = n
end
end
return args
end
--- parse an input source into fields.
-- By default, will fail if it cannot convert a field to a number.
-- @param ids a list of field indices, or a maximum field index
-- @string delim delimiter to parse fields (default space)
-- @param f a source @see create_getter
-- @tab opts option table, `{no_fail=true}`
-- @return an iterator with the field values
-- @usage for x,y in fields {2,3} do print(x,y) end -- 2nd and 3rd fields from stdin
function input.fields (ids,delim,f,opts)
local sep
local s
local getter = input.create_getter(f)
local no_fail = opts and opts.no_fail
local no_convert = opts and opts.no_convert
if not delim or delim == ' ' then
delim = '%s'
sep = '%s+'
s = '%s*'
else
sep = delim
s = ''
end
local max_id = 0
if type(ids) == 'table' then
for i,id in pairs(ids) do
if id > max_id then max_id = id end
end
else
max_id = ids
ids = {}
for i = 1,max_id do ids[#ids+1] = i end
end
local pat = '[^'..delim..']*'
local k = 1
for i = 1,max_id do
if ids[k] == i then
k = k + 1
s = s..'('..pat..')'
else
s = s..pat
end
if i < max_id then
s = s..sep
end
end
local linecount = 1
return function()
local line,results,err
repeat
line = getter()
linecount = linecount + 1
if not line then return nil end
if no_convert then
results = {strmatch(line,s)}
else
results,err = apply_tonumber(no_fail,strmatch(line,s))
if not results then
utils.quit("line "..(linecount-1)..": cannot convert '"..err.."' to number")
end
end
until #results > 0
return unpack(results)
end
end
return input

View file

@ -0,0 +1,458 @@
--- Simple command-line parsing using human-readable specification.
-- Supports GNU-style parameters.
--
-- lapp = require 'pl.lapp'
-- local args = lapp [[
-- Does some calculations
-- -o,--offset (default 0.0) Offset to add to scaled number
-- -s,--scale (number) Scaling factor
-- <number> (number) Number to be scaled
-- ]]
--
-- print(args.offset + args.scale * args.number)
--
-- Lines beginning with `'-'` are flags; there may be a short and a long name;
-- lines beginning with `'<var>'` are arguments. Anything in parens after
-- the flag/argument is either a default, a type name or a range constraint.
--
-- See @{08-additional.md.Command_line_Programs_with_Lapp|the Guide}
--
-- Dependencies: `pl.sip`
-- @module pl.lapp
local status,sip = pcall(require,'pl.sip')
if not status then
sip = require 'sip'
end
local match = sip.match_at_start
local append,tinsert = table.insert,table.insert
sip.custom_pattern('X','(%a[%w_%-]*)')
local function lines(s) return s:gmatch('([^\n]*)\n') end
local function lstrip(str) return str:gsub('^%s+','') end
local function strip(str) return lstrip(str):gsub('%s+$','') end
local function at(s,k) return s:sub(k,k) end
local lapp = {}
local open_files,parms,aliases,parmlist,usage,script
lapp.callback = false -- keep Strict happy
local filetypes = {
stdin = {io.stdin,'file-in'}, stdout = {io.stdout,'file-out'},
stderr = {io.stderr,'file-out'}
}
--- controls whether to dump usage on error.
-- Defaults to true
lapp.show_usage_error = true
--- quit this script immediately.
-- @string msg optional message
-- @bool no_usage suppress 'usage' display
function lapp.quit(msg,no_usage)
if no_usage == 'throw' then
error(msg)
end
if msg then
io.stderr:write(msg..'\n\n')
end
if not no_usage then
io.stderr:write(usage)
end
os.exit(1)
end
--- print an error to stderr and quit.
-- @string msg a message
-- @bool no_usage suppress 'usage' display
function lapp.error(msg,no_usage)
if not lapp.show_usage_error then
no_usage = true
elseif lapp.show_usage_error == 'throw' then
no_usage = 'throw'
end
lapp.quit(script..': '..msg,no_usage)
end
--- open a file.
-- This will quit on error, and keep a list of file objects for later cleanup.
-- @string file filename
-- @string[opt] opt same as second parameter of `io.open`
function lapp.open (file,opt)
local val,err = io.open(file,opt)
if not val then lapp.error(err,true) end
append(open_files,val)
return val
end
--- quit if the condition is false.
-- @bool condn a condition
-- @string msg message text
function lapp.assert(condn,msg)
if not condn then
lapp.error(msg)
end
end
local function range_check(x,min,max,parm)
lapp.assert(min <= x and max >= x,parm..' out of range')
end
local function xtonumber(s)
local val = tonumber(s)
if not val then lapp.error("unable to convert to number: "..s) end
return val
end
local types = {}
local builtin_types = {string=true,number=true,['file-in']='file',['file-out']='file',boolean=true}
local function convert_parameter(ps,val)
if ps.converter then
val = ps.converter(val)
end
if ps.type == 'number' then
val = xtonumber(val)
elseif builtin_types[ps.type] == 'file' then
val = lapp.open(val,(ps.type == 'file-in' and 'r') or 'w' )
elseif ps.type == 'boolean' then
return val
end
if ps.constraint then
ps.constraint(val)
end
return val
end
--- add a new type to Lapp. These appear in parens after the value like
-- a range constraint, e.g. '<ival> (integer) Process PID'
-- @string name name of type
-- @param converter either a function to convert values, or a Lua type name.
-- @func[opt] constraint optional function to verify values, should use lapp.error
-- if failed.
function lapp.add_type (name,converter,constraint)
types[name] = {converter=converter,constraint=constraint}
end
local function force_short(short)
lapp.assert(#short==1,short..": short parameters should be one character")
end
-- deducing type of variable from default value;
local function process_default (sval,vtype)
local val, success
if not vtype or vtype == 'number' then
val = tonumber(sval)
end
if val then -- we have a number!
return val,'number'
elseif filetypes[sval] then
local ft = filetypes[sval]
return ft[1],ft[2]
else
if sval == 'true' and not vtype then
return true, 'boolean'
end
if sval:match '^["\']' then sval = sval:sub(2,-2) end
local ps = types[vtype] or {}
ps.type = vtype
local show_usage_error = lapp.show_usage_error
lapp.show_usage_error = "throw"
success, val = pcall(convert_parameter, ps, sval)
lapp.show_usage_error = show_usage_error
if success then
return val, vtype or 'string'
end
return sval,vtype or 'string'
end
end
--- process a Lapp options string.
-- Usually called as `lapp()`.
-- @string str the options text
-- @tparam {string} args a table of arguments (default is `_G.arg`)
-- @return a table with parameter-value pairs
function lapp.process_options_string(str,args)
local results = {}
local varargs
local arg = args or _G.arg
open_files = {}
parms = {}
aliases = {}
parmlist = {}
local function check_varargs(s)
local res,cnt = s:gsub('^%.%.%.%s*','')
return res, (cnt > 0)
end
local function set_result(ps,parm,val)
parm = type(parm) == "string" and parm:gsub("%W", "_") or parm -- so foo-bar becomes foo_bar in Lua
if not ps.varargs then
results[parm] = val
else
if not results[parm] then
results[parm] = { val }
else
append(results[parm],val)
end
end
end
usage = str
for _,a in ipairs(arg) do
if a == "-h" or a == "--help" then
return lapp.quit()
end
end
for line in lines(str) do
local res = {}
local optparm,defval,vtype,constraint,rest
line = lstrip(line)
local function check(str)
return match(str,line,res)
end
-- flags: either '-<short>', '-<short>,--<long>' or '--<long>'
if check '-$v{short}, --$o{long} $' or check '-$v{short} $' or check '--$o{long} $' then
if res.long then
optparm = res.long:gsub('[^%w%-]','_') -- I'm not sure the $o pattern will let anything else through?
if #res.rest == 1 then optparm = optparm .. res.rest end
if res.short then aliases[res.short] = optparm end
else
optparm = res.short
end
if res.short and not lapp.slack then force_short(res.short) end
res.rest, varargs = check_varargs(res.rest)
elseif check '$<{name} $' then -- is it <parameter_name>?
-- so <input file...> becomes input_file ...
optparm,rest = res.name:match '([^%.]+)(.*)'
-- follow lua legal variable names
optparm = optparm:sub(1,1):gsub('%A','_') .. optparm:sub(2):gsub('%W', '_')
varargs = rest == '...'
append(parmlist,optparm)
end
-- this is not a pure doc line and specifies the flag/parameter type
if res.rest then
line = res.rest
res = {}
local optional
local defval_str
-- do we have ([optional] [<type>] [default <val>])?
if match('$({def} $',line,res) or match('$({def}',line,res) then
local typespec = strip(res.def)
local ftype, rest = typespec:match('^(%S+)(.*)$')
rest = strip(rest)
if ftype == 'optional' then
ftype, rest = rest:match('^(%S+)(.*)$')
rest = strip(rest)
optional = true
end
local default
if ftype == 'default' then
default = true
if rest == '' then lapp.error("value must follow default") end
else -- a type specification
if match('$f{min}..$f{max}',ftype,res) then
-- a numerical range like 1..10
local min,max = res.min,res.max
vtype = 'number'
constraint = function(x)
range_check(x,min,max,optparm)
end
elseif not ftype:match '|' then -- plain type
vtype = ftype
else
-- 'enum' type is a string which must belong to
-- one of several distinct values
local enums = ftype
local enump = '|' .. enums .. '|'
vtype = 'string'
constraint = function(s)
lapp.assert(enump:find('|'..s..'|', 1, true),
"value '"..s.."' not in "..enums
)
end
end
end
res.rest = rest
typespec = res.rest
-- optional 'default value' clause. Type is inferred as
-- 'string' or 'number' if there's no explicit type
if default or match('default $r{rest}',typespec,res) then
defval_str = res.rest
defval,vtype = process_default(res.rest,vtype)
end
else -- must be a plain flag, no extra parameter required
defval = false
vtype = 'boolean'
end
local ps = {
type = vtype,
defval = defval,
defval_str = defval_str,
required = defval == nil and not optional,
comment = res.rest or optparm,
constraint = constraint,
varargs = varargs
}
varargs = nil
if types[vtype] then
local converter = types[vtype].converter
if type(converter) == 'string' then
ps.type = converter
else
ps.converter = converter
end
ps.constraint = types[vtype].constraint
elseif not builtin_types[vtype] and vtype then
lapp.error(vtype.." is unknown type")
end
parms[optparm] = ps
end
end
-- cool, we have our parms, let's parse the command line args
local iparm = 1
local iextra = 1
local i = 1
local parm,ps,val
local end_of_flags = false
local function check_parm (parm)
local eqi = parm:find '[=:]'
if eqi then
tinsert(arg,i+1,parm:sub(eqi+1))
parm = parm:sub(1,eqi-1)
end
return parm,eqi
end
local function is_flag (parm)
return parms[aliases[parm] or parm]
end
while i <= #arg do
local theArg = arg[i]
local res = {}
-- after '--' we don't parse args and they end up in
-- the array part of the result (args[1] etc)
if theArg == '--' then
end_of_flags = true
iparm = #parmlist + 1
i = i + 1
theArg = arg[i]
if not theArg then
break
end
end
-- look for a flag, -<short flags> or --<long flag>
if not end_of_flags and (match('--$S{long}',theArg,res) or match('-$S{short}',theArg,res)) then
if res.long then -- long option
parm = check_parm(res.long)
elseif #res.short == 1 or is_flag(res.short) then
parm = res.short
else
local parmstr,eq = check_parm(res.short)
if not eq then
parm = at(parmstr,1)
local flag = is_flag(parm)
if flag and flag.type ~= 'boolean' then
--if isdigit(at(parmstr,2)) then
-- a short option followed by a digit is an exception (for AW;))
-- push ahead into the arg array
tinsert(arg,i+1,parmstr:sub(2))
else
-- push multiple flags into the arg array!
for k = 2,#parmstr do
tinsert(arg,i+k-1,'-'..at(parmstr,k))
end
end
else
parm = parmstr
end
end
if aliases[parm] then parm = aliases[parm] end
if not parms[parm] and (parm == 'h' or parm == 'help') then
lapp.quit()
end
else -- a parameter
parm = parmlist[iparm]
if not parm then
-- extra unnamed parameters are indexed starting at 1
parm = iextra
ps = { type = 'string' }
parms[parm] = ps
iextra = iextra + 1
else
ps = parms[parm]
end
if not ps.varargs then
iparm = iparm + 1
end
val = theArg
end
ps = parms[parm]
if not ps then lapp.error("unrecognized parameter: "..parm) end
if ps.type ~= 'boolean' then -- we need a value! This should follow
if not val then
i = i + 1
val = arg[i]
theArg = val
end
lapp.assert(val,parm.." was expecting a value")
else -- toggle boolean flags (usually false -> true)
val = not ps.defval
end
ps.used = true
val = convert_parameter(ps,val)
set_result(ps,parm,val)
if builtin_types[ps.type] == 'file' then
set_result(ps,parm..'_name',theArg)
end
if lapp.callback then
lapp.callback(parm,theArg,res)
end
i = i + 1
val = nil
end
-- check unused parms, set defaults and check if any required parameters were missed
for parm,ps in pairs(parms) do
if not ps.used then
if ps.required then lapp.error("missing required parameter: "..parm) end
set_result(ps,parm,ps.defval)
if builtin_types[ps.type] == "file" then
set_result(ps, parm .. "_name", ps.defval_str)
end
end
end
return results
end
if arg then
script = arg[0]
script = script or rawget(_G,"LAPP_SCRIPT") or "unknown"
-- strip dir and extension to get current script name
script = script:gsub('.+[\\/]',''):gsub('%.%a+$','')
else
script = "inter"
end
setmetatable(lapp, {
__call = function(tbl,str,args) return lapp.process_options_string(str,args) end,
})
return lapp

View file

@ -0,0 +1,515 @@
--- Lexical scanner for creating a sequence of tokens from text.
-- `lexer.scan(s)` returns an iterator over all tokens found in the
-- string `s`. This iterator returns two values, a token type string
-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the
-- token.
--
-- Versions specialized for Lua and C are available; these also handle block comments
-- and classify keywords as 'keyword' tokens. For example:
--
-- > s = 'for i=1,n do'
-- > for t,v in lexer.lua(s) do print(t,v) end
-- keyword for
-- iden i
-- = =
-- number 1
-- , ,
-- iden n
-- keyword do
--
-- See the Guide for further @{06-data.md.Lexical_Scanning|discussion}
-- @module pl.lexer
local strfind = string.find
local strsub = string.sub
local append = table.insert
local function assert_arg(idx,val,tp)
if type(val) ~= tp then
error("argument "..idx.." must be "..tp, 2)
end
end
local lexer = {}
local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+'
local NUMBER1a = '^[%+%-]?%d*%.%d+[eE][%+%-]?%d+'
local NUMBER2 = '^[%+%-]?%d+%.?%d*'
local NUMBER2a = '^[%+%-]?%d*%.%d+'
local NUMBER3 = '^0x[%da-fA-F]+'
local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+'
local NUMBER4a = '^%d*%.%d+[eE][%+%-]?%d+'
local NUMBER5 = '^%d+%.?%d*'
local NUMBER5a = '^%d*%.%d+'
local IDEN = '^[%a_][%w_]*'
local WSPACE = '^%s+'
local STRING1 = "^(['\"])%1" -- empty string
local STRING2 = [[^(['"])(\*)%2%1]]
local STRING3 = [[^(['"]).-[^\](\*)%2%1]]
local CHAR1 = "^''"
local CHAR2 = [[^'(\*)%1']]
local CHAR3 = [[^'.-[^\](\*)%1']]
local PREPRO = '^#.-[^\\]\n'
local plain_matches,lua_matches,cpp_matches,lua_keyword,cpp_keyword
local function tdump(tok)
return tok,tok
end
local function ndump(tok,options)
if options and options.number then
tok = tonumber(tok)
end
return "number",tok
end
-- regular strings, single or double quotes; usually we want them
-- without the quotes
local function sdump(tok,options)
if options and options.string then
tok = tok:sub(2,-2)
end
return "string",tok
end
-- long Lua strings need extra work to get rid of the quotes
local function sdump_l(tok,options,findres)
if options and options.string then
local quotelen = 3
if findres[3] then
quotelen = quotelen + findres[3]:len()
end
tok = tok:sub(quotelen, -quotelen)
if tok:sub(1, 1) == "\n" then
tok = tok:sub(2)
end
end
return "string",tok
end
local function chdump(tok,options)
if options and options.string then
tok = tok:sub(2,-2)
end
return "char",tok
end
local function cdump(tok)
return "comment",tok
end
local function wsdump (tok)
return "space",tok
end
local function pdump (tok)
return "prepro",tok
end
local function plain_vdump(tok)
return "iden",tok
end
local function lua_vdump(tok)
if lua_keyword[tok] then
return "keyword",tok
else
return "iden",tok
end
end
local function cpp_vdump(tok)
if cpp_keyword[tok] then
return "keyword",tok
else
return "iden",tok
end
end
--- create a plain token iterator from a string or file-like object.
-- @tparam string|file s a string or a file-like object with `:read()` method returning lines.
-- @tab matches an optional match table - array of token descriptions.
-- A token is described by a `{pattern, action}` pair, where `pattern` should match
-- token body and `action` is a function called when a token of described type is found.
-- @tab[opt] filter a table of token types to exclude, by default `{space=true}`
-- @tab[opt] options a table of options; by default, `{number=true,string=true}`,
-- which means convert numbers and strip string quotes.
function lexer.scan(s,matches,filter,options)
local file = type(s) ~= 'string' and s
filter = filter or {space=true}
options = options or {number=true,string=true}
if filter then
if filter.space then filter[wsdump] = true end
if filter.comments then
filter[cdump] = true
end
end
if not matches then
if not plain_matches then
plain_matches = {
{WSPACE,wsdump},
{NUMBER3,ndump},
{IDEN,plain_vdump},
{NUMBER1,ndump},
{NUMBER1a,ndump},
{NUMBER2,ndump},
{NUMBER2a,ndump},
{STRING1,sdump},
{STRING2,sdump},
{STRING3,sdump},
{'^.',tdump}
}
end
matches = plain_matches
end
local line_nr = 0
local next_line = file and file:read()
local sz = file and 0 or #s
local idx = 1
local tlist_i
local tlist
local first_hit = true
local function iter(res)
local tp = type(res)
if tlist then -- returning the inserted token list
local cur = tlist[tlist_i]
if cur then
tlist_i = tlist_i + 1
return cur[1], cur[2]
else
tlist = nil
end
end
if tp == 'string' then -- search up to some special pattern
local i1,i2 = strfind(s,res,idx)
if i1 then
local tok = strsub(s,i1,i2)
idx = i2 + 1
return '', tok
else
idx = sz + 1
return '', ''
end
elseif tp == 'table' then -- insert a token list
tlist_i = 1
tlist = res
return '', ''
elseif tp ~= 'nil' then -- return position
return line_nr, idx
else -- look for next token
if first_hit then
if not file then line_nr = 1 end
first_hit = false
end
if idx > sz then
if file then
if not next_line then
return -- past the end of file, done
end
s = next_line
line_nr = line_nr + 1
next_line = file:read()
if next_line then
s = s .. '\n'
end
idx, sz = 1, #s
else
return -- past the end of input, done
end
end
for _,m in ipairs(matches) do
local pat = m[1]
local fun = m[2]
local findres = {strfind(s,pat,idx)}
local i1, i2 = findres[1], findres[2]
if i1 then
local tok = strsub(s,i1,i2)
idx = i2 + 1
local ret1, ret2
if not (filter and filter[fun]) then
lexer.finished = idx > sz
ret1, ret2 = fun(tok, options, findres)
end
if not file and tok:find("\n") then
-- Update line number.
local _, newlines = tok:gsub("\n", {})
line_nr = line_nr + newlines
end
if ret1 then
return ret1, ret2 -- found a match
else
return iter() -- tail-call to try again
end
end
end
end
end
return iter
end
local function isstring (s)
return type(s) == 'string'
end
--- insert tokens into a stream.
-- @param tok a token stream
-- @param a1 a string is the type, a table is a token list and
-- a function is assumed to be a token-like iterator (returns type & value)
-- @string a2 a string is the value
function lexer.insert (tok,a1,a2)
if not a1 then return end
local ts
if isstring(a1) and isstring(a2) then
ts = {{a1,a2}}
elseif type(a1) == 'function' then
ts = {}
for t,v in a1() do
append(ts,{t,v})
end
else
ts = a1
end
tok(ts)
end
--- get everything in a stream upto a newline.
-- @param tok a token stream
-- @return a string
function lexer.getline (tok)
local _,v = tok('.-\n')
return v
end
--- get current line number.
-- @param tok a token stream
-- @return the line number.
-- if the input source is a file-like object,
-- also return the column.
function lexer.lineno (tok)
return tok(0)
end
--- get the rest of the stream.
-- @param tok a token stream
-- @return a string
function lexer.getrest (tok)
local _,v = tok('.+')
return v
end
--- get the Lua keywords as a set-like table.
-- So `res["and"]` etc would be `true`.
-- @return a table
function lexer.get_keywords ()
if not lua_keyword then
lua_keyword = {
["and"] = true, ["break"] = true, ["do"] = true,
["else"] = true, ["elseif"] = true, ["end"] = true,
["false"] = true, ["for"] = true, ["function"] = true,
["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true,
["not"] = true, ["or"] = true, ["repeat"] = true,
["return"] = true, ["then"] = true, ["true"] = true,
["until"] = true, ["while"] = true
}
end
return lua_keyword
end
--- create a Lua token iterator from a string or file-like object.
-- Will return the token type and value.
-- @string s the string
-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}`
-- @tab[opt] options a table of options; by default, `{number=true,string=true}`,
-- which means convert numbers and strip string quotes.
function lexer.lua(s,filter,options)
filter = filter or {space=true,comments=true}
lexer.get_keywords()
if not lua_matches then
lua_matches = {
{WSPACE,wsdump},
{NUMBER3,ndump},
{IDEN,lua_vdump},
{NUMBER4,ndump},
{NUMBER4a,ndump},
{NUMBER5,ndump},
{NUMBER5a,ndump},
{STRING1,sdump},
{STRING2,sdump},
{STRING3,sdump},
{'^%-%-%[(=*)%[.-%]%1%]',cdump},
{'^%-%-.-\n',cdump},
{'^%[(=*)%[.-%]%1%]',sdump_l},
{'^==',tdump},
{'^~=',tdump},
{'^<=',tdump},
{'^>=',tdump},
{'^%.%.%.',tdump},
{'^%.%.',tdump},
{'^.',tdump}
}
end
return lexer.scan(s,lua_matches,filter,options)
end
--- create a C/C++ token iterator from a string or file-like object.
-- Will return the token type type and value.
-- @string s the string
-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}`
-- @tab[opt] options a table of options; by default, `{number=true,string=true}`,
-- which means convert numbers and strip string quotes.
function lexer.cpp(s,filter,options)
filter = filter or {space=true,comments=true}
if not cpp_keyword then
cpp_keyword = {
["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true,
["else"] = true, ["continue"] = true, ["struct"] = true,
["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true,
["private"] = true, ["protected"] = true, ["goto"] = true,
["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true,
["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true,
["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true,
["double"] = true, ["while"] = true, ["new"] = true,
["namespace"] = true, ["try"] = true, ["catch"] = true,
["switch"] = true, ["case"] = true, ["extern"] = true,
["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true,
["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true,
}
end
if not cpp_matches then
cpp_matches = {
{WSPACE,wsdump},
{PREPRO,pdump},
{NUMBER3,ndump},
{IDEN,cpp_vdump},
{NUMBER4,ndump},
{NUMBER4a,ndump},
{NUMBER5,ndump},
{NUMBER5a,ndump},
{CHAR1,chdump},
{CHAR2,chdump},
{CHAR3,chdump},
{STRING1,sdump},
{STRING2,sdump},
{STRING3,sdump},
{'^//.-\n',cdump},
{'^/%*.-%*/',cdump},
{'^==',tdump},
{'^!=',tdump},
{'^<=',tdump},
{'^>=',tdump},
{'^->',tdump},
{'^&&',tdump},
{'^||',tdump},
{'^%+%+',tdump},
{'^%-%-',tdump},
{'^%+=',tdump},
{'^%-=',tdump},
{'^%*=',tdump},
{'^/=',tdump},
{'^|=',tdump},
{'^%^=',tdump},
{'^::',tdump},
{'^.',tdump}
}
end
return lexer.scan(s,cpp_matches,filter,options)
end
--- get a list of parameters separated by a delimiter from a stream.
-- @param tok the token stream
-- @string[opt=')'] endtoken end of list. Can be '\n'
-- @string[opt=','] delim separator
-- @return a list of token lists.
function lexer.get_separated_list(tok,endtoken,delim)
endtoken = endtoken or ')'
delim = delim or ','
local parm_values = {}
local level = 1 -- used to count ( and )
local tl = {}
local function tappend (tl,t,val)
val = val or t
append(tl,{t,val})
end
local is_end
if endtoken == '\n' then
is_end = function(t,val)
return t == 'space' and val:find '\n'
end
else
is_end = function (t)
return t == endtoken
end
end
local token,value
while true do
token,value=tok()
if not token then return nil,'EOS' end -- end of stream is an error!
if is_end(token,value) and level == 1 then
append(parm_values,tl)
break
elseif token == '(' then
level = level + 1
tappend(tl,'(')
elseif token == ')' then
level = level - 1
if level == 0 then -- finished with parm list
append(parm_values,tl)
break
else
tappend(tl,')')
end
elseif token == delim and level == 1 then
append(parm_values,tl) -- a new parm
tl = {}
else
tappend(tl,token,value)
end
end
return parm_values,{token,value}
end
--- get the next non-space token from the stream.
-- @param tok the token stream.
function lexer.skipws (tok)
local t,v = tok()
while t == 'space' do
t,v = tok()
end
return t,v
end
local skipws = lexer.skipws
--- get the next token, which must be of the expected type.
-- Throws an error if this type does not match!
-- @param tok the token stream
-- @string expected_type the token type
-- @bool no_skip_ws whether we should skip whitespace
function lexer.expecting (tok,expected_type,no_skip_ws)
assert_arg(1,tok,'function')
assert_arg(2,expected_type,'string')
local t,v
if no_skip_ws then
t,v = tok()
else
t,v = skipws(tok)
end
if t ~= expected_type then error ("expecting "..expected_type,2) end
return v
end
return lexer

View file

@ -0,0 +1,264 @@
--- Extract delimited Lua sequences from strings.
-- Inspired by Damian Conway's Text::Balanced in Perl. <br/>
-- <ul>
-- <li>[1] <a href="http://lua-users.org/wiki/LuaBalanced">Lua Wiki Page</a></li>
-- <li>[2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm</li>
-- </ul> <br/>
-- <pre class=example>
-- local lb = require "pl.luabalanced"
-- --Extract Lua expression starting at position 4.
-- print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4))
-- --> x^2 + x > 5 16
-- --Extract Lua string starting at (default) position 1.
-- print(lb.match_string([["test\"123" .. "more"]]))
-- --> "test\"123" 12
-- </pre>
-- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license).
-- @class module
-- @name pl.luabalanced
local M = {}
local assert = assert
-- map opening brace <-> closing brace.
local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' }
local begins = {}; for k,v in pairs(ends) do begins[v] = k end
-- Match Lua string in string <s> starting at position <pos>.
-- Returns <string>, <posnew>, where <string> is the matched
-- string (or nil on no match) and <posnew> is the character
-- following the match (or <pos> on no match).
-- Supports all Lua string syntax: "...", '...', [[...]], [=[...]=], etc.
local function match_string(s, pos)
pos = pos or 1
local posa = pos
local c = s:sub(pos,pos)
if c == '"' or c == "'" then
pos = pos + 1
while 1 do
pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error')
if s:sub(pos,pos) == c then
local part = s:sub(posa, pos)
return part, pos + 1
else
pos = pos + 2
end
end
else
local sc = s:match("^%[(=*)%[", pos)
if sc then
local _; _, pos = s:find("%]" .. sc .. "%]", pos)
assert(pos)
local part = s:sub(posa, pos)
return part, pos + 1
else
return nil, pos
end
end
end
M.match_string = match_string
-- Match bracketed Lua expression, e.g. "(...)", "{...}", "[...]", "[[...]]",
-- [=[...]=], etc.
-- Function interface is similar to match_string.
local function match_bracketed(s, pos)
pos = pos or 1
local posa = pos
local ca = s:sub(pos,pos)
if not ends[ca] then
return nil, pos
end
local stack = {}
while 1 do
pos = s:find('[%(%{%[%)%}%]\"\']', pos)
assert(pos, 'syntax error: unbalanced')
local c = s:sub(pos,pos)
if c == '"' or c == "'" then
local part; part, pos = match_string(s, pos)
assert(part)
elseif ends[c] then -- open
local mid, posb
if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end
if mid then
pos = s:match('%]' .. mid .. '%]()', posb)
assert(pos, 'syntax error: long string not terminated')
if #stack == 0 then
local part = s:sub(posa, pos-1)
return part, pos
end
else
stack[#stack+1] = c
pos = pos + 1
end
else -- close
assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced')
stack[#stack] = nil
if #stack == 0 then
local part = s:sub(posa, pos)
return part, pos+1
end
pos = pos + 1
end
end
end
M.match_bracketed = match_bracketed
-- Match Lua comment, e.g. "--...\n", "--[[...]]", "--[=[...]=]", etc.
-- Function interface is similar to match_string.
local function match_comment(s, pos)
pos = pos or 1
if s:sub(pos, pos+1) ~= '--' then
return nil, pos
end
pos = pos + 2
local partt, post = match_string(s, pos)
if partt then
return '--' .. partt, post
end
local part; part, pos = s:match('^([^\n]*\n?)()', pos)
return '--' .. part, pos
end
-- Match Lua expression, e.g. "a + b * c[e]".
-- Function interface is similar to match_string.
local wordop = {['and']=true, ['or']=true, ['not']=true}
local is_compare = {['>']=true, ['<']=true, ['~']=true}
local function match_expression(s, pos)
pos = pos or 1
local _
local posa = pos
local lastident
local poscs, posce
while pos do
local c = s:sub(pos,pos)
if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then
local part; part, pos = match_string(s, pos)
assert(part, 'syntax error')
elseif c == '-' and s:sub(pos+1,pos+1) == '-' then
-- note: handle adjacent comments in loop to properly support
-- backtracing (poscs/posce).
poscs = pos
while s:sub(pos,pos+1) == '--' do
local part; part, pos = match_comment(s, pos)
assert(part)
pos = s:match('^%s*()', pos)
posce = pos
end
elseif c == '(' or c == '{' or c == '[' then
_, pos = match_bracketed(s, pos)
elseif c == '=' and s:sub(pos+1,pos+1) == '=' then
pos = pos + 2 -- skip over two-char op containing '='
elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then
pos = pos + 1 -- skip over two-char op containing '='
elseif c:match'^[%)%}%];,=]' then
local part = s:sub(posa, pos-1)
return part, pos
elseif c:match'^[%w_]' then
local newident,newpos = s:match('^([%w_]+)()', pos)
if pos ~= posa and not wordop[newident] then -- non-first ident
local pose = ((posce == pos) and poscs or pos) - 1
while s:match('^%s', pose) do pose = pose - 1 end
local ce = s:sub(pose,pose)
if ce:match'[%)%}\'\"%]]' or
ce:match'[%w_]' and not wordop[lastident]
then
local part = s:sub(posa, pos-1)
return part, pos
end
end
lastident, pos = newident, newpos
else
pos = pos + 1
end
pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos)
end
local part = s:sub(posa, #s)
return part, #s+1
end
M.match_expression = match_expression
-- Match name list (zero or more names). E.g. "a,b,c"
-- Function interface is similar to match_string,
-- but returns array as match.
local function match_namelist(s, pos)
pos = pos or 1
local list = {}
while 1 do
local c = #list == 0 and '^' or '^%s*,%s*'
local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos)
if item then pos = post else break end
list[#list+1] = item
end
return list, pos
end
M.match_namelist = match_namelist
-- Match expression list (zero or more expressions). E.g. "a+b,b*c".
-- Function interface is similar to match_string,
-- but returns array as match.
local function match_explist(s, pos)
pos = pos or 1
local list = {}
while 1 do
if #list ~= 0 then
local post = s:match('^%s*,%s*()', pos)
if post then pos = post else break end
end
local item; item, pos = match_expression(s, pos)
assert(item, 'syntax error')
list[#list+1] = item
end
return list, pos
end
M.match_explist = match_explist
-- Replace snippets of code in Lua code string <s>
-- using replacement function f(u,sin) --> sout.
-- <u> is the type of snippet ('c' = comment, 's' = string,
-- 'e' = any other code).
-- Snippet is replaced with <sout> (unless <sout> is nil or false, in
-- which case the original snippet is kept)
-- This is somewhat analogous to string.gsub .
local function gsub(s, f)
local pos = 1
local posa = 1
local sret = ''
while 1 do
pos = s:find('[%-\'\"%[]', pos)
if not pos then break end
if s:match('^%-%-', pos) then
local exp = s:sub(posa, pos-1)
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
local comment; comment, pos = match_comment(s, pos)
sret = sret .. (f('c', assert(comment)) or comment)
posa = pos
else
local posb = s:find('^[\'\"%[]', pos)
local str
if posb then str, pos = match_string(s, posb) end
if str then
local exp = s:sub(posa, posb-1)
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
sret = sret .. (f('s', str) or str)
posa = pos
else
pos = pos + 1
end
end
end
local exp = s:sub(posa)
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
return sret
end
M.gsub = gsub
return M

View file

@ -0,0 +1,209 @@
--- Lua operators available as functions.
--
-- (similar to the Python module of the same name)
--
-- There is a module field `optable` which maps the operator strings
-- onto these functions, e.g. `operator.optable['()']==operator.call`
--
-- Operator strings like '>' and '{}' can be passed to most Penlight functions
-- expecting a function argument.
--
-- @module pl.operator
local strfind = string.find
local operator = {}
--- apply function to some arguments **()**
-- @param fn a function or callable object
-- @param ... arguments
function operator.call(fn,...)
return fn(...)
end
--- get the indexed value from a table **[]**
-- @param t a table or any indexable object
-- @param k the key
function operator.index(t,k)
return t[k]
end
--- returns true if arguments are equal **==**
-- @param a value
-- @param b value
function operator.eq(a,b)
return a==b
end
--- returns true if arguments are not equal **~=**
-- @param a value
-- @param b value
function operator.neq(a,b)
return a~=b
end
--- returns true if a is less than b **<**
-- @param a value
-- @param b value
function operator.lt(a,b)
return a < b
end
--- returns true if a is less or equal to b **<=**
-- @param a value
-- @param b value
function operator.le(a,b)
return a <= b
end
--- returns true if a is greater than b **>**
-- @param a value
-- @param b value
function operator.gt(a,b)
return a > b
end
--- returns true if a is greater or equal to b **>=**
-- @param a value
-- @param b value
function operator.ge(a,b)
return a >= b
end
--- returns length of string or table **#**
-- @param a a string or a table
function operator.len(a)
return #a
end
--- add two values **+**
-- @param a value
-- @param b value
function operator.add(a,b)
return a+b
end
--- subtract b from a **-**
-- @param a value
-- @param b value
function operator.sub(a,b)
return a-b
end
--- multiply two values __*__
-- @param a value
-- @param b value
function operator.mul(a,b)
return a*b
end
--- divide first value by second **/**
-- @param a value
-- @param b value
function operator.div(a,b)
return a/b
end
--- raise first to the power of second **^**
-- @param a value
-- @param b value
function operator.pow(a,b)
return a^b
end
--- modulo; remainder of a divided by b **%**
-- @param a value
-- @param b value
function operator.mod(a,b)
return a%b
end
--- concatenate two values (either strings or `__concat` defined) **..**
-- @param a value
-- @param b value
function operator.concat(a,b)
return a..b
end
--- return the negative of a value **-**
-- @param a value
function operator.unm(a)
return -a
end
--- false if value evaluates as true **not**
-- @param a value
function operator.lnot(a)
return not a
end
--- true if both values evaluate as true **and**
-- @param a value
-- @param b value
function operator.land(a,b)
return a and b
end
--- true if either value evaluate as true **or**
-- @param a value
-- @param b value
function operator.lor(a,b)
return a or b
end
--- make a table from the arguments **{}**
-- @param ... non-nil arguments
-- @return a table
function operator.table (...)
return {...}
end
--- match two strings **~**.
-- uses @{string.find}
function operator.match (a,b)
return strfind(a,b)~=nil
end
--- the null operation.
-- @param ... arguments
-- @return the arguments
function operator.nop (...)
return ...
end
---- Map from operator symbol to function.
-- Most of these map directly from operators;
-- But note these extras
--
-- * __'()'__ `call`
-- * __'[]'__ `index`
-- * __'{}'__ `table`
-- * __'~'__ `match`
--
-- @table optable
-- @field operator
operator.optable = {
['+']=operator.add,
['-']=operator.sub,
['*']=operator.mul,
['/']=operator.div,
['%']=operator.mod,
['^']=operator.pow,
['..']=operator.concat,
['()']=operator.call,
['[]']=operator.index,
['<']=operator.lt,
['<=']=operator.le,
['>']=operator.gt,
['>=']=operator.ge,
['==']=operator.eq,
['~=']=operator.neq,
['#']=operator.len,
['and']=operator.land,
['or']=operator.lor,
['{}']=operator.table,
['~']=operator.match,
['']=operator.nop,
}
return operator

View file

@ -0,0 +1,572 @@
--- Path manipulation and file queries.
--
-- This is modelled after Python's os.path library (10.1); see @{04-paths.md|the Guide}.
--
-- NOTE: the functions assume the paths being dealt with to originate
-- from the OS the application is running on. Windows drive letters are not
-- to be used when running on a Unix system for example. The one exception
-- is Windows paths to allow both forward and backward slashes (since Lua
-- also accepts those)
--
-- Dependencies: `pl.utils`, `lfs`
-- @module pl.path
-- imports and locals
local _G = _G
local sub = string.sub
local getenv = os.getenv
local tmpnam = os.tmpname
local package = package
local append, concat, remove = table.insert, table.concat, table.remove
local utils = require 'pl.utils'
local assert_string,raise = utils.assert_string,utils.raise
local res,lfs = _G.pcall(_G.require,'lfs')
if not res then
error("pl.path requires LuaFileSystem")
end
local attrib = lfs.attributes
local currentdir = lfs.currentdir
local link_attrib = lfs.symlinkattributes
local path = {}
local function err_func(name, param, err, code)
local ret = ("%s failed"):format(tostring(name))
if param ~= nil then
ret = ret .. (" for '%s'"):format(tostring(param))
end
ret = ret .. (": %s"):format(tostring(err))
if code ~= nil then
ret = ret .. (" (code %s)"):format(tostring(code))
end
return ret
end
--- Lua iterator over the entries of a given directory.
-- Implicit link to [`luafilesystem.dir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
-- @function dir
path.dir = lfs.dir
--- Creates a directory.
-- Implicit link to [`luafilesystem.mkdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
-- @function mkdir
path.mkdir = function(d)
local ok, err, code = lfs.mkdir(d)
if not ok then
return ok, err_func("mkdir", d, err, code), code
end
return ok, err, code
end
--- Removes a directory.
-- Implicit link to [`luafilesystem.rmdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
-- @function rmdir
path.rmdir = function(d)
local ok, err, code = lfs.rmdir(d)
if not ok then
return ok, err_func("rmdir", d, err, code), code
end
return ok, err, code
end
--- Gets attributes.
-- Implicit link to [`luafilesystem.attributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
-- @function attrib
path.attrib = function(d, r)
local ok, err, code = attrib(d, r)
if not ok then
return ok, err_func("attrib", d, err, code), code
end
return ok, err, code
end
--- Get the working directory.
-- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
-- @function currentdir
path.currentdir = function()
local ok, err, code = currentdir()
if not ok then
return ok, err_func("currentdir", nil, err, code), code
end
return ok, err, code
end
--- Gets symlink attributes.
-- Implicit link to [`luafilesystem.symlinkattributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
-- @function link_attrib
path.link_attrib = function(d, r)
local ok, err, code = link_attrib(d, r)
if not ok then
return ok, err_func("link_attrib", d, err, code), code
end
return ok, err, code
end
--- Changes the working directory.
-- On Windows, if a drive is specified, it also changes the current drive. If
-- only specifying the drive, it will only switch drive, but not modify the path.
-- Implicit link to [`luafilesystem.chdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
-- @function chdir
path.chdir = function(d)
local ok, err, code = lfs.chdir(d)
if not ok then
return ok, err_func("chdir", d, err, code), code
end
return ok, err, code
end
--- is this a directory?
-- @string P A file path
function path.isdir(P)
assert_string(1,P)
return attrib(P,'mode') == 'directory'
end
--- is this a file?
-- @string P A file path
function path.isfile(P)
assert_string(1,P)
return attrib(P,'mode') == 'file'
end
-- is this a symbolic link?
-- @string P A file path
function path.islink(P)
assert_string(1,P)
if link_attrib then
return link_attrib(P,'mode')=='link'
else
return false
end
end
--- return size of a file.
-- @string P A file path
function path.getsize(P)
assert_string(1,P)
return attrib(P,'size')
end
--- does a path exist?
-- @string P A file path
-- @return the file path if it exists (either as file, directory, socket, etc), nil otherwise
function path.exists(P)
assert_string(1,P)
return attrib(P,'mode') ~= nil and P
end
--- Return the time of last access as the number of seconds since the epoch.
-- @string P A file path
function path.getatime(P)
assert_string(1,P)
return attrib(P,'access')
end
--- Return the time of last modification as the number of seconds since the epoch.
-- @string P A file path
function path.getmtime(P)
assert_string(1,P)
return attrib(P,'modification')
end
---Return the system's ctime as the number of seconds since the epoch.
-- @string P A file path
function path.getctime(P)
assert_string(1,P)
return path.attrib(P,'change')
end
local function at(s,i)
return sub(s,i,i)
end
path.is_windows = utils.is_windows
local sep, other_sep, seps
-- constant sep is the directory separator for this platform.
-- constant dirsep is the separator in the PATH environment variable
if path.is_windows then
path.sep = '\\'; other_sep = '/'
path.dirsep = ';'
seps = { ['/'] = true, ['\\'] = true }
else
path.sep = '/'
path.dirsep = ':'
seps = { ['/'] = true }
end
sep = path.sep
--- are we running Windows?
-- @class field
-- @name path.is_windows
--- path separator for this platform.
-- @class field
-- @name path.sep
--- separator for PATH for this platform
-- @class field
-- @name path.dirsep
--- given a path, return the directory part and a file part.
-- if there's no directory part, the first value will be empty
-- @string P A file path
-- @return directory part
-- @return file part
-- @usage
-- local dir, file = path.splitpath("some/dir/myfile.txt")
-- assert(dir == "some/dir")
-- assert(file == "myfile.txt")
--
-- local dir, file = path.splitpath("some/dir/")
-- assert(dir == "some/dir")
-- assert(file == "")
--
-- local dir, file = path.splitpath("some_dir")
-- assert(dir == "")
-- assert(file == "some_dir")
function path.splitpath(P)
assert_string(1,P)
local i = #P
local ch = at(P,i)
while i > 0 and ch ~= sep and ch ~= other_sep do
i = i - 1
ch = at(P,i)
end
if i == 0 then
return '',P
else
return sub(P,1,i-1), sub(P,i+1)
end
end
--- return an absolute path.
-- @string P A file path
-- @string[opt] pwd optional start path to use (default is current dir)
function path.abspath(P,pwd)
assert_string(1,P)
if pwd then assert_string(2,pwd) end
local use_pwd = pwd ~= nil
if not use_pwd and not currentdir() then return P end
P = P:gsub('[\\/]$','')
pwd = pwd or currentdir()
if not path.isabs(P) then
P = path.join(pwd,P)
elseif path.is_windows and not use_pwd and at(P,2) ~= ':' and at(P,2) ~= '\\' then
P = pwd:sub(1,2)..P -- attach current drive to path like '\\fred.txt'
end
return path.normpath(P)
end
--- given a path, return the root part and the extension part.
-- if there's no extension part, the second value will be empty
-- @string P A file path
-- @treturn string root part (everything upto the "."", maybe empty)
-- @treturn string extension part (including the ".", maybe empty)
-- @usage
-- local file_path, ext = path.splitext("/bonzo/dog_stuff/cat.txt")
-- assert(file_path == "/bonzo/dog_stuff/cat")
-- assert(ext == ".txt")
--
-- local file_path, ext = path.splitext("")
-- assert(file_path == "")
-- assert(ext == "")
function path.splitext(P)
assert_string(1,P)
local i = #P
local ch = at(P,i)
while i > 0 and ch ~= '.' do
if seps[ch] then
return P,''
end
i = i - 1
ch = at(P,i)
end
if i == 0 then
return P,''
else
return sub(P,1,i-1),sub(P,i)
end
end
--- return the directory part of a path
-- @string P A file path
-- @treturn string everything before the last dir-separator
-- @see splitpath
-- @usage
-- path.dirname("/some/path/file.txt") -- "/some/path"
-- path.dirname("file.txt") -- "" (empty string)
function path.dirname(P)
assert_string(1,P)
local p1 = path.splitpath(P)
return p1
end
--- return the file part of a path
-- @string P A file path
-- @treturn string
-- @see splitpath
-- @usage
-- path.basename("/some/path/file.txt") -- "file.txt"
-- path.basename("/some/path/file/") -- "" (empty string)
function path.basename(P)
assert_string(1,P)
local _,p2 = path.splitpath(P)
return p2
end
--- get the extension part of a path.
-- @string P A file path
-- @treturn string
-- @see splitext
-- @usage
-- path.extension("/some/path/file.txt") -- ".txt"
-- path.extension("/some/path/file_txt") -- "" (empty string)
function path.extension(P)
assert_string(1,P)
local _,p2 = path.splitext(P)
return p2
end
--- is this an absolute path?
-- @string P A file path
-- @usage
-- path.isabs("hello/path") -- false
-- path.isabs("/hello/path") -- true
-- -- Windows;
-- path.isabs("hello\path") -- false
-- path.isabs("\hello\path") -- true
-- path.isabs("C:\hello\path") -- true
-- path.isabs("C:hello\path") -- false
function path.isabs(P)
assert_string(1,P)
if path.is_windows and at(P,2) == ":" then
return seps[at(P,3)] ~= nil
end
return seps[at(P,1)] ~= nil
end
--- return the path resulting from combining the individual paths.
-- if the second (or later) path is absolute, we return the last absolute path (joined with any non-absolute paths following).
-- empty elements (except the last) will be ignored.
-- @string p1 A file path
-- @string p2 A file path
-- @string ... more file paths
-- @treturn string the combined path
-- @usage
-- path.join("/first","second","third") -- "/first/second/third"
-- path.join("first","second/third") -- "first/second/third"
-- path.join("/first","/second","third") -- "/second/third"
function path.join(p1,p2,...)
assert_string(1,p1)
assert_string(2,p2)
if select('#',...) > 0 then
local p = path.join(p1,p2)
local args = {...}
for i = 1,#args do
assert_string(i,args[i])
p = path.join(p,args[i])
end
return p
end
if path.isabs(p2) then return p2 end
local endc = at(p1,#p1)
if endc ~= path.sep and endc ~= other_sep and endc ~= "" then
p1 = p1..path.sep
end
return p1..p2
end
--- normalize the case of a pathname. On Unix, this returns the path unchanged,
-- for Windows it converts;
--
-- * the path to lowercase
-- * forward slashes to backward slashes
-- @string P A file path
-- @usage path.normcase("/Some/Path/File.txt")
-- -- Windows: "\some\path\file.txt"
-- -- Others : "/Some/Path/File.txt"
function path.normcase(P)
assert_string(1,P)
if path.is_windows then
return P:gsub('/','\\'):lower()
else
return P
end
end
--- normalize a path name.
-- `A//B`, `A/./B`, and `A/foo/../B` all become `A/B`.
--
-- An empty path results in '.'.
-- @string P a file path
function path.normpath(P)
assert_string(1,P)
-- Split path into anchor and relative path.
local anchor = ''
if path.is_windows then
if P:match '^\\\\' then -- UNC
anchor = '\\\\'
P = P:sub(3)
elseif seps[at(P, 1)] then
anchor = '\\'
P = P:sub(2)
elseif at(P, 2) == ':' then
anchor = P:sub(1, 2)
P = P:sub(3)
if seps[at(P, 1)] then
anchor = anchor..'\\'
P = P:sub(2)
end
end
P = P:gsub('/','\\')
else
-- According to POSIX, in path start '//' and '/' are distinct,
-- but '///+' is equivalent to '/'.
if P:match '^//' and at(P, 3) ~= '/' then
anchor = '//'
P = P:sub(3)
elseif at(P, 1) == '/' then
anchor = '/'
P = P:match '^/*(.*)$'
end
end
local parts = {}
for part in P:gmatch('[^'..sep..']+') do
if part == '..' then
if #parts ~= 0 and parts[#parts] ~= '..' then
remove(parts)
else
append(parts, part)
end
elseif part ~= '.' then
append(parts, part)
end
end
P = anchor..concat(parts, sep)
if P == '' then P = '.' end
return P
end
--- relative path from current directory or optional start point
-- @string P a path
-- @string[opt] start optional start point (default current directory)
function path.relpath (P,start)
assert_string(1,P)
if start then assert_string(2,start) end
local split,min,append = utils.split, math.min, table.insert
P = path.abspath(P,start)
start = start or currentdir()
local compare
if path.is_windows then
P = P:gsub("/","\\")
start = start:gsub("/","\\")
compare = function(v) return v:lower() end
else
compare = function(v) return v end
end
local startl, Pl = split(start,sep), split(P,sep)
local n = min(#startl,#Pl)
if path.is_windows and n > 0 and at(Pl[1],2) == ':' and Pl[1] ~= startl[1] then
return P
end
local k = n+1 -- default value if this loop doesn't bail out!
for i = 1,n do
if compare(startl[i]) ~= compare(Pl[i]) then
k = i
break
end
end
local rell = {}
for i = 1, #startl-k+1 do rell[i] = '..' end
if k <= #Pl then
for i = k,#Pl do append(rell,Pl[i]) end
end
return table.concat(rell,sep)
end
--- Replace a starting '~' with the user's home directory.
-- In windows, if HOME isn't set, then USERPROFILE is used in preference to
-- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows.
-- @string P A file path
function path.expanduser(P)
assert_string(1,P)
if at(P,1) == '~' then
local home = getenv('HOME')
if not home then -- has to be Windows
home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH')
end
return home..sub(P,2)
else
return P
end
end
---Return a suitable full path to a new temporary file name.
-- unlike os.tmpname(), it always gives you a writeable path (uses TEMP environment variable on Windows)
function path.tmpname ()
local res = tmpnam()
-- On Windows if Lua is compiled using MSVC14 os.tmpname
-- already returns an absolute path within TEMP env variable directory,
-- no need to prepend it.
if path.is_windows and not res:find(':') then
res = getenv('TEMP')..res
end
return res
end
--- return the largest common prefix path of two paths.
-- @string path1 a file path
-- @string path2 a file path
-- @return the common prefix (Windows: separators will be normalized, casing will be original)
function path.common_prefix (path1,path2)
assert_string(1,path1)
assert_string(2,path2)
-- get them in order!
if #path1 > #path2 then path2,path1 = path1,path2 end
local compare
if path.is_windows then
path1 = path1:gsub("/", "\\")
path2 = path2:gsub("/", "\\")
compare = function(v) return v:lower() end
else
compare = function(v) return v end
end
for i = 1,#path1 do
if compare(at(path1,i)) ~= compare(at(path2,i)) then
local cp = path1:sub(1,i-1)
if at(path1,i-1) ~= sep then
cp = path.dirname(cp)
end
return cp
end
end
if at(path2,#path1+1) ~= sep then path1 = path.dirname(path1) end
return path1
--return ''
end
--- return the full path where a particular Lua module would be found.
-- Both package.path and package.cpath is searched, so the result may
-- either be a Lua file or a shared library.
-- @string mod name of the module
-- @return on success: path of module, lua or binary
-- @return on error: nil, error string listing paths tried
function path.package_path(mod)
assert_string(1,mod)
local res, err1, err2
res, err1 = package.searchpath(mod,package.path)
if res then return res,true end
res, err2 = package.searchpath(mod,package.cpath)
if res then return res,false end
return raise ('cannot find module on path\n' .. err1 .. "\n" .. err2)
end
---- finis -----
return path

View file

@ -0,0 +1,196 @@
--- Permutation operations.
--
-- Dependencies: `pl.utils`, `pl.tablex`
-- @module pl.permute
local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local copy = tablex.deepcopy
local append = table.insert
local assert_arg = utils.assert_arg
local permute = {}
--- an iterator over all order-permutations of the elements of a list.
-- Please note that the same list is returned each time, so do not keep references!
-- @param a list-like table
-- @return an iterator which provides the next permutation as a list
function permute.order_iter(a)
assert_arg(1,a,'table')
local t = #a
local stack = { 1 }
local function iter()
local h = #stack
local n = t - h + 1
local i = stack[h]
if i > t then
return
end
if n == 0 then
table.remove(stack)
h = h - 1
stack[h] = stack[h] + 1
return a
elseif i <= n then
-- put i-th element as the last one
a[n], a[i] = a[i], a[n]
-- generate all permutations of the other elements
table.insert(stack, 1)
else
table.remove(stack)
h = h - 1
n = n + 1
i = stack[h]
-- restore i-th element
a[n], a[i] = a[i], a[n]
stack[h] = stack[h] + 1
end
return iter() -- tail-call
end
return iter
end
--- construct a table containing all the order-permutations of a list.
-- @param a list-like table
-- @return a table of tables
-- @usage permute.order_table {1,2,3} --> {{2,3,1},{3,2,1},{3,1,2},{1,3,2},{2,1,3},{1,2,3}}
function permute.order_table (a)
assert_arg(1,a,'table')
local res = {}
for t in permute.iter(a) do
append(res,copy(t))
end
return res
end
--- an iterator over all permutations of the elements of the given lists.
-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided (see `utils.pack`)
-- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceeded by an index
-- @usage
-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety
-- local bools = utils.pack(true, false)
-- local iter = permute.list_iter(strs, bools)
--
-- print(iter()) --> 1, one, true
-- print(iter()) --> 2, nil, true
-- print(iter()) --> 3, three, true
-- print(iter()) --> 4, one, false
-- print(iter()) --> 5, nil, false
-- print(iter()) --> 6, three, false
function permute.list_iter(...)
local elements = {...}
local pointers = {}
local sizes = {}
local size = #elements
for i, list in ipairs(elements) do
assert_arg(i,list,'table')
pointers[i] = 1
sizes[i] = list.n or #list
end
local count = 0
return function()
if pointers[size] > sizes[size] then return end -- we're done
count = count + 1
local r = { n = #elements }
local cascade_up = true
for i = 1, size do
r[i] = elements[i][pointers[i]]
if cascade_up then
pointers[i] = pointers[i] + 1
if pointers[i] <= sizes[i] then
-- this list is not done yet, stop cascade
cascade_up = false
else
-- this list is done
if i ~= size then
-- reset pointer
pointers[i] = 1
end
end
end
end
return count, utils.unpack(r)
end
end
--- construct a table containing all the permutations of a set of lists.
-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided
-- @return a list of lists, the sub-lists have an 'n' field for nil-safety
-- @usage
-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety
-- local bools = utils.pack(true, false)
-- local results = permute.list_table(strs, bools)
-- -- results = {
-- -- { "one, true, n = 2 }
-- -- { nil, true, n = 2 },
-- -- { "three, true, n = 2 },
-- -- { "one, false, n = 2 },
-- -- { nil, false, n = 2 },
-- -- { "three", false, n = 2 },
-- -- }
function permute.list_table(...)
local iter = permute.list_iter(...)
local results = {}
local i = 1
while true do
local values = utils.pack(iter())
if values[1] == nil then return results end
for i = 1, values.n do values[i] = values[i+1] end
values.n = values.n - 1
results[i] = values
i = i + 1
end
end
-- backward compat, to be deprecated
--- deprecated.
-- @param ...
-- @see permute.order_iter
function permute.iter(...)
utils.raise_deprecation {
source = "Penlight " .. utils._VERSION,
message = "function 'iter' was renamed to 'order_iter'",
version_removed = "2.0.0",
deprecated_after = "1.9.2",
}
return permute.order_iter(...)
end
--- deprecated.
-- @param ...
-- @see permute.order_iter
function permute.table(...)
utils.raise_deprecation {
source = "Penlight " .. utils._VERSION,
message = "function 'table' was renamed to 'order_table'",
version_removed = "2.0.0",
deprecated_after = "1.9.2",
}
return permute.order_table(...)
end
return permute

View file

@ -0,0 +1,438 @@
--- Pretty-printing Lua tables.
-- Also provides a sandboxed Lua table reader and
-- a function to present large numbers in human-friendly format.
--
-- Dependencies: `pl.utils`, `pl.lexer`, `pl.stringx`, `debug`
-- @module pl.pretty
local append = table.insert
local concat = table.concat
local mfloor, mhuge = math.floor, math.huge
local mtype = math.type
local utils = require 'pl.utils'
local lexer = require 'pl.lexer'
local debug = require 'debug'
local quote_string = require'pl.stringx'.quote_string
local assert_arg = utils.assert_arg
local original_tostring = tostring
-- Patch tostring to format numbers with better precision
-- and to produce cross-platform results for
-- infinite values and NaN.
local function tostring(value)
if type(value) ~= "number" then
return original_tostring(value)
elseif value ~= value then
return "NaN"
elseif value == mhuge then
return "Inf"
elseif value == -mhuge then
return "-Inf"
elseif (_VERSION ~= "Lua 5.3" or mtype(value) == "integer") and mfloor(value) == value then
return ("%d"):format(value)
else
local res = ("%.14g"):format(value)
if _VERSION == "Lua 5.3" and mtype(value) == "float" and not res:find("%.") then
-- Number is internally a float but looks like an integer.
-- Insert ".0" after first run of digits.
res = res:gsub("%d+", "%0.0", 1)
end
return res
end
end
local pretty = {}
local function save_global_env()
local env = {}
env.hook, env.mask, env.count = debug.gethook()
-- env.hook is "external hook" if is a C hook function
if env.hook~="external hook" then
debug.sethook()
end
env.string_mt = getmetatable("")
debug.setmetatable("", nil)
return env
end
local function restore_global_env(env)
if env then
debug.setmetatable("", env.string_mt)
if env.hook~="external hook" then
debug.sethook(env.hook, env.mask, env.count)
end
end
end
--- Read a string representation of a Lua table.
-- This function loads and runs the string as Lua code, but bails out
-- if it contains a function definition.
-- Loaded string is executed in an empty environment.
-- @string s string to read in `{...}` format, possibly with some whitespace
-- before or after the curly braces. A single line comment may be present
-- at the beginning.
-- @return a table in case of success.
-- If loading the string failed, return `nil` and error message.
-- If executing loaded string failed, return `nil` and the error it raised.
function pretty.read(s)
assert_arg(1,s,'string')
if s:find '^%s*%-%-' then -- may start with a comment..
s = s:gsub('%-%-.-\n','')
end
if not s:find '^%s*{' then return nil,"not a Lua table" end
if s:find '[^\'"%w_]function[^\'"%w_]' then
local tok = lexer.lua(s)
for t,v in tok do
if t == 'keyword' and v == 'function' then
return nil,"cannot have functions in table definition"
end
end
end
s = 'return '..s
local chunk,err = utils.load(s,'tbl','t',{})
if not chunk then return nil,err end
local global_env = save_global_env()
local ok,ret = pcall(chunk)
restore_global_env(global_env)
if ok then return ret
else
return nil,ret
end
end
--- Read a Lua chunk.
-- @string s Lua code.
-- @tab[opt] env environment used to run the code, empty by default.
-- @bool[opt] paranoid abort loading if any looping constructs a found in the code
-- and disable string methods.
-- @return the environment in case of success or `nil` and syntax or runtime error
-- if something went wrong.
function pretty.load (s, env, paranoid)
env = env or {}
if paranoid then
local tok = lexer.lua(s)
for t,v in tok do
if t == 'keyword'
and (v == 'for' or v == 'repeat' or v == 'function' or v == 'goto')
then
return nil,"looping not allowed"
end
end
end
local chunk,err = utils.load(s,'tbl','t',env)
if not chunk then return nil,err end
local global_env = paranoid and save_global_env()
local ok,err = pcall(chunk)
restore_global_env(global_env)
if not ok then return nil,err end
return env
end
local function quote_if_necessary (v)
if not v then return ''
else
--AAS
if v:find ' ' then v = quote_string(v) end
end
return v
end
local keywords
local function is_identifier (s)
return type(s) == 'string' and s:find('^[%a_][%w_]*$') and not keywords[s]
end
local function quote (s)
if type(s) == 'table' then
return pretty.write(s,'')
else
--AAS
return quote_string(s)-- ('%q'):format(tostring(s))
end
end
local function index (numkey,key)
--AAS
if not numkey then
key = quote(key)
key = key:find("^%[") and (" " .. key .. " ") or key
end
return '['..key..']'
end
--- Create a string representation of a Lua table.
-- This function never fails, but may complain by returning an
-- extra value. Normally puts out one item per line, using
-- the provided indent; set the second parameter to an empty string
-- if you want output on one line.
--
-- *NOTE:* this is NOT a serialization function, not a full blown
-- debug function. Checkout out respectively the
-- [serpent](https://github.com/pkulchenko/serpent)
-- or [inspect](https://github.com/kikito/inspect.lua)
-- Lua modules for that if you need them.
-- @tab tbl Table to serialize to a string.
-- @string[opt] space The indent to use.
-- Defaults to two spaces; pass an empty string for no indentation.
-- @bool[opt] not_clever Pass `true` for plain output, e.g `{['key']=1}`.
-- Defaults to `false`.
-- @return a string
-- @return an optional error message
function pretty.write (tbl,space,not_clever)
if type(tbl) ~= 'table' then
local res = tostring(tbl)
if type(tbl) == 'string' then return quote(tbl) end
return res, 'not a table'
end
if not keywords then
keywords = lexer.get_keywords()
end
local set = ' = '
if space == '' then set = '=' end
space = space or ' '
local lines = {}
local line = ''
local tables = {}
local function put(s)
if #s > 0 then
line = line..s
end
end
local function putln (s)
if #line > 0 then
line = line..s
append(lines,line)
line = ''
else
append(lines,s)
end
end
local function eat_last_comma ()
local n = #lines
local lastch = lines[n]:sub(-1,-1)
if lastch == ',' then
lines[n] = lines[n]:sub(1,-2)
end
end
-- safe versions for iterators since 5.3+ honors metamethods that can throw
-- errors
local ipairs = function(t)
local i = 0
local ok, v
local getter = function() return t[i] end
return function()
i = i + 1
ok, v = pcall(getter)
if v == nil or not ok then return end
return i, t[i]
end
end
local pairs = function(t)
local k, v, ok
local getter = function() return next(t, k) end
return function()
ok, k, v = pcall(getter)
if not ok then return end
return k, v
end
end
local writeit
writeit = function (t,oldindent,indent)
local tp = type(t)
if tp ~= 'string' and tp ~= 'table' then
putln(quote_if_necessary(tostring(t))..',')
elseif tp == 'string' then
-- if t:find('\n') then
-- putln('[[\n'..t..']],')
-- else
-- putln(quote(t)..',')
-- end
--AAS
putln(quote_string(t) ..",")
elseif tp == 'table' then
if tables[t] then
putln('<cycle>,')
return
end
tables[t] = true
local newindent = indent..space
putln('{')
local used = {}
if not not_clever then
for i,val in ipairs(t) do
put(indent)
writeit(val,indent,newindent)
used[i] = true
end
end
local ordered_keys = {}
for k,v in pairs(t) do
if type(k) ~= 'number' then
ordered_keys[#ordered_keys + 1] = k
end
end
table.sort(ordered_keys, function (a, b)
if type(a) == type(b) then
return tostring(a) < tostring(b)
else
return type(a) < type(b)
end
end)
local function write_entry (key, val)
local tkey = type(key)
local numkey = tkey == 'number'
if not_clever then
key = tostring(key)
put(indent..index(numkey,key)..set)
writeit(val,indent,newindent)
else
if not numkey or not used[key] then -- non-array indices
if tkey ~= 'string' then
key = tostring(key)
end
if numkey or not is_identifier(key) then
key = index(numkey,key)
end
put(indent..key..set)
writeit(val,indent,newindent)
end
end
end
for i = 1, #ordered_keys do
local key = ordered_keys[i]
local val = t[key]
write_entry(key, val)
end
for key,val in pairs(t) do
if type(key) == 'number' then
write_entry(key, val)
end
end
tables[t] = nil
eat_last_comma()
putln(oldindent..'},')
else
putln(tostring(t)..',')
end
end
writeit(tbl,'',space)
eat_last_comma()
return concat(lines,#space > 0 and '\n' or '')
end
--- Dump a Lua table out to a file or stdout.
-- @tab t The table to write to a file or stdout.
-- @string[opt] filename File name to write too. Defaults to writing
-- to stdout.
function pretty.dump (t, filename)
if not filename then
print(pretty.write(t))
return true
else
return utils.writefile(filename, pretty.write(t))
end
end
--- Dump a series of arguments to stdout for debug purposes.
-- This function is attached to the module table `__call` method, to make it
-- extra easy to access. So the full:
--
-- print(require("pl.pretty").write({...}))
--
-- Can be shortened to:
--
-- require"pl.pretty" (...)
--
-- Any `nil` entries will be printed as `"<nil>"` to make them explicit.
-- @param ... the parameters to dump to stdout.
-- @usage
-- -- example debug output
-- require"pl.pretty" ("hello", nil, "world", { bye = "world", true} )
--
-- -- output:
-- {
-- ["arg 1"] = "hello",
-- ["arg 2"] = "<nil>",
-- ["arg 3"] = "world",
-- ["arg 4"] = {
-- true,
-- bye = "world"
-- }
-- }
function pretty.debug(...)
local n = select("#", ...)
local t = { ... }
for i = 1, n do
local value = t[i]
if value == nil then
value = "<nil>"
end
t[i] = nil
t["arg " .. i] = value
end
print(pretty.write(t))
return true
end
local memp,nump = {'B','KiB','MiB','GiB'},{'','K','M','B'}
local function comma (val)
local thou = math.floor(val/1000)
if thou > 0 then return comma(thou)..','.. tostring(val % 1000)
else return tostring(val) end
end
--- Format large numbers nicely for human consumption.
-- @number num a number.
-- @string[opt] kind one of `'M'` (memory in `KiB`, `MiB`, etc.),
-- `'N'` (postfixes are `'K'`, `'M'` and `'B'`),
-- or `'T'` (use commas as thousands separator), `'N'` by default.
-- @int[opt] prec number of digits to use for `'M'` and `'N'`, `1` by default.
function pretty.number (num,kind,prec)
local fmt = '%.'..(prec or 1)..'f%s'
if kind == 'T' then
return comma(num)
else
local postfixes, fact
if kind == 'M' then
fact = 1024
postfixes = memp
else
fact = 1000
postfixes = nump
end
local div = fact
local k = 1
while num >= div and k <= #postfixes do
div = div * fact
k = k + 1
end
div = div / fact
if k > #postfixes then k = k - 1; div = div/fact end
if k > 1 then
return fmt:format(num/div,postfixes[k] or 'duh')
else
return num..postfixes[1]
end
end
end
return setmetatable(pretty, {
__call = function(self, ...)
return self.debug(...)
end
})

View file

@ -0,0 +1,544 @@
--- Manipulating iterators as sequences.
-- See @{07-functional.md.Sequences|The Guide}
--
-- Dependencies: `pl.utils`, `pl.types`, `debug`
-- @module pl.seq
local next,assert,pairs,tonumber,type,setmetatable = next,assert,pairs,tonumber,type,setmetatable
local strfind,format = string.find,string.format
local mrandom = math.random
local tsort,tappend = table.sort,table.insert
local io = io
local utils = require 'pl.utils'
local callable = require 'pl.types'.is_callable
local function_arg = utils.function_arg
local assert_arg = utils.assert_arg
local debug = require 'debug'
local seq = {}
-- given a number, return a function(y) which returns true if y > x
-- @param x a number
function seq.greater_than(x)
return function(v)
return tonumber(v) > x
end
end
-- given a number, returns a function(y) which returns true if y < x
-- @param x a number
function seq.less_than(x)
return function(v)
return tonumber(v) < x
end
end
-- given any value, return a function(y) which returns true if y == x
-- @param x a value
function seq.equal_to(x)
if type(x) == "number" then
return function(v)
return tonumber(v) == x
end
else
return function(v)
return v == x
end
end
end
--- given a string, return a function(y) which matches y against the string.
-- @param s a string
function seq.matching(s)
return function(v)
return strfind(v,s)
end
end
local nexti
--- sequence adaptor for a table. Note that if any generic function is
-- passed a table, it will automatically use seq.list()
-- @param t a list-like table
-- @usage sum(list(t)) is the sum of all elements of t
-- @usage for x in list(t) do...end
function seq.list(t)
assert_arg(1,t,'table')
if not nexti then
nexti = ipairs{}
end
local key,value = 0
return function()
key,value = nexti(t,key)
return value
end
end
--- return the keys of the table.
-- @param t an arbitrary table
-- @return iterator over keys
function seq.keys(t)
assert_arg(1,t,'table')
local key
return function()
key = next(t,key)
return key
end
end
local list = seq.list
local function default_iter(iter)
if type(iter) == 'table' then return list(iter)
else return iter end
end
seq.iter = default_iter
--- create an iterator over a numerical range. Like the standard Python function xrange.
-- @param start a number
-- @param finish a number greater than start
function seq.range(start,finish)
local i = start - 1
return function()
i = i + 1
if i > finish then return nil
else return i end
end
end
-- count the number of elements in the sequence which satisfy the predicate
-- @param iter a sequence
-- @param condn a predicate function (must return either true or false)
-- @param optional argument to be passed to predicate as second argument.
-- @return count
function seq.count(iter,condn,arg)
local i = 0
seq.foreach(iter,function(val)
if condn(val,arg) then i = i + 1 end
end)
return i
end
--- return the minimum and the maximum value of the sequence.
-- @param iter a sequence
-- @return minimum value
-- @return maximum value
function seq.minmax(iter)
local vmin,vmax = 1e70,-1e70
for v in default_iter(iter) do
v = tonumber(v)
if v < vmin then vmin = v end
if v > vmax then vmax = v end
end
return vmin,vmax
end
--- return the sum and element count of the sequence.
-- @param iter a sequence
-- @param fn an optional function to apply to the values
function seq.sum(iter,fn)
local s = 0
local i = 0
for v in default_iter(iter) do
if fn then v = fn(v) end
s = s + v
i = i + 1
end
return s,i
end
--- create a table from the sequence. (This will make the result a List.)
-- @param iter a sequence
-- @return a List
-- @usage copy(list(ls)) is equal to ls
-- @usage copy(list {1,2,3}) == List{1,2,3}
function seq.copy(iter)
local res,k = {},1
for v in default_iter(iter) do
res[k] = v
k = k + 1
end
setmetatable(res, require('pl.List'))
return res
end
--- create a table of pairs from the double-valued sequence.
-- @param iter a double-valued sequence
-- @param i1 used to capture extra iterator values
-- @param i2 as with pairs & ipairs
-- @usage copy2(ipairs{10,20,30}) == {{1,10},{2,20},{3,30}}
-- @return a list-like table
function seq.copy2 (iter,i1,i2)
local res,k = {},1
for v1,v2 in iter,i1,i2 do
res[k] = {v1,v2}
k = k + 1
end
return res
end
--- create a table of 'tuples' from a multi-valued sequence.
-- A generalization of copy2 above
-- @param iter a multiple-valued sequence
-- @return a list-like table
function seq.copy_tuples (iter)
iter = default_iter(iter)
local res = {}
local row = {iter()}
while #row > 0 do
tappend(res,row)
row = {iter()}
end
return res
end
--- return an iterator of random numbers.
-- @param n the length of the sequence
-- @param l same as the first optional argument to math.random
-- @param u same as the second optional argument to math.random
-- @return a sequence
function seq.random(n,l,u)
local rand
assert(type(n) == 'number')
if u then
rand = function() return mrandom(l,u) end
elseif l then
rand = function() return mrandom(l) end
else
rand = mrandom
end
return function()
if n == 0 then return nil
else
n = n - 1
return rand()
end
end
end
--- return an iterator to the sorted elements of a sequence.
-- @param iter a sequence
-- @param comp an optional comparison function (comp(x,y) is true if x < y)
function seq.sort(iter,comp)
local t = seq.copy(iter)
tsort(t,comp)
return list(t)
end
--- return an iterator which returns elements of two sequences.
-- @param iter1 a sequence
-- @param iter2 a sequence
-- @usage for x,y in seq.zip(ls1,ls2) do....end
function seq.zip(iter1,iter2)
iter1 = default_iter(iter1)
iter2 = default_iter(iter2)
return function()
return iter1(),iter2()
end
end
--- Makes a table where the key/values are the values and value counts of the sequence.
-- This version works with 'hashable' values like strings and numbers.
-- `pl.tablex.count_map` is more general.
-- @param iter a sequence
-- @return a map-like table
-- @return a table
-- @see pl.tablex.count_map
function seq.count_map(iter)
local t = {}
local v
for s in default_iter(iter) do
v = t[s]
if v then t[s] = v + 1
else t[s] = 1 end
end
return setmetatable(t, require('pl.Map'))
end
-- given a sequence, return all the unique values in that sequence.
-- @param iter a sequence
-- @param returns_table true if we return a table, not a sequence
-- @return a sequence or a table; defaults to a sequence.
function seq.unique(iter,returns_table)
local t = seq.count_map(iter)
local res,k = {},1
for key in pairs(t) do res[k] = key; k = k + 1 end
table.sort(res)
if returns_table then
return res
else
return list(res)
end
end
--- print out a sequence iter with a separator.
-- @param iter a sequence
-- @param sep the separator (default space)
-- @param nfields maximum number of values per line (default 7)
-- @param fmt optional format function for each value
function seq.printall(iter,sep,nfields,fmt)
local write = io.write
if not sep then sep = ' ' end
if not nfields then
if sep == '\n' then nfields = 1e30
else nfields = 7 end
end
if fmt then
local fstr = fmt
fmt = function(v) return format(fstr,v) end
end
local k = 1
for v in default_iter(iter) do
if fmt then v = fmt(v) end
if k < nfields then
write(v,sep)
k = k + 1
else
write(v,'\n')
k = 1
end
end
write '\n'
end
-- return an iterator running over every element of two sequences (concatenation).
-- @param iter1 a sequence
-- @param iter2 a sequence
function seq.splice(iter1,iter2)
iter1 = default_iter(iter1)
iter2 = default_iter(iter2)
local iter = iter1
return function()
local ret = iter()
if ret == nil then
if iter == iter1 then
iter = iter2
return iter()
else return nil end
else
return ret
end
end
end
--- return a sequence where every element of a sequence has been transformed
-- by a function. If you don't supply an argument, then the function will
-- receive both values of a double-valued sequence, otherwise behaves rather like
-- tablex.map.
-- @param fn a function to apply to elements; may take two arguments
-- @param iter a sequence of one or two values
-- @param arg optional argument to pass to function.
function seq.map(fn,iter,arg)
fn = function_arg(1,fn)
iter = default_iter(iter)
return function()
local v1,v2 = iter()
if v1 == nil then return nil end
return fn(v1,arg or v2) or false
end
end
--- filter a sequence using a predicate function.
-- @param iter a sequence of one or two values
-- @param pred a boolean function; may take two arguments
-- @param arg optional argument to pass to function.
function seq.filter (iter,pred,arg)
pred = function_arg(2,pred)
return function ()
local v1,v2
while true do
v1,v2 = iter()
if v1 == nil then return nil end
if pred(v1,arg or v2) then return v1,v2 end
end
end
end
--- 'reduce' a sequence using a binary function.
-- @func fn a function of two arguments
-- @param iter a sequence
-- @param initval optional initial value
-- @usage seq.reduce(operator.add,seq.list{1,2,3,4}) == 10
-- @usage seq.reduce('-',{1,2,3,4,5}) == -13
function seq.reduce (fn,iter,initval)
fn = function_arg(1,fn)
iter = default_iter(iter)
local val = initval or iter()
if val == nil then return nil end
for v in iter do
val = fn(val,v)
end
return val
end
--- take the first n values from the sequence.
-- @param iter a sequence of one or two values
-- @param n number of items to take
-- @return a sequence of at most n items
function seq.take (iter,n)
iter = default_iter(iter)
return function()
if n < 1 then return end
local val1,val2 = iter()
if not val1 then return end
n = n - 1
return val1,val2
end
end
--- skip the first n values of a sequence
-- @param iter a sequence of one or more values
-- @param n number of items to skip
function seq.skip (iter,n)
n = n or 1
for i = 1,n do
if iter() == nil then return list{} end
end
return iter
end
--- a sequence with a sequence count and the original value.
-- enum(copy(ls)) is a roundabout way of saying ipairs(ls).
-- @param iter a single or double valued sequence
-- @return sequence of (i,v), i = 1..n and v is from iter.
function seq.enum (iter)
local i = 0
iter = default_iter(iter)
return function ()
local val1,val2 = iter()
if not val1 then return end
i = i + 1
return i,val1,val2
end
end
--- map using a named method over a sequence.
-- @param iter a sequence
-- @param name the method name
-- @param arg1 optional first extra argument
-- @param arg2 optional second extra argument
function seq.mapmethod (iter,name,arg1,arg2)
iter = default_iter(iter)
return function()
local val = iter()
if not val then return end
local fn = val[name]
if not fn then error(type(val).." does not have method "..name) end
return fn(val,arg1,arg2)
end
end
--- a sequence of (last,current) values from another sequence.
-- This will return S(i-1),S(i) if given S(i)
-- @param iter a sequence
function seq.last (iter)
iter = default_iter(iter)
local val, l = iter(), nil
if val == nil then return list{} end
return function ()
val,l = iter(),val
if val == nil then return nil end
return val,l
end
end
--- call the function on each element of the sequence.
-- @param iter a sequence with up to 3 values
-- @param fn a function
function seq.foreach(iter,fn)
fn = function_arg(2,fn)
for i1,i2,i3 in default_iter(iter) do fn(i1,i2,i3) end
end
---------------------- Sequence Adapters ---------------------
local SMT
local function SW (iter,...)
if callable(iter) then
return setmetatable({iter=iter},SMT)
else
return iter,...
end
end
-- can't directly look these up in seq because of the wrong argument order...
local map,reduce,mapmethod = seq.map, seq.reduce, seq.mapmethod
local overrides = {
map = function(self,fun,arg)
return map(fun,self,arg)
end,
reduce = function(self,fun,initval)
return reduce(fun,self,initval)
end
}
SMT = {
__index = function (tbl,key)
local fn = overrides[key] or seq[key]
if fn then
return function(sw,...) return SW(fn(sw.iter,...)) end
else
return function(sw,...) return SW(mapmethod(sw.iter,key,...)) end
end
end,
__call = function (sw)
return sw.iter()
end,
}
setmetatable(seq,{
__call = function(tbl,iter,extra)
if not callable(iter) then
if type(iter) == 'table' then iter = seq.list(iter)
else return iter
end
end
if extra then
return setmetatable({iter=function()
return iter(extra)
end},SMT)
else
return setmetatable({iter=iter},SMT)
end
end
})
--- create a wrapped iterator over all lines in the file.
-- @param f either a filename, file-like object, or 'STDIN' (for standard input)
-- @param ... for Lua 5.2 only, optional format specifiers, as in `io.read`.
-- @return a sequence wrapper
function seq.lines (f,...)
local iter,obj
if f == 'STDIN' then
f = io.stdin
elseif type(f) == 'string' then
iter,obj = io.lines(f,...)
elseif not f.read then
error("Pass either a string or a file-like object",2)
end
if not iter then
iter,obj = f:lines(...)
end
if obj then -- LuaJIT version returns a function operating on a file
local lines,file = iter,obj
iter = function() return lines(file) end
end
return SW(iter)
end
function seq.import ()
debug.setmetatable(function() end,{
__index = function(tbl,key)
local s = overrides[key] or seq[key]
if s then return s
else
return function(s,...) return seq.mapmethod(s,key,...) end
end
end
})
end
return seq

View file

@ -0,0 +1,337 @@
--- Simple Input Patterns (SIP).
-- SIP patterns start with '$', then a
-- one-letter type, and then an optional variable in curly braces.
--
-- sip.match('$v=$q','name="dolly"',res)
-- ==> res=={'name','dolly'}
-- sip.match('($q{first},$q{second})','("john","smith")',res)
-- ==> res=={second='smith',first='john'}
--
-- Type names:
--
-- v identifier
-- i integer
-- f floating-point
-- q quoted string
-- ([{< match up to closing bracket
--
-- See @{08-additional.md.Simple_Input_Patterns|the Guide}
--
-- @module pl.sip
local loadstring = rawget(_G,'loadstring') or load
local unpack = rawget(_G,'unpack') or rawget(table,'unpack')
local append,concat = table.insert,table.concat
local ipairs,type = ipairs,type
local io,_G = io,_G
local print,rawget = print,rawget
local patterns = {
FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*',
INTEGER = '[+%-%d]%d*',
IDEN = '[%a_][%w_]*',
OPTION = '[%a_][%w_%-]*',
}
local function assert_arg(idx,val,tp)
if type(val) ~= tp then
error("argument "..idx.." must be "..tp, 2)
end
end
local sip = {}
local brackets = {['<'] = '>', ['('] = ')', ['{'] = '}', ['['] = ']' }
local stdclasses = {a=1,c=0,d=1,l=1,p=0,u=1,w=1,x=1,s=0}
local function group(s)
return '('..s..')'
end
-- escape all magic characters except $, which has special meaning
-- Also, un-escape any characters after $, so $( and $[ passes through as is.
local function escape (spec)
return (spec:gsub('[%-%.%+%[%]%(%)%^%%%?%*]','%%%0'):gsub('%$%%(%S)','$%1'))
end
-- Most spaces within patterns can match zero or more spaces.
-- Spaces between alphanumeric characters or underscores or between
-- patterns that can match these characters, however, must match at least
-- one space. Otherwise '$v $v' would match 'abcd' as {'abc', 'd'}.
-- This function replaces continuous spaces within a pattern with either
-- '%s*' or '%s+' according to this rule. The pattern has already
-- been stripped of pattern names by now.
local function compress_spaces(patt)
return (patt:gsub("()%s+()", function(i1, i2)
local before = patt:sub(i1 - 2, i1 - 1)
if before:match('%$[vifadxlu]') or before:match('^[^%$]?[%w_]$') then
local after = patt:sub(i2, i2 + 1)
if after:match('%$[vifadxlu]') or after:match('^[%w_]') then
return '%s+'
end
end
return '%s*'
end))
end
local pattern_map = {
v = group(patterns.IDEN),
i = group(patterns.INTEGER),
f = group(patterns.FLOAT),
o = group(patterns.OPTION),
r = '(%S.*)',
p = '([%a]?[:]?[\\/%.%w_]+)'
}
function sip.custom_pattern(flag,patt)
pattern_map[flag] = patt
end
--- convert a SIP pattern into the equivalent Lua string pattern.
-- @param spec a SIP pattern
-- @param options a table; only the <code>at_start</code> field is
-- currently meaningful and ensures that the pattern is anchored
-- at the start of the string.
-- @return a Lua string pattern.
function sip.create_pattern (spec,options)
assert_arg(1,spec,'string')
local fieldnames,fieldtypes = {},{}
if type(spec) == 'string' then
spec = escape(spec)
else
local res = {}
for i,s in ipairs(spec) do
res[i] = escape(s)
end
spec = concat(res,'.-')
end
local kount = 1
local function addfield (name,type)
name = name or kount
append(fieldnames,name)
fieldtypes[name] = type
kount = kount + 1
end
local named_vars = spec:find('{%a+}')
if options and options.at_start then
spec = '^'..spec
end
if spec:sub(-1,-1) == '$' then
spec = spec:sub(1,-2)..'$r'
if named_vars then spec = spec..'{rest}' end
end
local names
if named_vars then
names = {}
spec = spec:gsub('{(%a+)}',function(name)
append(names,name)
return ''
end)
end
spec = compress_spaces(spec)
local k = 1
local err
local r = (spec:gsub('%$%S',function(s)
local type,name
type = s:sub(2,2)
if names then name = names[k]; k=k+1 end
-- this kludge is necessary because %q generates two matches, and
-- we want to ignore the first. Not a problem for named captures.
if not names and type == 'q' then
addfield(nil,'Q')
else
addfield(name,type)
end
local res
if pattern_map[type] then
res = pattern_map[type]
elseif type == 'q' then
-- some Lua pattern matching voodoo; we want to match '...' as
-- well as "...", and can use the fact that %n will match a
-- previous capture. Adding the extra field above comes from needing
-- to accommodate the extra spurious match (which is either ' or ")
addfield(name,type)
res = '(["\'])(.-)%'..(kount-2)
else
local endbracket = brackets[type]
if endbracket then
res = '(%b'..type..endbracket..')'
elseif stdclasses[type] or stdclasses[type:lower()] then
res = '(%'..type..'+)'
else
err = "unknown format type or character class"
end
end
return res
end))
if err then
return nil,err
else
return r,fieldnames,fieldtypes
end
end
local function tnumber (s)
return s == 'd' or s == 'i' or s == 'f'
end
function sip.create_spec_fun(spec,options)
local fieldtypes,fieldnames
local ls = {}
spec,fieldnames,fieldtypes = sip.create_pattern(spec,options)
if not spec then return spec,fieldnames end
local named_vars = type(fieldnames[1]) == 'string'
for i = 1,#fieldnames do
append(ls,'mm'..i)
end
ls[1] = ls[1] or "mm1" -- behave correctly if there are no patterns
local fun = ('return (function(s,res)\n\tlocal %s = s:match(%q)\n'):format(concat(ls,','),spec)
fun = fun..'\tif not mm1 then return false end\n'
local k=1
for i,f in ipairs(fieldnames) do
if f ~= '_' then
local var = 'mm'..i
if tnumber(fieldtypes[f]) then
var = 'tonumber('..var..')'
elseif brackets[fieldtypes[f]] then
var = var..':sub(2,-2)'
end
if named_vars then
fun = ('%s\tres.%s = %s\n'):format(fun,f,var)
else
if fieldtypes[f] ~= 'Q' then -- we skip the string-delim capture
fun = ('%s\tres[%d] = %s\n'):format(fun,k,var)
k = k + 1
end
end
end
end
return fun..'\treturn true\nend)\n', named_vars
end
--- convert a SIP pattern into a matching function.
-- The returned function takes two arguments, the line and an empty table.
-- If the line matched the pattern, then this function returns true
-- and the table is filled with field-value pairs.
-- @param spec a SIP pattern
-- @param options optional table; {at_start=true} ensures that the pattern
-- is anchored at the start of the string.
-- @return a function if successful, or nil,error
function sip.compile(spec,options)
assert_arg(1,spec,'string')
local fun,names = sip.create_spec_fun(spec,options)
if not fun then return nil,names end
if rawget(_G,'_DEBUG') then print(fun) end
local chunk,err = loadstring(fun,'tmp')
if err then return nil,err end
return chunk(),names
end
local cache = {}
--- match a SIP pattern against a string.
-- @param spec a SIP pattern
-- @param line a string
-- @param res a table to receive values
-- @param options (optional) option table
-- @return true or false
function sip.match (spec,line,res,options)
assert_arg(1,spec,'string')
assert_arg(2,line,'string')
assert_arg(3,res,'table')
if not cache[spec] then
cache[spec] = sip.compile(spec,options)
end
return cache[spec](line,res)
end
--- match a SIP pattern against the start of a string.
-- @param spec a SIP pattern
-- @param line a string
-- @param res a table to receive values
-- @return true or false
function sip.match_at_start (spec,line,res)
return sip.match(spec,line,res,{at_start=true})
end
--- given a pattern and a file object, return an iterator over the results
-- @param spec a SIP pattern
-- @param f a file-like object.
function sip.fields (spec,f)
assert_arg(1,spec,'string')
if not f then return nil,"no file object" end
local fun,err = sip.compile(spec)
if not fun then return nil,err end
local res = {}
return function()
while true do
local line = f:read()
if not line then return end
if fun(line,res) then
local values = res
res = {}
return unpack(values)
end
end
end
end
local read_patterns = {}
--- register a match which will be used in the read function.
-- @string spec a SIP pattern
-- @func fun a function to be called with the results of the match
-- @see read
function sip.pattern (spec,fun)
assert_arg(1,spec,'string')
local pat,named = sip.compile(spec)
append(read_patterns,{pat=pat,named=named,callback=fun})
end
--- enter a loop which applies all registered matches to the input file.
-- @param f a file-like object
-- @array matches optional list of `{spec,fun}` pairs, as for `pattern` above.
function sip.read (f,matches)
local owned,err
if not f then return nil,"no file object" end
if type(f) == 'string' then
f,err = io.open(f)
if not f then return nil,err end
owned = true
end
if matches then
for _,p in ipairs(matches) do
sip.pattern(p[1],p[2])
end
end
local res = {}
for line in f:lines() do
for _,item in ipairs(read_patterns) do
if item.pat(line,res) then
if item.callback then
if item.named then
item.callback(res)
else
item.callback(unpack(res))
end
end
res = {}
break
end
end
end
if owned then f:close() end
end
return sip

View file

@ -0,0 +1,138 @@
--- Checks uses of undeclared global variables.
-- All global variables must be 'declared' through a regular assignment
-- (even assigning `nil` will do) in a main chunk before being used
-- anywhere or assigned to inside a function. Existing metatables `__newindex` and `__index`
-- metamethods are respected.
--
-- You can set any table to have strict behaviour using `strict.module`. Creating a new
-- module with `strict.closed_module` makes the module immune to monkey-patching, if
-- you don't wish to encourage monkey business.
--
-- If the global `PENLIGHT_NO_GLOBAL_STRICT` is defined, then this module won't make the
-- global environment strict - if you just want to explicitly set table strictness.
--
-- @module pl.strict
require 'debug' -- for Lua 5.2
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
local strict = {}
local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"
end
--- make an existing table strict.
-- @string[opt] name name of table
-- @tab[opt] mod the table to protect - if `nil` then we'll return a new table
-- @tab[opt] predeclared - table of variables that are to be considered predeclared.
-- @return the given table, or a new table
-- @usage
-- local M = { hello = "world" }
-- strict.module ("Awesome_Module", M, {
-- Lua = true, -- defines allowed keys
-- })
--
-- assert(M.hello == "world")
-- assert(M.Lua == nil) -- access allowed, but has no value yet
-- M.Lua = "Rocks"
-- assert(M.Lua == "Rocks")
-- M.not_allowed = "bad boy" -- throws an error
function strict.module (name,mod,predeclared)
local mt, old_newindex, old_index, old_index_type, global
if predeclared then
global = predeclared.__global
end
if type(mod) == 'table' then
mt = getmetatable(mod)
if mt and rawget(mt,'__declared') then return end -- already patched...
else
mod = {}
end
if mt == nil then
mt = {}
setmetatable(mod, mt)
else
old_newindex = mt.__newindex
old_index = mt.__index
old_index_type = type(old_index)
end
mt.__declared = predeclared or {}
mt.__newindex = function(t, n, v)
if old_newindex then
old_newindex(t, n, v)
if rawget(t,n)~=nil then return end
end
if not mt.__declared[n] then
if global then
local w = what()
if w ~= "main" and w ~= "C" then
error("assign to undeclared global '"..n.."'", 2)
end
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
mt.__index = function(t,n)
if not mt.__declared[n] and what() ~= "C" then
if old_index then
if old_index_type == "table" then
local fallback = old_index[n]
if fallback ~= nil then
return fallback
end
else
local res = old_index(t, n)
if res ~= nil then
return res
end
end
end
local msg = "variable '"..n.."' is not declared"
if name then
msg = msg .. " in '"..tostring(name).."'"
end
error(msg, 2)
end
return rawget(t, n)
end
return mod
end
--- make all tables in a table strict.
-- So `strict.make_all_strict(_G)` prevents monkey-patching
-- of any global table
-- @tab T the table containing the tables to protect. Table `T` itself will NOT be protected.
function strict.make_all_strict (T)
for k,v in pairs(T) do
if type(v) == 'table' and v ~= T then
strict.module(k,v)
end
end
end
--- make a new module table which is closed to further changes.
-- @tab mod module table
-- @string name module name
function strict.closed_module (mod,name)
-- No clue to what this is useful for? see tests
-- Deprecate this and remove???
local M = {}
mod = mod or {}
local mt = getmetatable(mod)
if not mt then
mt = {}
setmetatable(mod,mt)
end
mt.__newindex = function(t,k,v)
M[k] = v
end
return strict.module(name,M)
end
if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then
strict.module(nil,_G,{_PROMPT=true,_PROMPT2=true,__global=true})
end
return strict

View file

@ -0,0 +1,158 @@
--- Reading and writing strings using file-like objects. <br>
--
-- f = stringio.open(text)
-- l1 = f:read() -- read first line
-- n,m = f:read ('*n','*n') -- read two numbers
-- for line in f:lines() do print(line) end -- iterate over all lines
-- f = stringio.create()
-- f:write('hello')
-- f:write('dolly')
-- assert(f:value(),'hellodolly')
--
-- See @{03-strings.md.File_style_I_O_on_Strings|the Guide}.
-- @module pl.stringio
local unpack = rawget(_G,'unpack') or rawget(table,'unpack')
local tonumber = tonumber
local concat,append = table.concat,table.insert
local stringio = {}
-- Writer class
local SW = {}
SW.__index = SW
local function xwrite(self,...)
local args = {...} --arguments may not be nil!
for i = 1, #args do
append(self.tbl,args[i])
end
end
function SW:write(arg1,arg2,...)
if arg2 then
xwrite(self,arg1,arg2,...)
else
append(self.tbl,arg1)
end
end
function SW:writef(fmt,...)
self:write(fmt:format(...))
end
function SW:value()
return concat(self.tbl)
end
function SW:__tostring()
return self:value()
end
function SW:close() -- for compatibility only
end
function SW:seek()
end
-- Reader class
local SR = {}
SR.__index = SR
function SR:_read(fmt)
local i,str = self.i,self.str
local sz = #str
if i > sz then return nil end
local res
if fmt == '*l' or fmt == '*L' then
local idx = str:find('\n',i) or (sz+1)
res = str:sub(i,fmt == '*l' and idx-1 or idx)
self.i = idx+1
elseif fmt == '*a' then
res = str:sub(i)
self.i = sz
elseif fmt == '*n' then
local _,i2,idx
_,idx = str:find ('%s*%d+',i)
_,i2 = str:find ('^%.%d+',idx+1)
if i2 then idx = i2 end
_,i2 = str:find ('^[eE][%+%-]*%d+',idx+1)
if i2 then idx = i2 end
local val = str:sub(i,idx)
res = tonumber(val)
self.i = idx+1
elseif type(fmt) == 'number' then
res = str:sub(i,i+fmt-1)
self.i = i + fmt
else
error("bad read format",2)
end
return res
end
function SR:read(...)
if select('#',...) == 0 then
return self:_read('*l')
else
local res, fmts = {},{...}
for i = 1, #fmts do
res[i] = self:_read(fmts[i])
end
return unpack(res)
end
end
function SR:seek(whence,offset)
local base
whence = whence or 'cur'
offset = offset or 0
if whence == 'set' then
base = 1
elseif whence == 'cur' then
base = self.i
elseif whence == 'end' then
base = #self.str
end
self.i = base + offset
return self.i
end
function SR:lines(...)
local n, args = select('#',...)
if n > 0 then
args = {...}
end
return function()
if n == 0 then
return self:_read '*l'
else
return self:read(unpack(args))
end
end
end
function SR:close() -- for compatibility only
end
--- create a file-like object which can be used to construct a string.
-- The resulting object has an extra `value()` method for
-- retrieving the string value. Implements `file:write`, `file:seek`, `file:lines`,
-- plus an extra `writef` method which works like `utils.printf`.
-- @usage f = create(); f:write('hello, dolly\n'); print(f:value())
function stringio.create()
return setmetatable({tbl={}},SW)
end
--- create a file-like object for reading from a given string.
-- Implements `file:read`.
-- @string s The input string.
-- @usage fs = open '20 10'; x,y = f:read ('*n','*n'); assert(x == 20 and y == 10)
function stringio.open(s)
return setmetatable({str=s,i=1},SR)
end
function stringio.lines(s,...)
return stringio.open(s):lines(...)
end
return stringio

View file

@ -0,0 +1,917 @@
--- Python-style extended string library.
--
-- see 3.6.1 of the Python reference.
-- If you want to make these available as string methods, then say
-- `stringx.import()` to bring them into the standard `string` table.
--
-- See @{03-strings.md|the Guide}
--
-- Dependencies: `pl.utils`, `pl.types`
-- @module pl.stringx
local utils = require 'pl.utils'
local is_callable = require 'pl.types'.is_callable
local string = string
local find = string.find
local type,setmetatable,ipairs = type,setmetatable,ipairs
local error = error
local gsub = string.gsub
local rep = string.rep
local sub = string.sub
local reverse = string.reverse
local concat = table.concat
local append = table.insert
local remove = table.remove
local escape = utils.escape
local ceil, max = math.ceil, math.max
local assert_arg,usplit = utils.assert_arg,utils.split
local lstrip
local unpack = utils.unpack
local pack = utils.pack
local function assert_string (n,s)
assert_arg(n,s,'string')
end
local function non_empty(s)
return #s > 0
end
local function assert_nonempty_string(n,s)
assert_arg(n,s,'string',non_empty,'must be a non-empty string')
end
local function makelist(l)
return setmetatable(l, require('pl.List'))
end
local stringx = {}
------------------
-- String Predicates
-- @section predicates
--- does s only contain alphabetic characters?
-- @string s a string
function stringx.isalpha(s)
assert_string(1,s)
return find(s,'^%a+$') == 1
end
--- does s only contain digits?
-- @string s a string
function stringx.isdigit(s)
assert_string(1,s)
return find(s,'^%d+$') == 1
end
--- does s only contain alphanumeric characters?
-- @string s a string
function stringx.isalnum(s)
assert_string(1,s)
return find(s,'^%w+$') == 1
end
--- does s only contain whitespace?
-- Matches on pattern '%s' so matches space, newline, tabs, etc.
-- @string s a string
function stringx.isspace(s)
assert_string(1,s)
return find(s,'^%s+$') == 1
end
--- does s only contain lower case characters?
-- @string s a string
function stringx.islower(s)
assert_string(1,s)
return find(s,'^[%l%s]+$') == 1
end
--- does s only contain upper case characters?
-- @string s a string
function stringx.isupper(s)
assert_string(1,s)
return find(s,'^[%u%s]+$') == 1
end
local function raw_startswith(s, prefix)
return find(s,prefix,1,true) == 1
end
local function raw_endswith(s, suffix)
return #s >= #suffix and find(s, suffix, #s-#suffix+1, true) and true or false
end
local function test_affixes(s, affixes, fn)
if type(affixes) == 'string' then
return fn(s,affixes)
elseif type(affixes) == 'table' then
for _,affix in ipairs(affixes) do
if fn(s,affix) then return true end
end
return false
else
error(("argument #2 expected a 'string' or a 'table', got a '%s'"):format(type(affixes)))
end
end
--- does s start with prefix or one of prefixes?
-- @string s a string
-- @param prefix a string or an array of strings
function stringx.startswith(s,prefix)
assert_string(1,s)
return test_affixes(s,prefix,raw_startswith)
end
--- does s end with suffix or one of suffixes?
-- @string s a string
-- @param suffix a string or an array of strings
function stringx.endswith(s,suffix)
assert_string(1,s)
return test_affixes(s,suffix,raw_endswith)
end
--- Strings and Lists
-- @section lists
--- concatenate the strings using this string as a delimiter.
-- Note that the arguments are reversed from `string.concat`.
-- @string s the string
-- @param seq a table of strings or numbers
-- @usage stringx.join(' ', {1,2,3}) == '1 2 3'
function stringx.join(s,seq)
assert_string(1,s)
return concat(seq,s)
end
--- Split a string into a list of lines.
-- `"\r"`, `"\n"`, and `"\r\n"` are considered line ends.
-- They are not included in the lines unless `keepends` is passed.
-- Terminal line end does not produce an extra line.
-- Splitting an empty string results in an empty list.
-- @string s the string.
-- @bool[opt] keep_ends include line ends.
-- @return List of lines
function stringx.splitlines(s, keep_ends)
assert_string(1, s)
local res = {}
local pos = 1
while true do
local line_end_pos = find(s, '[\r\n]', pos)
if not line_end_pos then
break
end
local line_end = sub(s, line_end_pos, line_end_pos)
if line_end == '\r' and sub(s, line_end_pos + 1, line_end_pos + 1) == '\n' then
line_end = '\r\n'
end
local line = sub(s, pos, line_end_pos - 1)
if keep_ends then
line = line .. line_end
end
append(res, line)
pos = line_end_pos + #line_end
end
if pos <= #s then
append(res, sub(s, pos))
end
return makelist(res)
end
--- split a string into a list of strings using a delimiter.
-- @function split
-- @string s the string
-- @string[opt] re a delimiter (defaults to whitespace)
-- @int[opt] n maximum number of results
-- @return List
-- @usage #(stringx.split('one two')) == 2
-- @usage stringx.split('one,two,three', ',') == List{'one','two','three'}
-- @usage stringx.split('one,two,three', ',', 2) == List{'one','two,three'}
function stringx.split(s,re,n)
assert_string(1,s)
local plain = true
if not re then -- default spaces
s = lstrip(s)
plain = false
end
local res = usplit(s,re,plain,n)
if re and re ~= '' and
find(s,re,-#re,true) and
(n or math.huge) > #res then
res[#res+1] = ""
end
return makelist(res)
end
--- replace all tabs in s with tabsize spaces. If not specified, tabsize defaults to 8.
-- Tab stops will be honored.
-- @string s the string
-- @int tabsize[opt=8] number of spaces to expand each tab
-- @return expanded string
-- @usage stringx.expandtabs('\tone,two,three', 4) == ' one,two,three'
-- @usage stringx.expandtabs(' \tone,two,three', 4) == ' one,two,three'
function stringx.expandtabs(s,tabsize)
assert_string(1,s)
tabsize = tabsize or 8
return (s:gsub("([^\t\r\n]*)\t", function(before_tab)
if tabsize == 0 then
return before_tab
else
return before_tab .. (" "):rep(tabsize - #before_tab % tabsize)
end
end))
end
--- Finding and Replacing
-- @section find
local function _find_all(s,sub,first,last,allow_overlap)
first = first or 1
last = last or #s
if sub == '' then return last+1,last-first+1 end
local i1,i2 = find(s,sub,first,true)
local res
local k = 0
while i1 do
if last and i2 > last then break end
res = i1
k = k + 1
if allow_overlap then
i1,i2 = find(s,sub,i1+1,true)
else
i1,i2 = find(s,sub,i2+1,true)
end
end
return res,k
end
--- find index of first instance of sub in s from the left.
-- @string s the string
-- @string sub substring
-- @int[opt] first first index
-- @int[opt] last last index
-- @return start index, or nil if not found
function stringx.lfind(s,sub,first,last)
assert_string(1,s)
assert_string(2,sub)
local i1, i2 = find(s,sub,first,true)
if i1 and (not last or i2 <= last) then
return i1
else
return nil
end
end
--- find index of first instance of sub in s from the right.
-- @string s the string
-- @string sub substring
-- @int[opt] first first index
-- @int[opt] last last index
-- @return start index, or nil if not found
function stringx.rfind(s,sub,first,last)
assert_string(1,s)
assert_string(2,sub)
return (_find_all(s,sub,first,last,true))
end
--- replace up to n instances of old by new in the string s.
-- If n is not present, replace all instances.
-- @string s the string
-- @string old the target substring
-- @string new the substitution
-- @int[opt] n optional maximum number of substitutions
-- @return result string
function stringx.replace(s,old,new,n)
assert_string(1,s)
assert_string(2,old)
assert_string(3,new)
return (gsub(s,escape(old),new:gsub('%%','%%%%'),n))
end
--- count all instances of substring in string.
-- @string s the string
-- @string sub substring
-- @bool[opt] allow_overlap allow matches to overlap
-- @usage
-- assert(stringx.count('banana', 'ana') == 1)
-- assert(stringx.count('banana', 'ana', true) == 2)
function stringx.count(s,sub,allow_overlap)
assert_string(1,s)
local _,k = _find_all(s,sub,1,false,allow_overlap)
return k
end
--- Stripping and Justifying
-- @section strip
local function _just(s,w,ch,left,right)
local n = #s
if w > n then
if not ch then ch = ' ' end
local f1,f2
if left and right then
local rn = ceil((w-n)/2)
local ln = w - n - rn
f1 = rep(ch,ln)
f2 = rep(ch,rn)
elseif right then
f1 = rep(ch,w-n)
f2 = ''
else
f2 = rep(ch,w-n)
f1 = ''
end
return f1..s..f2
else
return s
end
end
--- left-justify s with width w.
-- @string s the string
-- @int w width of justification
-- @string[opt=' '] ch padding character
-- @usage stringx.ljust('hello', 10, '*') == '*****hello'
function stringx.ljust(s,w,ch)
assert_string(1,s)
assert_arg(2,w,'number')
return _just(s,w,ch,true,false)
end
--- right-justify s with width w.
-- @string s the string
-- @int w width of justification
-- @string[opt=' '] ch padding character
-- @usage stringx.rjust('hello', 10, '*') == 'hello*****'
function stringx.rjust(s,w,ch)
assert_string(1,s)
assert_arg(2,w,'number')
return _just(s,w,ch,false,true)
end
--- center-justify s with width w.
-- @string s the string
-- @int w width of justification
-- @string[opt=' '] ch padding character
-- @usage stringx.center('hello', 10, '*') == '**hello***'
function stringx.center(s,w,ch)
assert_string(1,s)
assert_arg(2,w,'number')
return _just(s,w,ch,true,true)
end
local function _strip(s,left,right,chrs)
if not chrs then
chrs = '%s'
else
chrs = '['..escape(chrs)..']'
end
local f = 1
local t
if left then
local i1,i2 = find(s,'^'..chrs..'*')
if i2 >= i1 then
f = i2+1
end
end
if right then
if #s < 200 then
local i1,i2 = find(s,chrs..'*$',f)
if i2 >= i1 then
t = i1-1
end
else
local rs = reverse(s)
local i1,i2 = find(rs, '^'..chrs..'*')
if i2 >= i1 then
t = -i2-1
end
end
end
return sub(s,f,t)
end
--- trim any characters on the left of s.
-- @string s the string
-- @string[opt='%s'] chrs default any whitespace character,
-- but can be a string of characters to be trimmed
function stringx.lstrip(s,chrs)
assert_string(1,s)
return _strip(s,true,false,chrs)
end
lstrip = stringx.lstrip
--- trim any characters on the right of s.
-- @string s the string
-- @string[opt='%s'] chrs default any whitespace character,
-- but can be a string of characters to be trimmed
function stringx.rstrip(s,chrs)
assert_string(1,s)
return _strip(s,false,true,chrs)
end
--- trim any characters on both left and right of s.
-- @string s the string
-- @string[opt='%s'] chrs default any whitespace character,
-- but can be a string of characters to be trimmed
-- @usage stringx.strip(' --== Hello ==-- ', "- =") --> 'Hello'
function stringx.strip(s,chrs)
assert_string(1,s)
return _strip(s,true,true,chrs)
end
--- Partitioning Strings
-- @section partitioning
--- split a string using a pattern. Note that at least one value will be returned!
-- @string s the string
-- @string[opt='%s'] re a Lua string pattern (defaults to whitespace)
-- @return the parts of the string
-- @usage a,b = line:splitv('=')
-- @see utils.splitv
function stringx.splitv(s,re)
assert_string(1,s)
return utils.splitv(s,re)
end
-- The partition functions split a string using a delimiter into three parts:
-- the part before, the delimiter itself, and the part afterwards
local function _partition(p,delim,fn)
local i1,i2 = fn(p,delim)
if not i1 or i1 == -1 then
return p,'',''
else
if not i2 then i2 = i1 end
return sub(p,1,i1-1),sub(p,i1,i2),sub(p,i2+1)
end
end
--- partition the string using first occurance of a delimiter
-- @string s the string
-- @string ch delimiter (match as plain string, no patterns)
-- @return part before ch
-- @return ch
-- @return part after ch
-- @usage {stringx.partition('a,b,c', ','))} == {'a', ',', 'b,c'}
-- @usage {stringx.partition('abc', 'x'))} == {'abc', '', ''}
function stringx.partition(s,ch)
assert_string(1,s)
assert_nonempty_string(2,ch)
return _partition(s,ch,stringx.lfind)
end
--- partition the string p using last occurance of a delimiter
-- @string s the string
-- @string ch delimiter (match as plain string, no patterns)
-- @return part before ch
-- @return ch
-- @return part after ch
-- @usage {stringx.rpartition('a,b,c', ','))} == {'a,b', ',', 'c'}
-- @usage {stringx.rpartition('abc', 'x'))} == {'', '', 'abc'}
function stringx.rpartition(s,ch)
assert_string(1,s)
assert_nonempty_string(2,ch)
local a,b,c = _partition(s,ch,stringx.rfind)
if a == s then -- no match found
return c,b,a
end
return a,b,c
end
--- return the 'character' at the index.
-- @string s the string
-- @int idx an index (can be negative)
-- @return a substring of length 1 if successful, empty string otherwise.
function stringx.at(s,idx)
assert_string(1,s)
assert_arg(2,idx,'number')
return sub(s,idx,idx)
end
--- Text handling
-- @section text
--- indent a multiline string.
-- @tparam string s the (multiline) string
-- @tparam integer n the size of the indent
-- @tparam[opt=' '] string ch the character to use when indenting
-- @return indented string
function stringx.indent (s,n,ch)
assert_arg(1,s,'string')
assert_arg(2,n,'number')
local lines = usplit(s ,'\n')
local prefix = string.rep(ch or ' ',n)
for i, line in ipairs(lines) do
lines[i] = prefix..line
end
return concat(lines,'\n')..'\n'
end
--- dedent a multiline string by removing any initial indent.
-- useful when working with [[..]] strings.
-- Empty lines are ignored.
-- @tparam string s the (multiline) string
-- @return a string with initial indent zero.
-- @usage
-- local s = dedent [[
-- One
--
-- Two
--
-- Three
-- ]]
-- assert(s == [[
-- One
--
-- Two
--
-- Three
-- ]])
function stringx.dedent (s)
assert_arg(1,s,'string')
local lst = usplit(s,'\n')
if #lst>0 then
local ind_size = math.huge
for i, line in ipairs(lst) do
local i1, i2 = lst[i]:find('^%s*[^%s]')
if i1 and i2 < ind_size then
ind_size = i2
end
end
for i, line in ipairs(lst) do
lst[i] = lst[i]:sub(ind_size, -1)
end
end
return concat(lst,'\n')..'\n'
end
do
local buildline = function(words, size, breaklong)
-- if overflow is set, a word longer than size, will overflow the size
-- otherwise it will be chopped in line-length pieces
local line = {}
if #words[1] > size then
-- word longer than line
if not breaklong then
line[1] = words[1]
remove(words, 1)
else
line[1] = words[1]:sub(1, size)
words[1] = words[1]:sub(size + 1, -1)
end
else
local len = 0
while words[1] and (len + #words[1] <= size) or
(len == 0 and #words[1] == size) do
if words[1] ~= "" then
line[#line+1] = words[1]
len = len + #words[1] + 1
end
remove(words, 1)
end
end
return stringx.strip(concat(line, " ")), words
end
--- format a paragraph into lines so that they fit into a line width.
-- It will not break long words by default, so lines can be over the length
-- to that extent.
-- @tparam string s the string to format
-- @tparam[opt=70] integer width the margin width
-- @tparam[opt=false] boolean breaklong if truthy, words longer than the width given will be forced split.
-- @return a list of lines (List object), use `fill` to return a string instead of a `List`.
-- @see pl.List
-- @see fill
stringx.wrap = function(s, width, breaklong)
s = s:gsub('\n',' ') -- remove line breaks
s = stringx.strip(s) -- remove leading/trailing whitespace
if s == "" then
return { "" }
end
width = width or 70
local out = {}
local words = usplit(s, "%s")
while words[1] do
out[#out+1], words = buildline(words, width, breaklong)
end
return makelist(out)
end
end
--- format a paragraph so that it fits into a line width.
-- @tparam string s the string to format
-- @tparam[opt=70] integer width the margin width
-- @tparam[opt=false] boolean breaklong if truthy, words longer than the width given will be forced split.
-- @return a string, use `wrap` to return a list of lines instead of a string.
-- @see wrap
function stringx.fill (s,width,breaklong)
return concat(stringx.wrap(s,width,breaklong),'\n') .. '\n'
end
--- Template
-- @section Template
local function _substitute(s,tbl,safe)
local subst
if is_callable(tbl) then
subst = tbl
else
function subst(f)
local s = tbl[f]
if not s then
if safe then
return f
else
error("not present in table "..f)
end
else
return s
end
end
end
local res = gsub(s,'%${([%w_]+)}',subst)
return (gsub(res,'%$([%w_]+)',subst))
end
local Template = {}
stringx.Template = Template
Template.__index = Template
setmetatable(Template, {
__call = function(obj,tmpl)
return Template.new(tmpl)
end
})
--- Creates a new Template class.
-- This is a shortcut to `Template.new(tmpl)`.
-- @tparam string tmpl the template string
-- @function Template
-- @treturn Template
function Template.new(tmpl)
assert_arg(1,tmpl,'string')
local res = {}
res.tmpl = tmpl
setmetatable(res,Template)
return res
end
--- substitute values into a template, throwing an error.
-- This will throw an error if no name is found.
-- @tparam table tbl a table of name-value pairs.
-- @return string with place holders substituted
function Template:substitute(tbl)
assert_arg(1,tbl,'table')
return _substitute(self.tmpl,tbl,false)
end
--- substitute values into a template.
-- This version just passes unknown names through.
-- @tparam table tbl a table of name-value pairs.
-- @return string with place holders substituted
function Template:safe_substitute(tbl)
assert_arg(1,tbl,'table')
return _substitute(self.tmpl,tbl,true)
end
--- substitute values into a template, preserving indentation. <br>
-- If the value is a multiline string _or_ a template, it will insert
-- the lines at the correct indentation. <br>
-- Furthermore, if a template, then that template will be substituted
-- using the same table.
-- @tparam table tbl a table of name-value pairs.
-- @return string with place holders substituted
function Template:indent_substitute(tbl)
assert_arg(1,tbl,'table')
if not self.strings then
self.strings = usplit(self.tmpl,'\n')
end
-- the idea is to substitute line by line, grabbing any spaces as
-- well as the $var. If the value to be substituted contains newlines,
-- then we split that into lines and adjust the indent before inserting.
local function subst(line)
return line:gsub('(%s*)%$([%w_]+)',function(sp,f)
local subtmpl
local s = tbl[f]
if not s then error("not present in table "..f) end
if getmetatable(s) == Template then
subtmpl = s
s = s.tmpl
else
s = tostring(s)
end
if s:find '\n' then
local lines = usplit(s, '\n')
for i, line in ipairs(lines) do
lines[i] = sp..line
end
s = concat(lines, '\n') .. '\n'
end
if subtmpl then
return _substitute(s, tbl)
else
return s
end
end)
end
local lines = {}
for i, line in ipairs(self.strings) do
lines[i] = subst(line)
end
return concat(lines,'\n')..'\n'
end
--- Miscelaneous
-- @section misc
--- return an iterator over all lines in a string
-- @string s the string
-- @return an iterator
-- @usage
-- local line_no = 1
-- for line in stringx.lines(some_text) do
-- print(line_no, line)
-- line_no = line_no + 1
-- end
function stringx.lines(s)
assert_string(1,s)
if not s:find '\n$' then s = s..'\n' end
return s:gmatch('([^\n]*)\n')
end
--- inital word letters uppercase ('title case').
-- Here 'words' mean chunks of non-space characters.
-- @string s the string
-- @return a string with each word's first letter uppercase
-- @usage stringx.title("hello world") == "Hello World")
function stringx.title(s)
assert_string(1,s)
return (s:gsub('(%S)(%S*)',function(f,r)
return f:upper()..r:lower()
end))
end
stringx.capitalize = stringx.title
do
local ellipsis = '...'
local n_ellipsis = #ellipsis
--- Return a shortened version of a string.
-- Fits string within w characters. Removed characters are marked with ellipsis.
-- @string s the string
-- @int w the maxinum size allowed
-- @bool tail true if we want to show the end of the string (head otherwise)
-- @usage ('1234567890'):shorten(8) == '12345...'
-- @usage ('1234567890'):shorten(8, true) == '...67890'
-- @usage ('1234567890'):shorten(20) == '1234567890'
function stringx.shorten(s,w,tail)
assert_string(1,s)
if #s > w then
if w < n_ellipsis then return ellipsis:sub(1,w) end
if tail then
local i = #s - w + 1 + n_ellipsis
return ellipsis .. s:sub(i)
else
return s:sub(1,w-n_ellipsis) .. ellipsis
end
end
return s
end
end
do
-- Utility function that finds any patterns that match a long string's an open or close.
-- Note that having this function use the least number of equal signs that is possible is a harder algorithm to come up with.
-- Right now, it simply returns the greatest number of them found.
-- @param s The string
-- @return 'nil' if not found. If found, the maximum number of equal signs found within all matches.
local function has_lquote(s)
local lstring_pat = '([%[%]])(=*)%1'
local equals, new_equals, _
local finish = 1
repeat
_, finish, _, new_equals = s:find(lstring_pat, finish)
if new_equals then
equals = max(equals or 0, #new_equals)
end
until not new_equals
return equals
end
--- Quote the given string and preserve any control or escape characters, such that reloading the string in Lua returns the same result.
-- @param s The string to be quoted.
-- @return The quoted string.
function stringx.quote_string(s)
assert_string(1,s)
-- Find out if there are any embedded long-quote sequences that may cause issues.
-- This is important when strings are embedded within strings, like when serializing.
-- Append a closing bracket to catch unfinished long-quote sequences at the end of the string.
local equal_signs = has_lquote(s .. "]")
-- Note that strings containing "\r" can't be quoted using long brackets
-- as Lua lexer converts all newlines to "\n" within long strings.
if (s:find("\n") or equal_signs) and not s:find("\r") then
-- If there is an embedded sequence that matches a long quote, then
-- find the one with the maximum number of = signs and add one to that number.
equal_signs = ("="):rep((equal_signs or -1) + 1)
-- Long strings strip out leading newline. We want to retain that, when quoting.
if s:find("^\n") then s = "\n" .. s end
local lbracket, rbracket =
"[" .. equal_signs .. "[",
"]" .. equal_signs .. "]"
s = lbracket .. s .. rbracket
else
-- Escape funny stuff. Lua 5.1 does not handle "\r" correctly.
s = ("%q"):format(s):gsub("\r", "\\r")
end
return s
end
end
--- Python-style formatting operator.
-- Calling `text.format_operator()` overloads the % operator for strings to give
-- Python/Ruby style formated output.
-- This is extended to also do template-like substitution for map-like data.
--
-- Note this goes further than the original, and will allow these cases:
--
-- 1. a single value
-- 2. a list of values
-- 3. a map of var=value pairs
-- 4. a function, as in gsub
--
-- For the second two cases, it uses $-variable substituion.
--
-- When called, this function will monkey-patch the global `string` metatable by
-- adding a `__mod` method.
--
-- See <a href="http://lua-users.org/wiki/StringInterpolation">the lua-users wiki</a>
--
-- @usage
-- require 'pl.text'.format_operator()
-- local out1 = '%s = %5.3f' % {'PI',math.pi} --> 'PI = 3.142'
-- local out2 = '$name = $value' % {name='dog',value='Pluto'} --> 'dog = Pluto'
function stringx.format_operator()
local format = string.format
-- a more forgiving version of string.format, which applies
-- tostring() to any value with a %s format.
local function formatx (fmt,...)
local args = pack(...)
local i = 1
for p in fmt:gmatch('%%.') do
if p == '%s' and type(args[i]) ~= 'string' then
args[i] = tostring(args[i])
end
i = i + 1
end
return format(fmt,unpack(args))
end
local function basic_subst(s,t)
return (s:gsub('%$([%w_]+)',t))
end
getmetatable("").__mod = function(a, b)
if b == nil then
return a
elseif type(b) == "table" and getmetatable(b) == nil then
if #b == 0 then -- assume a map-like table
return _substitute(a,b,true)
else
return formatx(a,unpack(b))
end
elseif type(b) == 'function' then
return basic_subst(a,b)
else
return formatx(a,b)
end
end
end
--- import the stringx functions into the global string (meta)table
function stringx.import()
utils.import(stringx,string)
end
return stringx

View file

@ -0,0 +1,999 @@
--- Extended operations on Lua tables.
--
-- See @{02-arrays.md.Useful_Operations_on_Tables|the Guide}
--
-- Dependencies: `pl.utils`, `pl.types`
-- @module pl.tablex
local utils = require ('pl.utils')
local types = require ('pl.types')
local getmetatable,setmetatable,require = getmetatable,setmetatable,require
local tsort,append,remove = table.sort,table.insert,table.remove
local min = math.min
local pairs,type,unpack,select,tostring = pairs,type,utils.unpack,select,tostring
local function_arg = utils.function_arg
local assert_arg = utils.assert_arg
local tablex = {}
-- generally, functions that make copies of tables try to preserve the metatable.
-- However, when the source has no obvious type, then we attach appropriate metatables
-- like List, Map, etc to the result.
local function setmeta (res,tbl,pl_class)
local mt = getmetatable(tbl) or pl_class and require('pl.' .. pl_class)
return mt and setmetatable(res, mt) or res
end
local function makelist(l)
return setmetatable(l, require('pl.List'))
end
local function makemap(m)
return setmetatable(m, require('pl.Map'))
end
local function complain (idx,msg)
error(('argument %d is not %s'):format(idx,msg),3)
end
local function assert_arg_indexable (idx,val)
if not types.is_indexable(val) then
complain(idx,"indexable")
end
end
local function assert_arg_iterable (idx,val)
if not types.is_iterable(val) then
complain(idx,"iterable")
end
end
local function assert_arg_writeable (idx,val)
if not types.is_writeable(val) then
complain(idx,"writeable")
end
end
--- copy a table into another, in-place.
-- @within Copying
-- @tab t1 destination table
-- @tab t2 source (actually any iterable object)
-- @return first table
function tablex.update (t1,t2)
assert_arg_writeable(1,t1)
assert_arg_iterable(2,t2)
for k,v in pairs(t2) do
t1[k] = v
end
return t1
end
--- total number of elements in this table.
-- Note that this is distinct from `#t`, which is the number
-- of values in the array part; this value will always
-- be greater or equal. The difference gives the size of
-- the hash part, for practical purposes. Works for any
-- object with a __pairs metamethod.
-- @tab t a table
-- @return the size
function tablex.size (t)
assert_arg_iterable(1,t)
local i = 0
for k in pairs(t) do i = i + 1 end
return i
end
--- make a shallow copy of a table
-- @within Copying
-- @tab t an iterable source
-- @return new table
function tablex.copy (t)
assert_arg_iterable(1,t)
local res = {}
for k,v in pairs(t) do
res[k] = v
end
return res
end
local function cycle_aware_copy(t, cache)
if type(t) ~= 'table' then return t end
if cache[t] then return cache[t] end
assert_arg_iterable(1,t)
local res = {}
cache[t] = res
local mt = getmetatable(t)
for k,v in pairs(t) do
k = cycle_aware_copy(k, cache)
v = cycle_aware_copy(v, cache)
res[k] = v
end
setmetatable(res,mt)
return res
end
--- make a deep copy of a table, recursively copying all the keys and fields.
-- This supports cycles in tables; cycles will be reproduced in the copy.
-- This will also set the copied table's metatable to that of the original.
-- @within Copying
-- @tab t A table
-- @return new table
function tablex.deepcopy(t)
return cycle_aware_copy(t,{})
end
local abs = math.abs
local function cycle_aware_compare(t1,t2,ignore_mt,eps,cache)
if cache[t1] and cache[t1][t2] then return true end
local ty1 = type(t1)
local ty2 = type(t2)
if ty1 ~= ty2 then return false end
-- non-table types can be directly compared
if ty1 ~= 'table' then
if ty1 == 'number' and eps then return abs(t1-t2) < eps end
return t1 == t2
end
-- as well as tables which have the metamethod __eq
local mt = getmetatable(t1)
if not ignore_mt and mt and mt.__eq then return t1 == t2 end
for k1 in pairs(t1) do
if t2[k1]==nil then return false end
end
for k2 in pairs(t2) do
if t1[k2]==nil then return false end
end
cache[t1] = cache[t1] or {}
cache[t1][t2] = true
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if not cycle_aware_compare(v1,v2,ignore_mt,eps,cache) then return false end
end
return true
end
--- compare two values.
-- if they are tables, then compare their keys and fields recursively.
-- @within Comparing
-- @param t1 A value
-- @param t2 A value
-- @bool[opt] ignore_mt if true, ignore __eq metamethod (default false)
-- @number[opt] eps if defined, then used for any number comparisons
-- @return true or false
function tablex.deepcompare(t1,t2,ignore_mt,eps)
return cycle_aware_compare(t1,t2,ignore_mt,eps,{})
end
--- compare two arrays using a predicate.
-- @within Comparing
-- @array t1 an array
-- @array t2 an array
-- @func cmp A comparison function; `bool = cmp(t1_value, t2_value)`
-- @return true or false
-- @usage
-- assert(tablex.compare({ 1, 2, 3 }, { 1, 2, 3 }, "=="))
--
-- assert(tablex.compare(
-- {1,2,3, hello = "world"}, -- fields are not compared!
-- {1,2,3}, function(v1, v2) return v1 == v2 end)
function tablex.compare (t1,t2,cmp)
assert_arg_indexable(1,t1)
assert_arg_indexable(2,t2)
if #t1 ~= #t2 then return false end
cmp = function_arg(3,cmp)
for k = 1,#t1 do
if not cmp(t1[k],t2[k]) then return false end
end
return true
end
--- compare two list-like tables using an optional predicate, without regard for element order.
-- @within Comparing
-- @array t1 a list-like table
-- @array t2 a list-like table
-- @param cmp A comparison function (may be nil)
function tablex.compare_no_order (t1,t2,cmp)
assert_arg_indexable(1,t1)
assert_arg_indexable(2,t2)
if cmp then cmp = function_arg(3,cmp) end
if #t1 ~= #t2 then return false end
local visited = {}
for i = 1,#t1 do
local val = t1[i]
local gotcha
for j = 1,#t2 do
if not visited[j] then
local match
if cmp then match = cmp(val,t2[j]) else match = val == t2[j] end
if match then
gotcha = j
break
end
end
end
if not gotcha then return false end
visited[gotcha] = true
end
return true
end
--- return the index of a value in a list.
-- Like string.find, there is an optional index to start searching,
-- which can be negative.
-- @within Finding
-- @array t A list-like table
-- @param val A value
-- @int idx index to start; -1 means last element,etc (default 1)
-- @return index of value or nil if not found
-- @usage find({10,20,30},20) == 2
-- @usage find({'a','b','a','c'},'a',2) == 3
function tablex.find(t,val,idx)
assert_arg_indexable(1,t)
idx = idx or 1
if idx < 0 then idx = #t + idx + 1 end
for i = idx,#t do
if t[i] == val then return i end
end
return nil
end
--- return the index of a value in a list, searching from the end.
-- Like string.find, there is an optional index to start searching,
-- which can be negative.
-- @within Finding
-- @array t A list-like table
-- @param val A value
-- @param idx index to start; -1 means last element,etc (default `#t`)
-- @return index of value or nil if not found
-- @usage rfind({10,10,10},10) == 3
function tablex.rfind(t,val,idx)
assert_arg_indexable(1,t)
idx = idx or #t
if idx < 0 then idx = #t + idx + 1 end
for i = idx,1,-1 do
if t[i] == val then return i end
end
return nil
end
--- return the index (or key) of a value in a table using a comparison function.
--
-- *NOTE*: the 2nd return value of this function, the value returned
-- by the comparison function, has a limitation that it cannot be `false`.
-- Because if it is, then it indicates the comparison failed, and the
-- function will continue the search. See examples.
-- @within Finding
-- @tab t A table
-- @func cmp A comparison function
-- @param arg an optional second argument to the function
-- @return index of value, or nil if not found
-- @return value returned by comparison function (cannot be `false`!)
-- @usage
-- -- using an operator
-- local lst = { "Rudolph", true, false, 15 }
-- local idx, cmp_result = tablex.rfind(lst, "==", "Rudolph")
-- assert(idx == 1)
-- assert(cmp_result == true)
--
-- local idx, cmp_result = tablex.rfind(lst, "==", false)
-- assert(idx == 3)
-- assert(cmp_result == true) -- looking up 'false' works!
--
-- -- using a function returning the value looked up
-- local cmp = function(v1, v2) return v1 == v2 and v2 end
-- local idx, cmp_result = tablex.rfind(lst, cmp, "Rudolph")
-- assert(idx == 1)
-- assert(cmp_result == "Rudolph") -- the value is returned
--
-- -- NOTE: this fails, since 'false' cannot be returned!
-- local idx, cmp_result = tablex.rfind(lst, cmp, false)
-- assert(idx == nil) -- looking up 'false' failed!
-- assert(cmp_result == nil)
function tablex.find_if(t,cmp,arg)
assert_arg_iterable(1,t)
cmp = function_arg(2,cmp)
for k,v in pairs(t) do
local c = cmp(v,arg)
if c then return k,c end
end
return nil
end
--- return a list of all values in a table indexed by another list.
-- @tab tbl a table
-- @array idx an index table (a list of keys)
-- @return a list-like table
-- @usage index_by({10,20,30,40},{2,4}) == {20,40}
-- @usage index_by({one=1,two=2,three=3},{'one','three'}) == {1,3}
function tablex.index_by(tbl,idx)
assert_arg_indexable(1,tbl)
assert_arg_indexable(2,idx)
local res = {}
for i = 1,#idx do
res[i] = tbl[idx[i]]
end
return setmeta(res,tbl,'List')
end
--- apply a function to all values of a table.
-- This returns a table of the results.
-- Any extra arguments are passed to the function.
-- @within MappingAndFiltering
-- @func fun A function that takes at least one argument
-- @tab t A table
-- @param ... optional arguments
-- @usage map(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900,fred=4}
function tablex.map(fun,t,...)
assert_arg_iterable(1,t)
fun = function_arg(1,fun)
local res = {}
for k,v in pairs(t) do
res[k] = fun(v,...)
end
return setmeta(res,t)
end
--- apply a function to all values of a list.
-- This returns a table of the results.
-- Any extra arguments are passed to the function.
-- @within MappingAndFiltering
-- @func fun A function that takes at least one argument
-- @array t a table (applies to array part)
-- @param ... optional arguments
-- @return a list-like table
-- @usage imap(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900}
function tablex.imap(fun,t,...)
assert_arg_indexable(1,t)
fun = function_arg(1,fun)
local res = {}
for i = 1,#t do
res[i] = fun(t[i],...) or false
end
return setmeta(res,t,'List')
end
--- apply a named method to values from a table.
-- @within MappingAndFiltering
-- @string name the method name
-- @array t a list-like table
-- @param ... any extra arguments to the method
-- @return a `List` with the results of the method (1st result only)
-- @usage
-- local Car = {}
-- Car.__index = Car
-- function Car.new(car)
-- return setmetatable(car or {}, Car)
-- end
-- Car.speed = 0
-- function Car:faster(increase)
-- self.speed = self.speed + increase
-- return self.speed
-- end
--
-- local ferrari = Car.new{ name = "Ferrari" }
-- local lamborghini = Car.new{ name = "Lamborghini", speed = 50 }
-- local cars = { ferrari, lamborghini }
--
-- assert(ferrari.speed == 0)
-- assert(lamborghini.speed == 50)
-- tablex.map_named_method("faster", cars, 10)
-- assert(ferrari.speed == 10)
-- assert(lamborghini.speed == 60)
function tablex.map_named_method (name,t,...)
utils.assert_string(1,name)
assert_arg_indexable(2,t)
local res = {}
for i = 1,#t do
local val = t[i]
local fun = val[name]
res[i] = fun(val,...)
end
return setmeta(res,t,'List')
end
--- apply a function to all values of a table, in-place.
-- Any extra arguments are passed to the function.
-- @func fun A function that takes at least one argument
-- @tab t a table
-- @param ... extra arguments passed to `fun`
-- @see tablex.foreach
function tablex.transform (fun,t,...)
assert_arg_iterable(1,t)
fun = function_arg(1,fun)
for k,v in pairs(t) do
t[k] = fun(v,...)
end
end
--- generate a table of all numbers in a range.
-- This is consistent with a numerical for loop.
-- @int start number
-- @int finish number
-- @int[opt=1] step make this negative for start < finish
function tablex.range (start,finish,step)
local res
step = step or 1
if start == finish then
res = {start}
elseif (start > finish and step > 0) or (finish > start and step < 0) then
res = {}
else
local k = 1
res = {}
for i=start,finish,step do res[k]=i; k=k+1 end
end
return makelist(res)
end
--- apply a function to values from two tables.
-- @within MappingAndFiltering
-- @func fun a function of at least two arguments
-- @tab t1 a table
-- @tab t2 a table
-- @param ... extra arguments
-- @return a table
-- @usage map2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23,m=44}
function tablex.map2 (fun,t1,t2,...)
assert_arg_iterable(1,t1)
assert_arg_iterable(2,t2)
fun = function_arg(1,fun)
local res = {}
for k,v in pairs(t1) do
res[k] = fun(v,t2[k],...)
end
return setmeta(res,t1,'List')
end
--- apply a function to values from two arrays.
-- The result will be the length of the shortest array.
-- @within MappingAndFiltering
-- @func fun a function of at least two arguments
-- @array t1 a list-like table
-- @array t2 a list-like table
-- @param ... extra arguments
-- @usage imap2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23}
function tablex.imap2 (fun,t1,t2,...)
assert_arg_indexable(2,t1)
assert_arg_indexable(3,t2)
fun = function_arg(1,fun)
local res,n = {},math.min(#t1,#t2)
for i = 1,n do
res[i] = fun(t1[i],t2[i],...)
end
return res
end
--- 'reduce' a list using a binary function.
-- @func fun a function of two arguments
-- @array t a list-like table
-- @array memo optional initial memo value. Defaults to first value in table.
-- @return the result of the function
-- @usage reduce('+',{1,2,3,4}) == 10
function tablex.reduce (fun,t,memo)
assert_arg_indexable(2,t)
fun = function_arg(1,fun)
local n = #t
if n == 0 then
return memo
end
local res = memo and fun(memo, t[1]) or t[1]
for i = 2,n do
res = fun(res,t[i])
end
return res
end
--- apply a function to all elements of a table.
-- The arguments to the function will be the value,
-- the key and _finally_ any extra arguments passed to this function.
-- Note that the Lua 5.0 function table.foreach passed the _key_ first.
-- @within Iterating
-- @tab t a table
-- @func fun a function on the elements; `function(value, key, ...)`
-- @param ... extra arguments passed to `fun`
-- @see tablex.transform
function tablex.foreach(t,fun,...)
assert_arg_iterable(1,t)
fun = function_arg(2,fun)
for k,v in pairs(t) do
fun(v,k,...)
end
end
--- apply a function to all elements of a list-like table in order.
-- The arguments to the function will be the value,
-- the index and _finally_ any extra arguments passed to this function
-- @within Iterating
-- @array t a table
-- @func fun a function with at least one argument
-- @param ... optional arguments
function tablex.foreachi(t,fun,...)
assert_arg_indexable(1,t)
fun = function_arg(2,fun)
for i = 1,#t do
fun(t[i],i,...)
end
end
--- Apply a function to a number of tables.
-- A more general version of map
-- The result is a table containing the result of applying that function to the
-- ith value of each table. Length of output list is the minimum length of all the lists
-- @within MappingAndFiltering
-- @func fun a function of n arguments
-- @tab ... n tables
-- @usage mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}) is {111,222,333}
-- @usage mapn(math.max, {1,20,300},{10,2,3},{100,200,100}) is {100,200,300}
-- @param fun A function that takes as many arguments as there are tables
function tablex.mapn(fun,...)
fun = function_arg(1,fun)
local res = {}
local lists = {...}
local minn = 1e40
for i = 1,#lists do
minn = min(minn,#(lists[i]))
end
for i = 1,minn do
local args,k = {},1
for j = 1,#lists do
args[k] = lists[j][i]
k = k + 1
end
res[#res+1] = fun(unpack(args))
end
return res
end
--- call the function with the key and value pairs from a table.
-- The function can return a value and a key (note the order!). If both
-- are not nil, then this pair is inserted into the result: if the key already exists, we convert the value for that
-- key into a table and append into it. If only value is not nil, then it is appended to the result.
-- @within MappingAndFiltering
-- @func fun A function which will be passed each key and value as arguments, plus any extra arguments to pairmap.
-- @tab t A table
-- @param ... optional arguments
-- @usage pairmap(function(k,v) return v end,{fred=10,bonzo=20}) is {10,20} _or_ {20,10}
-- @usage pairmap(function(k,v) return {k,v},k end,{one=1,two=2}) is {one={'one',1},two={'two',2}}
function tablex.pairmap(fun,t,...)
assert_arg_iterable(1,t)
fun = function_arg(1,fun)
local res = {}
for k,v in pairs(t) do
local rv,rk = fun(k,v,...)
if rk then
if res[rk] then
if type(res[rk]) == 'table' then
table.insert(res[rk],rv)
else
res[rk] = {res[rk], rv}
end
else
res[rk] = rv
end
else
res[#res+1] = rv
end
end
return res
end
local function keys_op(i,v) return i end
--- return all the keys of a table in arbitrary order.
-- @within Extraction
-- @tab t A list-like table where the values are the keys of the input table
function tablex.keys(t)
assert_arg_iterable(1,t)
return makelist(tablex.pairmap(keys_op,t))
end
local function values_op(i,v) return v end
--- return all the values of the table in arbitrary order
-- @within Extraction
-- @tab t A list-like table where the values are the values of the input table
function tablex.values(t)
assert_arg_iterable(1,t)
return makelist(tablex.pairmap(values_op,t))
end
local function index_map_op (i,v) return i,v end
--- create an index map from a list-like table. The original values become keys,
-- and the associated values are the indices into the original list.
-- @array t a list-like table
-- @return a map-like table
function tablex.index_map (t)
assert_arg_indexable(1,t)
return makemap(tablex.pairmap(index_map_op,t))
end
local function set_op(i,v) return true,v end
--- create a set from a list-like table. A set is a table where the original values
-- become keys, and the associated values are all true.
-- @array t a list-like table
-- @return a set (a map-like table)
function tablex.makeset (t)
assert_arg_indexable(1,t)
return setmetatable(tablex.pairmap(set_op,t),require('pl.Set'))
end
--- combine two tables, either as union or intersection. Corresponds to
-- set operations for sets () but more general. Not particularly
-- useful for list-like tables.
-- @within Merging
-- @tab t1 a table
-- @tab t2 a table
-- @bool dup true for a union, false for an intersection.
-- @usage merge({alice=23,fred=34},{bob=25,fred=34}) is {fred=34}
-- @usage merge({alice=23,fred=34},{bob=25,fred=34},true) is {bob=25,fred=34,alice=23}
-- @see tablex.index_map
function tablex.merge (t1,t2,dup)
assert_arg_iterable(1,t1)
assert_arg_iterable(2,t2)
local res = {}
for k,v in pairs(t1) do
if dup or t2[k] then res[k] = v end
end
if dup then
for k,v in pairs(t2) do
res[k] = v
end
end
return setmeta(res,t1,'Map')
end
--- the union of two map-like tables.
-- If there are duplicate keys, the second table wins.
-- @tab t1 a table
-- @tab t2 a table
-- @treturn tab
-- @see tablex.merge
function tablex.union(t1, t2)
return tablex.merge(t1, t2, true)
end
--- the intersection of two map-like tables.
-- @tab t1 a table
-- @tab t2 a table
-- @treturn tab
-- @see tablex.merge
function tablex.intersection(t1, t2)
return tablex.merge(t1, t2, false)
end
--- a new table which is the difference of two tables.
-- With sets (where the values are all true) this is set difference and
-- symmetric difference depending on the third parameter.
-- @within Merging
-- @tab s1 a map-like table or set
-- @tab s2 a map-like table or set
-- @bool symm symmetric difference (default false)
-- @return a map-like table or set
function tablex.difference (s1,s2,symm)
assert_arg_iterable(1,s1)
assert_arg_iterable(2,s2)
local res = {}
for k,v in pairs(s1) do
if s2[k] == nil then res[k] = v end
end
if symm then
for k,v in pairs(s2) do
if s1[k] == nil then res[k] = v end
end
end
return setmeta(res,s1,'Map')
end
--- A table where the key/values are the values and value counts of the table.
-- @array t a list-like table
-- @func cmp a function that defines equality (otherwise uses ==)
-- @return a map-like table
-- @see seq.count_map
function tablex.count_map (t,cmp)
assert_arg_indexable(1,t)
local res,mask = {},{}
cmp = function_arg(2,cmp or '==')
local n = #t
for i = 1,#t do
local v = t[i]
if not mask[v] then
mask[v] = true
-- check this value against all other values
res[v] = 1 -- there's at least one instance
for j = i+1,n do
local w = t[j]
local ok = cmp(v,w)
if ok then
res[v] = res[v] + 1
mask[w] = true
end
end
end
end
return makemap(res)
end
--- filter an array's values using a predicate function
-- @within MappingAndFiltering
-- @array t a list-like table
-- @func pred a boolean function
-- @param arg optional argument to be passed as second argument of the predicate
function tablex.filter (t,pred,arg)
assert_arg_indexable(1,t)
pred = function_arg(2,pred)
local res,k = {},1
for i = 1,#t do
local v = t[i]
if pred(v,arg) then
res[k] = v
k = k + 1
end
end
return setmeta(res,t,'List')
end
--- return a table where each element is a table of the ith values of an arbitrary
-- number of tables. It is equivalent to a matrix transpose.
-- @within Merging
-- @usage zip({10,20,30},{100,200,300}) is {{10,100},{20,200},{30,300}}
-- @array ... arrays to be zipped
function tablex.zip(...)
return tablex.mapn(function(...) return {...} end,...)
end
local _copy
function _copy (dest,src,idest,isrc,nsrc,clean_tail)
idest = idest or 1
isrc = isrc or 1
local iend
if not nsrc then
nsrc = #src
iend = #src
else
iend = isrc + min(nsrc-1,#src-isrc)
end
if dest == src then -- special case
if idest > isrc and iend >= idest then -- overlapping ranges
src = tablex.sub(src,isrc,nsrc)
isrc = 1; iend = #src
end
end
for i = isrc,iend do
dest[idest] = src[i]
idest = idest + 1
end
if clean_tail then
tablex.clear(dest,idest)
end
return dest
end
--- copy an array into another one, clearing `dest` after `idest+nsrc`, if necessary.
-- @within Copying
-- @array dest a list-like table
-- @array src a list-like table
-- @int[opt=1] idest where to start copying values into destination
-- @int[opt=1] isrc where to start copying values from source
-- @int[opt=#src] nsrc number of elements to copy from source
function tablex.icopy (dest,src,idest,isrc,nsrc)
assert_arg_indexable(1,dest)
assert_arg_indexable(2,src)
return _copy(dest,src,idest,isrc,nsrc,true)
end
--- copy an array into another one.
-- @within Copying
-- @array dest a list-like table
-- @array src a list-like table
-- @int[opt=1] idest where to start copying values into destination
-- @int[opt=1] isrc where to start copying values from source
-- @int[opt=#src] nsrc number of elements to copy from source
function tablex.move (dest,src,idest,isrc,nsrc)
assert_arg_indexable(1,dest)
assert_arg_indexable(2,src)
return _copy(dest,src,idest,isrc,nsrc,false)
end
function tablex._normalize_slice(self,first,last)
local sz = #self
if not first then first=1 end
if first<0 then first=sz+first+1 end
-- make the range _inclusive_!
if not last then last=sz end
if last < 0 then last=sz+1+last end
return first,last
end
--- Extract a range from a table, like 'string.sub'.
-- If first or last are negative then they are relative to the end of the list
-- eg. sub(t,-2) gives last 2 entries in a list, and
-- sub(t,-4,-2) gives from -4th to -2nd
-- @within Extraction
-- @array t a list-like table
-- @int first An index
-- @int last An index
-- @return a new List
function tablex.sub(t,first,last)
assert_arg_indexable(1,t)
first,last = tablex._normalize_slice(t,first,last)
local res={}
for i=first,last do append(res,t[i]) end
return setmeta(res,t,'List')
end
--- set an array range to a value. If it's a function we use the result
-- of applying it to the indices.
-- @array t a list-like table
-- @param val a value
-- @int[opt=1] i1 start range
-- @int[opt=#t] i2 end range
function tablex.set (t,val,i1,i2)
assert_arg_indexable(1,t)
i1,i2 = i1 or 1,i2 or #t
if types.is_callable(val) then
for i = i1,i2 do
t[i] = val(i)
end
else
for i = i1,i2 do
t[i] = val
end
end
end
--- create a new array of specified size with initial value.
-- @int n size
-- @param val initial value (can be `nil`, but don't expect `#` to work!)
-- @return the table
function tablex.new (n,val)
local res = {}
tablex.set(res,val,1,n)
return res
end
--- clear out the contents of a table.
-- @array t a list
-- @param istart optional start position
function tablex.clear(t,istart)
istart = istart or 1
for i = istart,#t do remove(t) end
end
--- insert values into a table.
-- similar to `table.insert` but inserts values from given table `values`,
-- not the object itself, into table `t` at position `pos`.
-- @within Copying
-- @array t the list
-- @int[opt] position (default is at end)
-- @array values
function tablex.insertvalues(t, ...)
assert_arg(1,t,'table')
local pos, values
if select('#', ...) == 1 then
pos,values = #t+1, ...
else
pos,values = ...
end
if #values > 0 then
for i=#t,pos,-1 do
t[i+#values] = t[i]
end
local offset = 1 - pos
for i=pos,pos+#values-1 do
t[i] = values[i + offset]
end
end
return t
end
--- remove a range of values from a table.
-- End of range may be negative.
-- @array t a list-like table
-- @int i1 start index
-- @int i2 end index
-- @return the table
function tablex.removevalues (t,i1,i2)
assert_arg(1,t,'table')
i1,i2 = tablex._normalize_slice(t,i1,i2)
for i = i1,i2 do
remove(t,i1)
end
return t
end
local _find
_find = function (t,value,tables)
for k,v in pairs(t) do
if v == value then return k end
end
for k,v in pairs(t) do
if not tables[v] and type(v) == 'table' then
tables[v] = true
local res = _find(v,value,tables)
if res then
res = tostring(res)
if type(k) ~= 'string' then
return '['..k..']'..res
else
return k..'.'..res
end
end
end
end
end
--- find a value in a table by recursive search.
-- @within Finding
-- @tab t the table
-- @param value the value
-- @array[opt] exclude any tables to avoid searching
-- @return a fieldspec, e.g. 'a.b' or 'math.sin'
-- @usage search(_G,math.sin,{package.path}) == 'math.sin'
function tablex.search (t,value,exclude)
assert_arg_iterable(1,t)
local tables = {[t]=true}
if exclude then
for _,v in pairs(exclude) do tables[v] = true end
end
return _find(t,value,tables)
end
--- return an iterator to a table sorted by its keys
-- @within Iterating
-- @tab t the table
-- @func f an optional comparison function (f(x,y) is true if x < y)
-- @usage for k,v in tablex.sort(t) do print(k,v) end
-- @return an iterator to traverse elements sorted by the keys
function tablex.sort(t,f)
local keys = {}
for k in pairs(t) do keys[#keys + 1] = k end
tsort(keys,f)
local i = 0
return function()
i = i + 1
return keys[i], t[keys[i]]
end
end
--- return an iterator to a table sorted by its values
-- @within Iterating
-- @tab t the table
-- @func f an optional comparison function (f(x,y) is true if x < y)
-- @usage for k,v in tablex.sortv(t) do print(k,v) end
-- @return an iterator to traverse elements sorted by the values
function tablex.sortv(t,f)
f = function_arg(2, f or '<')
local keys = {}
for k in pairs(t) do keys[#keys + 1] = k end
tsort(keys,function(x, y) return f(t[x], t[y]) end)
local i = 0
return function()
i = i + 1
return keys[i], t[keys[i]]
end
end
--- modifies a table to be read only.
-- This only offers weak protection. Tables can still be modified with
-- `table.insert` and `rawset`.
--
-- *NOTE*: for Lua 5.1 length, pairs and ipairs will not work, since the
-- equivalent metamethods are only available in Lua 5.2 and newer.
-- @tab t the table
-- @return the table read only (a proxy).
function tablex.readonly(t)
local mt = {
__index=t,
__newindex=function(t, k, v) error("Attempt to modify read-only table", 2) end,
__pairs=function() return pairs(t) end,
__ipairs=function() return ipairs(t) end,
__len=function() return #t end,
__metatable=false
}
return setmetatable({}, mt)
end
return tablex

View file

@ -0,0 +1,202 @@
--- A template preprocessor.
-- Originally by [Ricki Lake](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor)
--
-- There are two rules:
--
-- * lines starting with # are Lua
-- * otherwise, `$(expr)` is the result of evaluating `expr`
--
-- Example:
--
-- # for i = 1,3 do
-- $(i) Hello, Word!
-- # end
-- ===>
-- 1 Hello, Word!
-- 2 Hello, Word!
-- 3 Hello, Word!
--
-- Other escape characters can be used, when the defaults conflict
-- with the output language.
--
-- > for _,n in pairs{'one','two','three'} do
-- static int l_${n} (luaState *state);
-- > end
--
-- See @{03-strings.md.Another_Style_of_Template|the Guide}.
--
-- Dependencies: `pl.utils`
-- @module pl.template
local utils = require 'pl.utils'
local append,format,strsub,strfind,strgsub = table.insert,string.format,string.sub,string.find,string.gsub
local APPENDER = "\n__R_size = __R_size + 1; __R_table[__R_size] = "
local function parseDollarParen(pieces, chunk, exec_pat, newline)
local s = 1
for term, executed, e in chunk:gmatch(exec_pat) do
executed = '('..strsub(executed,2,-2)..')'
append(pieces, APPENDER..format("%q", strsub(chunk,s, term - 1)))
append(pieces, APPENDER..format("__tostring(%s or '')", executed))
s = e
end
local r
if newline then
r = format("%q", strgsub(strsub(chunk,s),"\n",""))
else
r = format("%q", strsub(chunk,s))
end
if r ~= '""' then
append(pieces, APPENDER..r)
end
end
local function parseHashLines(chunk,inline_escape,brackets,esc,newline)
local exec_pat = "()"..inline_escape.."(%b"..brackets..")()"
local esc_pat = esc.."+([^\n]*\n?)"
local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat
local pieces, s = {"return function()\nlocal __R_size, __R_table, __tostring = 0, {}, __tostring", n = 1}, 1
while true do
local _, e, lua = strfind(chunk,esc_pat1, s)
if not e then
local ss
ss, e, lua = strfind(chunk,esc_pat2, s)
parseDollarParen(pieces, strsub(chunk,s, ss), exec_pat, newline)
if not e then break end
end
if strsub(lua, -1, -1) == "\n" then lua = strsub(lua, 1, -2) end
append(pieces, "\n"..lua)
s = e + 1
end
append(pieces, "\nreturn __R_table\nend")
-- let's check for a special case where there is nothing to template, but it's
-- just a single static string
local short = false
if (#pieces == 3) and (pieces[2]:find(APPENDER, 1, true) == 1) then
pieces = { "return " .. pieces[2]:sub(#APPENDER+1,-1) }
short = true
end
-- if short == true, the generated function will not return a table of strings,
-- but a single string
return table.concat(pieces), short
end
local template = {}
--- expand the template using the specified environment.
-- This function will compile and render the template. For more performant
-- recurring usage use the two step approach by using `compile` and `ct:render`.
-- There are six special fields in the environment table `env`
--
-- * `_parent`: continue looking up in this table (e.g. `_parent=_G`).
-- * `_brackets`: bracket pair that wraps inline Lua expressions, default is '()'.
-- * `_escape`: character marking Lua lines, default is '#'
-- * `_inline_escape`: character marking inline Lua expression, default is '$'.
-- * `_chunk_name`: chunk name for loaded templates, used if there
-- is an error in Lua code. Default is 'TMP'.
-- * `_debug`: if truthy, the generated code will be printed upon a render error
--
-- @string str the template string
-- @tab[opt] env the environment
-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last
-- return value (`source_code`) is only returned if the debug option is used.
function template.substitute(str,env)
env = env or {}
local t, err = template.compile(str, {
chunk_name = rawget(env,"_chunk_name"),
escape = rawget(env,"_escape"),
inline_escape = rawget(env,"_inline_escape"),
inline_brackets = rawget(env,"_brackets"),
newline = nil,
debug = rawget(env,"_debug")
})
if not t then return t, err end
return t:render(env, rawget(env,"_parent"), rawget(env,"_debug"))
end
--- executes the previously compiled template and renders it.
-- @function ct:render
-- @tab[opt] env the environment.
-- @tab[opt] parent continue looking up in this table (e.g. `parent=_G`).
-- @bool[opt] db if thruthy, it will print the code upon a render error
-- (provided the template was compiled with the debug option).
-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last return value
-- (`source_code`) is only returned if the template was compiled with the debug option.
-- @usage
-- local ct, err = template.compile(my_template)
-- local rendered , err = ct:render(my_env, parent)
local render = function(self, env, parent, db)
env = env or {}
if parent then -- parent is a bit silly, but for backward compatibility retained
setmetatable(env, {__index = parent})
end
setmetatable(self.env, {__index = env})
local res, out = xpcall(self.fn, debug.traceback)
if not res then
if self.code and db then print(self.code) end
return nil, out, self.code
end
return table.concat(out), nil, self.code
end
--- compiles the template.
-- Returns an object that can repeatedly be rendered without parsing/compiling
-- the template again.
-- The options passed in the `opts` table support the following options:
--
-- * `chunk_name`: chunk name for loaded templates, used if there
-- is an error in Lua code. Default is 'TMP'.
-- * `escape`: character marking Lua lines, default is '#'
-- * `inline_escape`: character marking inline Lua expression, default is '$'.
-- * `inline_brackets`: bracket pair that wraps inline Lua expressions, default is '()'.
-- * `newline`: string to replace newline characters, default is `nil` (not replacing newlines).
-- * `debug`: if truthy, the generated source code will be retained within the compiled template object, default is `nil`.
--
-- @string str the template string
-- @tab[opt] opts the compilation options to use
-- @return template object, or `nil + error + source_code`
-- @usage
-- local ct, err = template.compile(my_template)
-- local rendered , err = ct:render(my_env, parent)
function template.compile(str, opts)
opts = opts or {}
local chunk_name = opts.chunk_name or 'TMP'
local escape = opts.escape or '#'
local inline_escape = opts.inline_escape or '$'
local inline_brackets = opts.inline_brackets or '()'
local code, short = parseHashLines(str,inline_escape,inline_brackets,escape,opts.newline)
local env = { __tostring = tostring }
local fn, err = utils.load(code, chunk_name,'t',env)
if not fn then return nil, err, code end
if short then
-- the template returns a single constant string, let's optimize for that
local constant_string = fn()
return {
fn = fn(),
env = env,
render = function(self) -- additional params can be ignored
-- skip the metatable magic and error handling in the render
-- function above for this special case
return constant_string, nil, self.code
end,
code = opts.debug and code or nil,
}
end
return {
fn = fn(),
env = env,
render = render,
code = opts.debug and code or nil,
}
end
return template

View file

@ -0,0 +1,164 @@
--- Useful test utilities.
--
-- test.asserteq({1,2},{1,2}) -- can compare tables
-- test.asserteq(1.2,1.19,0.02) -- compare FP numbers within precision
-- T = test.tuple -- used for comparing multiple results
-- test.asserteq(T(string.find(" me","me")),T(2,3))
--
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.pretty`, `pl.path`, `debug`
-- @module pl.test
local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local pretty = require 'pl.pretty'
local path = require 'pl.path'
local type,unpack,pack = type,utils.unpack,utils.pack
local clock = os.clock
local debug = require 'debug'
local io = io
local function dump(x)
if type(x) == 'table' and not (getmetatable(x) and getmetatable(x).__tostring) then
return pretty.write(x,' ',true)
elseif type(x) == 'string' then
return '"'..x..'"'
else
return tostring(x)
end
end
local test = {}
---- error handling for test results.
-- By default, this writes to stderr and exits the program.
-- Re-define this function to raise an error and/or redirect output
function test.error_handler(file,line,got_text, needed_text,msg)
local err = io.stderr
err:write(path.basename(file)..':'..line..': assertion failed\n')
err:write("got:\t",got_text,'\n')
err:write("needed:\t",needed_text,'\n')
utils.quit(1,msg or "these values were not equal")
end
local function complain (x,y,msg,where)
local i = debug.getinfo(3 + (where or 0))
test.error_handler(i.short_src,i.currentline,dump(x),dump(y),msg)
end
--- general test complain message.
-- Useful for composing new test functions (see tests/tablex.lua for an example)
-- @param x a value
-- @param y value to compare first value against
-- @param msg message
-- @param where extra level offset for errors
-- @function complain
test.complain = complain
--- like assert, except takes two arguments that must be equal and can be tables.
-- If they are plain tables, it will use tablex.deepcompare.
-- @param x any value
-- @param y a value equal to x
-- @param eps an optional tolerance for numerical comparisons
-- @param where extra level offset
function test.asserteq (x,y,eps,where)
local res = x == y
if not res then
res = tablex.deepcompare(x,y,true,eps)
end
if not res then
complain(x,y,nil,where)
end
end
--- assert that the first string matches the second.
-- @param s1 a string
-- @param s2 a string
-- @param where extra level offset
function test.assertmatch (s1,s2,where)
if not s1:match(s2) then
complain (s1,s2,"these strings did not match",where)
end
end
--- assert that the function raises a particular error.
-- @param fn a function or a table of the form {function,arg1,...}
-- @param e a string to match the error against
-- @param where extra level offset
function test.assertraise(fn,e,where)
local ok, err
if type(fn) == 'table' then
ok, err = pcall(unpack(fn))
else
ok, err = pcall(fn)
end
if ok or err:match(e)==nil then
complain (err,e,"these errors did not match",where)
end
end
--- a version of asserteq that takes two pairs of values.
-- <code>x1==y1 and x2==y2</code> must be true. Useful for functions that naturally
-- return two values.
-- @param x1 any value
-- @param x2 any value
-- @param y1 any value
-- @param y2 any value
-- @param where extra level offset
function test.asserteq2 (x1,x2,y1,y2,where)
if x1 ~= y1 then complain(x1,y1,nil,where) end
if x2 ~= y2 then complain(x2,y2,nil,where) end
end
-- tuple type --
local tuple_mt = {
unpack = unpack
}
tuple_mt.__index = tuple_mt
function tuple_mt.__tostring(self)
local ts = {}
for i=1, self.n do
local s = self[i]
ts[i] = type(s) == 'string' and ('%q'):format(s) or tostring(s)
end
return 'tuple(' .. table.concat(ts, ', ') .. ')'
end
function tuple_mt.__eq(a, b)
if a.n ~= b.n then return false end
for i=1, a.n do
if a[i] ~= b[i] then return false end
end
return true
end
function tuple_mt.__len(self)
return self.n
end
--- encode an arbitrary argument list as a tuple.
-- This can be used to compare to other argument lists, which is
-- very useful for testing functions which return a number of values.
-- Unlike regular array-like tables ('sequences') they may contain nils.
-- Tuples understand equality and know how to print themselves out.
-- The # operator is defined to be the size, irrespecive of any nils,
-- and there is an `unpack` method.
-- @usage asserteq(tuple( ('ab'):find 'a'), tuple(1,1))
function test.tuple(...)
return setmetatable(pack(...), tuple_mt)
end
--- Time a function. Call the function a given number of times, and report the number of seconds taken,
-- together with a message. Any extra arguments will be passed to the function.
-- @string msg a descriptive message
-- @int n number of times to call the function
-- @func fun the function
-- @param ... optional arguments to fun
function test.timer(msg,n,fun,...)
local start = clock()
for i = 1,n do fun(...) end
utils.printf("%s: took %7.2f sec\n",msg,clock()-start)
end
return test

View file

@ -0,0 +1,26 @@
--- Text processing utilities.
--
-- This provides a Template class (modeled after the same from the Python
-- libraries, see string.Template). It also provides similar functions to those
-- found in the textwrap module.
--
-- IMPORTANT: this module has been deprecated and will be removed in a future
-- version (2.0). The contents of this module have moved to the `pl.stringx`
-- module.
--
-- See @{03-strings.md.String_Templates|the Guide}.
--
-- Dependencies: `pl.stringx`, `pl.utils`
-- @module pl.text
local utils = require("pl.utils")
utils.raise_deprecation {
source = "Penlight " .. utils._VERSION,
message = "the contents of module 'pl.text' has moved into 'pl.stringx'",
version_removed = "2.0.0",
deprecated_after = "1.11.0",
no_trace = true,
}
return require "pl.stringx"

View file

@ -0,0 +1,183 @@
---- Dealing with Detailed Type Information
-- Dependencies `pl.utils`
-- @module pl.types
local utils = require 'pl.utils'
local math_ceil = math.ceil
local assert_arg = utils.assert_arg
local types = {}
--- is the object either a function or a callable object?.
-- @param obj Object to check.
function types.is_callable (obj)
return type(obj) == 'function' or getmetatable(obj) and getmetatable(obj).__call and true
end
--- is the object of the specified type?.
-- If the type is a string, then use type, otherwise compare with metatable.
--
-- NOTE: this function is imported from `utils.is_type`.
-- @param obj An object to check
-- @param tp The expected type
-- @function is_type
-- @see utils.is_type
types.is_type = utils.is_type
local fileMT = getmetatable(io.stdout)
--- a string representation of a type.
-- For tables and userdata with metatables, we assume that the metatable has a `_name`
-- field. If the field is not present it will return 'unknown table' or
-- 'unknown userdata'.
-- Lua file objects return the type 'file'.
-- @param obj an object
-- @return a string like 'number', 'table', 'file' or 'List'
function types.type (obj)
local t = type(obj)
if t == 'table' or t == 'userdata' then
local mt = getmetatable(obj)
if mt == fileMT then
return 'file'
elseif mt == nil then
return t
else
-- TODO: the "unknown" is weird, it should just return the type
return mt._name or "unknown "..t
end
else
return t
end
end
--- is this number an integer?
-- @param x a number
-- @raise error if x is not a number
-- @return boolean
function types.is_integer (x)
return math_ceil(x)==x
end
--- Check if the object is "empty".
-- An object is considered empty if it is:
--
-- - `nil`
-- - a table without any items (key-value pairs or indexes)
-- - a string with no content ("")
-- - not a nil/table/string
-- @param o The object to check if it is empty.
-- @param ignore_spaces If the object is a string and this is true the string is
-- considered empty if it only contains spaces.
-- @return `true` if the object is empty, otherwise a falsy value.
function types.is_empty(o, ignore_spaces)
if o == nil then
return true
elseif type(o) == "table" then
return next(o) == nil
elseif type(o) == "string" then
return o == "" or (not not ignore_spaces and (not not o:find("^%s+$")))
else
return true
end
end
local function check_meta (val)
if type(val) == 'table' then return true end
return getmetatable(val)
end
--- is an object 'array-like'?
-- An object is array like if:
--
-- - it is a table, or
-- - it has a metatable with `__len` and `__index` methods
--
-- NOTE: since `__len` is 5.2+, on 5.1 is usually returns `false` for userdata
-- @param val any value.
-- @return `true` if the object is array-like, otherwise a falsy value.
function types.is_indexable (val)
local mt = check_meta(val)
if mt == true then return true end
return mt and mt.__len and mt.__index and true
end
--- can an object be iterated over with `pairs`?
-- An object is iterable if:
--
-- - it is a table, or
-- - it has a metatable with a `__pairs` meta method
--
-- NOTE: since `__pairs` is 5.2+, on 5.1 is usually returns `false` for userdata
-- @param val any value.
-- @return `true` if the object is iterable, otherwise a falsy value.
function types.is_iterable (val)
local mt = check_meta(val)
if mt == true then return true end
return mt and mt.__pairs and true
end
--- can an object accept new key/pair values?
-- An object is iterable if:
--
-- - it is a table, or
-- - it has a metatable with a `__newindex` meta method
--
-- @param val any value.
-- @return `true` if the object is writeable, otherwise a falsy value.
function types.is_writeable (val)
local mt = check_meta(val)
if mt == true then return true end
return mt and mt.__newindex and true
end
-- Strings that should evaluate to true. -- TODO: add on/off ???
local trues = { yes=true, y=true, ["true"]=true, t=true, ["1"]=true }
-- Conditions types should evaluate to true.
local true_types = {
boolean=function(o, true_strs, check_objs) return o end,
string=function(o, true_strs, check_objs)
o = o:lower()
if trues[o] then
return true
end
-- Check alternative user provided strings.
for _,v in ipairs(true_strs or {}) do
if type(v) == "string" and o == v:lower() then
return true
end
end
return false
end,
number=function(o, true_strs, check_objs) return o ~= 0 end,
table=function(o, true_strs, check_objs) if check_objs and next(o) ~= nil then return true end return false end
}
--- Convert to a boolean value.
-- True values are:
--
-- * boolean: true.
-- * string: 'yes', 'y', 'true', 't', '1' or additional strings specified by `true_strs`.
-- * number: Any non-zero value.
-- * table: Is not empty and `check_objs` is true.
-- * everything else: Is not `nil` and `check_objs` is true.
--
-- @param o The object to evaluate.
-- @param[opt] true_strs optional Additional strings that when matched should evaluate to true. Comparison is case insensitive.
-- This should be a List of strings. E.g. "ja" to support German.
-- @param[opt] check_objs True if objects should be evaluated.
-- @return true if the input evaluates to true, otherwise false.
function types.to_bool(o, true_strs, check_objs)
local true_func
if true_strs then
assert_arg(2, true_strs, "table")
end
true_func = true_types[type(o)]
if true_func then
return true_func(o, true_strs, check_objs)
elseif check_objs and o ~= nil then
return true
end
return false
end
return types

View file

@ -0,0 +1,51 @@
--- Python-style URL quoting library.
--
-- @module pl.url
local url = {}
local function quote_char(c)
return string.format("%%%02X", string.byte(c))
end
--- Quote the url, replacing special characters using the '%xx' escape.
-- @string s the string
-- @bool quote_plus Also escape slashes and replace spaces by plus signs.
-- @return The quoted string, or if `s` wasn't a string, just plain unaltered `s`.
function url.quote(s, quote_plus)
if type(s) ~= "string" then
return s
end
s = s:gsub("\n", "\r\n")
s = s:gsub("([^A-Za-z0-9 %-_%./])", quote_char)
if quote_plus then
s = s:gsub(" ", "+")
s = s:gsub("/", quote_char)
else
s = s:gsub(" ", "%%20")
end
return s
end
local function unquote_char(h)
return string.char(tonumber(h, 16))
end
--- Unquote the url, replacing '%xx' escapes and plus signs.
-- @string s the string
-- @return The unquoted string, or if `s` wasn't a string, just plain unaltered `s`.
function url.unquote(s)
if type(s) ~= "string" then
return s
end
s = s:gsub("+", " ")
s = s:gsub("%%(%x%x)", unquote_char)
s = s:gsub("\r\n", "\n")
return s
end
return url

View file

@ -0,0 +1,947 @@
--- Generally useful routines.
-- See @{01-introduction.md.Generally_useful_functions|the Guide}.
--
-- Dependencies: `pl.compat`, all exported fields and functions from
-- `pl.compat` are also available in this module.
--
-- @module pl.utils
local format = string.format
local compat = require 'pl.compat'
local stdout = io.stdout
local append = table.insert
local concat = table.concat
local _unpack = table.unpack -- always injected by 'compat'
local find = string.find
local sub = string.sub
local next = next
local floor = math.floor
local is_windows = compat.is_windows
local err_mode = 'default'
local raise
local operators
local _function_factories = {}
local utils = { _VERSION = "1.13.1" }
for k, v in pairs(compat) do utils[k] = v end
--- Some standard patterns
-- @table patterns
utils.patterns = {
FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', -- floating point number
INTEGER = '[+%-%d]%d*', -- integer number
IDEN = '[%a_][%w_]*', -- identifier
FILE = '[%a%.\\][:%][%w%._%-\\]*', -- file
}
--- Standard meta-tables as used by other Penlight modules
-- @table stdmt
-- @field List the List metatable
-- @field Map the Map metatable
-- @field Set the Set metatable
-- @field MultiMap the MultiMap metatable
utils.stdmt = {
List = {_name='List'},
Map = {_name='Map'},
Set = {_name='Set'},
MultiMap = {_name='MultiMap'},
}
--- pack an argument list into a table.
-- @param ... any arguments
-- @return a table with field `n` set to the length
-- @function utils.pack
-- @see compat.pack
-- @see utils.npairs
-- @see utils.unpack
utils.pack = table.pack -- added here to be symmetrical with unpack
--- unpack a table and return its contents.
--
-- NOTE: this implementation differs from the Lua implementation in the way
-- that this one DOES honor the `n` field in the table `t`, such that it is 'nil-safe'.
-- @param t table to unpack
-- @param[opt] i index from which to start unpacking, defaults to 1
-- @param[opt] j index of the last element to unpack, defaults to `t.n` or else `#t`
-- @return multiple return values from the table
-- @function utils.unpack
-- @see compat.unpack
-- @see utils.pack
-- @see utils.npairs
-- @usage
-- local t = table.pack(nil, nil, nil, 4)
-- local a, b, c, d = table.unpack(t) -- this `unpack` is NOT nil-safe, so d == nil
--
-- local a, b, c, d = utils.unpack(t) -- this is nil-safe, so d == 4
function utils.unpack(t, i, j)
return _unpack(t, i or 1, j or t.n or #t)
end
--- print an arbitrary number of arguments using a format.
-- Output will be sent to `stdout`.
-- @param fmt The format (see `string.format`)
-- @param ... Extra arguments for format
function utils.printf(fmt, ...)
utils.assert_string(1, fmt)
utils.fprintf(stdout, fmt, ...)
end
--- write an arbitrary number of arguments to a file using a format.
-- @param f File handle to write to.
-- @param fmt The format (see `string.format`).
-- @param ... Extra arguments for format
function utils.fprintf(f,fmt,...)
utils.assert_string(2,fmt)
f:write(format(fmt,...))
end
do
local function import_symbol(T,k,v,libname)
local key = rawget(T,k)
-- warn about collisions!
if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then
utils.fprintf(io.stderr,"warning: '%s.%s' will not override existing symbol\n",libname,k)
return
end
rawset(T,k,v)
end
local function lookup_lib(T,t)
for k,v in pairs(T) do
if v == t then return k end
end
return '?'
end
local already_imported = {}
--- take a table and 'inject' it into the local namespace.
-- @param t The table (table), or module name (string), defaults to this `utils` module table
-- @param T An optional destination table (defaults to callers environment)
function utils.import(t,T)
T = T or _G
t = t or utils
if type(t) == 'string' then
t = require (t)
end
local libname = lookup_lib(T,t)
if already_imported[t] then return end
already_imported[t] = libname
for k,v in pairs(t) do
import_symbol(T,k,v,libname)
end
end
end
--- return either of two values, depending on a condition.
-- @param cond A condition
-- @param value1 Value returned if cond is truthy
-- @param value2 Value returned if cond is falsy
function utils.choose(cond, value1, value2)
return cond and value1 or value2
end
--- convert an array of values to strings.
-- @param t a list-like table
-- @param[opt] temp (table) buffer to use, otherwise allocate
-- @param[opt] tostr custom tostring function, called with (value,index). Defaults to `tostring`.
-- @return the converted buffer
function utils.array_tostring (t,temp,tostr)
temp, tostr = temp or {}, tostr or tostring
for i = 1,#t do
temp[i] = tostr(t[i],i)
end
return temp
end
--- is the object of the specified type?
-- If the type is a string, then use type, otherwise compare with metatable
-- @param obj An object to check
-- @param tp String of what type it should be
-- @return boolean
-- @usage utils.is_type("hello world", "string") --> true
-- -- or check metatable
-- local my_mt = {}
-- local my_obj = setmetatable(my_obj, my_mt)
-- utils.is_type(my_obj, my_mt) --> true
function utils.is_type (obj,tp)
if type(tp) == 'string' then return type(obj) == tp end
local mt = getmetatable(obj)
return tp == mt
end
--- an iterator with indices, similar to `ipairs`, but with a range.
-- This is a nil-safe index based iterator that will return `nil` when there
-- is a hole in a list. To be safe ensure that table `t.n` contains the length.
-- @tparam table t the table to iterate over
-- @tparam[opt=1] integer i_start start index
-- @tparam[opt=t.n or #t] integer i_end end index
-- @tparam[opt=1] integer step step size
-- @treturn integer index
-- @treturn any value at index (which can be `nil`!)
-- @see utils.pack
-- @see utils.unpack
-- @usage
-- local t = utils.pack(nil, 123, nil) -- adds an `n` field when packing
--
-- for i, v in utils.npairs(t, 2) do -- start at index 2
-- t[i] = tostring(t[i])
-- end
--
-- -- t = { n = 3, [2] = "123", [3] = "nil" }
function utils.npairs(t, i_start, i_end, step)
step = step or 1
if step == 0 then
error("iterator step-size cannot be 0", 2)
end
local i = (i_start or 1) - step
i_end = i_end or t.n or #t
if step < 0 then
return function()
i = i + step
if i < i_end then
return nil
end
return i, t[i]
end
else
return function()
i = i + step
if i > i_end then
return nil
end
return i, t[i]
end
end
end
--- an iterator over all non-integer keys (inverse of `ipairs`).
-- It will skip any key that is an integer number, so negative indices or an
-- array with holes will not return those either (so it returns slightly less than
-- 'the inverse of `ipairs`').
--
-- This uses `pairs` under the hood, so any value that is iterable using `pairs`
-- will work with this function.
-- @tparam table t the table to iterate over
-- @treturn key
-- @treturn value
-- @usage
-- local t = {
-- "hello",
-- "world",
-- hello = "hallo",
-- world = "Welt",
-- }
--
-- for k, v in utils.kpairs(t) do
-- print("German: ", v)
-- end
--
-- -- output;
-- -- German: hallo
-- -- German: Welt
function utils.kpairs(t)
local index
return function()
local value
while true do
index, value = next(t, index)
if type(index) ~= "number" or floor(index) ~= index then
break
end
end
return index, value
end
end
--- Error handling
-- @section Error-handling
--- assert that the given argument is in fact of the correct type.
-- @param n argument index
-- @param val the value
-- @param tp the type
-- @param verify an optional verification function
-- @param msg an optional custom message
-- @param lev optional stack position for trace, default 2
-- @return the validated value
-- @raise if `val` is not the correct type
-- @usage
-- local param1 = assert_arg(1,"hello",'table') --> error: argument 1 expected a 'table', got a 'string'
-- local param4 = assert_arg(4,'!@#$%^&*','string',path.isdir,'not a directory')
-- --> error: argument 4: '!@#$%^&*' not a directory
function utils.assert_arg (n,val,tp,verify,msg,lev)
if type(val) ~= tp then
error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),lev or 2)
end
if verify and not verify(val) then
error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2)
end
return val
end
--- creates an Enum or constants lookup table for improved error handling.
-- This helps prevent magic strings in code by throwing errors for accessing
-- non-existing values, and/or converting strings/identifiers to other values.
--
-- Calling on the object does the same, but returns a soft error; `nil + err`, if
-- the call is succesful (the key exists), it will return the value.
--
-- When calling with varargs or an array the values will be equal to the keys.
-- The enum object is read-only.
-- @tparam table|vararg ... the input for the Enum. If varargs or an array then the
-- values in the Enum will be equal to the names (must be strings), if a hash-table
-- then values remain (any type), and the keys must be strings.
-- @return Enum object (read-only table/object)
-- @usage -- Enum access at runtime
-- local obj = {}
-- obj.MOVEMENT = utils.enum("FORWARD", "REVERSE", "LEFT", "RIGHT")
--
-- if current_movement == obj.MOVEMENT.FORWARD then
-- -- do something
--
-- elseif current_movement == obj.MOVEMENT.REVERES then
-- -- throws error due to typo 'REVERES', so a silent mistake becomes a hard error
-- -- "'REVERES' is not a valid value (expected one of: 'FORWARD', 'REVERSE', 'LEFT', 'RIGHT')"
--
-- end
-- @usage -- standardized error codes
-- local obj = {
-- ERR = utils.enum {
-- NOT_FOUND = "the item was not found",
-- OUT_OF_BOUNDS = "the index is outside the allowed range"
-- },
--
-- some_method = function(self)
-- return self.ERR.OUT_OF_BOUNDS
-- end,
-- }
--
-- local result, err = obj:some_method()
-- if not result then
-- if err == obj.ERR.NOT_FOUND then
-- -- check on error code, not magic strings
--
-- else
-- -- return the error description, contained in the constant
-- return nil, "error: "..err -- "error: the index is outside the allowed range"
-- end
-- end
-- @usage -- validating/converting user-input
-- local color = "purple"
-- local ansi_colors = utils.enum {
-- black = 30,
-- red = 31,
-- green = 32,
-- }
-- local color_code, err = ansi_colors(color) -- calling on the object, returns the value from the enum
-- if not color_code then
-- print("bad 'color', " .. err)
-- -- "bad 'color', 'purple' is not a valid value (expected one of: 'black', 'red', 'green')"
-- os.exit(1)
-- end
function utils.enum(...)
local first = select(1, ...)
local enum = {}
local lst
if type(first) ~= "table" then
-- vararg with strings
lst = utils.pack(...)
for i, value in utils.npairs(lst) do
utils.assert_arg(i, value, "string")
enum[value] = value
end
else
-- table/array with values
utils.assert_arg(1, first, "table")
lst = {}
-- first add array part
for i, value in ipairs(first) do
if type(value) ~= "string" then
error(("expected 'string' but got '%s' at index %d"):format(type(value), i), 2)
end
lst[i] = value
enum[value] = value
end
-- add key-ed part
for key, value in utils.kpairs(first) do
if type(key) ~= "string" then
error(("expected key to be 'string' but got '%s'"):format(type(key)), 2)
end
if enum[key] then
error(("duplicate entry in array and hash part: '%s'"):format(key), 2)
end
enum[key] = value
lst[#lst+1] = key
end
end
if not lst[1] then
error("expected at least 1 entry", 2)
end
local valid = "(expected one of: '" .. concat(lst, "', '") .. "')"
setmetatable(enum, {
__index = function(self, key)
error(("'%s' is not a valid value %s"):format(tostring(key), valid), 2)
end,
__newindex = function(self, key, value)
error("the Enum object is read-only", 2)
end,
__call = function(self, key)
if type(key) == "string" then
local v = rawget(self, key)
if v ~= nil then
return v
end
end
return nil, ("'%s' is not a valid value %s"):format(tostring(key), valid)
end
})
return enum
end
--- process a function argument.
-- This is used throughout Penlight and defines what is meant by a function:
-- Something that is callable, or an operator string as defined by <code>pl.operator</code>,
-- such as '>' or '#'. If a function factory has been registered for the type, it will
-- be called to get the function.
-- @param idx argument index
-- @param f a function, operator string, or callable object
-- @param msg optional error message
-- @return a callable
-- @raise if idx is not a number or if f is not callable
function utils.function_arg (idx,f,msg)
utils.assert_arg(1,idx,'number')
local tp = type(f)
if tp == 'function' then return f end -- no worries!
-- ok, a string can correspond to an operator (like '==')
if tp == 'string' then
if not operators then operators = require 'pl.operator'.optable end
local fn = operators[f]
if fn then return fn end
local fn, err = utils.string_lambda(f)
if not fn then error(err..': '..f) end
return fn
elseif tp == 'table' or tp == 'userdata' then
local mt = getmetatable(f)
if not mt then error('not a callable object',2) end
local ff = _function_factories[mt]
if not ff then
if not mt.__call then error('not a callable object',2) end
return f
else
return ff(f) -- we have a function factory for this type!
end
end
if not msg then msg = " must be callable" end
if idx > 0 then
error("argument "..idx..": "..msg,2)
else
error(msg,2)
end
end
--- assert the common case that the argument is a string.
-- @param n argument index
-- @param val a value that must be a string
-- @return the validated value
-- @raise val must be a string
-- @usage
-- local val = 42
-- local param2 = utils.assert_string(2, val) --> error: argument 2 expected a 'string', got a 'number'
function utils.assert_string (n, val)
return utils.assert_arg(n,val,'string',nil,nil,3)
end
--- control the error strategy used by Penlight.
-- This is a global setting that controls how `utils.raise` behaves:
--
-- - 'default': return `nil + error` (this is the default)
-- - 'error': throw a Lua error
-- - 'quit': exit the program
--
-- @param mode either 'default', 'quit' or 'error'
-- @see utils.raise
function utils.on_error (mode)
mode = tostring(mode)
if ({['default'] = 1, ['quit'] = 2, ['error'] = 3})[mode] then
err_mode = mode
else
-- fail loudly
local err = "Bad argument expected string; 'default', 'quit', or 'error'. Got '"..tostring(mode).."'"
if err_mode == 'default' then
error(err, 2) -- even in 'default' mode fail loud in this case
end
raise(err)
end
end
--- used by Penlight functions to return errors. Its global behaviour is controlled
-- by `utils.on_error`.
-- To use this function you MUST use it in conjunction with `return`, since it might
-- return `nil + error`.
-- @param err the error string.
-- @see utils.on_error
-- @usage
-- if some_condition then
-- return utils.raise("some condition was not met") -- MUST use 'return'!
-- end
function utils.raise (err)
if err_mode == 'default' then
return nil, err
elseif err_mode == 'quit' then
return utils.quit(err)
else
error(err, 2)
end
end
raise = utils.raise
--- File handling
-- @section files
--- return the contents of a file as a string
-- @param filename The file path
-- @param is_bin open in binary mode
-- @return file contents
function utils.readfile(filename,is_bin)
local mode = is_bin and 'b' or ''
utils.assert_string(1,filename)
local f,open_err = io.open(filename,'r'..mode)
if not f then return raise (open_err) end
local res,read_err = f:read('*a')
f:close()
if not res then
-- Errors in io.open have "filename: " prefix,
-- error in file:read don't, add it.
return raise (filename..": "..read_err)
end
return res
end
--- write a string to a file
-- @param filename The file path
-- @param str The string
-- @param is_bin open in binary mode
-- @return true or nil
-- @return error message
-- @raise error if filename or str aren't strings
function utils.writefile(filename,str,is_bin)
local mode = is_bin and 'b' or ''
utils.assert_string(1,filename)
utils.assert_string(2,str)
local f,err = io.open(filename,'w'..mode)
if not f then return raise(err) end
local ok, write_err = f:write(str)
f:close()
if not ok then
-- Errors in io.open have "filename: " prefix,
-- error in file:write don't, add it.
return raise (filename..": "..write_err)
end
return true
end
--- return the contents of a file as a list of lines
-- @param filename The file path
-- @return file contents as a table
-- @raise error if filename is not a string
function utils.readlines(filename)
utils.assert_string(1,filename)
local f,err = io.open(filename,'r')
if not f then return raise(err) end
local res = {}
for line in f:lines() do
append(res,line)
end
f:close()
return res
end
--- OS functions
-- @section OS-functions
--- execute a shell command and return the output.
-- This function redirects the output to tempfiles and returns the content of those files.
-- @param cmd a shell command
-- @param bin boolean, if true, read output as binary file
-- @return true if successful
-- @return actual return code
-- @return stdout output (string)
-- @return errout output (string)
function utils.executeex(cmd, bin)
local outfile = os.tmpname()
local errfile = os.tmpname()
if is_windows and not outfile:find(':') then
outfile = os.getenv('TEMP')..outfile
errfile = os.getenv('TEMP')..errfile
end
cmd = cmd .. " > " .. utils.quote_arg(outfile) .. " 2> " .. utils.quote_arg(errfile)
local success, retcode = utils.execute(cmd)
local outcontent = utils.readfile(outfile, bin)
local errcontent = utils.readfile(errfile, bin)
os.remove(outfile)
os.remove(errfile)
return success, retcode, (outcontent or ""), (errcontent or "")
end
--- Quote and escape an argument of a command.
-- Quotes a single (or list of) argument(s) of a command to be passed
-- to `os.execute`, `pl.utils.execute` or `pl.utils.executeex`.
-- @param argument (string or table/list) the argument to quote. If a list then
-- all arguments in the list will be returned as a single string quoted.
-- @return quoted and escaped argument.
-- @usage
-- local options = utils.quote_arg {
-- "-lluacov",
-- "-e",
-- "utils = print(require('pl.utils')._VERSION",
-- }
-- -- returns: -lluacov -e 'utils = print(require('\''pl.utils'\'')._VERSION'
function utils.quote_arg(argument)
if type(argument) == "table" then
-- encode an entire table
local r = {}
for i, arg in ipairs(argument) do
r[i] = utils.quote_arg(arg)
end
return concat(r, " ")
end
-- only a single argument
if is_windows then
if argument == "" or argument:find('[ \f\t\v]') then
-- Need to quote the argument.
-- Quotes need to be escaped with backslashes;
-- additionally, backslashes before a quote, escaped or not,
-- need to be doubled.
-- See documentation for CommandLineToArgvW Windows function.
argument = '"' .. argument:gsub([[(\*)"]], [[%1%1\"]]):gsub([[\+$]], "%0%0") .. '"'
end
-- os.execute() uses system() C function, which on Windows passes command
-- to cmd.exe. Escape its special characters.
return (argument:gsub('["^<>!|&%%]', "^%0"))
else
if argument == "" or argument:find('[^a-zA-Z0-9_@%+=:,./-]') then
-- To quote arguments on posix-like systems use single quotes.
-- To represent an embedded single quote close quoted string ('),
-- add escaped quote (\'), open quoted string again (').
argument = "'" .. argument:gsub("'", [['\'']]) .. "'"
end
return argument
end
end
--- error out of this program gracefully.
-- @param[opt] code The exit code, defaults to -`1` if omitted
-- @param msg The exit message will be sent to `stderr` (will be formatted with the extra parameters)
-- @param ... extra arguments for message's format'
-- @see utils.fprintf
-- @usage utils.quit(-1, "Error '%s' happened", "42")
-- -- is equivalent to
-- utils.quit("Error '%s' happened", "42") --> Error '42' happened
function utils.quit(code, msg, ...)
if type(code) == 'string' then
utils.fprintf(io.stderr, code, msg, ...)
io.stderr:write('\n')
code = -1 -- TODO: this is odd, see the test. Which returns 255 as exit code
elseif msg then
utils.fprintf(io.stderr, msg, ...)
io.stderr:write('\n')
end
os.exit(code, true)
end
--- String functions
-- @section string-functions
--- escape any Lua 'magic' characters in a string
-- @param s The input string
function utils.escape(s)
utils.assert_string(1,s)
return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1'))
end
--- split a string into a list of strings separated by a delimiter.
-- @param s The input string
-- @param re optional A Lua string pattern; defaults to '%s+'
-- @param plain optional If truthy don't use Lua patterns
-- @param n optional maximum number of elements (if there are more, the last will remian un-split)
-- @return a list-like table
-- @raise error if s is not a string
-- @see splitv
function utils.split(s,re,plain,n)
utils.assert_string(1,s)
local i1,ls = 1,{}
if not re then re = '%s+' end
if re == '' then return {s} end
while true do
local i2,i3 = find(s,re,i1,plain)
if not i2 then
local last = sub(s,i1)
if last ~= '' then append(ls,last) end
if #ls == 1 and ls[1] == '' then
return {}
else
return ls
end
end
append(ls,sub(s,i1,i2-1))
if n and #ls == n then
ls[#ls] = sub(s,i1)
return ls
end
i1 = i3+1
end
end
--- split a string into a number of return values.
-- Identical to `split` but returns multiple sub-strings instead of
-- a single list of sub-strings.
-- @param s the string
-- @param re A Lua string pattern; defaults to '%s+'
-- @param plain don't use Lua patterns
-- @param n optional maximum number of splits
-- @return n values
-- @usage first,next = splitv('user=jane=doe','=', false, 2)
-- assert(first == "user")
-- assert(next == "jane=doe")
-- @see split
function utils.splitv (s,re, plain, n)
return _unpack(utils.split(s,re, plain, n))
end
--- Functional
-- @section functional
--- 'memoize' a function (cache returned value for next call).
-- This is useful if you have a function which is relatively expensive,
-- but you don't know in advance what values will be required, so
-- building a table upfront is wasteful/impossible.
-- @param func a function of at least one argument
-- @return a function with at least one argument, which is used as the key.
function utils.memoize(func)
local cache = {}
return function(k)
local res = cache[k]
if res == nil then
res = func(k)
cache[k] = res
end
return res
end
end
--- associate a function factory with a type.
-- A function factory takes an object of the given type and
-- returns a function for evaluating it
-- @tab mt metatable
-- @func fun a callable that returns a function
function utils.add_function_factory (mt,fun)
_function_factories[mt] = fun
end
local function _string_lambda(f)
if f:find '^|' or f:find '_' then
local args,body = f:match '|([^|]*)|(.+)'
if f:find '_' then
args = '_'
body = f
else
if not args then return raise 'bad string lambda' end
end
local fstr = 'return function('..args..') return '..body..' end'
local fn,err = utils.load(fstr)
if not fn then return raise(err) end
fn = fn()
return fn
else
return raise 'not a string lambda'
end
end
--- an anonymous function as a string. This string is either of the form
-- '|args| expression' or is a function of one argument, '_'
-- @param lf function as a string
-- @return a function
-- @function utils.string_lambda
-- @usage
-- string_lambda '|x|x+1' (2) == 3
-- string_lambda '_+1' (2) == 3
utils.string_lambda = utils.memoize(_string_lambda)
--- bind the first argument of the function to a value.
-- @param fn a function of at least two values (may be an operator string)
-- @param p a value
-- @return a function such that f(x) is fn(p,x)
-- @raise same as @{function_arg}
-- @see func.bind1
-- @usage local function f(msg, name)
-- print(msg .. " " .. name)
-- end
--
-- local hello = utils.bind1(f, "Hello")
--
-- print(hello("world")) --> "Hello world"
-- print(hello("sunshine")) --> "Hello sunshine"
function utils.bind1 (fn,p)
fn = utils.function_arg(1,fn)
return function(...) return fn(p,...) end
end
--- bind the second argument of the function to a value.
-- @param fn a function of at least two values (may be an operator string)
-- @param p a value
-- @return a function such that f(x) is fn(x,p)
-- @raise same as @{function_arg}
-- @usage local function f(a, b, c)
-- print(a .. " " .. b .. " " .. c)
-- end
--
-- local hello = utils.bind1(f, "world")
--
-- print(hello("Hello", "!")) --> "Hello world !"
-- print(hello("Bye", "?")) --> "Bye world ?"
function utils.bind2 (fn,p)
fn = utils.function_arg(1,fn)
return function(x,...) return fn(x,p,...) end
end
--- Deprecation
-- @section deprecation
do
-- the default implementation
local deprecation_func = function(msg, trace)
if trace then
warn(msg, "\n", trace) -- luacheck: ignore
else
warn(msg) -- luacheck: ignore
end
end
--- Sets a deprecation warning function.
-- An application can override this function to support proper output of
-- deprecation warnings. The warnings can be generated from libraries or
-- functions by calling `utils.raise_deprecation`. The default function
-- will write to the 'warn' system (introduced in Lua 5.4, or the compatibility
-- function from the `compat` module for earlier versions).
--
-- Note: only applications should set/change this function, libraries should not.
-- @param func a callback with signature: `function(msg, trace)` both arguments are strings, the latter being optional.
-- @see utils.raise_deprecation
-- @usage
-- -- write to the Nginx logs with OpenResty
-- utils.set_deprecation_func(function(msg, trace)
-- ngx.log(ngx.WARN, msg, (trace and (" " .. trace) or nil))
-- end)
--
-- -- disable deprecation warnings
-- utils.set_deprecation_func()
function utils.set_deprecation_func(func)
if func == nil then
deprecation_func = function() end
else
utils.assert_arg(1, func, "function")
deprecation_func = func
end
end
--- raises a deprecation warning.
-- For options see the usage example below.
--
-- Note: the `opts.deprecated_after` field is the last version in which
-- a feature or option was NOT YET deprecated! Because when writing the code it
-- is quite often not known in what version the code will land. But the last
-- released version is usually known.
-- @param opts options table
-- @see utils.set_deprecation_func
-- @usage
-- warn("@on") -- enable Lua warnings, they are usually off by default
--
-- function stringx.islower(str)
-- raise_deprecation {
-- source = "Penlight " .. utils._VERSION, -- optional
-- message = "function 'islower' was renamed to 'is_lower'", -- required
-- version_removed = "2.0.0", -- optional
-- deprecated_after = "1.2.3", -- optional
-- no_trace = true, -- optional
-- }
-- return stringx.is_lower(str)
-- end
-- -- output: "[Penlight 1.9.2] function 'islower' was renamed to 'is_lower' (deprecated after 1.2.3, scheduled for removal in 2.0.0)"
function utils.raise_deprecation(opts)
utils.assert_arg(1, opts, "table")
if type(opts.message) ~= "string" then
error("field 'message' of the options table must be a string", 2)
end
local trace
if not opts.no_trace then
trace = debug.traceback("", 2):match("[\n%s]*(.-)$")
end
local msg
if opts.deprecated_after and opts.version_removed then
msg = (" (deprecated after %s, scheduled for removal in %s)"):format(
tostring(opts.deprecated_after), tostring(opts.version_removed))
elseif opts.deprecated_after then
msg = (" (deprecated after %s)"):format(tostring(opts.deprecated_after))
elseif opts.version_removed then
msg = (" (scheduled for removal in %s)"):format(tostring(opts.version_removed))
else
msg = ""
end
msg = opts.message .. msg
if opts.source then
msg = "[" .. opts.source .."] " .. msg
else
if msg:sub(1,1) == "@" then
-- in Lua 5.4 "@" prefixed messages are control messages to the warn system
error("message cannot start with '@'", 2)
end
end
deprecation_func(msg, trace)
end
end
return utils

File diff suppressed because it is too large Load diff

View file

@ -12,29 +12,29 @@ new_type("luautil", "5 Lua utility modules", true)
not_luadoc = true
local version = "1.2"
project = "TombEngine"
local version = "1.8.1"
project = "&nbsp;TombEngine"
title = "TombEngine " .. version .. " Lua API"
description = "TombEngine " .. version .. " scripting interface"
full_description = [[Welcome to the TombEngine scripting API. This is a work in progress and some information might be wrong or outdated. Please also note that this is primarily a reference document, not a tutorial, so expect descriptions to be fairly sparse.
full_description = [[Welcome to the TombEngine scripting API.
Note that this is primarily a reference document, not a tutorial, so expect descriptions to be fairly sparse.
At the time of writing, there is a tutorial describing the basics of Lua, as well as a number of example scripts, on <a href="https://www.tombengine.com">the TombEngine website</a>.
####Module Hierarchy (boring but important)
Other than the "special tables" (GameVars, LevelVars and LevelFuncs), every module described herein is held in a master table called TEN.
####Module Hierarchy
Other than the "special tables" (`GameVars`, `LevelVars` and `LevelFuncs`), every module described herein is held in a master table called TEN.
For convenience, these modules and classes are automatically put in the global table. For example, you can call GetMoveableByName either of these two ways:
local door = TEN.Objects.GetMoveableByName("door_type4_14")
local door = GetMoveableByName("door_type4_14")
####Always check logs/TENLog.txt
If you are scripting levels, TombEngine will often kick you back to the title screen, even if `errorMode` (see Flow.Settings) is set to `ErrorMode.WARN` or `ErrorMode.SILENT`.
If you are scripting levels, TombEngine will often kick you back to the title screen, even if `errorMode` (see @{Flow.Settings}) is set to `ErrorMode.WARN` or `ErrorMode.SILENT`.
This might get annoying, but it's on purpose. If your Lua script contains a syntax error (e.g. you're missing `end` at the end of a function), the Lua interpreter will not be able to continue running the script. If it tried to keep running, you'd probably see some pretty strange behaviour, and would possibly get a crash regardless.
This might get annoying, but it's on purpose. If your Lua script contains a syntax error (e.g. you're missing `end` at the end of a function), the Lua interpreter will not be able to continue running the script. If it tried to keep running, you'd probably see some pretty strange behaviour, and would possibly get a crash regardless. If this happens, check __logs/TENLog.txt__ and look for an error message with the word "unrecoverable".
If this happens, check __logs/TENLog.txt__ and look for an error message with the word "unrecoverable".
Happy building!
Enjoy.
\- _squidshire_
\- _squidshire and the TombEngine development team._
]]
convert_opt=true
@ -51,4 +51,4 @@ custom_display_name_handler = function(item, default_handler)
end
local hand = default_handler(item)
return hand
end
end

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -106,26 +124,38 @@
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#EmitLightningArc">EmitLightningArc(src, dest, color, lifetime, amplitude, beamWidth, detail, smooth, endDrift)</a></td>
<td class="name" ><a href="#EmitLightningArc">EmitLightningArc(origin, target, color, life, amplitude, beamWidth, detail, smooth, endDrift)</a></td>
<td class="summary">Emit a lightning arc.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitParticle">EmitParticle(pos, velocity, spriteIndex, gravity, rot, startColor, endColor, blendMode, startSize, endSize, lifetime, damage, poison)</a></td>
<td class="name" ><a href="#EmitParticle">EmitParticle(pos, vel, spriteID, gravity, rotVel, startColor, endColor, blendMode, startSize, endSize, life, damage, poison, spriteSeqID, startRot)</a></td>
<td class="summary">Emit a particle.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitAdvancedParticle">EmitAdvancedParticle(ParticleData)</a></td>
<td class="summary">Emit a particle with extensive configuration options, including sprite sequence animation, lights, sounds, and damage effects.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitShockwave">EmitShockwave(pos, innerRadius, outerRadius, color, lifetime, speed, angle, hurtsLara)</a></td>
<td class="summary">Emit a shockwave, similar to that seen when a harpy projectile hits something.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitLight">EmitLight(pos, color, radius)</a></td>
<td class="name" ><a href="#EmitLight">EmitLight(pos[, color][, radius][, shadows][, name])</a></td>
<td class="summary">Emit dynamic light that lasts for a single frame.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitSpotLight">EmitSpotLight(pos, dir[, color][, radius][, falloff][, distance][, shadows][, name])</a></td>
<td class="summary">Emit dynamic directional spotlight that lasts for a single frame.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitBlood">EmitBlood(pos, count)</a></td>
<td class="summary">Emit blood.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitAirBubble">EmitAirBubble(pos[, size][, amp])</a></td>
<td class="summary">Emit an air bubble in a water room.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitFire">EmitFire(pos, size)</a></td>
<td class="summary">Emit fire for one frame.</td>
</tr>
@ -137,6 +167,21 @@
<td class="name" ><a href="#MakeEarthquake">MakeEarthquake(strength)</a></td>
<td class="summary">Make an earthquake</td>
</tr>
<tr>
<td class="name" ><a href="#GetWind">GetWind()</a></td>
<td class="summary">Get the wind vector for the current game frame.</td>
</tr>
<tr>
<td class="name" ><a href="#EmitStreamer">EmitStreamer(mov, tag, pos, dir[, rot][, startColor][, endColor][, width][, life][, vel][, expRate][, rotRate][, edgeFeatherMode][, lengthFeatherMode][, blendID])</a></td>
<td class="summary">Emit an extending streamer effect.</td>
</tr>
</table>
<h2><a href="#Tables">Tables</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#ParticleData">ParticleData</a></td>
<td class="summary">Structure for EmitAdvancedParticle table.</td>
</tr>
</table>
<br/>
@ -148,7 +193,7 @@
<dl class="function">
<dt>
<a name = "EmitLightningArc"></a>
<strong>EmitLightningArc(src, dest, color, lifetime, amplitude, beamWidth, detail, smooth, endDrift)</strong>
<strong>EmitLightningArc(origin, target, color, life, amplitude, beamWidth, detail, smooth, endDrift)</strong>
</dt>
<dd>
Emit a lightning arc.
@ -157,13 +202,13 @@
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">src</span>
<li><span class="parameter">origin</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
</li>
<li><span class="parameter">dest</span>
<li><span class="parameter">target</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
@ -173,29 +218,29 @@
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
(default Color(255, 255, 255))
</li>
<li><span class="parameter">lifetime</span>
<li><span class="parameter">life</span>
<span class="types"><span class="type">float</span></span>
Lifetime in seconds. Clamped to [0, 4.233] for now because of strange internal maths. (default 1.0)
Lifetime in seconds. Clamped to [0, 4.233] for now because of strange internal maths. <strong>default: 1</strong>
</li>
<li><span class="parameter">amplitude</span>
<span class="types"><span class="type">int</span></span>
"strength" of the lightning - the higher the value, the "taller" the arcs. Clamped to [1, 255]. (default 20)
"strength" of the lightning - the higher the value, the "taller" the arcs. Clamped to [1, 255]. <strong>default: 20</strong>
</li>
<li><span class="parameter">beamWidth</span>
<span class="types"><span class="type">int</span></span>
Clamped to [1, 127]. (default 2)
Clamped to [1, 127]. <strong>default 2</strong>
</li>
<li><span class="parameter">detail</span>
<span class="types"><span class="type">int</span></span>
Higher numbers equal more segments, but it's not a 1:1 correlation. Clamped to [1, 127]. (default 10)
Higher numbers equal more segments, but it's not a 1:1 correlation. Clamped to [1, 127]. <strong>default: 10</strong>
</li>
<li><span class="parameter">smooth</span>
<span class="types"><span class="type">bool</span></span>
If true, the arc will have large, smooth curves; if false, it will have small, jagged spikes. (default false)
If true, the arc will have large, smooth curves; if false, it will have small, jagged spikes. <strong>default: false</strong>
</li>
<li><span class="parameter">endDrift</span>
<span class="types"><span class="type">bool</span></span>
If true, the end of the arc will be able to gradually drift away from its destination in a random direction (default false)
If true, the end of the arc will be able to gradually drift away from its destination in a random direction <strong>default: false</strong>
</li>
</ul>
@ -206,14 +251,10 @@
</dd>
<dt>
<a name = "EmitParticle"></a>
<strong>EmitParticle(pos, velocity, spriteIndex, gravity, rot, startColor, endColor, blendMode, startSize, endSize, lifetime, damage, poison)</strong>
<strong>EmitParticle(pos, vel, spriteID, gravity, rotVel, startColor, endColor, blendMode, startSize, endSize, life, damage, poison, spriteSeqID, startRot)</strong>
</dt>
<dd>
<p>Emit a particle. </p>
<pre><code> See the sprite editor in WadTool for DEFAULT_SPRITES to see a list of sprite indices.
</code></pre>
Emit a particle.
@ -221,59 +262,63 @@
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
World position.
</li>
<li><span class="parameter">velocity</span>
<li><span class="parameter">vel</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Directional velocity.
</li>
<li><span class="parameter">spriteIndex</span>
<li><span class="parameter">spriteID</span>
<span class="types"><span class="type">int</span></span>
an index of a sprite in DEFAULT_SPRITES object.
Sprite ID in the sprite sequence slot.
</li>
<li><span class="parameter">gravity</span>
<span class="types"><span class="type">int</span></span>
(default 0) Specifies whether particle will fall (positive values) or ascend (negative values) over time. Clamped to [-32768, 32767], but values between -1000 and 1000 are recommended; values too high or too low (e.g. under -2000 or above 2000) will cause the velocity of the particle to "wrap around" and switch directions.
</li>
<li><span class="parameter">rot</span>
<span class="types"><span class="type">float</span></span>
(default 0) specifies a speed with which it will rotate (0 = no rotation, negative = anticlockwise rotation, positive = clockwise rotation).
Effect of gravity. Positive value ascends, negative value descends. <strong>default: 0</strong>
</li>
<li><span class="parameter">rotVel</span>
<span class="types"><span class="type">float</span></span>
Rotational velocity in degrees. <strong>default: 0</strong>
</li>
<li><span class="parameter">startColor</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
(default Color(255, 255, 255)) color at start of life
Color at start of life. <strong>default: Color(255, 255, 255)</strong>
</li>
<li><span class="parameter">endColor</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
(default Color(255, 255, 255)) color to fade to - at the time of writing this fade will finish long before the end of the particle's life due to internal maths
Color at end of life. This will finish long before the end of the particle's life due to internal math. <strong>default: Color(255, 255, 255)</strong>
</li>
<li><span class="parameter">blendMode</span>
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
(default TEN.Effects.BlendID.ALPHABLEND) How will we blend this with its surroundings?
Render blend mode. <strong>TEN.Effects.BlendID.ALPHA_BLEND</strong>
</li>
<li><span class="parameter">startSize</span>
<span class="types"><span class="type">int</span></span>
(default 10) Size on spawn. A value of 15 is approximately the size of Lara's head.
<span class="types"><span class="type">float</span></span>
Size at start of life. <strong>default: 10</strong>
</li>
<li><span class="parameter">endSize</span>
<span class="types"><span class="type">int</span></span>
(default 0) Size on death - the particle will linearly shrink or grow to this size during its lifespan
</li>
<li><span class="parameter">lifetime</span>
<span class="types"><span class="type">float</span></span>
(default 2) Lifespan in seconds
Size at end of life. <strong>default: 0</strong>
</li>
<li><span class="parameter">life</span>
<span class="types"><span class="type">float</span></span>
Lifespan in seconds. <strong>default: 2</strong>
</li>
<li><span class="parameter">damage</span>
<span class="types"><span class="type">bool</span></span>
(default false) specifies whether particle can damage Lara (does a very small amount of damage, like the small lava emitters in TR1)
Harm the player on collision. <strong>default: false</strong>
</li>
<li><span class="parameter">poison</span>
<span class="types"><span class="type">bool</span></span>
(default false) specifies whether particle can poison Lara
Poison the player on collision. <strong>default: false</strong>
</li>
<li><span class="parameter">spriteSeqID</span>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#SpriteConstants">SpriteConstants</a></span>
Sprite sequence slot ID. <strong>default: Objects.ObjID.DEFAULT_SPRITES</strong>
</li>
<li><span class="parameter">startRot</span>
<span class="types"><span class="type">float</span></span>
Rotation at start of life. <strong>default: random</strong>
</li>
</ul>
@ -283,20 +328,77 @@
<h3>Usage:</h3>
<ul>
<pre class="example">EmitParticle(
yourPositionVarHere,
Vec3(<span class="global">math</span>.random(), <span class="global">math</span>.random(), <span class="global">math</span>.random()),
<span class="number">22</span>, <span class="comment">-- spriteIndex
</span> <span class="number">0</span>, <span class="comment">-- gravity
</span> -<span class="number">2</span>, <span class="comment">-- rot
</span> Color(<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>), <span class="comment">-- startColor
</span> Color(<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="comment">-- endColor
</span> TEN.Effects.BlendID.ADDITIVE, <span class="comment">-- blendMode
</span> <span class="number">15</span>, <span class="comment">-- startSize
</span> <span class="number">50</span>, <span class="comment">-- endSize
</span> <span class="number">20</span>, <span class="comment">-- lifetime
</span> <span class="keyword">false</span>, <span class="comment">-- damage
</span> <span class="keyword">true</span> <span class="comment">-- poison
</span> )</pre>
pos,
Vec3(<span class="global">math</span>.random(), <span class="global">math</span>.random(), <span class="global">math</span>.random()),
<span class="number">22</span>, <span class="comment">-- spriteID
</span> <span class="number">0</span>, <span class="comment">-- gravity
</span> -<span class="number">2</span>, <span class="comment">-- rotVel
</span> Color(<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>), <span class="comment">-- startColor
</span> Color(<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="comment">-- endColor
</span> TEN.Effects.BlendID.ADDITIVE, <span class="comment">-- blendMode
</span> <span class="number">15</span>, <span class="comment">-- startSize
</span> <span class="number">50</span>, <span class="comment">-- endSize
</span> <span class="number">20</span>, <span class="comment">-- life
</span> <span class="keyword">false</span>, <span class="comment">-- damage
</span> <span class="keyword">true</span>, <span class="comment">-- poison
</span> Objects.ObjID.DEFAULT_SPRITES, <span class="comment">-- spriteSeqID
</span> <span class="number">180</span>) <span class="comment">-- startRot</span></pre>
</ul>
</dd>
<dt>
<a name = "EmitAdvancedParticle"></a>
<strong>EmitAdvancedParticle(ParticleData)</strong>
</dt>
<dd>
Emit a particle with extensive configuration options, including sprite sequence animation, lights, sounds, and damage effects.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">ParticleData</span>
<span class="types"><a class="type" href="../1 modules/Effects.html#ParticleData">ParticleData</a></span>
Table containing particle data.
</li>
</ul>
<h3>Usage:</h3>
<ul>
<pre class="example"><span class="keyword">local</span> particle =
{
pos = GetMoveableByName(<span class="string">"camera_target_6"</span>):GetPosition(),
vel = Vec3(<span class="number">0</span>, <span class="number">0</span>, <span class="number">10</span>),
spriteSeqID = TEN.Objects.ObjID.CUSTOM_BAR_GRAPHIC,
spriteID = <span class="number">0</span>,
life = <span class="number">10</span>,
maxYVel = <span class="number">0</span>,
gravity = <span class="number">0</span>,
friction = <span class="number">10</span>,
startRot = <span class="number">0</span>,
rotVel = <span class="number">0</span>,
startSize = <span class="number">80</span>,
endSize = <span class="number">80</span>,
startColor = TEN.Color(<span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>),
endColor = TEN.Color(<span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>),
blendMode = TEN.Effects.BlendID.ADDITIVE,
wind = <span class="keyword">false</span>,
damage = <span class="keyword">true</span>,
poison = <span class="keyword">false</span>,
burn = <span class="keyword">false</span>,
damageHit = <span class="number">80</span>,
soundID = <span class="number">197</span>,
light = <span class="keyword">true</span>,
lightRadius = <span class="number">6</span>,
lightFlicker = <span class="number">5</span>,
animated = <span class="keyword">true</span>,
frameRate = <span class="number">0.25</span>,
animType = TEN.Effects.ParticleAnimationType.LOOP,
}
EmitAdvancedParticle(particle)</pre>
</ul>
</dd>
@ -352,7 +454,7 @@
</dd>
<dt>
<a name = "EmitLight"></a>
<strong>EmitLight(pos, color, radius)</strong>
<strong>EmitLight(pos[, color][, radius][, shadows][, name])</strong>
</dt>
<dd>
Emit dynamic light that lasts for a single frame.
@ -364,17 +466,84 @@
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
position of the light
</li>
<li><span class="parameter">color</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
light color (default Color(255, 255, 255))
(<em>optional</em>)
</li>
<li><span class="parameter">radius</span>
<span class="types"><span class="type">int</span></span>
measured in "clicks" or 256 world units (default 20)
(<em>optional</em>)
</li>
<li><span class="parameter">shadows</span>
<span class="types"><span class="type">bool</span></span>
determines whether light should generate dynamic shadows for applicable moveables (default is false)
(<em>optional</em>)
</li>
<li><span class="parameter">name</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights)
(<em>optional</em>)
</li>
</ul>
</dd>
<dt>
<a name = "EmitSpotLight"></a>
<strong>EmitSpotLight(pos, dir[, color][, radius][, falloff][, distance][, shadows][, name])</strong>
</dt>
<dd>
Emit dynamic directional spotlight that lasts for a single frame.
If you want a light that sticks around, you must call this each frame.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
position of the light
</li>
<li><span class="parameter">dir</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
normal which indicates light direction
</li>
<li><span class="parameter">color</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
(default Color(255, 255, 255))
(<em>optional</em>)
</li>
<li><span class="parameter">radius</span>
<span class="types"><span class="type">int</span></span>
(default 20) corresponds loosely to both intensity and range
overall radius at the endpoint of a light cone, measured in "clicks" or 256 world units (default 10)
(<em>optional</em>)
</li>
<li><span class="parameter">falloff</span>
<span class="types"><span class="type">int</span></span>
radius, at which light starts to fade out, measured in "clicks" (default 5)
(<em>optional</em>)
</li>
<li><span class="parameter">distance</span>
<span class="types"><span class="type">int</span></span>
distance, at which light cone fades out, measured in "clicks" (default 20)
(<em>optional</em>)
</li>
<li><span class="parameter">shadows</span>
<span class="types"><span class="type">bool</span></span>
determines whether light should generate dynamic shadows for applicable moveables (default is false)
(<em>optional</em>)
</li>
<li><span class="parameter">name</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights)
(<em>optional</em>)
</li>
</ul>
@ -402,7 +571,39 @@
</li>
<li><span class="parameter">count</span>
<span class="types"><span class="type">int</span></span>
(default 1) "amount" of blood. Higher numbers won't add more blood but will make it more "flickery", with higher numbers turning it into a kind of red orb.
Sprite count. <strong>default: 1</strong>
</li>
</ul>
</dd>
<dt>
<a name = "EmitAirBubble"></a>
<strong>EmitAirBubble(pos[, size][, amp])</strong>
</dt>
<dd>
Emit an air bubble in a water room.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
World position where the effect will be spawned. Must be in a water room.
</li>
<li><span class="parameter">size</span>
<span class="types"><span class="type">float</span></span>
Sprite size. <strong>Default: 32</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">amp</span>
<span class="types"><span class="type">float</span></span>
Oscillation amplitude. <strong>Default: 32</strong>
(<em>optional</em>)
</li>
</ul>
@ -416,7 +617,7 @@
<strong>EmitFire(pos, size)</strong>
</dt>
<dd>
Emit fire for one frame. Will not hurt Lara. Call this each frame if you want a continuous fire.
Emit fire for one frame. Will not hurt player. Call this each frame if you want a continuous fire.
@ -430,7 +631,7 @@
</li>
<li><span class="parameter">size</span>
<span class="types"><span class="type">float</span></span>
(default 1.0)
Fire size. <strong>default: 1</strong>
</li>
</ul>
@ -492,6 +693,272 @@
</dd>
<dt>
<a name = "GetWind"></a>
<strong>GetWind()</strong>
</dt>
<dd>
Get the wind vector for the current game frame.
This represents the 3D displacement applied by the engine on things like particles affected by wind.()
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Wind vector.
</ol>
</dd>
<dt>
<a name = "EmitStreamer"></a>
<strong>EmitStreamer(mov, tag, pos, dir[, rot][, startColor][, endColor][, width][, life][, vel][, expRate][, rotRate][, edgeFeatherMode][, lengthFeatherMode][, blendID])</strong>
</dt>
<dd>
Emit an extending streamer effect.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">mov</span>
<span class="types"><span class="type">Moveable</span></span>
Moveable object with which to associate the effect.
</li>
<li><span class="parameter">tag</span>
<span class="types"><span class="type">int[opt]</span></span>
Numeric tag with which to associate the effect on the moveable. <strong>Default: 0</strong>
</li>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
World position.
</li>
<li><span class="parameter">dir</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Direction vector of movement velocity.
</li>
<li><span class="parameter">rot</span>
<span class="types"><span class="type">float</span></span>
Start rotation in degrees. <strong>Default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">startColor</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
Color at the start of life. <strong>Default: Color(255, 255, 255, 255))</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">endColor</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
Color at the end of life. <strong>Default: Color(0, 0, 0, 0))</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">width</span>
<span class="types"><span class="type">float</span></span>
Width in world units. <strong>Default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">life</span>
<span class="types"><span class="type">float</span></span>
Lifetime in seconds. <strong>Default: 1</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">vel</span>
<span class="types"><span class="type">float</span></span>
Movement velocity in world units per second. <strong>Default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">expRate</span>
<span class="types"><span class="type">float</span></span>
Width expansion rate in world units per second. <strong>Default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">rotRate</span>
<span class="types"><span class="type">float</span></span>
Rotation rate in degrees per second. <strong>Default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">edgeFeatherMode</span>
<span class="types"><a class="type" href="../4 enums/Effects.StreamerFeatherMode.html#">StreamerFeatherMode</a></span>
Edge feather mode. <strong>Default: Effects.StreamerFeatherMode.NONE</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">lengthFeatherMode</span>
<span class="types"><a class="type" href="../4 enums/Effects.StreamerFeatherMode.html#">StreamerFeatherMode</a></span>
Length feather mode. <strong>UNIMPLEMENTED, currently will always leave a fading tail</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">blendID</span>
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
Renderer blend ID. <strong>Default: Effects.BlendID.ALPHA_BLEND</strong>
(<em>optional</em>)
</li>
</ul>
</dd>
</dl>
<h2 class="section-header "><a name="Tables"></a>Tables</h2>
<dl class="function">
<dt>
<a name = "ParticleData"></a>
<strong>ParticleData</strong>
</dt>
<dd>
Structure for EmitAdvancedParticle table.
<h3>Fields:</h3>
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
World position.
</li>
<li><span class="parameter">vel</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Directional velocity in world units per second.
</li>
<li><span class="parameter">spriteSeqID</span>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#SpriteConstants">SpriteConstants</a></span>
Sprite sequence slot ID. <strong>default: Objects.ObjID.DEFAULT_SPRITES</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">spriteID</span>
<span class="types"><span class="type">int</span></span>
Sprite ID in the sprite sequence slot. <strong>default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">life</span>
<span class="types"><span class="type">float</span></span>
Lifespan in seconds. <strong>default: 2</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">maxYVel</span>
<span class="types"><span class="type">float</span></span>
Maximum vertical velocity in world units per second. <strong>default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">gravity</span>
<span class="types"><span class="type">float</span></span>
Effect of gravity in world units per second. Positive value ascend, negative value descend. <strong>default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">friction</span>
<span class="types"><span class="type">float</span></span>
Friction affecting velocity over time in world units per second. <strong>default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">startRot</span>
<span class="types"><span class="type">float</span></span>
Rotation at start of life. <strong>default: random</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">rotVel</span>
<span class="types"><span class="type">float</span></span>
Rotational velocity in degrees per second. <strong>default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">startSize</span>
<span class="types"><span class="type">float</span></span>
Size at start of life. <strong>default: 10</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">endSize</span>
<span class="types"><span class="type">float</span></span>
Size at end of life. <strong>default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">startColor</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
Color at start of life. <strong>default: Color(255, 255, 255)</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">endColor</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
Color at end of life. Note that this will finish long before the end of life due to internal math. <strong>default: Color(255, 255, 255)</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">blendMode</span>
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
Render blend mode. <strong>default: TEN.Effects.BlendID.ALPHA_BLEND</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">damage</span>
<span class="types"><span class="type">bool</span></span>
Harm the player on collision. <strong>default: false</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">poison</span>
<span class="types"><span class="type">bool</span></span>
Poison the player on collision. <strong>default: false</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">burn</span>
<span class="types"><span class="type">bool</span></span>
Burn the player on collision. <strong>default: false</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">wind</span>
<span class="types"><span class="type">bool</span></span>
Affect position by wind in outside rooms. <strong>default: false</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">damageHit</span>
<span class="types"><span class="type">int</span></span>
Player damage amount on collision. <strong>default: 2</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">light</span>
<span class="types"><span class="type">bool</span></span>
Emit a colored light. CAUTION: Recommended only for a single particle. Too many particles with lights can overwhelm the lighting system. <strong>default: false</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">lightRadius</span>
<span class="types"><span class="type">int</span></span>
Light radius in 1/4 blocks. <strong>default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">lightFlicker</span>
<span class="types"><span class="type">int</span></span>
Interval at which the light should flicker. <strong>default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">soundID</span>
<span class="types"><span class="type">int</span></span>
Sound ID to play. CAUTION: Recommended only for a single particle. Too many particles with sounds can overwhelm the sound system. <strong>default: none</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">animated</span>
<span class="types"><span class="type">bool</span></span>
Play animates sprite sequence. <strong>default: false</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">animType</span>
<span class="types"><a class="type" href="../4 enums/Effects.ParticleAnimationType.html#">ParticleAnimationType</a></span>
Animation type of the sprite sequence. <strong>default: TEN.Effects.ParticleAnimationType.LOOP</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">frameRate</span>
<span class="types"><span class="type">float</span></span>
Sprite sequence animation framerate. <strong>default: 1</strong>
(<em>optional</em>)
</li>
</ul>
</dd>
</dl>
@ -500,7 +967,6 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -126,6 +144,10 @@ scripts too.</p>
<td class="summary">Enable or disable level selection in title flyby.</td>
</tr>
<tr>
<td class="name" ><a href="#EnableHomeLevel">EnableHomeLevel(enabled)</a></td>
<td class="summary">Enable or disable Home Level entry in the main menu.</td>
</tr>
<tr>
<td class="name" ><a href="#EnableLoadSave">EnableLoadSave(enabled)</a></td>
<td class="summary">Enable or disable saving and loading of savegames.</td>
</tr>
@ -134,7 +156,7 @@ scripts too.</p>
<table class="function_list">
<tr>
<td class="name" ><a href="#EnableFlyCheat">EnableFlyCheat(enabled)</a></td>
<td class="summary">Enable or disable DOZY mode (fly cheat).</td>
<td class="summary">Enable or disable the fly cheat.</td>
</tr>
<tr>
<td class="name" ><a href="#EnablePointFilter">EnablePointFilter(enabled)</a></td>
@ -153,8 +175,28 @@ scripts too.</p>
<td class="summary">Returns the level that the game control is running in that moment.</td>
</tr>
<tr>
<td class="name" ><a href="#EndLevel">EndLevel([index])</a></td>
<td class="summary">Finishes the current level, with optional level index provided.</td>
<td class="name" ><a href="#EndLevel">EndLevel([index][, startPos])</a></td>
<td class="summary">Finishes the current level, with optional level index and start position index provided.</td>
</tr>
<tr>
<td class="name" ><a href="#GetStatistics">GetStatistics(game)</a></td>
<td class="summary">Get game or level statistics.</td>
</tr>
<tr>
<td class="name" ><a href="#SetStatistics">SetStatistics(statistics, game)</a></td>
<td class="summary">Set game or level statistics.</td>
</tr>
<tr>
<td class="name" ><a href="#GetGameStatus">GetGameStatus()</a></td>
<td class="summary">Get current game status, such as normal game loop, exiting to title, etc.</td>
</tr>
<tr>
<td class="name" ><a href="#GetFreezeMode">GetFreezeMode()</a></td>
<td class="summary">Get current freeze mode, such as none, full, spectator or player.</td>
</tr>
<tr>
<td class="name" ><a href="#SetFreezeMode">SetFreezeMode(freezeMode)</a></td>
<td class="summary">Set current freeze mode, such as none, full, spectator or player.</td>
</tr>
<tr>
<td class="name" ><a href="#SaveGame">SaveGame(slotID)</a></td>
@ -185,23 +227,31 @@ scripts too.</p>
<td class="summary">Adds one secret to current level secret count and also plays secret music track.</td>
</tr>
<tr>
<td class="name" ><a href="#SetTotalSecretCount">SetTotalSecretCount(total)</a></td>
<td class="summary">Total number of secrets in game.</td>
<td class="name" ><a href="#GetTotalSecretCount">GetTotalSecretCount()</a></td>
<td class="summary">Get total number of secrets in the game.</td>
</tr>
<tr>
<td class="name" ><a href="#SetTotalSecretCount">SetTotalSecretCount(count)</a></td>
<td class="summary">Set total number of secrets in the game.</td>
</tr>
<tr>
<td class="name" ><a href="#FlipMap">FlipMap(flipmap)</a></td>
<td class="summary">Do FlipMap with specific group ID.</td>
</tr>
<tr>
<td class="name" ><a href="#GetFlipMapStatus">GetFlipMapStatus([index])</a></td>
<td class="summary">Get current FlipMap status for specific group ID.</td>
</tr>
</table>
<h2><a href="#settings_lua">settings.lua </a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#SetSettings">SetSettings(settings)</a></td>
<td class="summary">
</td>
<td class="summary">Set provided settings table to an engine.</td>
</tr>
<tr>
<td class="name" ><a href="#SetAnimations">SetAnimations(animations)</a></td>
<td class="summary">
</td>
<td class="name" ><a href="#GetSettings">GetSettings()</a></td>
<td class="summary">Get settings table from an engine.</td>
</tr>
</table>
<h2><a href="#strings_lua">strings.lua </a></h2>
@ -215,6 +265,10 @@ scripts too.</p>
<td class="summary">Get translated string.</td>
</tr>
<tr>
<td class="name" ><a href="#IsStringPresent">IsStringPresent(string)</a></td>
<td class="summary">Check if translated string is present.</td>
</tr>
<tr>
<td class="name" ><a href="#SetLanguageNames">SetLanguageNames(table)</a></td>
<td class="summary">Set language names for translations.</td>
</tr>
@ -348,11 +402,11 @@ Must be true or false
</dd>
<dt>
<a name = "EnableLoadSave"></a>
<strong>EnableLoadSave(enabled)</strong>
<a name = "EnableHomeLevel"></a>
<strong>EnableHomeLevel(enabled)</strong>
</dt>
<dd>
Enable or disable saving and loading of savegames.
Enable or disable Home Level entry in the main menu. ()
@ -360,7 +414,29 @@ Must be true or false
<ul>
<li><span class="parameter">enabled</span>
<span class="types"><span class="type">bool</span></span>
true or false.
True or false.
</li>
</ul>
</dd>
<dt>
<a name = "EnableLoadSave"></a>
<strong>EnableLoadSave(enabled)</strong>
</dt>
<dd>
Enable or disable saving and loading of savegames. ()
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">enabled</span>
<span class="types"><span class="type">bool</span></span>
True or false.
</li>
</ul>
@ -378,8 +454,7 @@ Must be true or false
<strong>EnableFlyCheat(enabled)</strong>
</dt>
<dd>
Enable or disable DOZY mode (fly cheat).
Must be true or false
Enable or disable the fly cheat. ()
@ -387,7 +462,7 @@ Must be true or false
<ul>
<li><span class="parameter">enabled</span>
<span class="types"><span class="type">bool</span></span>
true or false
True or false.
</li>
</ul>
@ -495,12 +570,13 @@ have an ID of 0, the second an ID of 1, and so on.
</dd>
<dt>
<a name = "EndLevel"></a>
<strong>EndLevel([index])</strong>
<strong>EndLevel([index][, startPos])</strong>
</dt>
<dd>
Finishes the current level, with optional level index provided. If level index
is not provided or is zero, jumps to next level. If level index is more than
level count, jumps to title.
Finishes the current level, with optional level index and start position index provided.
If level index is not provided or is zero, jumps to next level. If level index is more than
level count, jumps to title. If LARA_START_POS objects are present in level, player will be
teleported to such object with OCB similar to provided second argument.
@ -511,6 +587,131 @@ level count, jumps to title.
level index (default 0)
(<em>optional</em>)
</li>
<li><span class="parameter">startPos</span>
<span class="types"><span class="type">int</span></span>
player start position (default 0)
(<em>optional</em>)
</li>
</ul>
</dd>
<dt>
<a name = "GetStatistics"></a>
<strong>GetStatistics(game)</strong>
</dt>
<dd>
Get game or level statistics. For reference about statistics class, see <a href="../2 classes/Flow.Statistics.html#">Flow.Statistics</a>.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">game</span>
<span class="types"><span class="type">bool</span></span>
if true, returns overall game statistics, otherwise returns current level statistics (default: false)
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/Flow.Statistics.html#">Statistics</a></span>
statistics structure representing game or level statistics
</ol>
</dd>
<dt>
<a name = "SetStatistics"></a>
<strong>SetStatistics(statistics, game)</strong>
</dt>
<dd>
Set game or level statistics. For reference about statistics class, see <a href="../2 classes/Flow.Statistics.html#">Flow.Statistics</a>.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">statistics</span>
<span class="types"><a class="type" href="../2 classes/Flow.Statistics.html#">Statistics</a></span>
statistic object to set
</li>
<li><span class="parameter">game</span>
<span class="types"><span class="type">bool</span></span>
if true, sets overall game statistics, otherwise sets current level statistics (default: false)
</li>
</ul>
</dd>
<dt>
<a name = "GetGameStatus"></a>
<strong>GetGameStatus()</strong>
</dt>
<dd>
Get current game status, such as normal game loop, exiting to title, etc.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../4 enums/Flow.GameStatus.html#">GameStatus</a></span>
the current game status
</ol>
</dd>
<dt>
<a name = "GetFreezeMode"></a>
<strong>GetFreezeMode()</strong>
</dt>
<dd>
Get current freeze mode, such as none, full, spectator or player.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../4 enums/Flow.FreezeMode.html#">FreezeMode</a></span>
the current freeze mode
</ol>
</dd>
<dt>
<a name = "SetFreezeMode"></a>
<strong>SetFreezeMode(freezeMode)</strong>
</dt>
<dd>
Set current freeze mode, such as none, full, spectator or player. <br/>
Freeze mode specifies whether game is in normal mode or paused in a particular way to allow
custom menu creation, photo mode or time freeze.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">freezeMode</span>
<span class="types"><a class="type" href="../4 enums/Flow.FreezeMode.html#">FreezeMode</a></span>
new freeze mode to set.
</li>
</ul>
@ -679,20 +880,41 @@ The index argument corresponds to the secret's unique ID, the same that would go
</dd>
<dt>
<a name = "SetTotalSecretCount"></a>
<strong>SetTotalSecretCount(total)</strong>
<a name = "GetTotalSecretCount"></a>
<strong>GetTotalSecretCount()</strong>
</dt>
<dd>
Total number of secrets in game.
Get total number of secrets in the game.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">int</span></span>
Total number of secrets in the game.
</ol>
</dd>
<dt>
<a name = "SetTotalSecretCount"></a>
<strong>SetTotalSecretCount(count)</strong>
</dt>
<dd>
Set total number of secrets in the game.
Must be an integer value (0 means no secrets).
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">total</span>
<li><span class="parameter">count</span>
<span class="types"><span class="type">int</span></span>
number of secrets
Total number of secrets in the game.
</li>
</ul>
@ -700,13 +922,63 @@ Must be an integer value (0 means no secrets).
</dd>
<dt>
<a name = "FlipMap"></a>
<strong>FlipMap(flipmap)</strong>
</dt>
<dd>
Do FlipMap with specific group ID.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">flipmap</span>
<span class="types"><span class="type">int</span></span>
ID of flipmap group to actuvate / deactivate.
</li>
</ul>
</dd>
<dt>
<a name = "GetFlipMapStatus"></a>
<strong>GetFlipMapStatus([index])</strong>
</dt>
<dd>
Get current FlipMap status for specific group ID.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">index</span>
<span class="types"><span class="type">int</span></span>
Flipmap group ID to check. If no group specified or group is -1, function returns overall flipmap status (on or off).
(<em>optional</em>)
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">int</span></span>
Status of the flipmap group (true means on, false means off).
</ol>
</dd>
</dl>
<h2 class="section-header has-description"><a name="settings_lua"></a>settings.lua </h2>
<div class="section-description">
These functions are called in settings.lua, a file which holds your local settings.
settings.lua shouldn't be bundled with any finished levels/games.
These functions are called in settings.lua, a file which holds global settings, such as system settings, flare color or animation movesets.
</div>
<dl class="function">
<dt>
@ -714,9 +986,7 @@ settings.lua shouldn't be bundled with any finished levels/games.
<strong>SetSettings(settings)</strong>
</dt>
<dd>
Set provided settings table to an engine.
@ -724,7 +994,7 @@ settings.lua shouldn't be bundled with any finished levels/games.
<ul>
<li><span class="parameter">settings</span>
<span class="types"><a class="type" href="../2 classes/Flow.Settings.html#">Settings</a></span>
a settings object
a settings table
</li>
</ul>
@ -734,24 +1004,21 @@ settings.lua shouldn't be bundled with any finished levels/games.
</dd>
<dt>
<a name = "SetAnimations"></a>
<strong>SetAnimations(animations)</strong>
<a name = "GetSettings"></a>
<strong>GetSettings()</strong>
</dt>
<dd>
Get settings table from an engine.
<h3>Returns:</h3>
<ol>
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">animations</span>
<span class="types"><a class="type" href="../2 classes/Flow.Animations.html#">Animations</a></span>
an animations object
</li>
</ul>
<span class="types"><a class="type" href="../2 classes/Flow.Settings.html#">Settings</a></span>
current settings table
</ol>
@ -809,6 +1076,28 @@ You will not need to call them manually.
</dd>
<dt>
<a name = "IsStringPresent"></a>
<strong>IsStringPresent(string)</strong>
</dt>
<dd>
Check if translated string is present.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">string</span>
<span class="types"><span class="type">key</span></span>
key for translated string
</li>
</ul>
</dd>
<dt>
<a name = "SetLanguageNames"></a>
@ -840,7 +1129,6 @@ Specify which translations in the strings table correspond to which languages.
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -126,7 +144,11 @@
<td class="summary">Clear an action key.</td>
</tr>
<tr>
<td class="name" ><a href="#GetCursorDisplayPosition">GetCursorDisplayPosition()</a></td>
<td class="name" ><a href="#KeyClearAll">KeyClearAll()</a></td>
<td class="summary">Clear all action keys.</td>
</tr>
<tr>
<td class="name" ><a href="#GetMouseDisplayPosition">GetMouseDisplayPosition()</a></td>
<td class="summary">Get the display position of the cursor in percent.</td>
</tr>
</table>
@ -253,8 +275,23 @@
</dd>
<dt>
<a name = "GetCursorDisplayPosition"></a>
<strong>GetCursorDisplayPosition()</strong>
<a name = "KeyClearAll"></a>
<strong>KeyClearAll()</strong>
</dt>
<dd>
Clear all action keys.
</dd>
<dt>
<a name = "GetMouseDisplayPosition"></a>
<strong>GetMouseDisplayPosition()</strong>
</dt>
<dd>
Get the display position of the cursor in percent. ()
@ -280,7 +317,6 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -121,6 +139,18 @@
<td class="name" ><a href="#SetItemCount">SetItemCount(objectID, count)</a></td>
<td class="summary">Set the amount of an item in the player's inventory.</td>
</tr>
<tr>
<td class="name" ><a href="#GetUsedItem">GetUsedItem()</a></td>
<td class="summary">Get last item used in the player's inventory.</td>
</tr>
<tr>
<td class="name" ><a href="#SetUsedItem">SetUsedItem(objectID)</a></td>
<td class="summary">Set last item used in the player's inventory.</td>
</tr>
<tr>
<td class="name" ><a href="#ClearUsedItem">ClearUsedItem()</a></td>
<td class="summary">Clear last item used in the player's inventory.</td>
</tr>
</table>
<br/>
@ -242,6 +272,70 @@
</dd>
<dt>
<a name = "GetUsedItem"></a>
<strong>GetUsedItem()</strong>
</dt>
<dd>
Get last item used in the player's inventory.
This value will be valid only for a single frame after exiting inventory, after which Lara says "No".
Therefore, this function must be preferably used either in OnLoop or OnUseItem events.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
Last item used in the inventory.
</ol>
</dd>
<dt>
<a name = "SetUsedItem"></a>
<strong>SetUsedItem(objectID)</strong>
</dt>
<dd>
Set last item used in the player's inventory.
You will be able to specify only objects which already exist in the inventory.
Will only be valid for the next frame. If not processed by the game, Lara will say "No".
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">objectID</span>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
Object ID of the item to select from inventory.
</li>
</ul>
</dd>
<dt>
<a name = "ClearUsedItem"></a>
<strong>ClearUsedItem()</strong>
</dt>
<dd>
Clear last item used in the player's inventory.
When this function is used in OnUseItem level function, it allows to override existing item functionality.
For items without existing functionality, this function is needed to avoid Lara saying "No" after using it.
</dd>
</dl>
@ -250,7 +344,6 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -106,16 +124,24 @@
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#AddCallback">AddCallback(CallbackPoint, func)</a></td>
<td class="name" ><a href="#AddCallback">AddCallback(point, func)</a></td>
<td class="summary">Register a function as a callback.</td>
</tr>
<tr>
<td class="name" ><a href="#RemoveCallback">RemoveCallback(CallbackPoint, LevelFunc)</a></td>
<td class="name" ><a href="#RemoveCallback">RemoveCallback(point, func)</a></td>
<td class="summary">Deregister a function as a callback.</td>
</tr>
<tr>
<td class="name" ><a href="#HandleEvent">HandleEvent(string, EventType, Moveable)</a></td>
<td class="summary">Attempt to find an event set and exectute a particular event from it.</td>
<td class="name" ><a href="#HandleEvent">HandleEvent(name, type, activator)</a></td>
<td class="summary">Attempt to find an event set and execute a particular event from it.</td>
</tr>
<tr>
<td class="name" ><a href="#EnableEvent">EnableEvent(name, type)</a></td>
<td class="summary">Attempt to find an event set and enable specified event in it.</td>
</tr>
<tr>
<td class="name" ><a href="#DisableEvent">DisableEvent(name, type)</a></td>
<td class="summary">Attempt to find an event set and disable specified event in it.</td>
</tr>
</table>
<h2><a href="#Special_objects">Special objects </a></h2>
@ -150,7 +176,7 @@
<dl class="function">
<dt>
<a name = "AddCallback"></a>
<strong>AddCallback(CallbackPoint, func)</strong>
<strong>AddCallback(point, func)</strong>
</dt>
<dd>
Register a function as a callback.
@ -159,39 +185,46 @@
stuff during level start/load/end/save/control phase, but don't want the level
designer to add calls to <code>OnStart</code>, <code>OnLoad</code>, etc. in their level script.</p>
<p>Possible values for CallbackPoint:</p>
<p>Possible values for <code>point</code>:</p>
<pre class="example"><span class="comment">-- These take functions which accept no arguments
</span>PRESTART <span class="comment">-- will be called immediately before OnStart
</span>POSTSTART <span class="comment">-- will be called immediately after OnStart
</span>PRE_START <span class="comment">-- will be called immediately before OnStart
</span>POST_START <span class="comment">-- will be called immediately after OnStart
</span>
PRESAVE <span class="comment">-- will be called immediately before OnSave
</span>POSTSAVE <span class="comment">-- will be called immediately after OnSave
PRE_SAVE <span class="comment">-- will be called immediately before OnSave
</span>POST_SAVE <span class="comment">-- will be called immediately after OnSave
</span>
PRELOAD <span class="comment">-- will be called immediately before OnLoad
</span>POSTLOAD <span class="comment">-- will be called immediately after OnLoad
PRE_LOAD <span class="comment">-- will be called immediately before OnLoad
</span>POST_LOAD <span class="comment">-- will be called immediately after OnLoad
</span>
PRE_FREEZE <span class="comment">-- will be called before entering freeze mode
</span>POST_FREEZE <span class="comment">-- will be called immediately after exiting freeze mode
</span>
<span class="comment">-- These take a LevelEndReason arg, like OnEnd
</span>PREEND <span class="comment">-- will be called immediately before OnEnd
</span>POSTEND <span class="comment">-- will be called immediately after OnEnd
</span>PRE_END <span class="comment">-- will be called immediately before OnEnd
</span>POST_END <span class="comment">-- will be called immediately after OnEnd
</span>
<span class="comment">-- These take functions which accepts a deltaTime argument
</span>PRECONTROLPHASE <span class="comment">-- will be called immediately before OnControlPhase
</span>POSTCONTROLPHASE <span class="comment">-- will be called immediately after OnControlPhase
</span>PRE_LOOP <span class="comment">-- will be called in the beginning of game loop
</span>POST_LOOP <span class="comment">-- will be called at the end of game loop
</span>
<span class="comment">-- These take functions which accepts an objectNumber argument, like OnUseItem
</span>PRE_USE_ITEM <span class="comment">-- will be called immediately before OnUseItem
</span>POST_USE_ITEM <span class="comment">-- will be called immediately after OnUseItem
</span></pre>
<p>The order in which two functions with the same CallbackPoint are called is undefined.
i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRECONTROLPHASE</code>, both will be called before <code>OnControlPhase</code>, but there is no guarantee that <code>MyFunc</code> will be called before <code>MyFunc2</code>, or vice-versa.</p>
i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRELOOP</code>, both will be called in the beginning of game loop, but there is no guarantee that <code>MyFunc</code> will be called before <code>MyFunc2</code>, or vice-versa.</p>
<p>Any returned value will be discarded.</p>
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">CallbackPoint</span>
<span class="types"><span class="type">point</span></span>
<li><span class="parameter">point</span>
<span class="types"><span class="type">CallbackPoint</span></span>
When should the callback be called?
</li>
<li><span class="parameter">func</span>
<span class="types"><span class="type">function</span></span>
<span class="types"><span class="type">LevelFunc</span></span>
The function to be called (must be in the <a href="../1 modules/Logic.html#LevelFuncs">LevelFuncs</a> hierarchy). Will receive, as an argument, the time in seconds since the last frame.
</li>
</ul>
@ -202,13 +235,13 @@ i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRE
<h3>Usage:</h3>
<ul>
<pre class="example">LevelFuncs.MyFunc = <span class="keyword">function</span>(dt) <span class="global">print</span>(dt) <span class="keyword">end</span>
TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.MyFunc)</pre>
TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRELOOP, LevelFuncs.MyFunc)</pre>
</ul>
</dd>
<dt>
<a name = "RemoveCallback"></a>
<strong>RemoveCallback(CallbackPoint, LevelFunc)</strong>
<strong>RemoveCallback(point, func)</strong>
</dt>
<dd>
Deregister a function as a callback.
@ -218,13 +251,13 @@ Will have no effect if the function was not registered as a callback
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">CallbackPoint</span>
<span class="types"><span class="type">point</span></span>
<li><span class="parameter">point</span>
<span class="types"><span class="type">CallbackPoint</span></span>
The callback point the function was registered with. See <a href="../1 modules/Logic.html#AddCallback">AddCallback</a>
</li>
<li><span class="parameter">LevelFunc</span>
<span class="types"><span class="type">func</span></span>
the function to remove; must be in the LevelFuncs hierarchy.
<li><span class="parameter">func</span>
<span class="types"><span class="type">LevelFunc</span></span>
The function to remove; must be in the LevelFuncs hierarchy.
</li>
</ul>
@ -233,32 +266,95 @@ Will have no effect if the function was not registered as a callback
<h3>Usage:</h3>
<ul>
<pre class="example">TEN.Logic.RemoveCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.MyFunc)</pre>
<pre class="example">TEN.Logic.RemoveCallback(TEN.Logic.CallbackPoint.PRELOOP, LevelFuncs.MyFunc)</pre>
</ul>
</dd>
<dt>
<a name = "HandleEvent"></a>
<strong>HandleEvent(string, EventType, Moveable)</strong>
<strong>HandleEvent(name, type, activator)</strong>
</dt>
<dd>
Attempt to find an event set and exectute a particular event from it.
Attempt to find an event set and execute a particular event from it.
<p>Possible event type values:</p>
<pre class="example">ENTER
INSIDE
LEAVE
LOAD
SAVE
START
END
LOOP
USE_ITEM
MENU</pre>
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">name</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
Name of the event set to find.
</li>
<li><span class="parameter">type</span>
<span class="types"><span class="type">EventType</span></span>
Event to execute.
</li>
<li><span class="parameter">activator</span>
<span class="types"><a class="type" href="../2 classes/Objects.Moveable.html#">Moveable</a></span>
Optional activator. Default is the player object.
</li>
</ul>
</dd>
<dt>
<a name = "EnableEvent"></a>
<strong>EnableEvent(name, type)</strong>
</dt>
<dd>
Attempt to find an event set and enable specified event in it.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">string</span>
<span class="types"><span class="type">name</span></span>
<li><span class="parameter">name</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
Name of the event set to find.
</li>
<li><span class="parameter">EventType</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#pdf-type">type</a></span>
Event to execute.
<li><span class="parameter">type</span>
<span class="types"><span class="type">EventType</span></span>
Event to enable.
</li>
<li><span class="parameter">Moveable</span>
<span class="types"><span class="type">activator</span></span>
Optional activator. Default is the player object.
</ul>
</dd>
<dt>
<a name = "DisableEvent"></a>
<strong>DisableEvent(name, type)</strong>
</dt>
<dd>
Attempt to find an event set and disable specified event in it.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">name</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
Name of the event set to find.
</li>
<li><span class="parameter">type</span>
<span class="types"><span class="type">EventType</span></span>
Event to disable.
</li>
</ul>
@ -390,7 +486,7 @@ you can just leave out <code>LevelFuncs.OnStart</code>.</p>
<li>The level script itself is run (i.e. any code you put outside the <a href="../1 modules/Logic.html#LevelFuncs">LevelFuncs</a> callbacks is executed).</li>
<li>Save data is loaded, if saving from a saved game (will empty <a href="../1 modules/Logic.html#LevelVars">LevelVars</a> and <a href="../1 modules/Logic.html#GameVars">GameVars</a> and repopulate them with what they contained when the game was saved).</li>
<li>If loading from a save, <code>OnLoaded</code> will be called. Otherwise, <code>OnStart</code> will be called.</li>
<li>The control loop, in which <code>OnControlPhase</code> will be called once per frame, begins.</li>
<li>The control loop, in which <code>OnLoop</code> will be called once per frame, begins.</li>
</ol>
</p>
@ -406,7 +502,7 @@ you can just leave out <code>LevelFuncs.OnStart</code>.</p>
<span class="types"><span class="type">function</span></span>
Will be called when a saved game is loaded, just <em>after</em> data is loaded
</li>
<li><span class="parameter">OnControlPhase</span>
<li><span class="parameter">OnLoop</span>
<span class="types"><span class="type">function(float)</span></span>
Will be called during the game's update loop,
and provides the delta time (a float representing game time since last call) via its argument.
@ -420,9 +516,9 @@ and provides the delta time (a float representing game time since last call) via
<p>(EndReason) Will be called when leaving a level. This includes finishing it, exiting to the menu, or loading a save in a different level. It can take an <code>EndReason</code> arg:</p>
<pre><code>EXITTOTITLE
LEVELCOMPLETE
LOADGAME
<pre><code>EXIT_TO_TITLE
LEVEL_COMPLETE
LOAD_GAME
DEATH
OTHER
</code></pre>
@ -436,6 +532,14 @@ end
</code></pre>
</li>
<li><span class="parameter">OnUseItem</span>
<span class="types"><span class="type">function</span></span>
Will be called when using an item from inventory.
</li>
<li><span class="parameter">OnFreeze</span>
<span class="types"><span class="type">function</span></span>
Will be called when any of the Freeze modes are activated.
</li>
</ul>
@ -450,7 +554,6 @@ end
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -97,7 +115,7 @@
<div id="content">
<h1>Table <code>Objects</code></h1>
<p>Moveables, statics, cameras, and so on.</p>
<p>Objects including moveables, statics, cameras, and others.</p>
<p>
</p>
@ -473,7 +491,6 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -106,40 +124,40 @@
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#PlayAudioTrack">PlayAudioTrack(name, type)</a></td>
<td class="summary">Play an audio track</td>
<td class="name" ><a href="#PlayAudioTrack">PlayAudioTrack(filename, type)</a></td>
<td class="summary">Play an audio track.</td>
</tr>
<tr>
<td class="name" ><a href="#SetAmbientTrack">SetAmbientTrack(name)</a></td>
<td class="summary">Set and play an ambient track</td>
<td class="name" ><a href="#SetAmbientTrack">SetAmbientTrack(name, fromStart)</a></td>
<td class="summary">Set and play an ambient track.</td>
</tr>
<tr>
<td class="name" ><a href="#StopAudioTracks">StopAudioTracks()</a></td>
<td class="summary">Stop any audio tracks currently playing</td>
<td class="summary">Stop any audio tracks currently playing.</td>
</tr>
<tr>
<td class="name" ><a href="#StopAudioTrack">StopAudioTrack(type)</a></td>
<td class="summary">Stop audio track that is currently playing</td>
<td class="summary">Stop audio track that is currently playing.</td>
</tr>
<tr>
<td class="name" ><a href="#GetAudioTrackLoudness">GetAudioTrackLoudness(type)</a></td>
<td class="summary">Get current loudness level for specified track type</td>
<td class="summary">Get current loudness level for specified track type.</td>
</tr>
<tr>
<td class="name" ><a href="#PlaySound">PlaySound(sound[, position])</a></td>
<td class="summary">Play sound effect</td>
<td class="name" ><a href="#PlaySound">PlaySound(soundID[, position])</a></td>
<td class="summary">Play sound effect.</td>
</tr>
<tr>
<td class="name" ><a href="#StopSound">StopSound(sound)</a></td>
<td class="summary">Stop sound effect</td>
<td class="name" ><a href="#StopSound">StopSound(soundID)</a></td>
<td class="summary">Stop sound effect.</td>
</tr>
<tr>
<td class="name" ><a href="#IsSoundPlaying">IsSoundPlaying(Sound)</a></td>
<td class="summary">Check if the sound effect is playing</td>
<td class="name" ><a href="#IsSoundPlaying">IsSoundPlaying(soundID)</a></td>
<td class="summary">Check if the sound effect is playing.</td>
</tr>
<tr>
<td class="name" ><a href="#IsAudioTrackPlaying">IsAudioTrackPlaying(Track)</a></td>
<td class="summary">Check if the audio track is playing</td>
<td class="summary">Check if the audio track is playing.</td>
</tr>
<tr>
<td class="name" ><a href="#GetCurrentSubtitle">GetCurrentSubtitle()</a></td>
@ -156,22 +174,22 @@
<dl class="function">
<dt>
<a name = "PlayAudioTrack"></a>
<strong>PlayAudioTrack(name, type)</strong>
<strong>PlayAudioTrack(filename, type)</strong>
</dt>
<dd>
Play an audio track
Play an audio track. Supported formats are wav, mp3 and ogg.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">name</span>
<li><span class="parameter">filename</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
of track (without file extension) to play
Filename of a track (without file extension) to play.
</li>
<li><span class="parameter">type</span>
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
of the audio track to play
Type of the audio track to play.
</li>
</ul>
@ -182,10 +200,10 @@
</dd>
<dt>
<a name = "SetAmbientTrack"></a>
<strong>SetAmbientTrack(name)</strong>
<strong>SetAmbientTrack(name, fromStart)</strong>
</dt>
<dd>
Set and play an ambient track
Set and play an ambient track.
@ -193,7 +211,11 @@
<ul>
<li><span class="parameter">name</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
of track (without file extension) to play
Name of track (without file extension) to play.
</li>
<li><span class="parameter">fromStart</span>
<span class="types"><span class="type">bool</span></span>
Specifies whether ambient track should play from the start, or crossfade at a random position.
</li>
</ul>
@ -207,7 +229,7 @@
<strong>StopAudioTracks()</strong>
</dt>
<dd>
Stop any audio tracks currently playing
Stop any audio tracks currently playing.
@ -222,7 +244,7 @@
<strong>StopAudioTrack(type)</strong>
</dt>
<dd>
Stop audio track that is currently playing
Stop audio track that is currently playing.
@ -230,7 +252,7 @@
<ul>
<li><span class="parameter">type</span>
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
of the audio track
Type of the audio track.
</li>
</ul>
@ -244,7 +266,7 @@
<strong>GetAudioTrackLoudness(type)</strong>
</dt>
<dd>
Get current loudness level for specified track type
Get current loudness level for specified track type.
@ -252,7 +274,7 @@
<ul>
<li><span class="parameter">type</span>
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
of the audio track
Type of the audio track.
</li>
</ul>
@ -260,7 +282,7 @@
<ol>
<span class="types"><span class="type">float</span></span>
current loudness of a specified audio track
Current loudness of a specified audio track.
</ol>
@ -269,18 +291,18 @@
</dd>
<dt>
<a name = "PlaySound"></a>
<strong>PlaySound(sound[, position])</strong>
<strong>PlaySound(soundID[, position])</strong>
</dt>
<dd>
Play sound effect
Play sound effect.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">sound</span>
<li><span class="parameter">soundID</span>
<span class="types"><span class="type">int</span></span>
ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
Sound ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
</li>
<li><span class="parameter">position</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
@ -296,18 +318,18 @@
</dd>
<dt>
<a name = "StopSound"></a>
<strong>StopSound(sound)</strong>
<strong>StopSound(soundID)</strong>
</dt>
<dd>
Stop sound effect
Stop sound effect.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">sound</span>
<li><span class="parameter">soundID</span>
<span class="types"><span class="type">int</span></span>
ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
Sound ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
</li>
</ul>
@ -318,18 +340,18 @@
</dd>
<dt>
<a name = "IsSoundPlaying"></a>
<strong>IsSoundPlaying(Sound)</strong>
<strong>IsSoundPlaying(soundID)</strong>
</dt>
<dd>
Check if the sound effect is playing
Check if the sound effect is playing.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">Sound</span>
<li><span class="parameter">soundID</span>
<span class="types"><span class="type">int</span></span>
ID to check. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
Sound ID to check. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
</li>
</ul>
@ -343,7 +365,7 @@
<strong>IsAudioTrackPlaying(Track)</strong>
</dt>
<dd>
Check if the audio track is playing
Check if the audio track is playing.
@ -351,7 +373,7 @@
<ul>
<li><span class="parameter">Track</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
filename to check. Should be without extension and without full directory path.
Filename to check. Should be without extension and without full directory path.
</li>
</ul>
@ -366,8 +388,8 @@
</dt>
<dd>
Get current subtitle string for a voice track currently playing.
Subtitle file must be in .srt format, have same filename as voice track, and be placed in same directory as voice track.
Returns nil if no voice track is playing or no subtitle present.
Subtitle file must be in .srt format, have same filename as voice track, and be placed in same directory as voice track.
Returns nil if no voice track is playing or no subtitle present.
@ -376,7 +398,7 @@ Returns nil if no voice track is playing or no subtitle present.
<ol>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
current subtitle string
Current subtitle string.
</ol>
@ -390,7 +412,6 @@ Returns nil if no voice track is playing or no subtitle present.
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -106,7 +124,7 @@
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#ShowString">ShowString(str, time)</a></td>
<td class="name" ><a href="#ShowString">ShowString(str, time, autoDelete)</a></td>
<td class="summary">Show some text on-screen.</td>
</tr>
<tr>
@ -128,7 +146,7 @@
<dl class="function">
<dt>
<a name = "ShowString"></a>
<strong>ShowString(str, time)</strong>
<strong>ShowString(str, time, autoDelete)</strong>
</dt>
<dd>
Show some text on-screen.
@ -148,6 +166,13 @@ If not given, the string will have an "infinite" life, and will show
until <a href="../1 modules/Strings.html#HideString">HideString</a> is called or until the level is finished.
Default: nil (i.e. infinite)
</li>
<li><span class="parameter">autoDelete</span>
<span class="types"><span class="type">bool</span></span>
should be string automatically deleted after timeout is reached.
If not given, the string will remain allocated even after timeout is reached, and can be
shown again without re-initialization.
Default: true
</li>
</ul>
@ -213,7 +238,6 @@ with a call to <a href="../1 modules/Strings.html#ShowString">ShowString</a>, or
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -110,14 +128,14 @@
<td class="summary">Determine if there is a clear line of sight between two positions.</td>
</tr>
<tr>
<td class="name" ><a href="#CalculateDistance">CalculateDistance(posA, posB)</a></td>
<td class="summary">Calculate the distance between two positions.</td>
</tr>
<tr>
<td class="name" ><a href="#CalculateHorizontalDistance">CalculateHorizontalDistance(posA, posB)</a></td>
<td class="summary">Calculate the horizontal distance between two positions.</td>
</tr>
<tr>
<td class="name" ><a href="#GetDisplayPosition">GetDisplayPosition(worldPos)</a></td>
<td class="summary">Get the projected display space position of a 3D world position.</td>
</tr>
<tr>
<td class="name" ><a href="#PercentToScreen">PercentToScreen(x, y)</a></td>
<td class="summary">Translate a pair display position coordinates to pixel coordinates.</td>
</tr>
@ -126,17 +144,18 @@
<td class="summary">Translate a pair of pixel coordinates to display position coordinates.</td>
</tr>
<tr>
<td class="name" ><a href="#PickMoveableByDisplayPosition">PickMoveableByDisplayPosition(Display)</a></td>
<td class="summary">Pick a moveable by the given display position.</td>
</tr>
<tr>
<td class="name" ><a href="#PickStaticByDisplayPosition">PickStaticByDisplayPosition(Display)</a></td>
<td class="summary">Pick a static mesh by the given display position.</td>
</tr>
<tr>
<td class="name" ><a href="#PrintLog">PrintLog(message, logLevel[, allowSpam])</a></td>
<td class="summary">Write messages within the Log file</td>
</tr>
</table>
<h2><a href="#Fields">Fields</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#worldPos">worldPos</a></td>
<td class="summary">Get the projected display space position of a 3D world position.</td>
</tr>
</table>
<br/>
<br/>
@ -186,38 +205,6 @@
<span class="global">print</span>(Misc.HasLineOfSight(enemyHead:GetRoomNumber(), enemyHead:GetPosition(), flamePlinthPos))</pre>
</ul>
</dd>
<dt>
<a name = "CalculateDistance"></a>
<strong>CalculateDistance(posA, posB)</strong>
</dt>
<dd>
Calculate the distance between two positions.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">posA</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
First position.
</li>
<li><span class="parameter">posB</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Second position.
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">float</span></span>
Distance between two positions.
</ol>
</dd>
<dt>
<a name = "CalculateHorizontalDistance"></a>
@ -250,6 +237,41 @@
</dd>
<dt>
<a name = "GetDisplayPosition"></a>
<strong>GetDisplayPosition(worldPos)</strong>
</dt>
<dd>
Get the projected display space position of a 3D world position. Returns nil if the world position is behind the camera view.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">worldPos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
3D world position.
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
Projected display space position in percent.
</ol>
<h3>Usage:</h3>
<ul>
<pre class="example">Example: Display a <span class="global">string</span> at the player<span class="string">'s position.
local string = DisplayString('</span>Example', <span class="number">0</span>, <span class="number">0</span>, Color(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>), <span class="keyword">false</span>)
<span class="keyword">local</span> displayPos = GetDisplayPosition(Lara:GetPosition())
<span class="global">string</span>:SetPosition(PercentToScreen(displayPos.x, displayPos.y))</pre>
</ul>
</dd>
<dt>
<a name = "PercentToScreen"></a>
@ -333,6 +355,62 @@ To be used with <a href="../2 classes/Strings.DisplayString.html#DisplayString:G
</dd>
<dt>
<a name = "PickMoveableByDisplayPosition"></a>
<strong>PickMoveableByDisplayPosition(Display)</strong>
</dt>
<dd>
Pick a moveable by the given display position.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">Display</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
space position in percent.
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/Objects.Moveable.html#">Moveable</a></span>
Picked moveable (nil if no moveable was found under the cursor).
</ol>
</dd>
<dt>
<a name = "PickStaticByDisplayPosition"></a>
<strong>PickStaticByDisplayPosition(Display)</strong>
</dt>
<dd>
Pick a static mesh by the given display position.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">Display</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
space position in percent.
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/Objects.Static.html#">Static</a></span>
Picked static mesh (nil if no static mesh was found under the cursor).
</ol>
</dd>
<dt>
<a name = "PrintLog"></a>
@ -355,7 +433,7 @@ To be used with <a href="../2 classes/Strings.DisplayString.html#DisplayString:G
to be displayed within the Log
</li>
<li><span class="parameter">logLevel</span>
<span class="types"><span class="type">Misc.LogLevel</span></span>
<span class="types"><a class="type" href="../4 enums/Util.LogLevel.html#">LogLevel</a></span>
log level to be displayed
</li>
<li><span class="parameter">allowSpam</span>
@ -377,38 +455,6 @@ PrintLog(<span class="string">'test error log'</span>, LogLevel.ERROR)
</span>PrintLog(<span class="string">'test spam log'</span>, LogLevel.INFO, <span class="keyword">true</span>) </pre>
</ul>
</dd>
</dl>
<h2 class="section-header "><a name="Fields"></a>Fields</h2>
<dl class="function">
<dt>
<a name = "worldPos"></a>
<strong>worldPos</strong>
</dt>
<dd>
Get the projected display space position of a 3D world position. Returns nil if the world position is behind the camera view.
<ul>
<li><span class="parameter">worldPos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
3D world position.
</li>
</ul>
<h3>Usage:</h3>
<ul>
<pre class="example">Example: Display a <span class="global">string</span> at the player<span class="string">'s position.
local string = DisplayString('</span>Example', <span class="number">0</span>, <span class="number">0</span>, Color(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>), <span class="keyword">false</span>)
<span class="keyword">local</span> displayPos = GetDisplayPosition(Lara:GetPosition())
<span class="global">string</span>:SetPosition(PercentToScreen(displayPos.x, displayPos.y))</pre>
</ul>
</dd>
</dl>
@ -417,7 +463,6 @@ local string = DisplayString('</span>Example', <span class="number">0</span>, <s
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -105,10 +123,6 @@
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#GetAspectRatio">GetAspectRatio()</a></td>
<td class="summary">Get the display resolution's aspect ratio.</td>
</tr>
<tr>
<td class="name" ><a href="#FadeIn">FadeIn(speed)</a></td>
<td class="summary">Do a full-screen fade-in from black.</td>
@ -126,12 +140,48 @@
<td class="summary">Set field of view.</td>
</tr>
<tr>
<td class="name" ><a href="#GetFOV">GetFOV()</a></td>
<td class="summary">Get field of view.</td>
</tr>
<tr>
<td class="name" ><a href="#GetCameraType">GetCameraType()</a></td>
<td class="summary">Shows the mode of the game camera.</td>
</tr>
<tr>
<td class="name" ><a href="#PlayFlyBy">PlayFlyBy(flyby)</a></td>
<td class="summary">Enable FlyBy with specific ID</td>
<td class="name" ><a href="#GetCameraPosition">GetCameraPosition()</a></td>
<td class="summary">Gets current camera position.</td>
</tr>
<tr>
<td class="name" ><a href="#GetCameraTarget">GetCameraTarget()</a></td>
<td class="summary">Gets current camera target.</td>
</tr>
<tr>
<td class="name" ><a href="#GetCameraRoom">GetCameraRoom()</a></td>
<td class="summary">Gets current room where camera is positioned.</td>
</tr>
<tr>
<td class="name" ><a href="#SetPostProcessMode">SetPostProcessMode(effect)</a></td>
<td class="summary">Sets the post-process effect mode, like negative or monochrome.</td>
</tr>
<tr>
<td class="name" ><a href="#SetPostProcessStrength">SetPostProcessStrength(strength)</a></td>
<td class="summary">Sets the post-process effect strength.</td>
</tr>
<tr>
<td class="name" ><a href="#SetPostProcessTint">SetPostProcessTint(tint)</a></td>
<td class="summary">Sets the post-process tint.</td>
</tr>
<tr>
<td class="name" ><a href="#PlayFlyby">PlayFlyby(seqID)</a></td>
<td class="summary">Play a flyby sequence.</td>
</tr>
<tr>
<td class="name" ><a href="#GetFlybyPosition">GetFlybyPosition(seqID, progress[, loop])</a></td>
<td class="summary">Get a flyby sequence's position at a specified progress point in percent.</td>
</tr>
<tr>
<td class="name" ><a href="#GetFlybyRotation">GetFlybyRotation(seqID, progress[, loop])</a></td>
<td class="summary">Get a flyby sequence's rotation at a specified progress point in percent.</td>
</tr>
<tr>
<td class="name" ><a href="#ResetObjCamera">ResetObjCamera()</a></td>
@ -141,6 +191,10 @@
<td class="name" ><a href="#FlashScreen">FlashScreen(color, speed)</a></td>
<td class="summary">Flash screen.</td>
</tr>
<tr>
<td class="name" ><a href="#GetAspectRatio">GetAspectRatio()</a></td>
<td class="summary">Get the display resolution's aspect ratio.</td>
</tr>
</table>
<br/>
@ -150,27 +204,6 @@
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
<dl class="function">
<dt>
<a name = "GetAspectRatio"></a>
<strong>GetAspectRatio()</strong>
</dt>
<dd>
Get the display resolution's aspect ratio.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">float</span></span>
Display resolution's aspect ratio.
</ol>
</dd>
<dt>
<a name = "FadeIn"></a>
<strong>FadeIn(speed)</strong>
@ -184,7 +217,7 @@
<ul>
<li><span class="parameter">speed</span>
<span class="types"><span class="type">float</span></span>
(default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second.
(default 1.0). Speed in units per second. A value of 1 will make the fade take one second.
</li>
</ul>
@ -206,7 +239,7 @@
<ul>
<li><span class="parameter">speed</span>
<span class="types"><span class="type">float</span></span>
(default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second.
(default 1.0). Speed in units per second. A value of 1 will make the fade take one second.
</li>
</ul>
@ -262,6 +295,27 @@
</dd>
<dt>
<a name = "GetFOV"></a>
<strong>GetFOV()</strong>
</dt>
<dd>
Get field of view.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">float</span></span>
current FOV angle in degrees
</ol>
</dd>
<dt>
<a name = "GetCameraType"></a>
@ -284,8 +338,8 @@
<h3>Usage:</h3>
<ul>
<pre class="example">LevelFuncs.OnControlPhase = <span class="keyword">function</span>()
<span class="keyword">if</span> (View.GetCameraType() == CameraType.Combat) <span class="keyword">then</span>
<pre class="example">LevelFuncs.OnLoop = <span class="keyword">function</span>()
<span class="keyword">if</span> (View.GetCameraType() == CameraType.COMBAT) <span class="keyword">then</span>
<span class="comment">--Do your Actions here.
</span> <span class="keyword">end</span>
<span class="keyword">end</span></pre>
@ -293,19 +347,82 @@
</dd>
<dt>
<a name = "PlayFlyBy"></a>
<strong>PlayFlyBy(flyby)</strong>
<a name = "GetCameraPosition"></a>
<strong>GetCameraPosition()</strong>
</dt>
<dd>
Enable FlyBy with specific ID
Gets current camera position.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
current camera position
</ol>
</dd>
<dt>
<a name = "GetCameraTarget"></a>
<strong>GetCameraTarget()</strong>
</dt>
<dd>
Gets current camera target.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
current camera target
</ol>
</dd>
<dt>
<a name = "GetCameraRoom"></a>
<strong>GetCameraRoom()</strong>
</dt>
<dd>
Gets current room where camera is positioned.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/Objects.Room.html#">Room</a></span>
current room of the camera
</ol>
</dd>
<dt>
<a name = "SetPostProcessMode"></a>
<strong>SetPostProcessMode(effect)</strong>
</dt>
<dd>
Sets the post-process effect mode, like negative or monochrome.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">flyby</span>
<span class="types"><span class="type">short</span></span>
(ID of flyby)
<li><span class="parameter">effect</span>
<span class="types"><a class="type" href="../4 enums/View.PostProcessMode.html#">PostProcessMode</a></span>
type to set.
</li>
</ul>
@ -313,6 +430,146 @@
</dd>
<dt>
<a name = "SetPostProcessStrength"></a>
<strong>SetPostProcessStrength(strength)</strong>
</dt>
<dd>
Sets the post-process effect strength.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">strength</span>
<span class="types"><span class="type">float</span></span>
(default 1.0). How strong the effect is.
</li>
</ul>
</dd>
<dt>
<a name = "SetPostProcessTint"></a>
<strong>SetPostProcessTint(tint)</strong>
</dt>
<dd>
Sets the post-process tint.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">tint</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
value to use.
</li>
</ul>
</dd>
<dt>
<a name = "PlayFlyby"></a>
<strong>PlayFlyby(seqID)</strong>
</dt>
<dd>
Play a flyby sequence.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">seqID</span>
<span class="types"><span class="type">int</span></span>
Flyby sequence ID.
</li>
</ul>
</dd>
<dt>
<a name = "GetFlybyPosition"></a>
<strong>GetFlybyPosition(seqID, progress[, loop])</strong>
</dt>
<dd>
Get a flyby sequence's position at a specified progress point in percent.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">seqID</span>
<span class="types"><span class="type">int</span></span>
Flyby sequence ID.
</li>
<li><span class="parameter">progress</span>
<span class="types"><span class="type">float</span></span>
Progress point in percent. Clamped to [0, 100].
</li>
<li><span class="parameter">loop</span>
<span class="types"><span class="type">bool</span></span>
Smooth the position near start and end points, as if the sequence is looped.
(<em>optional</em>)
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Position at the given progress point.
</ol>
</dd>
<dt>
<a name = "GetFlybyRotation"></a>
<strong>GetFlybyRotation(seqID, progress[, loop])</strong>
</dt>
<dd>
Get a flyby sequence's rotation at a specified progress point in percent.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">seqID</span>
<span class="types"><span class="type">int</span></span>
Flyby sequence ID.
</li>
<li><span class="parameter">progress</span>
<span class="types"><span class="type">float</span></span>
Progress point in percent. Clamped to [0, 100].
</li>
<li><span class="parameter">loop</span>
<span class="types"><span class="type">bool</span></span>
Smooth the position near start and end points, as if the sequence is looped.
(<em>optional</em>)
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Rotation.html#">Rotation</a></span>
Rotation at the given progress point.
</ol>
</dd>
<dt>
<a name = "ResetObjCamera"></a>
@ -346,7 +603,7 @@
</li>
<li><span class="parameter">speed</span>
<span class="types"><span class="type">float</span></span>
(default 1.0). Speed in "amount" per second. Value of 1 will make flash take one second. Clamped to [0.005, 1.0].
(default 1.0). Speed in units per second. Value of 1 will make flash take one second. Clamped to [0.005, 1.0].
</li>
</ul>
@ -354,6 +611,27 @@
</dd>
<dt>
<a name = "GetAspectRatio"></a>
<strong>GetAspectRatio()</strong>
</dt>
<dd>
Get the display resolution's aspect ratio.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">float</span></span>
Display resolution's aspect ratio.
</ol>
</dd>
</dl>
@ -362,7 +640,6 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -0,0 +1,766 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>1 Modules</h2>
<ul class="nowrap">
<li> <a href="../1 modules/Effects.html">Effects</a></li>
<li> <a href="../1 modules/Flow.html">Flow</a></li>
<li> <a href="../1 modules/Input.html">Input</a></li>
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
<li> <a href="../1 modules/Logic.html">Logic</a></li>
<li> <a href="../1 modules/Objects.html">Objects</a></li>
<li> <a href="../1 modules/Sound.html">Sound</a></li>
<li> <a href="../1 modules/Strings.html">Strings</a></li>
<li> <a href="../1 modules/Util.html">Util</a></li>
<li> <a href="../1 modules/View.html">View</a></li>
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <here>Collision.Probe</here></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
<div id="content">
<h1>Class <code>Collision.Probe</code></h1>
<p>Represents a collision probe in the game world.</p>
<p> Provides collision information from a reference world position.</p>
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#Probe">Probe(pos[, roomNumber])</a></td>
<td class="summary">Create a Probe at a specified world position in a room.</td>
</tr>
<tr>
<td class="name" ><a href="#Probe">Probe(pos, roomNumber, dir, dist)</a></td>
<td class="summary">Create a Probe that casts from an origin world position in a room in a given direction for a specified distance.</td>
</tr>
<tr>
<td class="name" ><a href="#Probe">Probe(pos, roomNumber, rot, dist)</a></td>
<td class="summary">Create a Probe that casts from an origin world position in a room in the direction of a given rotation for a specified distance.</td>
</tr>
<tr>
<td class="name" ><a href="#Probe">Probe(pos, roomNumber, rot, relOffset)</a></td>
<td class="summary">Create a Probe that casts from an origin world position, where a given relative offset is rotated according to a given rotation.</td>
</tr>
<tr>
<td class="name" ><a href="#GetPosition">GetPosition()</a></td>
<td class="summary">Get the world position of this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetRoom">GetRoom()</a></td>
<td class="summary">Get the Room object of this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetRoomName">GetRoomName()</a></td>
<td class="summary">Get the room name of this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetFloorHeight">GetFloorHeight()</a></td>
<td class="summary">Get the floor height at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetCeilingHeight">GetCeilingHeight()</a></td>
<td class="summary">Get the ceiling height at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetWaterSurfaceHeight">GetWaterSurfaceHeight()</a></td>
<td class="summary">Get the water surface height at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetFloorNormal">GetFloorNormal()</a></td>
<td class="summary">Get the normal of the floor at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetCeilingNormal">GetCeilingNormal()</a></td>
<td class="summary">Get the normal of the ceiling at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetFloorMaterialType">GetFloorMaterialType()</a></td>
<td class="summary">Get the material type of the floor at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#GetCeilingMaterialType">GetCeilingMaterialType()</a></td>
<td class="summary">Get the material type of the ceiling at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#IsSteepFloor">IsSteepFloor()</a></td>
<td class="summary">Check if the floor at this Probe is steep.</td>
</tr>
<tr>
<td class="name" ><a href="#IsSteepCeiling">IsSteepCeiling()</a></td>
<td class="summary">Check if the ceiling at this Probe is steep.</td>
</tr>
<tr>
<td class="name" ><a href="#IsWall">IsWall()</a></td>
<td class="summary">Check if the Probe is inside a wall.</td>
</tr>
<tr>
<td class="name" ><a href="#IsInsideSolidGeometry">IsInsideSolidGeometry()</a></td>
<td class="summary">Check if this Probe is inside solid geometry (below a floor, above a ceiling, inside a bridge, or inside a wall).</td>
</tr>
<tr>
<td class="name" ><a href="#IsClimbableWall">IsClimbableWall(headingAngle)</a></td>
<td class="summary">Check if there is a climbable wall in the given heading angle at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#IsMonkeySwing">IsMonkeySwing()</a></td>
<td class="summary">Check if there is a monkey swing sector at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#IsDeath">IsDeath()</a></td>
<td class="summary">Check if there is a death sector at this Probe.</td>
</tr>
<tr>
<td class="name" ><a href="#Preview">Preview()</a></td>
<td class="summary">Preview this Probe in the Collision Stats debug page.</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
<dl class="function">
<dt>
<a name = "Probe"></a>
<strong>Probe(pos[, roomNumber])</strong>
</dt>
<dd>
Create a Probe at a specified world position in a room.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
World position.
</li>
<li><span class="parameter">roomNumber</span>
<span class="types"><span class="type">int</span></span>
Room number. Must be used if probing a position in an overlapping room.
(<em>optional</em>)
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/Collision.Probe.html#Probe">Probe</a></span>
A new Probe.
</ol>
</dd>
<dt>
<a name = "Probe"></a>
<strong>Probe(pos, roomNumber, dir, dist)</strong>
</dt>
<dd>
Create a Probe that casts from an origin world position in a room in a given direction for a specified distance.
Required to correctly traverse between rooms.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Origin world position to cast from.
</li>
<li><span class="parameter">roomNumber</span>
<span class="types"><span class="type">int</span></span>
Origin room number.
</li>
<li><span class="parameter">dir</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Direction in which to cast.
</li>
<li><span class="parameter">dist</span>
<span class="types"><span class="type">float</span></span>
Distance to cast.
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/Collision.Probe.html#Probe">Probe</a></span>
A new Probe.
</ol>
</dd>
<dt>
<a name = "Probe"></a>
<strong>Probe(pos, roomNumber, rot, dist)</strong>
</dt>
<dd>
Create a Probe that casts from an origin world position in a room in the direction of a given rotation for a specified distance.
Required to correctly traverse between rooms.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Origin world position to cast from.
</li>
<li><span class="parameter">roomNumber</span>
<span class="types"><span class="type">int</span></span>
Origin room number.
</li>
<li><span class="parameter">rot</span>
<span class="types"><a class="type" href="../3 primitive classes/Rotation.html#">Rotation</a></span>
Rotation defining the direction in which to cast.
</li>
<li><span class="parameter">dist</span>
<span class="types"><span class="type">float</span></span>
Distance to cast.
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/Collision.Probe.html#Probe">Probe</a></span>
A new Probe.
</ol>
</dd>
<dt>
<a name = "Probe"></a>
<strong>Probe(pos, roomNumber, rot, relOffset)</strong>
</dt>
<dd>
Create a Probe that casts from an origin world position, where a given relative offset is rotated according to a given rotation.
Required to correctly traverse between rooms.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Origin world position to cast from.
</li>
<li><span class="parameter">roomNumber</span>
<span class="types"><span class="type">int</span></span>
Origin room number.
</li>
<li><span class="parameter">rot</span>
<span class="types"><a class="type" href="../3 primitive classes/Rotation.html#">Rotation</a></span>
Rotation according to which the input relative offset is rotated.
</li>
<li><span class="parameter">relOffset</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Relative offset to cast.
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/Collision.Probe.html#Probe">Probe</a></span>
A new Probe.
</ol>
</dd>
<dt>
<a name = "GetPosition"></a>
<strong>GetPosition()</strong>
</dt>
<dd>
Get the world position of this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
World position.
</ol>
</dd>
<dt>
<a name = "GetRoom"></a>
<strong>GetRoom()</strong>
</dt>
<dd>
Get the Room object of this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">Room</span></span>
Room object.
</ol>
</dd>
<dt>
<a name = "GetRoomName"></a>
<strong>GetRoomName()</strong>
</dt>
<dd>
Get the room name of this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
Room name.
</ol>
</dd>
<dt>
<a name = "GetFloorHeight"></a>
<strong>GetFloorHeight()</strong>
</dt>
<dd>
Get the floor height at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">int</span></span>
Floor height. <strong>nil: no floor exists</strong>
</ol>
</dd>
<dt>
<a name = "GetCeilingHeight"></a>
<strong>GetCeilingHeight()</strong>
</dt>
<dd>
Get the ceiling height at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">int</span></span>
Ceiling height. <strong>nil: no ceiling exists</strong>
</ol>
</dd>
<dt>
<a name = "GetWaterSurfaceHeight"></a>
<strong>GetWaterSurfaceHeight()</strong>
</dt>
<dd>
Get the water surface height at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">int</span></span>
Water surface height. <strong>nil: no water surface exists</strong>
</ol>
</dd>
<dt>
<a name = "GetFloorNormal"></a>
<strong>GetFloorNormal()</strong>
</dt>
<dd>
Get the normal of the floor at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Floor normal. <strong>nil: no floor exists</strong>
</ol>
</dd>
<dt>
<a name = "GetCeilingNormal"></a>
<strong>GetCeilingNormal()</strong>
</dt>
<dd>
Get the normal of the ceiling at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
Ceiling normal. <strong>nil: no ceiling exists</strong>
</ol>
</dd>
<dt>
<a name = "GetFloorMaterialType"></a>
<strong>GetFloorMaterialType()</strong>
</dt>
<dd>
Get the material type of the floor at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../4 enums/Collision.MaterialType.html#">MaterialType</a></span>
Floor material type. <strong>nil: no floor exists</strong>
</ol>
</dd>
<dt>
<a name = "GetCeilingMaterialType"></a>
<strong>GetCeilingMaterialType()</strong>
</dt>
<dd>
Get the material type of the ceiling at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../4 enums/Collision.MaterialType.html#">MaterialType</a></span>
Ceiling material type. <strong>nil: no ceiling exists</strong>
</ol>
</dd>
<dt>
<a name = "IsSteepFloor"></a>
<strong>IsSteepFloor()</strong>
</dt>
<dd>
Check if the floor at this Probe is steep.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
Steep floor status. <strong>true: is a steep floor, false: isn't a steep floor, nil: no floor exists</strong>
</ol>
</dd>
<dt>
<a name = "IsSteepCeiling"></a>
<strong>IsSteepCeiling()</strong>
</dt>
<dd>
Check if the ceiling at this Probe is steep.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
Steep ceiling status. <strong>true: is a steep ceiling, false: isn't a steep ceiling, nil: no ceiling exists</strong>
</ol>
</dd>
<dt>
<a name = "IsWall"></a>
<strong>IsWall()</strong>
</dt>
<dd>
Check if the Probe is inside a wall. Can be used to determine if a wall and ceiling exist.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
Wall status. <strong>true: is a wall, false: isn't a wall</strong>
</ol>
</dd>
<dt>
<a name = "IsInsideSolidGeometry"></a>
<strong>IsInsideSolidGeometry()</strong>
</dt>
<dd>
Check if this Probe is inside solid geometry (below a floor, above a ceiling, inside a bridge, or inside a wall).
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
Inside geometry status. <strong>true: is inside, false: is outside</strong>
</ol>
</dd>
<dt>
<a name = "IsClimbableWall"></a>
<strong>IsClimbableWall(headingAngle)</strong>
</dt>
<dd>
Check if there is a climbable wall in the given heading angle at this Probe.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">headingAngle</span>
<span class="types"><span class="type">float</span></span>
Heading angle at which to check for a climbable wall.
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
Climbable wall status. <strong>true: is climbable wall, false: isn't climbable</strong>
</ol>
</dd>
<dt>
<a name = "IsMonkeySwing"></a>
<strong>IsMonkeySwing()</strong>
</dt>
<dd>
Check if there is a monkey swing sector at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
Monkey swing sector status. <strong>true: is a monkey swing, false: isn't a monkey swing</strong>
</ol>
</dd>
<dt>
<a name = "IsDeath"></a>
<strong>IsDeath()</strong>
</dt>
<dd>
Check if there is a death sector at this Probe.
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
Death sector status. <strong>true: is a death sector, false: isn't a death sector</strong>
</ol>
</dd>
<dt>
<a name = "Preview"></a>
<strong>Preview()</strong>
</dt>
<dd>
Preview this Probe in the Collision Stats debug page.
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View file

@ -1,529 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.1.0 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>1 Modules</h2>
<ul class="nowrap">
<li> <a href="../1 modules/Effects.html">Effects</a></li>
<li> <a href="../1 modules/Flow.html">Flow</a></li>
<li> <a href="../1 modules/Input.html">Input</a></li>
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
<li> <a href="../1 modules/Logic.html">Logic</a></li>
<li> <a href="../1 modules/Objects.html">Objects</a></li>
<li> <a href="../1 modules/Sound.html">Sound</a></li>
<li> <a href="../1 modules/Strings.html">Strings</a></li>
<li> <a href="../1 modules/Util.html">Util</a></li>
<li> <a href="../1 modules/View.html">View</a></li>
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <here>DisplaySprite</here></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/DisplaySprite.AlignMode.html">DisplaySprite.AlignMode</a></li>
<li> <a href="../4 enums/DisplaySprite.ScaleMode.html">DisplaySprite.ScaleMode</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
</ul>
</div>
<div id="content">
<h1>Class <code>DisplaySprite</code></h1>
<p>Represents a screen-space display sprite.</p>
<p>
</p>
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#DisplaySprite">DisplaySprite(ID, int, pos, rot, scale[, color])</a></td>
<td class="summary">Create a DisplaySprite object.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:GetObjectID">DisplaySprite:GetObjectID()</a></td>
<td class="summary">Get the object ID of the sprite sequence object used by the display sprite.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:GetSpriteID">DisplaySprite:GetSpriteID()</a></td>
<td class="summary">Get the sprite ID in the sprite sequence object used by the display sprite.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:GetPosition">DisplaySprite:GetPosition()</a></td>
<td class="summary">Get the display position of the display sprite in percent.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:GetRotation">DisplaySprite:GetRotation()</a></td>
<td class="summary">Get the rotation of the display sprite in degrees.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:GetScale">DisplaySprite:GetScale()</a></td>
<td class="summary">Get the horizontal and vertical scale of the display sprite in percent.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:GetColor">DisplaySprite:GetColor()</a></td>
<td class="summary">Get the color of the display sprite.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:SetObjectID">DisplaySprite:SetObjectID(New)</a></td>
<td class="summary">Set the sprite sequence object ID used by the display sprite.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:SetSpriteID">DisplaySprite:SetSpriteID(New)</a></td>
<td class="summary">Set the sprite ID in the sprite sequence object used by the display sprite.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:SetPosition">DisplaySprite:SetPosition(New)</a></td>
<td class="summary">Set the display position of the display sprite in percent.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:SetRotation">DisplaySprite:SetRotation(New)</a></td>
<td class="summary">Set the rotation of the display sprite in degrees.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:SetScale">DisplaySprite:SetScale(New)</a></td>
<td class="summary">Set the horizontal and vertical scale of the display sprite in percent.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:SetColor">DisplaySprite:SetColor(New)</a></td>
<td class="summary">Set the color of the display sprite.</td>
</tr>
<tr>
<td class="name" ><a href="#DisplaySprite:Draw">DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode])</a></td>
<td class="summary">Draw the display sprite in display space for the current frame.</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
<dl class="function">
<dt>
<a name = "DisplaySprite"></a>
<strong>DisplaySprite(ID, int, pos, rot, scale[, color])</strong>
</dt>
<dd>
Create a DisplaySprite object.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">ID</span>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
of the sprite sequence object.
</li>
<li><span class="parameter">int</span>
<span class="types"><span class="type">int</span></span>
spriteID ID of the sprite in the sequence.
</li>
<li><span class="parameter">pos</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
Display position in percent.
</li>
<li><span class="parameter">rot</span>
<span class="types"><span class="type">float</span></span>
Rotation in degrees.
</li>
<li><span class="parameter">scale</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
Horizontal and vertical scale in percent. Scaling is interpreted by the DisplaySpriteEnum.ScaleMode passed to the Draw() function call.
</li>
<li><span class="parameter">color</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
Color. <strong>Default: Color(255, 255, 255, 255)</strong>
(<em>optional</em>)
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../2 classes/DisplaySprite.html#">DisplaySprite</a></span>
A new DisplaySprite object.
</ol>
</dd>
<dt>
<a name = "DisplaySprite:GetObjectID"></a>
<strong>DisplaySprite:GetObjectID()</strong>
</dt>
<dd>
Get the object ID of the sprite sequence object used by the display sprite. ()
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
Sprite sequence object ID.
</ol>
</dd>
<dt>
<a name = "DisplaySprite:GetSpriteID"></a>
<strong>DisplaySprite:GetSpriteID()</strong>
</dt>
<dd>
Get the sprite ID in the sprite sequence object used by the display sprite. ()
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">int</span></span>
Sprite ID in the sprite sequence object.
</ol>
</dd>
<dt>
<a name = "DisplaySprite:GetPosition"></a>
<strong>DisplaySprite:GetPosition()</strong>
</dt>
<dd>
Get the display position of the display sprite in percent. ()
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
Display position in percent.
</ol>
</dd>
<dt>
<a name = "DisplaySprite:GetRotation"></a>
<strong>DisplaySprite:GetRotation()</strong>
</dt>
<dd>
Get the rotation of the display sprite in degrees. ()
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">float</span></span>
Rotation in degrees.
</ol>
</dd>
<dt>
<a name = "DisplaySprite:GetScale"></a>
<strong>DisplaySprite:GetScale()</strong>
</dt>
<dd>
Get the horizontal and vertical scale of the display sprite in percent. ()
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
Horizontal and vertical scale in percent.
</ol>
</dd>
<dt>
<a name = "DisplaySprite:GetColor"></a>
<strong>DisplaySprite:GetColor()</strong>
</dt>
<dd>
Get the color of the display sprite. ()
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
Color.
</ol>
</dd>
<dt>
<a name = "DisplaySprite:SetObjectID"></a>
<strong>DisplaySprite:SetObjectID(New)</strong>
</dt>
<dd>
Set the sprite sequence object ID used by the display sprite. (Objects.ObjID)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">New</span>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
sprite sequence object ID.
</li>
</ul>
</dd>
<dt>
<a name = "DisplaySprite:SetSpriteID"></a>
<strong>DisplaySprite:SetSpriteID(New)</strong>
</dt>
<dd>
Set the sprite ID in the sprite sequence object used by the display sprite. (int)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">New</span>
<span class="types"><span class="type">int</span></span>
sprite ID in the sprite sequence object.
</li>
</ul>
</dd>
<dt>
<a name = "DisplaySprite:SetPosition"></a>
<strong>DisplaySprite:SetPosition(New)</strong>
</dt>
<dd>
Set the display position of the display sprite in percent. (Vec2)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">New</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
display position in percent.
</li>
</ul>
</dd>
<dt>
<a name = "DisplaySprite:SetRotation"></a>
<strong>DisplaySprite:SetRotation(New)</strong>
</dt>
<dd>
Set the rotation of the display sprite in degrees. (float)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">New</span>
<span class="types"><span class="type">float</span></span>
rotation in degrees.
</li>
</ul>
</dd>
<dt>
<a name = "DisplaySprite:SetScale"></a>
<strong>DisplaySprite:SetScale(New)</strong>
</dt>
<dd>
Set the horizontal and vertical scale of the display sprite in percent. (Vec2)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">New</span>
<span class="types"><span class="type">float</span></span>
horizontal and vertical scale in percent.
</li>
</ul>
</dd>
<dt>
<a name = "DisplaySprite:SetColor"></a>
<strong>DisplaySprite:SetColor(New)</strong>
</dt>
<dd>
Set the color of the display sprite. (Color)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">New</span>
<span class="types"><span class="type">float</span></span>
color.
</li>
</ul>
</dd>
<dt>
<a name = "DisplaySprite:Draw"></a>
<strong>DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode])</strong>
</dt>
<dd>
Draw the display sprite in display space for the current frame.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">priority</span>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
Draw priority. Can be thought of as a layer, with higher values having precedence. <strong>Default: 0</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">alignMode</span>
<span class="types"><a class="type" href="../4 enums/DisplaySprite.AlignMode.html#">AlignMode</a></span>
Align mode interpreting an offset from the sprite's position. <strong>Default: DisplaySprite.AlignMode.CENTER</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">scaleMode</span>
<span class="types"><a class="type" href="../4 enums/DisplaySprite.ScaleMode.html#">ScaleMode</a></span>
Scale mode interpreting the display sprite's horizontal and vertical scale. <strong>Default: DisplaySprite.ScaleMode.FIT</strong>
(<em>optional</em>)
</li>
<li><span class="parameter">blendMode</span>
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
Blend mode. <strong>Default: Effects.BlendID.ALPHABLEND</strong>
(<em>optional</em>)
</li>
</ul>
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-09 18:25:22 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View file

@ -3,7 +3,7 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.2 Lua API</title>
<title>TombEngine 1.8.1 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
@ -24,7 +24,7 @@
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<h1>&nbsp;TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
@ -45,14 +45,10 @@
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
<li> <here>Flow.Level</here></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
@ -63,33 +59,55 @@
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
</ul>
</div>
@ -125,19 +143,31 @@
</tr>
<tr>
<td class="name" ><a href="#layer1">layer1</a></td>
<td class="summary">(<a href="../2 classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Primary sky layer</td>
<td class="summary">(<a href="../3 primitive classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Primary sky cloud layer.</td>
</tr>
<tr>
<td class="name" ><a href="#layer2">layer2</a></td>
<td class="summary">(<a href="../2 classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Secondary sky layer</td>
<td class="summary">(<a href="../3 primitive classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Secondary sky cloud layer.</td>
</tr>
<tr>
<td class="name" ><a href="#horizon1">horizon1</a></td>
<td class="summary">(<a href="../3 primitive classes/Flow.Horizon.html#">Flow.Horizon</a>) First horizon layer.</td>
</tr>
<tr>
<td class="name" ><a href="#horizon2">horizon2</a></td>
<td class="summary">(<a href="../3 primitive classes/Flow.Horizon.html#">Flow.Horizon</a>) Second horizon layer.</td>
</tr>
<tr>
<td class="name" ><a href="#starfield">starfield</a></td>
<td class="summary">(<a href="../3 primitive classes/Flow.Starfield.html#">Flow.Starfield</a>) Starfield in the sky.</td>
</tr>
<tr>
<td class="name" ><a href="#lensFlare">lensFlare</a></td>
<td class="summary">(<a href="../3 primitive classes/Flow.LensFlare.html#">Flow.LensFlare</a>) Global lens flare.</td>
</tr>
<tr>
<td class="name" ><a href="#fog">fog</a></td>
<td class="summary">(<a href="../2 classes/Flow.Fog.html#">Flow.Fog</a>) omni fog RGB color and distance.</td>
</tr>
<tr>
<td class="name" ><a href="#horizon">horizon</a></td>
<td class="summary">(bool) Draw sky layer?</td>
<td class="summary">(<a href="../3 primitive classes/Flow.Fog.html#">Flow.Fog</a>) Global distance fog, with specified RGB color and distance.</td>
</tr>
<tr>
<td class="name" ><a href="#storm">storm</a></td>
@ -160,24 +190,20 @@
<td class="summary">(bool) Enable occasional screen shake effect.</td>
</tr>
<tr>
<td class="name" ><a href="#mirror">mirror</a></td>
<td class="summary">(<a href="../2 classes/Flow.Mirror.html#">Flow.Mirror</a>) Location and size of the level's mirror, if present.</td>
<td class="name" ><a href="#farView">farView</a></td>
<td class="summary">(int) The maximum draw distance for level.</td>
</tr>
<tr>
<td class="name" ><a href="#unlimitedAir">unlimitedAir</a></td>
<td class="summary">(bool) Enable unlimited oxygen supply when in water.</td>
<td class="name" ><a href="#resetHub">resetHub</a></td>
<td class="summary">(bool) Reset hub data.</td>
</tr>
<tr>
<td class="name" ><a href="#objects">objects</a></td>
<td class="summary">(table of <a href="../2 classes/Flow.InventoryItem.html#">Flow.InventoryItem</a>s) table of inventory object overrides</td>
<td class="summary">(table of <a href="../3 primitive classes/Flow.InventoryItem.html#">Flow.InventoryItem</a>s) A table of inventory object layout overrides.</td>
</tr>
<tr>
<td class="name" ><a href="#secrets">secrets</a></td>
<td class="summary">(short) Set Secrets for Level</td>
</tr>
<tr>
<td class="name" ><a href="#farView">farView</a></td>
<td class="summary">(int) The maximum draw distance for level.</td>
<td class="summary">(short) Set total secret count for current level.</td>
</tr>
</table>
<h2><a href="#Functions">Functions</a></h2>
@ -280,7 +306,7 @@
<strong>layer1</strong>
</dt>
<dd>
(<a href="../2 classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Primary sky layer
(<a href="../3 primitive classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Primary sky cloud layer.
@ -295,7 +321,67 @@
<strong>layer2</strong>
</dt>
<dd>
(<a href="../2 classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Secondary sky layer
(<a href="../3 primitive classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Secondary sky cloud layer.
</dd>
<dt>
<a name = "horizon1"></a>
<strong>horizon1</strong>
</dt>
<dd>
(<a href="../3 primitive classes/Flow.Horizon.html#">Flow.Horizon</a>) First horizon layer.
</dd>
<dt>
<a name = "horizon2"></a>
<strong>horizon2</strong>
</dt>
<dd>
(<a href="../3 primitive classes/Flow.Horizon.html#">Flow.Horizon</a>) Second horizon layer.
</dd>
<dt>
<a name = "starfield"></a>
<strong>starfield</strong>
</dt>
<dd>
(<a href="../3 primitive classes/Flow.Starfield.html#">Flow.Starfield</a>) Starfield in the sky.
</dd>
<dt>
<a name = "lensFlare"></a>
<strong>lensFlare</strong>
</dt>
<dd>
(<a href="../3 primitive classes/Flow.LensFlare.html#">Flow.LensFlare</a>) Global lens flare.
@ -310,24 +396,8 @@
<strong>fog</strong>
</dt>
<dd>
(<a href="../2 classes/Flow.Fog.html#">Flow.Fog</a>) omni fog RGB color and distance.
As seen in TR4's Desert Railroad.
If not provided, distance fog will be black.
</dd>
<dt>
<a name = "horizon"></a>
<strong>horizon</strong>
</dt>
<dd>
(bool) Draw sky layer? (default: false)
(<a href="../3 primitive classes/Flow.Fog.html#">Flow.Fog</a>) Global distance fog, with specified RGB color and distance.
If not provided, distance fog will not be visible.
@ -403,7 +473,7 @@ Invisible
<p>e.g. <code>myLevel.laraType = LaraType.Divesuit</code></p>
<p> <strong>(not yet fully implemented)</strong>
<p> <strong>Not yet fully implemented.</strong> Only types <code>Normal</code> and <code>Young</code> are guaranteed to work.
@ -430,13 +500,12 @@ Invisible
</dd>
<dt>
<a name = "mirror"></a>
<strong>mirror</strong>
<a name = "farView"></a>
<strong>farView</strong>
</dt>
<dd>
(<a href="../2 classes/Flow.Mirror.html#">Flow.Mirror</a>) Location and size of the level's mirror, if present. </p>
<p> <strong>(not yet implemented)</strong>
(int) The maximum draw distance for level.
Given in sectors (blocks). Must be at least 4.
@ -447,13 +516,12 @@ Invisible
</dd>
<dt>
<a name = "unlimitedAir"></a>
<strong>unlimitedAir</strong>
<a name = "resetHub"></a>
<strong>resetHub</strong>
</dt>
<dd>
(bool) Enable unlimited oxygen supply when in water. </p>
<p> <strong>(not yet implemented)</strong>
(bool) Reset hub data.
Resets the state for all previous levels, including items, flipmaps and statistics.
@ -468,7 +536,7 @@ Invisible
<strong>objects</strong>
</dt>
<dd>
(table of <a href="../2 classes/Flow.InventoryItem.html#">Flow.InventoryItem</a>s) table of inventory object overrides
(table of <a href="../3 primitive classes/Flow.InventoryItem.html#">Flow.InventoryItem</a>s) A table of inventory object layout overrides.
@ -483,26 +551,7 @@ Invisible
<strong>secrets</strong>
</dt>
<dd>
(short) Set Secrets for Level
</dd>
<dt>
<a name = "farView"></a>
<strong>farView</strong>
</dt>
<dd>
(int) The maximum draw distance for level.
Given in sectors (blocks).
Must be at least 4.</p>
<p>This is equivalent to TRNG's LevelFarView variable.
(short) Set total secret count for current level.
@ -530,7 +579,7 @@ Must be at least 4.</p>
<ol>
<span class="types"><a class="type" href="../2 classes/Flow.Level.html#Level">Level</a></span>
a Level object
a Level object.
</ol>
@ -544,7 +593,6 @@ Must be at least 4.</p>
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

Some files were not shown because too many files have changed in this diff Show more