Compare commits

..

54 Commits

Author SHA1 Message Date
David Markowitz
a8a4da0679 Component work
Further component ripping

finish switch rip

Fix bad names

Get it running

Fix issues

Components: order is complete, need epilogue code now.

finished port of thingy

Update ComponentFactories.py

Update DoObjectPreLoad.py
2023-10-31 14:09:40 -07:00
David Markowitz
623da4bed9 Entity: Add missing destroyable case
Adds a missing edge case on the client where you may have the is_smashable flag set, but dont actually have a destroyable component.  In this case you should create only a destroyable component and add it to the component and serialize it last after all other data.  We also needed to add the set_faction var reading and setting as this is how Oliver Sudden actually gets their faction since they have no actual information in the database, it is all stored in ldf keys.

Tested that Oliver Sudden no longer logs Unable to unserialize logs when serialized or constructed.
2023-10-19 03:25:13 -07:00
David Markowitz
50921cce2d Loot: Fix rarity sorting (#1226)
Missed this step from the previous speedup.  rollloot numbers match within margin of error to numbers pre-cache improvement.
2023-10-18 09:18:47 -05:00
David Markowitz
3806891db0 Teams: Default team loot to ffa (#1224) 2023-10-18 09:18:22 -05:00
David Markowitz
ba91058736 Destroyable: Fix animated deaths (#1227)
Fixes #1222

addresses an issue where the death behavior of a destructible component was not being respected and enemies with destroyable components that had special death animations were not able to play the animation on death.  This pr adds in the hardcoded constant the client uses for the same metric of 12 seconds.

Tested that claiming Nimbus Rock and completing the property guards mission allows him to vacuum away and then network the destruction packet 12 seconds later.
2023-10-18 09:17:57 -05:00
David Markowitz
73e70badb7 Entity: fix bad ldf key serialization (#1225) 2023-10-17 08:45:46 -05:00
David Markowitz
e4cae35edb PhantomPhysics: Fix gravity (#1221)
Fix gravity not being adjusted when colliding with a phantom physics object

Tested that while on moonbase, the players gravity is no longer reset to 1 when they change their cheat info.
2023-10-16 07:55:57 -05:00
David Markowitz
c6087ce77a perf: Loot memory savings (#1165)
* Move away from constructor queries

Fix up other large tables to have proper backup lookups

Revert "idk im just dumb ig"

This reverts commit 5d5be5df53b8959b42b291613d7db749a65a3585.

idk im just dumb ig

* Fix slow components registry lookup

* add define for cdclient cache all

* Huge loot namespace rework

- Remove all excess memory usage
- do not cache components registry
- cache loot matrices on startup of the destroyable component
- convert loot singleton class to a namespace
- rework loot cdclient tables to operate closer to how someone would actually use them (basically doing the previous LootGenerator::LootGenerator caching but in those tables)
- Memory usage reduced by 10%+ across the board

* cache rebuild matrix

* Database: move reading to own function

Also change name of cache to PascalCase

* Database: Move common function rading
2023-10-09 15:33:22 -05:00
David Markowitz
e96fd56fbd Diagnostics: Fix demangling (#1215)
Rename to better names
More comments
Less branches
2023-10-09 15:31:43 -05:00
David Markowitz
500ae4d6e5 Scripts: Fix ambiguous base class (#1216) 2023-10-09 15:31:25 -05:00
David Markowitz
094797881b Cannon Cove: Fix incorrect sign (#1211)
Update CMakeVariables.txt

Cannon Cove: Fix incorrect sign
2023-10-09 15:29:11 -05:00
David Markowitz
3dd2791066 chore: Use TryParse for LDF parsing (#1206)
* LDF: Simplify parsing

* Update GeneralUtils.h
2023-10-09 15:22:40 -05:00
570c597148 WIP (#1203) 2023-10-09 15:20:56 -05:00
David Markowitz
ad003634f4 chore: Physics Component abstraction and addition of tests (#1159)
* Make serialize actually virtual

yep

* Abstract to PhysicsComponent

Move shared functionality of all physics related classes to a base class.

Tested that there were no failed to unserialize errors when in main gameplay in Gnarled Forest or in a race.

Tested that 2 players were able to see each other in the above scenarios just fine as well.

* Update PhantomPhysicsComponent.cpp

* Add SimplePhysicsTest

* Add construction test

* Update SimplePhysicsComponentTests.cpp

* remove flags and fix override

* Update VendorComponent.h
2023-10-09 15:19:38 -05:00
d8ac148cee refactor: re-write AOE, add FilterTargets, Update TacArc Reading (#1035)
* Re-write AOE behavior for new filter targets
Update Tacarc to use new filter targets
Added dev commands for skill and attack debugging

* Get all entities by detroyable
rather than controllable physics
Since destroyables are what can be hit

* Re-work filter targets to be 100% live accurate
reduce memory usage by only using one vector and removing invalid entries
get entities in the proximity rather than all entities with des comps in the instance, as was done in live

* remove debuging longs and remove oopsie

* address feedback

* make log more useful

* make filter more flat

* Add some more checks to filter targets
add pvp checks to isenemy

* fix typing

* Add filter target to TacArc and update filter target

* fix double declaration

* Some debugging logs

* Update TacArc reading

* make log clearer

* logs

* Update TacArcBehavior.cpp

* banana

* fix max targets

* remove extreanous parenthesesuuesdsds

* make behavior slot use a real type

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2023-10-09 15:18:51 -05:00
Gie "Max" Vanommeslaeghe
471d65707c Merge pull request #1217 from DarkflameUniverse/pre-req-fix
fix: Fix wisp lee mission
2023-10-09 15:11:23 +02:00
David Markowitz
94f8a99fba Revert: Fix wisp lee mission
This reverts commit d7e16ab589697fd1a0a270a02c622b8fa752638f.

Fixes an issue where the incorrect mission is marked as being offerable and this overwrote the original mission offered by wisp lee and forced him to offer a different, unacceptable mission.

Tested that completing the mission twice from a new daily state and a repeatable state both completed the chain correctly.  Unsure what the original bug was, but it does not appear to be present.
On top of that, there is no pre-requisite for mission 1883 anywhere in the cdclient, so at best, this check was always false, but the correct behavior is exact equivalence for the mission state.
2023-10-08 23:23:14 -07:00
TAHuntling
288991ef49 fix: Players are able to join a race without having a race car (#1149)
* Fixed Scrapped Racecar Stuck Issue

Changed RacingControlComponent to boot players back to the hub world when trying to race after dismantling a vehicle.

* Modified OnPlayerLoaded to fix Vehicle Inventory issue

* Change 3

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2023-10-08 19:38:48 -05:00
David Markowitz
74cc4176e1 Update RigidbodyPhantomPhysicsComponent.h (#1213) 2023-10-08 15:58:47 -05:00
b33e9935f5 fix: GM 1272 was labled as 1273 (#1210) 2023-10-05 23:20:20 -07:00
David Markowitz
258ee5c1ee CheatDetection: Move player access (#1209) 2023-10-05 15:31:05 -07:00
David Markowitz
a8820c14f2 AssetManager: Match allocators (#1205)
Currently on line 116 of AssetManager.cpp, we have the following
`*data = (char*)malloc(*len);`
however on line 45 of AssetManager.h we have `delete m_Base;` which is not the same allocator as we used to allocate the memory.
This PR matches the malloc and free to be the correct calls.
2023-09-30 18:48:12 -05:00
1ec8da8bf7 fix: remove foreign key on name in cheat_detection table (#1202) 2023-09-30 06:22:32 -05:00
David Markowitz
b24775f472 feat: Security improvements for spoofed packets (#1201)
* Add cheat detection for spoofed packets

* Add config option for ip loggin

* remove packet saving
2023-09-28 12:16:11 -05:00
David Markowitz
bd65fc6e33 Fix version number (#1199)
Accidentally reverted the changes in a previous PR.
2023-09-25 08:23:23 -05:00
David Markowitz
44f466ac72 fix: deleting last character still shows the character (#1123)
* Fix deleting last character

Fix an issue where deleting your last character caused the character to still show up on the character selection screen.

Tested that deleting my final character results in an empty character selection scene.

* Update UserManager.cpp
2023-09-24 02:55:54 -05:00
David Markowitz
51540568fb Strengthen checks for slash commands (#1198) 2023-09-22 17:48:35 -07:00
08020cd86d chore: cleanup LU(W)string writing (#1188)
* chore: cleanup LU(W)string writing
and add methods for reading
remove redunent "packet" from packet reading helpers
move write header to bitstreamutils since it's not packet related
add tests for reading/writing LU(W)Strings

* remove un-needed function defintions in header

* make reading and writing more efficient

* p

p

* quotes

* remove unneeded default

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2023-09-20 20:06:28 -05:00
ca78a166d9 fix #1194 (#1196) 2023-09-19 05:59:02 -05:00
2e386d29df fix: Limit the number of players that can join a team (#1187)
* Limit the number of players that can join a team
after being invited

* Notify player if they weren't added to the team

* check pointer
2023-08-18 18:58:27 -05:00
d893ecddeb Renamed RocketLaunchLUP to MultiZoneEntrance (#1185) 2023-08-18 14:14:40 -05:00
David Markowitz
f4f13e081a Update Commands.md (#1184)
If this fails to build im going to be nice to people
2023-08-13 17:05:04 -07:00
David Markowitz
c26086aff5 perf: Add path height correction on world load (#1178)
* Add path height correction on world load

* Increase height because Nexus Tower is tall

* Update height checker

- Only go up and down, do not deviate from the point you are on
- As a backup, use the nearestPoint on the nearestPoly, should detour be able to find one.
- Add a debug assert to fail the program should toReturn differ from nearestPoint[1].

Update dNavMesh.cpp

Update dNavMesh.cpp

* Fix if condition to actually return the value...

---------

Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2023-08-12 09:20:00 -05:00
Gie "Max" Vanommeslaeghe
0337449aa7 Merge pull request #1183 from DarkflameUniverse/fastcrashfixes
fix: dangling pointer in mission when saving character data
2023-08-12 12:21:14 +02:00
David Markowitz
598f4e1663 Fix dangling pointer 2023-08-12 02:11:25 -07:00
David Markowitz
5eca25e42a refactor: Move CDClient Database away from constructor queries (#1164)
* Move away from constructor queries

Fix up other large tables to have proper backup lookups

Revert "idk im just dumb ig"

This reverts commit 5d5be5df53b8959b42b291613d7db749a65a3585.

idk im just dumb ig

* Fix slow components registry lookup

* add define for cdclient cache all

* Update CDBehaviorParameterTable.cpp
2023-08-10 23:27:40 -05:00
Gie "Max" Vanommeslaeghe
d233c7e2aa Merge pull request #1182 from DarkflameUniverse/fixRawReading
fix: RAW to OBJ parsing
2023-08-10 23:50:11 +02:00
beaceb947b fix: Properly read in scene metadata (#1170)
* fix: Properly read in scene metadata

* Fix reading in rental time and period from property path

* remove useless name in var
change hex to decimal so it's readable
fix scene transistion loop logic

* fix typoe and remove whitespace
2023-08-10 16:35:12 -05:00
David Markowitz
2cc13c6499 chore: Make serialize actually virtual (#1156)
* Make serialize actually virtual

* fix serialize and make update virutal

* Update VendorComponent.h

* Remove flag var

* Update SoundTriggerComponent.h

---------

Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2023-08-10 14:33:15 -07:00
David Markowitz
ad81e341da add operator *= for vectors 2023-08-09 22:13:20 -07:00
David Markowitz
fe6be21008 fix raw reading 2023-08-09 22:08:32 -07:00
cefdfc696a fix: Implement proper Sound trigger component serialization (#1160)
* cleanup

* more cleanup and fully implement the sound trigger
and racing sound trigger

* more cleanup, and better defaults

* fixes and tested

* update initializor for guid and when to load sound guids

* make racing sound trigger it's own component

* fix type

* Remove global
move serializes

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2023-08-06 15:38:12 -05:00
7e2747a2d2 fix: load fdbchecksum to verify, even on character selection (world 0) (#1176) 2023-08-04 17:48:45 -05:00
a29253d2f0 feature: Donation Vendor Component (#1168)
* refactor: Vendor inventory loading
Implement proper delta compression
dynamically determine multicostitems and standard cost items
Quatantine max's custom code

* WIP

* progress missions

* address feedback

* fix newline

* Cleanup

* oops

* fix default for nexus tower jawbox
cleanup some logs

* remove log

* remove include that got added back
2023-08-03 21:44:03 -05:00
David Markowitz
e5b69745aa feat: Remove NET_VERSION define (#1153)
* Remove NET_VERSION define

I get to test this on 4 different platforms.  yay...

* move stuff around

finally fixed you docker

i love this

Update Dockerfile

Update Dockerfile

change type

Update AuthPackets.cpp

Update AuthServer.cpp

* Update CMakeVariables.txt

* Update sharedconfig.ini
2023-08-03 21:38:33 -05:00
David Markowitz
040a78c386 fix: Remove usage of rand() and seed randomness for chatserver (#1152)
* Remove usage of rand(), time(0) to time(NULL)

Replace it with the random engine mt19937.
convert time(0) to time(NULL)

* update

* revert a bunch of changes
2023-08-03 21:38:19 -05:00
David Markowitz
d8a5fd49a4 chore: Small movementAiComponent cleanup (#1145)
* rename and cleanup file

* more

* fix broken function

* Further naming fixes

t

Revert "Further naming fixes"

This reverts commit 057189982ba56788d48f9265d815e6c562ba6328.

* next step

* undo all testing changes

* minor tweaks
2023-08-03 21:38:04 -05:00
David Markowitz
208ed02158 Fix incorrect kick out of rebuilds (#1120) 2023-08-03 21:37:27 -05:00
952786c166 chore: cleanup some vanity npc logic (#1174)
* Dont setup talk for vanity npc's if they are no phrases
no ghosting ldfkey for canity npc's

* void, oops

* formatting
2023-08-03 21:37:09 -05:00
David Markowitz
9a07020a51 perf: Behavior Parameter memory savings (#1166)
* Memory Savings Behavior Parameter table

* Update CDBehaviorParameterTable.h
2023-08-03 19:31:02 -07:00
a22ecf385f feature: add full known GM list (#1171)
* feature: add full GM list

* Fix compiling

* add tests
Fix more issues
2023-08-03 21:27:40 -05:00
c2b4aa4026 refactor: Vendor inventory loading (#1163)
* refactor: Vendor inventory loading
Implement proper delta compression
dynamically determine multicostitems and standard cost items
Quatantine max's custom code

* address feedback

* fix newline

* oops

* remove header

* fix default and const for

* he said make it a reference too, not just const
2023-08-03 05:51:52 -05:00
David Markowitz
0d48cfe8c0 fix: Nipoint3 Operator+= (#1172)
* fix operator +=

* fix operator +=
2023-07-31 00:22:56 -07:00
e299bf9b62 fix: read and store the zone transition width properly (#1169) 2023-07-30 23:49:43 -07:00
227 changed files with 6531 additions and 3053 deletions

View File

@@ -14,4 +14,4 @@ EXTERNAL_IP=localhost
MARIADB_USER=darkflame MARIADB_USER=darkflame
MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_DATABASE=darkflame MARIADB_DATABASE=darkflame

View File

@@ -1,12 +1,8 @@
PROJECT_VERSION_MAJOR=1 PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=1 PROJECT_VERSION_MINOR=1
PROJECT_VERSION_PATCH=0 PROJECT_VERSION_PATCH=1
# LICENSE # LICENSE
LICENSE=AGPL-3.0 LICENSE=AGPL-3.0
# The network version.
# 171023 - Darkflame Universe client
# 171022 - Unmodded client
NET_VERSION=171022
# Debugging # Debugging
# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs. # Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
__dynamic=1 __dynamic=1
@@ -22,3 +18,5 @@ __maria_db_connector_compile_jobs__=1
__enable_testing__=1 __enable_testing__=1
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default. # The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/ OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
# Uncomment the below line to cache the entire CDClient into memory
# CDCLIENT_CACHE_ALL=1

264
ComponentDefinitions.py Normal file
View File

@@ -0,0 +1,264 @@
components = {
'COMPONENT_TYPE_CONTROLLABLE_PHYSICS': 1,
'COMPONENT_TYPE_RENDER': 2,
'COMPONENT_TYPE_SIMPLE_PHYSICS': 3,
'COMPONENT_TYPE_CHARACTER': 4,
'COMPONENT_TYPE_SCRIPT': 5,
'COMPONENT_TYPE_BOUNCER': 6,
'COMPONENT_TYPE_DESTROYABLE': 7,
'COMPONENT_TYPE_GHOST': 8,
'COMPONENT_TYPE_SKILL': 9,
'COMPONENT_TYPE_SPAWNER': 10,
'COMPONENT_TYPE_ITEM': 11,
'COMPONENT_TYPE_MODULAR_BUILD': 12,
'COMPONENT_TYPE_BUILD_CONTROLLER': 13,
'COMPONENT_TYPE_REBUILD_ACTIVATOR': 14,
'COMPONENT_TYPE_ICON_ONLY': 15,
'COMPONENT_TYPE_VENDOR': 16,
'COMPONENT_TYPE_INVENTORY': 17,
'COMPONENT_TYPE_PROJECTILE_PHYSICS': 18,
'COMPONENT_TYPE_SHOOTING_GALLERY': 19,
'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS': 20,
'COMPONENT_TYPE_DROP_EFFECT': 21,
'COMPONENT_TYPE_CHEST': 22,
'COMPONENT_TYPE_COLLECTIBLE': 23,
'COMPONENT_TYPE_BLUEPRINT': 24,
'COMPONENT_TYPE_MOVING_PLATFORM': 25,
'COMPONENT_TYPE_PET': 26,
'COMPONENT_TYPE_PLATFORM_BOUNDARY': 27,
'COMPONENT_TYPE_MODULE': 28,
'COMPONENT_TYPE_ARCADE': 29,
'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS': 30,
'COMPONENT_TYPE_MOVEMENT_AI': 31,
'COMPONENT_TYPE_EXHIBIT': 32,
'COMPONENT_TYPE_OVERHEAD_ICON': 33,
'COMPONENT_TYPE_PET_CONTROL': 34,
'COMPONENT_TYPE_MINIFIG': 35,
'COMPONENT_TYPE_PROPERTY': 36,
'COMPONENT_TYPE_PET_CREATOR': 37,
'COMPONENT_TYPE_MODEL_BUILDER': 38,
'COMPONENT_TYPE_SCRIPTED_ACTIVITY': 39,
'COMPONENT_TYPE_PHANTOM_PHYSICS': 40,
'COMPONENT_TYPE_SPRING_PAD': 41,
'COMPONENT_TYPE_MODEL': 42,
'COMPONENT_TYPE_PROPERTY_ENTRANCE': 43,
'COMPONENT_TYPE_FX': 44,
'COMPONENT_TYPE_PROPERTY_MANAGEMENT': 45,
'COMPONENT_TYPE_VEHICLE_PHYSICS': 46,
'COMPONENT_TYPE_PHYSICS_SYSTEM': 47,
'COMPONENT_TYPE_REBUILD': 48,
'COMPONENT_TYPE_SWITCH': 49,
'COMPONENT_TYPE_ZONE_CONTROL': 50,
'COMPONENT_TYPE_CHANGELING': 51,
'COMPONENT_TYPE_CHOICE_BUILD': 52,
'COMPONENT_TYPE_PACKAGE': 53,
'COMPONENT_TYPE_SOUND_REPEATER': 54,
'COMPONENT_TYPE_SOUND_AMBIENT_2D': 55,
'COMPONENT_TYPE_SOUND_AMBIENT_3D': 56,
'COMPONENT_TYPE_PRECONDITION': 57,
'COMPONENT_TYPE_PLAYER_FLAG': 58,
'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY': 59,
'COMPONENT_TYPE_BASE_COMBAT_AI': 60,
'COMPONENT_TYPE_MODULE_ASSEMBLY': 61,
'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER': 62,
'COMPONENT_TYPE_RACING_MODULE': 63,
'COMPONENT_TYPE_GENERIC_ACTIVATOR': 64,
'COMPONENT_TYPE_PROPERTY_VENDOR': 65,
'COMPONENT_TYPE_HF_LIGHT_DIRECTION_GADGET': 66,
'COMPONENT_TYPE_ROCKET_LAUNCH': 67,
'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT': 68,
'COMPONENT_TYPE_TRIGGER': 69,
'COMPONENT_TYPE_DROPPED_LOOT': 70,
'COMPONENT_TYPE_RACING_CONTROL': 71,
'COMPONENT_TYPE_FACTION_TRIGGER': 72,
'COMPONENT_TYPE_MISSION_OFFER': 73,
'COMPONENT_TYPE_RACING_STATS': 74,
'COMPONENT_TYPE_LUP_EXHIBIT': 75,
'COMPONENT_TYPE_BBB': 76,
'COMPONENT_TYPE_SOUND_TRIGGER': 77,
'COMPONENT_TYPE_PROXIMITY_MONITOR': 78,
'COMPONENT_TYPE_RACING_SOUND_TRIGGER': 79,
'COMPONENT_TYPE_CHAT': 80,
'COMPONENT_TYPE_FRIENDS_LIST': 81,
'COMPONENT_TYPE_GUILD': 82,
'COMPONENT_TYPE_LOCAL_SYSTEM': 83,
'COMPONENT_TYPE_MISSION': 84,
'COMPONENT_TYPE_MUTUABLE_MODEL_BEHAVIORS': 85,
'COMPONENT_TYPE_PATHFINDING_CONTROL': 86,
'COMPONENT_TYPE_PLAYER_PET_TAMING': 87,
'COMPONENT_TYPE_PROPERTY_EDITOR': 88,
'COMPONENT_TYPE_SKINNED_RENDER': 89,
'COMPONENT_TYPE_SLASH_COMMAND': 90,
'COMPONENT_TYPE_STATUS_EFFECT': 91,
'COMPONENT_TYPE_TEAMS': 92,
'COMPONENT_TYPE_TEXT_EFFECT': 93,
'COMPONENT_TYPE_TRADE': 94,
'COMPONENT_TYPE_USER_CONTROL': 95,
'COMPONENT_TYPE_IGNORE_LIST': 96,
'COMPONENT_TYPE_ROCKET_LAUNCH_LUP': 97,
'COMPONENT_TYPE_BUFF': 98,
'COMPONENT_TYPE_INTERACTION_MANAGER': 99,
'COMPONENT_TYPE_DONATION_VENDOR': 100,
'COMPONENT_TYPE_COMBAT_MEDIATOR': 101,
'COMPONENT_TYPE_ACHIEVEMENT_VENDOR': 102,
'COMPONENT_TYPE_GATE_RUSH': 103,
'COMPONENT_TYPE_RAIL_ACTIVATOR': 104,
'COMPONENT_TYPE_ROLLER': 105,
'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT': 106,
'COMPONENT_TYPE_CRAFTING': 107,
'COMPONENT_TYPE_POSSESSABLE': 108,
'COMPONENT_TYPE_LEVEL_PROGRESSION': 109,
'COMPONENT_TYPE_POSSESSOR': 110,
'COMPONENT_TYPE_MOUNT_CONTROL': 111,
'UNKNOWN_112': 112,
'COMPONENT_TYPE_PROPERTY_PLAQUE': 113,
'COMPONENT_TYPE_BUILD_BORDER': 114,
'UNKNOWN_115': 115,
'COMPONENT_TYPE_CULLING_PLANE': 116,
'INVALID': 4294967295
}
component_initialization_order = [
'COMPONENT_TYPE_POSSESSABLE',
'COMPONENT_TYPE_BLUEPRINT',
'COMPONENT_TYPE_MODULE_ASSEMBLY',
'COMPONENT_TYPE_CONTROLLABLE_PHYSICS',
'COMPONENT_TYPE_PROJECTILE_PHYSICS',
'COMPONENT_TYPE_PHYSICS_SYSTEM',
'COMPONENT_TYPE_VEHICLE_PHYSICS',
'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS',
'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS',
'COMPONENT_TYPE_SIMPLE_PHYSICS',
'COMPONENT_TYPE_PHANTOM_PHYSICS',
'COMPONENT_TYPE_DESTROYABLE',
'COMPONENT_TYPE_MINIFIG',
'COMPONENT_TYPE_CHARACTER',
'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT',
'COMPONENT_TYPE_POSSESSOR',
'COMPONENT_TYPE_MOUNT_CONTROL',
'COMPONENT_TYPE_PET',
'COMPONENT_TYPE_INVENTORY',
'COMPONENT_TYPE_PROXIMITY_MONITOR',
'COMPONENT_TYPE_MOVEMENT_AI',
'COMPONENT_TYPE_SCRIPT',
'COMPONENT_TYPE_SKILL',
'COMPONENT_TYPE_BASE_COMBAT_AI',
'COMPONENT_TYPE_SPAWNER',
'COMPONENT_TYPE_ITEM',
'COMPONENT_TYPE_REBUILD',
'COMPONENT_TYPE_BUILD_BORDER',
'COMPONENT_TYPE_MODULE',
'COMPONENT_TYPE_BUILD_CONTROLLER',
'COMPONENT_TYPE_REBUILD_ACTIVATOR',
'COMPONENT_TYPE_MODULAR_BUILD',
'COMPONENT_TYPE_MISSION_OFFER',
'COMPONENT_TYPE_VENDOR',
'COMPONENT_TYPE_DONATION_VENDOR',
'COMPONENT_TYPE_ACHIEVEMENT_VENDOR',
'COMPONENT_TYPE_SHOOTING_GALLERY',
'COMPONENT_TYPE_ROLLER',
'COMPONENT_TYPE_SCRIPTED_ACTIVITY',
'COMPONENT_TYPE_COLLECTIBLE',
'COMPONENT_TYPE_MOVING_PLATFORM',
'COMPONENT_TYPE_CHANGELING',
'COMPONENT_TYPE_CHOICE_BUILD',
'COMPONENT_TYPE_PACKAGE',
'COMPONENT_TYPE_PLATFORM_BOUNDARY',
'COMPONENT_TYPE_HF_LIGHT_DIRECTION_GADGET',
'COMPONENT_TYPE_CULLING_PLANE',
'COMPONENT_TYPE_EXHIBIT',
'COMPONENT_TYPE_LUP_EXHIBIT',
'COMPONENT_TYPE_PROPERTY',
'COMPONENT_TYPE_PROPERTY_PLAQUE',
'COMPONENT_TYPE_MODEL',
'COMPONENT_TYPE_PROPERTY_ENTRANCE',
'COMPONENT_TYPE_ROCKET_LAUNCH_LUP',
'COMPONENT_TYPE_PROPERTY_MANAGEMENT',
'COMPONENT_TYPE_RAIL_ACTIVATOR',
'COMPONENT_TYPE_BOUNCER',
'COMPONENT_TYPE_SPRING_PAD',
'COMPONENT_TYPE_SWITCH',
'COMPONENT_TYPE_ZONE_CONTROL',
'COMPONENT_TYPE_MODEL_BUILDER',
'COMPONENT_TYPE_SOUND_REPEATER',
'COMPONENT_TYPE_CHEST',
'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER',
'COMPONENT_TYPE_ARCADE',
'COMPONENT_TYPE_RENDER',
'COMPONENT_TYPE_SOUND_AMBIENT_2D',
'COMPONENT_TYPE_SOUND_AMBIENT_3D',
'COMPONENT_TYPE_SOUND_TRIGGER',
'COMPONENT_TYPE_RACING_SOUND_TRIGGER',
'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY',
'COMPONENT_TYPE_GENERIC_ACTIVATOR',
'COMPONENT_TYPE_PROPERTY_VENDOR',
'COMPONENT_TYPE_ROCKET_LAUNCH',
'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT',
'COMPONENT_TYPE_FACTION_TRIGGER',
'COMPONENT_TYPE_RACING_CONTROL',
'COMPONENT_TYPE_GATE_RUSH',
'COMPONENT_TYPE_BBB',
'COMPONENT_TYPE_CRAFTING'
]
component_order_as_ints = {}
component_int_to_name = {}
for component in component_initialization_order:
component_order_as_ints[components[component]] = component
for component in components:
component_int_to_name[components[component]] = component
class GameObject:
type = ""
class ObjectLoadState:
createdGameObject = GameObject()
components = []
hasPhysicsComponent = False
hasDestroyable = False
hasPathfindingComponent = False
hasProximityMonitorComponent = False
hasScriptComponent = False
hasDroppedLootComponent = False
hasModelbehaviors = False
somethingOrNothing = False
physicsComponentId = -1
# Begin custom attributes
serializedOrder = []
hasMissionOffer = False
class NiPoint3:
x = 0.0
y = 0.0
z = 0.0
class NiQuaternion:
w = 0.0
x = 0.0
y = 0.0
z = 0.0
class LoadObject:
objId = 0
owner = 0
lotToSpawn = 0
isLocalPlayer = False
hasDropEffect = False
bIsSmashable = False
dynamicPhysics = False
spawnPosition = NiPoint3()
spawnRotation = NiQuaternion()
scale = 0.0
legoBrickID = 0
legoColorID = 0
configData = {}
isHFPaletteObject = False
objType = 0
useSimplePhysics = False
bDelayedLoad = False
# Don't know the name yet of this
field8_0x20 = True

408
ComponentFactories.py Normal file
View File

@@ -0,0 +1,408 @@
import ComponentDefinitions as cd
def ApplyComponentWhitelist():
# Dont care to actually do this, just want the call for now.
pass
def ControllablePhysFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasModelbehaviors:
if not loadObject.hasDropEffect:
if not loadObject.useSimplePhysics:
objectLoadState.components.append('CONTROLLABLE_PHYSICS')
else:
objectLoadState.components.append('SIMPLE_PHYSICS')
else:
loadObject.physicsComponentId = componentId
def RenderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if loadObject.lotToSpawn == 176:
return
gmlevel = loadObject.configData.get('gmlevel', 0)
if gmlevel > 0 and gmlevel < 6:
componentId = 9940 # Override the componentId of the render component to be 9940, the Mythran Render Component
renderDisabled = loadObject.configData.get('renderDisabled', False)
if renderDisabled:
return
if objectLoadState.createdGameObject.type == 'BlockingVolume':
return
rejectedLots = [5937, 5938, 9741, 9742, 9862, 9863]
if loadObject.lotToSpawn in rejectedLots:
return
if loadObject.lotToSpawn == 6368:
FxFactory(loadObject, objectLoadState, componentId)
return
FxFactory(loadObject, objectLoadState, componentId)
defaultWrapperAsset = ""
hasNifAsset = False
hasKfmAsset = False
allowRenderWrapping = False
nifName = loadObject.configData.get('nif_name', '')
if nifName == '':
print('TODO query cdc for this data in the Render component')
nifName = 'mesh/something.nif' # if empty get nifname as the render_asset column of the RenderComponent table from the database
if nifName[-1] == 'f':
hasNifAsset = True
elif nifName[-1] == 'm':
hasKfmAsset = True
if defaultWrapperAsset != '':
if defaultWrapperAsset[-1] == 'm':
allowRenderWrapping = not loadObject.configData.get('preventRenderWrapping', False)
if allowRenderWrapping or hasKfmAsset or (not defaultWrapperAsset == 'PrimitiveModels' and not hasNifAsset and not loadObject.configData.get('CreateNULLRender', False)):
objectLoadState.components.append('SKINNED_RENDER')
else:
objectLoadState.components.append('RENDER')
def FxFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('FX')
def SimplePhysFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasModelbehaviors:
objectLoadState.physicsComponentId = componentId
return
if not loadObject.hasDropEffect:
return
markedAsPhantom = loadObject.configData.get('markedAsPhantom', False)
if markedAsPhantom:
objectLoadState.components.append('PHANTOM_PHYSICS')
else:
objectLoadState.components.append('SIMPLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def CharacterFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('POSSESSOR')
objectLoadState.components.append('MOUNT_CONTROL')
objectLoadState.components.append('PET_CREATOR')
objectLoadState.components.append('LEVEL_PROGRESSION')
objectLoadState.components.append('PLAYER_FORCED_MOVEMENT')
objectLoadState.components.append('CHARACTER')
def ScriptFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int, add_pathfinding_control: bool):
custom_script_client = loadObject.configData.get('custom_script_client', '')
custom_script_server = loadObject.configData.get('custom_script_server', '')
if componentId == 0 and custom_script_client == '' and custom_script_server == '':
return
objectLoadState.components.append('SCRIPT')
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
if not objectLoadState.hasPathfindingComponent and add_pathfinding_control:
objectLoadState.components.append('PATHFINDING_CONTROL')
objectLoadState.hasPathfindingComponent = True
def BouncerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BOUNCER')
def DestroyableFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('STATUS_EFFECT')
objectLoadState.components.append('BUFF')
objectLoadState.components.append('DESTROYABLE')
objectLoadState.hasDestroyable = True
def SkillFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SKILL')
def SpawnerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SPAWNER')
# If game state is happy flower
spawntemplate = loadObject.configData.get('spawntemplate', '')
print('TODO query cdc for this data in the Spawner component')
render_asset = loadObject.configData.get('render_asset', 'test.nif') # Get this from RenderComponent table
render_id = loadObject.configData.get('render_id', '') # Get this from RenderComponent table
if render_asset[-1] == 'f':
objectLoadState.components.append('SKINNED_RENDER')
else:
objectLoadState.components.append('RENDER')
def ItemFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ITEM')
def ModularBuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MODULAR_BUILD')
def BuildControllerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BUILD_CONTROLLER')
def VendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
objectLoadState.components.append('VENDOR')
def InventoryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('INVENTORY')
def ProjectilePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not loadObject.hasDropEffect:
objectLoadState.components.append('PROJECTILE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def ShootingGalleryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SHOOTING_GALLERY')
def RigibBodyPhantomPhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RIGID_BODY_PHANTOM_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def ChestFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CHEST')
def CollectibleFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('COLLECTIBLE')
if not objectLoadState.hasDestroyable:
objectLoadState.components.append('DESTROYABLE')
objectLoadState.hasDestroyable = True
def BlueprintFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BLUEPRINT')
def MovingPlatformFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MOVING_PLATFORM')
def PetFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
inInventory = loadObject.configData.get('inInventory', False)
if inInventory:
return
if not objectLoadState.hasPathfindingComponent:
objectLoadState.components.append('PATHFINDING_CONTROL')
objectLoadState.hasPathfindingComponent = True
objectLoadState.components.append('PET')
def PlatformBoundaryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PLATFORM_BOUNDARY')
def ModuleFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MODULE')
def ArcadeFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ARCADE')
def HavokVehiclePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
use_simple_physics = loadObject.configData.get('use_simple_physics', False)
if use_simple_physics:
objectLoadState.components.append('SIMPLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
else:
objectWorldState = loadObject.configData.get('objectWorldState', '')
if objectWorldState == 0 or objectWorldState != 2:
objectLoadState.components.append('HAVOK_VEHICLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def MovementAiFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MOVEMENT_AI')
def ExhibitFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
# Sorry aron, doesnt do anything :(
pass
def MinifigFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MINIFIG')
def PropertyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY')
def ModelBuilderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MODEL_BUILDER')
def ScriptedActivityFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SCRIPTED_ACTIVITY')
def PhantomPhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not loadObject.hasDropEffect:
objectLoadState.components.append('PHANTOM_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def SpringPadFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SPRING_PAD')
def ModelFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
modelType = loadObject.configData.get('modelType', 0)
if not objectLoadState.hasModelbehaviors:
if not objectLoadState.hasPhysicsComponent:
objectLoadState.components.append('SIMPLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
elif not objectLoadState.hasPhysicsComponent:
if modelType == 0:
objectLoadState.components.append('CONTROLLABLE_PHYSICS')
else:
objectLoadState.components.append('SIMPLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
if modelType != 0 and not objectLoadState.hasPathfindingComponent:
objectLoadState.components.append('PATHFINDING_CONTROL')
objectLoadState.hasPathfindingComponent = True
hasPropertyObjectID = loadObject.configData.get('propertyObjectID', 0) != 0
inInventory = loadObject.configData.get('inInventory', False)
if hasPropertyObjectID and inInventory:
objectLoadState.components.append('MODEL_BEHAVIORS')
else:
objectLoadState.components.append('MUTABLE_MODEL_BEHAVIORS')
def PropertyEntranceFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY_ENTRANCE')
def PropertyManagementFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY_MANAGEMENT')
def VehiclePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('VEHICLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def PhysicsSystemFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasModelbehaviors:
if not loadObject.hasDropEffect:
objectLoadState.components.append('PHYSICS_SYSTEM')
objectLoadState.hasPhysicsComponent = True
def RebuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('REBUILD')
if not objectLoadState.hasDestroyable:
objectLoadState.components.append('DESTROYABLE')
objectLoadState.hasDestroyable = True
loadObject.bIsSmashable = True
def BaseCombatAiFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int, add_pathfinding_control: bool):
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
if not objectLoadState.hasPathfindingComponent and add_pathfinding_control:
objectLoadState.components.append('PATHFINDING_CONTROL')
objectLoadState.hasPathfindingComponent = True
objectLoadState.components.append('BASE_COMBAT_AI')
def ProximityMonitorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
print('TODO query cdc for this data in prox mon')
# There must be a row for this component in the ProximityMonitorComponent table to get this component
LoadOnClient = loadObject.configData.get('LoadOnClient', False) # Get this from ProximityMonitorComponent table if you know there is a column for it
if LoadOnClient:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
def DonationVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
objectLoadState.components.append('DONATION_VENDOR')
def AchievmentVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
objectLoadState.components.append('ACHIEVEMENT_VENDOR')
def CraftingFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
# Game verson must be above 999 for this to work!
gameVersion = 1000
if gameVersion > 999:
objectLoadState.components.append('CRAFTING')
# Technically Possessable and Possessor are the exact same thing
def PossessableFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('POSSESSABLE')
def PossessorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('POSSESSOR')
def PropertyPlaqueFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY_PLAQUE')
def BuildBorderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BUILD_BORDER')
def CullingPlaneFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CULLING_PLANE')
def GateRushFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('GATE_RUSH')
def RailActivatorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RAIL_ACTIVATOR')
def RollerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ROLLER')
def PlayerForcedMovementFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PLAYER_FORCED_MOVEMENT')
def RacingSoundTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RACING_SOUND_TRIGGER')
def RocketLaunchLupFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ROCKET_LAUNCH_LUP')
def ModuleAssemblyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MODULE_ASSEMBLY')
def ShowcaseModelHandlerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SHOWCASE_MODEL_HANDLER')
def GenericActivatorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('GENERIC_ACTIVATOR')
def PropertyVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY_VENDOR')
def RocketLaunchFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ROCKET_LAUNCH')
def RocketLandingComponentFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ROCKET_LANDING_COMPONENT')
def RacingControlFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RACING_CONTROL')
def FactionTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('FACTION_TRIGGER')
def MissionOfferFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MISSION_OFFER')
objectLoadState.hasMissionOffer = True
def RacingStatsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RACING_STATS')
def LupExhibitFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('LUP_EXHIBIT')
def BBBFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BBB')
def SoundTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SOUND_TRIGGER')
def SwitchFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SWITCH')
def ZoneControlFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ZONE_CONTROL')
def ChanglingFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CHANGELING')
def ChoiceBuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CHOICE_BUILD')
def PackageFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
# Literally returns nothing
pass
def SoundRepeaterFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SOUND_REPEATER')
def SoundAmbient2DFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if loadObject.isLocalPlayer:
objectLoadState.components.append('SOUND_AMBIENT_2D')
def SoundAmbient3DFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SOUND_AMBIENT_3D')
def CustomBuildAssemblyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CUSTOM_BUILD_ASSEMBLY')

306
DoObjectPreLoad.py Normal file
View File

@@ -0,0 +1,306 @@
import ComponentDefinitions as cd
import ComponentFactories as cf
# If we have any of these components, we need pathfinding control
def CheckIfNeedsPathfindingControl(components_to_load: dict):
add_pathfinding_control = cd.components['COMPONENT_TYPE_CONTROLLABLE_PHYSICS'] in components_to_load
add_pathfinding_control |= cd.components['COMPONENT_TYPE_PROJECTILE_PHYSICS'] in components_to_load
add_pathfinding_control |= cd.components['COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS'] in components_to_load
add_pathfinding_control |= cd.components['COMPONENT_TYPE_VEHICLE_PHYSICS'] in components_to_load
return add_pathfinding_control
def DoObjectPreLoad(components_to_load: dict):
isHfActive = False
cf.ApplyComponentWhitelist()
objectLoadState = cd.ObjectLoadState()
loadObject = cd.LoadObject()
for componentType in cd.component_order_as_ints.keys():
try:
componentId = components_to_load[componentType]
except KeyError:
continue
try:
match cd.component_int_to_name[componentType]:
case 'COMPONENT_TYPE_CONTROLLABLE_PHYSICS':
if objectLoadState.hasDroppedLootComponent: continue
cf.ControllablePhysFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RENDER':
cf.RenderFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SIMPLE_PHYSICS':
if objectLoadState.hasDroppedLootComponent: continue
cf.SimplePhysFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CHARACTER':
cf.CharacterFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SCRIPT':
cf.ScriptFactory(loadObject, objectLoadState, componentId, CheckIfNeedsPathfindingControl(components_to_load))
case 'COMPONENT_TYPE_BOUNCER':
cf.BouncerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_DESTROYABLE':
cf.DestroyableFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SKILL':
cf.SkillFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SPAWNER':
cf.SpawnerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ITEM':
cf.ItemFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MODULAR_BUILD':
cf.ModularBuildFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BUILD_CONTROLLER':
cf.BuildControllerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_VENDOR':
cf.VendorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_INVENTORY':
cf.InventoryFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROJECTILE_PHYSICS':
if objectLoadState.hasDroppedLootComponent: continue
cf.ProjectilePhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SHOOTING_GALLERY':
cf.ShootingGalleryFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS':
cf.RigibBodyPhantomPhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CHEST':
cf.ChestFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_COLLECTIBLE':
cf.CollectibleFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BLUEPRINT':
cf.BlueprintFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MOVING_PLATFORM':
cf.MovingPlatformFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PET':
cf.PetFactory(loadObject, objectLoadState, componentId)
print('Deleting COMPONENT_TYPE_MODEL and COMPONENT_TYPE_ITEM due to COMPONENT_TYPE_PET being loaded')
toDel = cd.components['COMPONENT_TYPE_MODEL']
del components_to_load[toDel]
toDel = cd.components['COMPONENT_TYPE_ITEM']
del components_to_load[toDel]
case 'COMPONENT_TYPE_PLATFORM_BOUNDARY':
cf.PlatformBoundaryFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MODULE':
cf.ModuleFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ARCADE':
cf.ArcadeFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS':
cf.HavokVehiclePhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MOVEMENT_AI':
cf.MovementAiFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_EXHIBIT':
cf.ExhibitFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MINIFIG':
cf.MinifigFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY':
cf.PropertyFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MODEL_BUILDER':
cf.ModelBuilderFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SCRIPTED_ACTIVITY':
cf.ScriptedActivityFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PHANTOM_PHYSICS':
if objectLoadState.hasDroppedLootComponent: continue
cf.PhantomPhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SPRING_PAD':
cf.SpringPadFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MODEL':
cf.ModelFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY_ENTRANCE':
cf.PropertyEntranceFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY_MANAGEMENT':
cf.PropertyManagementFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_VEHICLE_PHYSICS':
cf.VehiclePhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PHYSICS_SYSTEM':
cf.PhysicsSystemFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_REBUILD':
cf.RebuildFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SWITCH':
cf.SwitchFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ZONE_CONTROL':
cf.ZoneControlFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CHANGELING':
cf.ChanglingFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CHOICE_BUILD':
cf.ChoiceBuildFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PACKAGE':
cf.PackageFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SOUND_REPEATER':
cf.SoundRepeaterFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SOUND_AMBIENT_2D':
cf.SoundAmbient2DFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SOUND_AMBIENT_3D':
cf.SoundAmbient3DFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY':
cf.CustomBuildAssemblyFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BASE_COMBAT_AI':
cf.BaseCombatAiFactory(loadObject, objectLoadState, componentId, CheckIfNeedsPathfindingControl(components_to_load))
case 'COMPONENT_TYPE_MODULE_ASSEMBLY':
cf.ModuleAssemblyFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER':
cf.ShowcaseModelHandlerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_GENERIC_ACTIVATOR':
cf.GenericActivatorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY_VENDOR':
cf.PropertyVendorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ROCKET_LAUNCH':
cf.RocketLaunchFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT':
cf.RocketLandingComponentFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RACING_CONTROL':
cf.RacingControlFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_FACTION_TRIGGER':
cf.FactionTriggerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MISSION_OFFER':
cf.MissionOfferFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RACING_STATS':
cf.RacingStatsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_LUP_EXHIBIT':
cf.LupExhibitFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BBB':
cf.BBBFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SOUND_TRIGGER':
cf.SoundTriggerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROXIMITY_MONITOR':
cf.ProximityMonitorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RACING_SOUND_TRIGGER':
cf.RacingSoundTriggerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ROCKET_LAUNCH_LUP':
cf.RocketLaunchLupFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_DONATION_VENDOR':
cf.DonationVendorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ACHIEVEMENT_VENDOR':
cf.AchievmentVendorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_GATE_RUSH':
cf.GateRushFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RAIL_ACTIVATOR':
cf.RailActivatorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ROLLER':
cf.RollerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT':
cf.PlayerForcedMovementFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CRAFTING':
cf.CraftingFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_POSSESSABLE':
cf.PossessableFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_POSSESSOR':
cf.PossessorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY_PLAQUE':
cf.PropertyPlaqueFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BUILD_BORDER':
cf.BuildBorderFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CULLING_PLANE':
cf.CullingPlaneFactory(loadObject, objectLoadState, componentId)
except KeyError:
print(f'Unknown component: {componentType}')
if not 'is_smashable' in loadObject.configData and loadObject.bIsSmashable:
loadObject.configData['is_smashable'] = True
is_smashable = loadObject.configData['is_smashable']
if is_smashable and not objectLoadState.hasDestroyable and loadObject.lotToSpawn != 176:
objectLoadState.components.append('DESTROYABLE')
if objectLoadState.hasMissionOffer and not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
if loadObject.field8_0x20 and loadObject.owner != 0:
objectLoadState.components.append('OVERHEAD_ICON')
trigger_id = loadObject.configData.get('trigger_id', 0)
if trigger_id != 0:
objectLoadState.components.append('TRIGGER')
return objectLoadState
components_to_load = {}
# Testing code to load all components
for i in range(110):
components_to_load[i] = i
objectLoadState = DoObjectPreLoad(components_to_load)
# Convert the components to ints
components_to_int = []
for component in objectLoadState.components:
components_to_int.append([cd.components[f'COMPONENT_TYPE_{component}'], component])
# Print out the components in a more readable format
for comp in components_to_int:
print(comp[0], comp[1])

View File

@@ -179,7 +179,7 @@ If you would like to build the server faster, append `-j<number>` where number i
### Notes ### Notes
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building: Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory. * If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
* If you are using a Darkflame Universe client, ensure NET_VERSION is changed to 171023. * If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023.
## Configuring your server ## Configuring your server
This server has a few steps that need to be taken to configure the server for your use case. This server has a few steps that need to be taken to configure the server for your use case.

Binary file not shown.

Binary file not shown.

View File

@@ -3,6 +3,7 @@
#include "Database.h" #include "Database.h"
#include <vector> #include <vector>
#include "PacketUtils.h" #include "PacketUtils.h"
#include "BitStreamUtils.h"
#include "Game.h" #include "Game.h"
#include "dServer.h" #include "dServer.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
@@ -75,11 +76,11 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Now, we need to send the friendlist to the server they came from: //Now, we need to send the friendlist to the server they came from:
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(playerID); bitStream.Write(playerID);
//portion that will get routed: //portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
bitStream.Write<uint8_t>(0); bitStream.Write<uint8_t>(0);
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it. bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
bitStream.Write((uint16_t)friends.size()); bitStream.Write((uint16_t)friends.size());
@@ -412,21 +413,21 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
const auto otherName = std::string(otherMember->playerName.c_str()); const auto otherName = std::string(otherMember->playerName.c_str());
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(otherMember->playerID); bitStream.Write(otherMember->playerID);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(otherMember->playerID); bitStream.Write(otherMember->playerID);
bitStream.Write<uint8_t>(8); bitStream.Write<uint8_t>(8);
bitStream.Write<unsigned int>(69); bitStream.Write<unsigned int>(69);
PacketUtils::WritePacketWString(senderName, 33, &bitStream); bitStream.Write(LUWString(senderName));
bitStream.Write(sender->playerID); bitStream.Write(sender->playerID);
bitStream.Write<uint16_t>(0); bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag bitStream.Write<uint8_t>(0); //not mythran nametag
PacketUtils::WritePacketWString(otherName, 33, &bitStream); bitStream.Write(LUWString(otherName));
bitStream.Write<uint8_t>(0); //not mythran for receiver bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(0); //teams? bitStream.Write<uint8_t>(0); //teams?
PacketUtils::WritePacketWString(message, 512, &bitStream); bitStream.Write(LUWString(message, 512));
SystemAddress sysAddr = otherMember->sysAddr; SystemAddress sysAddr = otherMember->sysAddr;
SEND_PACKET; SEND_PACKET;
@@ -434,7 +435,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
} }
void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
LWOOBJID senderID = PacketUtils::ReadPacketS64(0x08, packet); LWOOBJID senderID = PacketUtils::ReadS64(0x08, packet);
std::string receiverName = PacketUtils::ReadString(0x66, packet, true); std::string receiverName = PacketUtils::ReadString(0x66, packet, true);
std::string message = PacketUtils::ReadString(0xAA, packet, true, 512); std::string message = PacketUtils::ReadString(0xAA, packet, true, 512);
@@ -451,21 +452,21 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
//To the sender: //To the sender:
{ {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(goonA->playerID); bitStream.Write(goonA->playerID);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(goonA->playerID); bitStream.Write(goonA->playerID);
bitStream.Write<uint8_t>(7); bitStream.Write<uint8_t>(7);
bitStream.Write<unsigned int>(69); bitStream.Write<unsigned int>(69);
PacketUtils::WritePacketWString(goonAName, 33, &bitStream); bitStream.Write(LUWString(goonAName));
bitStream.Write(goonA->playerID); bitStream.Write(goonA->playerID);
bitStream.Write<uint16_t>(0); bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag bitStream.Write<uint8_t>(0); //not mythran nametag
PacketUtils::WritePacketWString(goonBName, 33, &bitStream); bitStream.Write(LUWString(goonBName));
bitStream.Write<uint8_t>(0); //not mythran for receiver bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(0); //success bitStream.Write<uint8_t>(0); //success
PacketUtils::WritePacketWString(message, 512, &bitStream); bitStream.Write(LUWString(message, 512));
SystemAddress sysAddr = goonA->sysAddr; SystemAddress sysAddr = goonA->sysAddr;
SEND_PACKET; SEND_PACKET;
@@ -474,21 +475,21 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
//To the receiver: //To the receiver:
{ {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(goonB->playerID); bitStream.Write(goonB->playerID);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(goonA->playerID); bitStream.Write(goonA->playerID);
bitStream.Write<uint8_t>(7); bitStream.Write<uint8_t>(7);
bitStream.Write<unsigned int>(69); bitStream.Write<unsigned int>(69);
PacketUtils::WritePacketWString(goonAName, 33, &bitStream); bitStream.Write(LUWString(goonAName));
bitStream.Write(goonA->playerID); bitStream.Write(goonA->playerID);
bitStream.Write<uint16_t>(0); bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag bitStream.Write<uint8_t>(0); //not mythran nametag
PacketUtils::WritePacketWString(goonBName, 33, &bitStream); bitStream.Write(LUWString(goonBName));
bitStream.Write<uint8_t>(0); //not mythran for receiver bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(3); //new whisper bitStream.Write<uint8_t>(3); //new whisper
PacketUtils::WritePacketWString(message, 512, &bitStream); bitStream.Write(LUWString(message, 512));
SystemAddress sysAddr = goonB->sysAddr; SystemAddress sysAddr = goonB->sysAddr;
SEND_PACKET; SEND_PACKET;
@@ -709,13 +710,13 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender) { void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE);
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream); bitStream.Write(LUWString(sender->playerName.c_str()));
bitStream.Write(sender->playerID); bitStream.Write(sender->playerID);
SystemAddress sysAddr = receiver->sysAddr; SystemAddress sysAddr = receiver->sysAddr;
@@ -724,7 +725,7 @@ void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender)
void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) { void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
@@ -751,7 +752,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeader
void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) { void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
@@ -776,7 +777,7 @@ void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderI
void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID) { void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
@@ -793,7 +794,7 @@ void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64Play
void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) { void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
@@ -822,7 +823,7 @@ void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTria
void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) { void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
@@ -848,7 +849,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband
void ChatPacketHandler::SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) { void ChatPacketHandler::SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
@@ -882,16 +883,16 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla
[bool] - is FTP*/ [bool] - is FTP*/
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(friendData->playerID); bitStream.Write(friendData->playerID);
//portion that will get routed: //portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY);
bitStream.Write<uint8_t>(notifyType); bitStream.Write<uint8_t>(notifyType);
std::string playerName = playerData->playerName.c_str(); std::string playerName = playerData->playerName.c_str();
PacketUtils::WritePacketWString(playerName, 33, &bitStream); bitStream.Write(LUWString(playerName));
bitStream.Write(playerData->zoneID.GetMapID()); bitStream.Write(playerData->zoneID.GetMapID());
bitStream.Write(playerData->zoneID.GetInstanceID()); bitStream.Write(playerData->zoneID.GetInstanceID());
@@ -921,12 +922,12 @@ void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* send
} }
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST);
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream); bitStream.Write(LUWString(sender->playerName.c_str()));
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side. bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
SystemAddress sysAddr = receiver->sysAddr; SystemAddress sysAddr = receiver->sysAddr;
@@ -937,16 +938,16 @@ void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sen
if (!receiver || !sender) return; if (!receiver || !sender) return;
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
// Portion that will get routed: // Portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE);
bitStream.Write(responseCode); bitStream.Write(responseCode);
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver. // For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender->sysAddr != UNASSIGNED_SYSTEM_ADDRESS); bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender->sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
// Then write the player name // Then write the player name
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream); bitStream.Write(LUWString(sender->playerName.c_str()));
// Then if this is an acceptance code, write the following extra info. // Then if this is an acceptance code, write the following extra info.
if (responseCode == eAddFriendResponseType::ACCEPTED) { if (responseCode == eAddFriendResponseType::ACCEPTED) {
bitStream.Write(sender->playerID); bitStream.Write(sender->playerID);
@@ -962,13 +963,13 @@ void ChatPacketHandler::SendRemoveFriend(PlayerData* receiver, std::string& pers
if (!receiver) return; if (!receiver) return;
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: //portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE);
bitStream.Write<uint8_t>(isSuccessful); //isOnline bitStream.Write<uint8_t>(isSuccessful); //isOnline
PacketUtils::WritePacketWString(personToRemove, 33, &bitStream); bitStream.Write(LUWString(personToRemove));
SystemAddress sysAddr = receiver->sysAddr; SystemAddress sysAddr = receiver->sysAddr;
SEND_PACKET; SEND_PACKET;

View File

@@ -33,6 +33,7 @@ namespace Game {
dChatFilter* chatFilter = nullptr; dChatFilter* chatFilter = nullptr;
AssetManager* assetManager = nullptr; AssetManager* assetManager = nullptr;
bool shouldShutdown = false; bool shouldShutdown = false;
std::mt19937 randomEngine;
} }
@@ -114,6 +115,8 @@ int main(int argc, char** argv) {
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown); Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
Game::randomEngine = std::mt19937(time(0));
//Run it until server gets a kill message from Master: //Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now(); auto t = std::chrono::high_resolution_clock::now();

View File

@@ -6,10 +6,12 @@
#include "dLogger.h" #include "dLogger.h"
#include "ChatPacketHandler.h" #include "ChatPacketHandler.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "PacketUtils.h" #include "BitStreamUtils.h"
#include "Database.h" #include "Database.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h" #include "eChatInternalMessageType.h"
#include "ChatPackets.h"
#include "dConfig.h"
PlayerContainer::PlayerContainer() { PlayerContainer::PlayerContainer() {
} }
@@ -18,6 +20,10 @@ PlayerContainer::~PlayerContainer() {
mPlayers.clear(); mPlayers.clear();
} }
TeamData::TeamData() {
lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
}
void PlayerContainer::InsertPlayer(Packet* packet) { void PlayerContainer::InsertPlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
PlayerData* data = new PlayerData(); PlayerData* data = new PlayerData();
@@ -146,7 +152,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) { void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
bitStream.Write(player); bitStream.Write(player);
bitStream.Write(time); bitStream.Write(time);
@@ -207,6 +213,14 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
} }
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
if (team->memberIDs.size() >= 4){
Game::logger->Log("PlayerContainer", "Tried to add player to team that already had 4 players");
auto* player = GetPlayerData(playerID);
if (!player) return;
ChatPackets::SendSystemMessage(player->sysAddr, u"The teams is full! You have not been added to a team!");
return;
}
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID); const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
if (index != team->memberIDs.end()) return; if (index != team->memberIDs.end()) return;
@@ -345,7 +359,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
bitStream.Write(team->teamID); bitStream.Write(team->teamID);
bitStream.Write(deleteTeam); bitStream.Write(deleteTeam);

View File

@@ -18,6 +18,7 @@ struct PlayerData {
}; };
struct TeamData { struct TeamData {
TeamData();
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
LWOOBJID leaderID = LWOOBJID_EMPTY; LWOOBJID leaderID = LWOOBJID_EMPTY;
std::vector<LWOOBJID> memberIDs{}; std::vector<LWOOBJID> memberIDs{};

View File

@@ -12,7 +12,7 @@ set(DCOMMON_SOURCES
"NiPoint3.cpp" "NiPoint3.cpp"
"NiQuaternion.cpp" "NiQuaternion.cpp"
"SHA512.cpp" "SHA512.cpp"
"Type.cpp" "Demangler.cpp"
"ZCompression.cpp" "ZCompression.cpp"
"BrickByBrickFix.cpp" "BrickByBrickFix.cpp"
"BinaryPathFinder.cpp" "BinaryPathFinder.cpp"

29
dCommon/Demangler.cpp Normal file
View File

@@ -0,0 +1,29 @@
#include "Demangler.h"
#ifdef __GNUG__
#include <cstdlib>
#include <cxxabi.h>
#include <memory>
#include <typeinfo>
std::string Demangler::Demangle(const char* name) {
// some arbitrary value to eliminate the compiler warning
// -4 is not a valid return value for __cxa_demangle so we'll use that.
int status = -4;
// __cxa_demangle requires that we free the returned char*
std::unique_ptr<char, void (*)(void*)> res{
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status == 0) ? res.get() : "";
}
#else // __GNUG__
// does nothing if not g++
std::string Demangler::Demangle(const char* name) {
return name;
}
#endif // __GNUG__

9
dCommon/Demangler.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <string>
namespace Demangler {
// Given a char* containing a mangled name, return a std::string containing the demangled name.
// If the function fails for any reason, it returns an empty string.
std::string Demangle(const char* name);
}

View File

@@ -107,7 +107,7 @@ static void ErrorCallback(void* data, const char* msg, int errnum) {
} }
#endif #endif
#include "Type.h" #include "Demangler.h"
void GenerateDump() { void GenerateDump() {
std::string cmd = "sudo gcore " + std::to_string(getpid()); std::string cmd = "sudo gcore " + std::to_string(getpid());
@@ -122,41 +122,43 @@ void CatchUnhandled(int sig) {
if (Diagnostics::GetProduceMemoryDump()) { if (Diagnostics::GetProduceMemoryDump()) {
GenerateDump(); GenerateDump();
} }
constexpr uint8_t MaxStackTrace = 32;
void* array[10]; void* array[MaxStackTrace];
size_t size; size_t size;
// get void*'s for all entries on the stack // get void*'s for all entries on the stack
size = backtrace(array, 10); size = backtrace(array, MaxStackTrace);
#if defined(__GNUG__) and defined(__dynamic) # if defined(__GNUG__)
// Loop through the returned addresses, and get the symbols to be demangled // Loop through the returned addresses, and get the symbols to be demangled
char** strings = backtrace_symbols(array, size); char** strings = backtrace_symbols(array, size);
// Print the stack trace // Print the stack trace
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' and extract the function name // Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
std::string functionName = strings[i]; std::string functionName = strings[i];
std::string::size_type start = functionName.find('('); std::string::size_type start = functionName.find('(');
std::string::size_type end = functionName.find('+'); std::string::size_type end = functionName.find('+');
if (start != std::string::npos && end != std::string::npos) { if (start != std::string::npos && end != std::string::npos) {
std::string demangled = functionName.substr(start + 1, end - start - 1); std::string demangled = functionName.substr(start + 1, end - start - 1);
demangled = demangle(functionName.c_str()); demangled = Demangler::Demangle(demangled.c_str());
if (demangled.empty()) { // If the demangled string is not empty, then we can replace the mangled string with the demangled one
Game::logger->Log("Diagnostics", "[%02zu] %s", i, demangled.c_str()); if (!demangled.empty()) {
} else { demangled.push_back('(');
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str()); demangled += functionName.substr(end);
functionName = demangled;
} }
} else {
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
} }
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
} }
#else # else // defined(__GNUG__)
backtrace_symbols_fd(array, size, STDOUT_FILENO); backtrace_symbols_fd(array, size, STDOUT_FILENO);
#endif # endif // defined(__GNUG__)
FILE* file = fopen(fileName.c_str(), "w+"); FILE* file = fopen(fileName.c_str(), "w+");
if (file != NULL) { if (file != NULL) {
@@ -166,7 +168,7 @@ void CatchUnhandled(int sig) {
fclose(file); fclose(file);
} }
#else #else // __include_backtrace__
struct backtrace_state* state = backtrace_create_state( struct backtrace_state* state = backtrace_create_state(
Diagnostics::GetProcessFileName().c_str(), Diagnostics::GetProcessFileName().c_str(),
@@ -177,7 +179,7 @@ void CatchUnhandled(int sig) {
struct bt_ctx ctx = { state, 0 }; struct bt_ctx ctx = { state, 0 };
Bt(state); Bt(state);
#endif #endif // __include_backtrace__
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }

View File

@@ -126,6 +126,11 @@ namespace GeneralUtils {
template <typename T> template <typename T>
T Parse(const char* value); T Parse(const char* value);
template <>
inline bool Parse(const char* value) {
return std::stoi(value);
}
template <> template <>
inline int32_t Parse(const char* value) { inline int32_t Parse(const char* value) {
return std::stoi(value); return std::stoi(value);

View File

@@ -61,35 +61,33 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} }
case LDF_TYPE_S32: { case LDF_TYPE_S32: {
try { int32_t data;
int32_t data = static_cast<int32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10)); if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
returnValue = new LDFData<int32_t>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<int32_t>(key, data);
break; break;
} }
case LDF_TYPE_FLOAT: { case LDF_TYPE_FLOAT: {
try { float data;
float data = strtof(ldfTypeAndValue.second.data(), &storage); if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
returnValue = new LDFData<float>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<float>(key, data);
break; break;
} }
case LDF_TYPE_DOUBLE: { case LDF_TYPE_DOUBLE: {
try { double data;
double data = strtod(ldfTypeAndValue.second.data(), &storage); if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
returnValue = new LDFData<double>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<double>(key, data);
break; break;
} }
@@ -102,9 +100,7 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") { } else if (ldfTypeAndValue.second == "false") {
data = 0; data = 0;
} else { } else {
try { if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
data = static_cast<uint32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10));
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
@@ -122,9 +118,7 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") { } else if (ldfTypeAndValue.second == "false") {
data = false; data = false;
} else { } else {
try { if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
data = static_cast<bool>(strtol(ldfTypeAndValue.second.data(), &storage, 10));
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
@@ -135,24 +129,22 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} }
case LDF_TYPE_U64: { case LDF_TYPE_U64: {
try { uint64_t data;
uint64_t data = static_cast<uint64_t>(strtoull(ldfTypeAndValue.second.data(), &storage, 10)); if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
returnValue = new LDFData<uint64_t>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<uint64_t>(key, data);
break; break;
} }
case LDF_TYPE_OBJID: { case LDF_TYPE_OBJID: {
try { LWOOBJID data;
LWOOBJID data = static_cast<LWOOBJID>(strtoll(ldfTypeAndValue.second.data(), &storage, 10)); if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
returnValue = new LDFData<LWOOBJID>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<LWOOBJID>(key, data);
break; break;
} }

View File

@@ -129,10 +129,19 @@ NiPoint3 NiPoint3::operator+(const NiPoint3& point) const {
} }
//! Operator for addition of vectors //! Operator for addition of vectors
NiPoint3 NiPoint3::operator+=(const NiPoint3& point) const { NiPoint3& NiPoint3::operator+=(const NiPoint3& point) {
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z); this->x += point.x;
this->y += point.y;
this->z += point.z;
return *this;
} }
NiPoint3& NiPoint3::operator*=(const float scalar) {
this->x *= scalar;
this->y *= scalar;
this->z *= scalar;
return *this;
}
//! Operator for subtraction of vectors //! Operator for subtraction of vectors
NiPoint3 NiPoint3::operator-(const NiPoint3& point) const { NiPoint3 NiPoint3::operator-(const NiPoint3& point) const {

View File

@@ -136,7 +136,9 @@ public:
NiPoint3 operator+(const NiPoint3& point) const; NiPoint3 operator+(const NiPoint3& point) const;
//! Operator for addition of vectors //! Operator for addition of vectors
NiPoint3 operator+=(const NiPoint3& point) const; NiPoint3& operator+=(const NiPoint3& point);
NiPoint3& operator*=(const float scalar);
//! Operator for subtraction of vectors //! Operator for subtraction of vectors
NiPoint3 operator-(const NiPoint3& point) const; NiPoint3 operator-(const NiPoint3& point) const;

View File

@@ -1,27 +0,0 @@
#include "Type.h"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
std::string demangle(const char* name) {
int status = -4; // some arbitrary value to eliminate the compiler warning
// enable c++11 by passing the flag -std=c++11 to g++
std::unique_ptr<char, void(*)(void*)> res{
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status == 0) ? res.get() : name;
}
#else
// does nothing if not g++
std::string demangle(const char* name) {
return name;
}
#endif

View File

@@ -1,12 +0,0 @@
#pragma once
#include <string>
#include <typeinfo>
std::string demangle(const char* name);
template <class T>
std::string type(const T& t) {
return demangle(typeid(t).name());
}

View File

@@ -42,7 +42,7 @@ struct AssetMemoryBuffer : std::streambuf {
} }
void close() { void close() {
delete m_Base; free(m_Base);
} }
}; };

View File

@@ -9,6 +9,7 @@
#include "BitStream.h" #include "BitStream.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eClientMessageType.h" #include "eClientMessageType.h"
#include "BitStreamUtils.h"
#pragma warning (disable:4251) //Disables SQL warnings #pragma warning (disable:4251) //Disables SQL warnings
@@ -32,7 +33,7 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
#define CBITSTREAM RakNet::BitStream bitStream; #define CBITSTREAM RakNet::BitStream bitStream;
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false); #define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false);
#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits()); #define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits());
#define CMSGHEADER PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); #define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
#define SEND_PACKET Game::server->Send(&bitStream, sysAddr, false); #define SEND_PACKET Game::server->Send(&bitStream, sysAddr, false);
#define SEND_PACKET_BROADCAST Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); #define SEND_PACKET_BROADCAST Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);

File diff suppressed because it is too large Load Diff

View File

@@ -101,7 +101,7 @@ enum class eReplicaComponentType : uint32_t {
TRADE, TRADE,
USER_CONTROL, USER_CONTROL,
IGNORE_LIST, IGNORE_LIST,
ROCKET_LAUNCH_LUP, MULTI_ZONE_ENTRANCE,
BUFF_REAL, // the real buff component, should just be name BUFF BUFF_REAL, // the real buff component, should just be name BUFF
INTERACTION_MANAGER, INTERACTION_MANAGER,
DONATION_VENDOR, DONATION_VENDOR,

View File

@@ -55,7 +55,7 @@ CDClientManager::CDClientManager() {
CDBehaviorParameterTable::Instance().LoadValuesFromDatabase(); CDBehaviorParameterTable::Instance().LoadValuesFromDatabase();
CDBehaviorTemplateTable::Instance().LoadValuesFromDatabase(); CDBehaviorTemplateTable::Instance().LoadValuesFromDatabase();
CDBrickIDTableTable::Instance().LoadValuesFromDatabase(); CDBrickIDTableTable::Instance().LoadValuesFromDatabase();
CDComponentsRegistryTable::Instance().LoadValuesFromDatabase(); CDCLIENT_DONT_CACHE_TABLE(CDComponentsRegistryTable::Instance().LoadValuesFromDatabase());
CDCurrencyTableTable::Instance().LoadValuesFromDatabase(); CDCurrencyTableTable::Instance().LoadValuesFromDatabase();
CDDestructibleComponentTable::Instance().LoadValuesFromDatabase(); CDDestructibleComponentTable::Instance().LoadValuesFromDatabase();
CDEmoteTableTable::Instance().LoadValuesFromDatabase(); CDEmoteTableTable::Instance().LoadValuesFromDatabase();
@@ -65,8 +65,8 @@ CDClientManager::CDClientManager() {
CDItemSetSkillsTable::Instance().LoadValuesFromDatabase(); CDItemSetSkillsTable::Instance().LoadValuesFromDatabase();
CDItemSetsTable::Instance().LoadValuesFromDatabase(); CDItemSetsTable::Instance().LoadValuesFromDatabase();
CDLevelProgressionLookupTable::Instance().LoadValuesFromDatabase(); CDLevelProgressionLookupTable::Instance().LoadValuesFromDatabase();
CDLootMatrixTable::Instance().LoadValuesFromDatabase(); CDCLIENT_DONT_CACHE_TABLE(CDLootMatrixTable::Instance().LoadValuesFromDatabase());
CDLootTableTable::Instance().LoadValuesFromDatabase(); CDCLIENT_DONT_CACHE_TABLE(CDLootTableTable::Instance().LoadValuesFromDatabase());
CDMissionEmailTable::Instance().LoadValuesFromDatabase(); CDMissionEmailTable::Instance().LoadValuesFromDatabase();
CDMissionNPCComponentTable::Instance().LoadValuesFromDatabase(); CDMissionNPCComponentTable::Instance().LoadValuesFromDatabase();
CDMissionTasksTable::Instance().LoadValuesFromDatabase(); CDMissionTasksTable::Instance().LoadValuesFromDatabase();

View File

@@ -1,26 +1,30 @@
#include "CDBehaviorParameterTable.h" #include "CDBehaviorParameterTable.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
uint64_t GetKey(const uint32_t behaviorID, const uint32_t parameterID) {
uint64_t key = behaviorID;
key <<= 31U;
key |= parameterID;
return key;
}
void CDBehaviorParameterTable::LoadValuesFromDatabase() { void CDBehaviorParameterTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter");
uint32_t uniqueParameterId = 0;
uint64_t hash = 0;
while (!tableData.eof()) { while (!tableData.eof()) {
CDBehaviorParameter entry; uint32_t behaviorID = tableData.getIntField("behaviorID", -1);
entry.behaviorID = tableData.getIntField("behaviorID", -1);
auto candidateStringToAdd = std::string(tableData.getStringField("parameterID", "")); auto candidateStringToAdd = std::string(tableData.getStringField("parameterID", ""));
auto parameter = m_ParametersList.find(candidateStringToAdd); auto parameter = m_ParametersList.find(candidateStringToAdd);
uint32_t parameterId;
if (parameter != m_ParametersList.end()) { if (parameter != m_ParametersList.end()) {
entry.parameterID = parameter; parameterId = parameter->second;
} else { } else {
entry.parameterID = m_ParametersList.insert(std::make_pair(candidateStringToAdd, uniqueParameterId)).first; parameterId = m_ParametersList.insert(std::make_pair(candidateStringToAdd, m_ParametersList.size())).first->second;
uniqueParameterId++;
} }
hash = entry.behaviorID; uint64_t hash = GetKey(behaviorID, parameterId);
hash = (hash << 31U) | entry.parameterID->second; float value = tableData.getFloatField("value", -1.0f);
entry.value = tableData.getFloatField("value", -1.0f);
m_Entries.insert(std::make_pair(hash, entry)); m_Entries.insert(std::make_pair(hash, value));
tableData.nextRow(); tableData.nextRow();
} }
@@ -30,25 +34,21 @@ void CDBehaviorParameterTable::LoadValuesFromDatabase() {
float CDBehaviorParameterTable::GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue) { float CDBehaviorParameterTable::GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue) {
auto parameterID = this->m_ParametersList.find(name); auto parameterID = this->m_ParametersList.find(name);
if (parameterID == this->m_ParametersList.end()) return defaultValue; if (parameterID == this->m_ParametersList.end()) return defaultValue;
auto hash = GetKey(behaviorID, parameterID->second);
uint64_t hash = behaviorID;
hash = (hash << 31U) | parameterID->second;
// Search for specific parameter // Search for specific parameter
const auto& it = m_Entries.find(hash); auto it = m_Entries.find(hash);
return it != m_Entries.end() ? it->second.value : defaultValue; return it != m_Entries.end() ? it->second : defaultValue;
} }
std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) { std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) {
uint64_t hashBase = behaviorID; uint64_t hashBase = behaviorID;
std::map<std::string, float> returnInfo; std::map<std::string, float> returnInfo;
uint64_t hash; for (auto& [parameterString, parameterId] : m_ParametersList) {
for (auto& parameterCandidate : m_ParametersList) { uint64_t hash = GetKey(hashBase, parameterId);
hash = (hashBase << 31U) | parameterCandidate.second;
auto infoCandidate = m_Entries.find(hash); auto infoCandidate = m_Entries.find(hash);
if (infoCandidate != m_Entries.end()) { if (infoCandidate != m_Entries.end()) {
returnInfo.insert(std::make_pair(infoCandidate->second.parameterID->first, infoCandidate->second.value)); returnInfo.insert(std::make_pair(parameterString, infoCandidate->second));
} }
} }
return returnInfo; return returnInfo;

View File

@@ -5,15 +5,11 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
struct CDBehaviorParameter {
unsigned int behaviorID; //!< The Behavior ID
std::unordered_map<std::string, uint32_t>::iterator parameterID; //!< The Parameter ID
float value; //!< The value of the behavior template
};
class CDBehaviorParameterTable : public CDTable<CDBehaviorParameterTable> { class CDBehaviorParameterTable : public CDTable<CDBehaviorParameterTable> {
private: private:
std::unordered_map<uint64_t, CDBehaviorParameter> m_Entries; typedef uint64_t BehaviorParameterHash;
typedef float BehaviorParameterValue;
std::unordered_map<BehaviorParameterHash, BehaviorParameterValue> m_Entries;
std::unordered_map<std::string, uint32_t> m_ParametersList; std::unordered_map<std::string, uint32_t> m_ParametersList;
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();

View File

@@ -11,6 +11,7 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() {
entry.component_id = tableData.getIntField("component_id", -1); entry.component_id = tableData.getIntField("component_id", -1);
this->mappedEntries.insert_or_assign(((uint64_t)entry.component_type) << 32 | ((uint64_t)entry.id), entry.component_id); this->mappedEntries.insert_or_assign(((uint64_t)entry.component_type) << 32 | ((uint64_t)entry.id), entry.component_id);
this->mappedEntries.insert_or_assign(entry.id, 0);
tableData.nextRow(); tableData.nextRow();
} }
@@ -19,19 +20,18 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() {
} }
int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) { int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) {
auto iter = mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id)); auto exists = mappedEntries.find(id);
if (exists != mappedEntries.end()) {
if (iter == this->mappedEntries.end()) { auto iter = mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
return defaultValue; return iter == mappedEntries.end() ? defaultValue : iter->second;
} }
return iter->second; // Now get the data. Get all components of this entity so we dont do a query for each component
// Now get the data auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ComponentsRegistry WHERE id = ?;");
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ComponentsRegistry WHERE id = ? AND component_type = ?;");
query.bind(1, static_cast<int32_t>(id)); query.bind(1, static_cast<int32_t>(id));
query.bind(2, static_cast<int32_t>(componentType));
auto tableData = query.execQuery(); auto tableData = query.execQuery();
while (!tableData.eof()) { while (!tableData.eof()) {
CDComponentsRegistry entry; CDComponentsRegistry entry;
entry.id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
@@ -43,7 +43,9 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
tableData.nextRow(); tableData.nextRow();
} }
iter = this->mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id)); mappedEntries.insert_or_assign(id, 0);
auto iter = this->mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
return iter == this->mappedEntries.end() ? defaultValue : iter->second; return iter == this->mappedEntries.end() ? defaultValue : iter->second;
} }

View File

@@ -13,7 +13,7 @@ struct CDComponentsRegistry {
class CDComponentsRegistryTable : public CDTable<CDComponentsRegistryTable> { class CDComponentsRegistryTable : public CDTable<CDComponentsRegistryTable> {
private: private:
std::map<uint64_t, uint32_t> mappedEntries; //id, component_type, component_id std::unordered_map<uint64_t, uint32_t> mappedEntries; //id, component_type, component_id
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();

View File

@@ -1,5 +1,18 @@
#include "CDLootMatrixTable.h" #include "CDLootMatrixTable.h"
CDLootMatrix CDLootMatrixTable::ReadRow(CppSQLite3Query& tableData) const {
CDLootMatrix entry{};
if (tableData.eof()) return entry;
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
entry.percent = tableData.getFloatField("percent", -1.0f);
entry.minToDrop = tableData.getIntField("minToDrop", -1);
entry.maxToDrop = tableData.getIntField("maxToDrop", -1);
entry.flagID = tableData.getIntField("flagID", -1);
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
return entry;
}
void CDLootMatrixTable::LoadValuesFromDatabase() { void CDLootMatrixTable::LoadValuesFromDatabase() {
// First, get the size of the table // First, get the size of the table
@@ -11,8 +24,6 @@ void CDLootMatrixTable::LoadValuesFromDatabase() {
tableSize.nextRow(); tableSize.nextRow();
} }
tableSize.finalize();
// Reserve the size // Reserve the size
this->entries.reserve(size); this->entries.reserve(size);
@@ -20,33 +31,28 @@ void CDLootMatrixTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootMatrix"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootMatrix");
while (!tableData.eof()) { while (!tableData.eof()) {
CDLootMatrix entry; CDLootMatrix entry;
entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1); uint32_t lootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
entry.percent = tableData.getFloatField("percent", -1.0f);
entry.minToDrop = tableData.getIntField("minToDrop", -1);
entry.maxToDrop = tableData.getIntField("maxToDrop", -1);
entry.id = tableData.getIntField("id", -1);
entry.flagID = tableData.getIntField("flagID", -1);
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
this->entries.push_back(entry); this->entries[lootMatrixIndex].push_back(ReadRow(tableData));
tableData.nextRow();
}
}
const LootMatrixEntries& CDLootMatrixTable::GetMatrix(uint32_t matrixId) {
auto itr = this->entries.find(matrixId);
if (itr != this->entries.end()) {
return itr->second;
}
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootMatrix where LootMatrixIndex = ?;");
query.bind(1, static_cast<int32_t>(matrixId));
auto tableData = query.execQuery();
while (!tableData.eof()) {
this->entries[matrixId].push_back(ReadRow(tableData));
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize(); return this->entries[matrixId];
}
std::vector<CDLootMatrix> CDLootMatrixTable::Query(std::function<bool(CDLootMatrix)> predicate) {
std::vector<CDLootMatrix> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
return data;
}
const std::vector<CDLootMatrix>& CDLootMatrixTable::GetEntries() const {
return this->entries;
} }

View File

@@ -4,26 +4,26 @@
#include "CDTable.h" #include "CDTable.h"
struct CDLootMatrix { struct CDLootMatrix {
unsigned int LootMatrixIndex; //!< The Loot Matrix Index
unsigned int LootTableIndex; //!< The Loot Table Index unsigned int LootTableIndex; //!< The Loot Table Index
unsigned int RarityTableIndex; //!< The Rarity Table Index unsigned int RarityTableIndex; //!< The Rarity Table Index
float percent; //!< The percent that this matrix is used? float percent; //!< The percent that this matrix is used?
unsigned int minToDrop; //!< The minimum amount of loot from this matrix to drop unsigned int minToDrop; //!< The minimum amount of loot from this matrix to drop
unsigned int maxToDrop; //!< The maximum amount of loot from this matrix to drop unsigned int maxToDrop; //!< The maximum amount of loot from this matrix to drop
unsigned int id; //!< The ID of the Loot Matrix
unsigned int flagID; //!< ??? unsigned int flagID; //!< ???
UNUSED(std::string gate_version); //!< The Gate Version UNUSED(std::string gate_version); //!< The Gate Version
}; };
class CDLootMatrixTable : public CDTable<CDLootMatrixTable> { typedef uint32_t LootMatrixIndex;
private: typedef std::vector<CDLootMatrix> LootMatrixEntries;
std::vector<CDLootMatrix> entries;
class CDLootMatrixTable : public CDTable<CDLootMatrixTable> {
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
std::vector<CDLootMatrix> Query(std::function<bool(CDLootMatrix)> predicate);
const std::vector<CDLootMatrix>& GetEntries() const; // Gets a matrix by ID or inserts a blank one if none existed.
const LootMatrixEntries& GetMatrix(uint32_t matrixId);
private:
CDLootMatrix ReadRow(CppSQLite3Query& tableData) const;
std::unordered_map<LootMatrixIndex, LootMatrixEntries> entries;
}; };

View File

@@ -1,4 +1,42 @@
#include "CDLootTableTable.h" #include "CDLootTableTable.h"
#include "CDClientManager.h"
#include "CDComponentsRegistryTable.h"
#include "CDItemComponentTable.h"
#include "eReplicaComponentType.h"
// Sort the tables by their rarity so the highest rarity items are first.
void SortTable(LootTableEntries& table) {
auto* componentsRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto* itemComponentTable = CDClientManager::Instance().GetTable<CDItemComponentTable>();
// We modify the table in place so the outer loop keeps track of what is sorted
// and the inner loop finds the highest rarity item and swaps it with the current position
// of the outer loop.
for (auto oldItrOuter = table.begin(); oldItrOuter != table.end(); oldItrOuter++) {
auto lootToInsert = oldItrOuter;
// Its fine if this starts at 0, even if this doesnt match lootToInsert as the actual highest will
// either be found and overwrite these values, or the original is somehow zero and is still the highest rarity.
uint32_t highestLootRarity = 0;
for (auto oldItrInner = oldItrOuter; oldItrInner != table.end(); oldItrInner++) {
uint32_t itemComponentId = componentsRegistryTable->GetByIDAndType(oldItrInner->itemid, eReplicaComponentType::ITEM);
uint32_t rarity = itemComponentTable->GetItemComponentByID(itemComponentId).rarity;
if (rarity > highestLootRarity) {
highestLootRarity = rarity;
lootToInsert = oldItrInner;
}
}
Game::logger->LogDebug("CDLootTableTable", "highest rarity %i item id %i", highestLootRarity, lootToInsert->itemid);
std::swap(*oldItrOuter, *lootToInsert);
}
}
CDLootTable CDLootTableTable::ReadRow(CppSQLite3Query& tableData) const {
CDLootTable entry{};
if (tableData.eof()) return entry;
entry.itemid = tableData.getIntField("itemid", -1);
entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false;
entry.sortPriority = tableData.getIntField("sortPriority", -1);
return entry;
}
void CDLootTableTable::LoadValuesFromDatabase() { void CDLootTableTable::LoadValuesFromDatabase() {
@@ -11,8 +49,6 @@ void CDLootTableTable::LoadValuesFromDatabase() {
tableSize.nextRow(); tableSize.nextRow();
} }
tableSize.finalize();
// Reserve the size // Reserve the size
this->entries.reserve(size); this->entries.reserve(size);
@@ -20,32 +56,32 @@ void CDLootTableTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootTable"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootTable");
while (!tableData.eof()) { while (!tableData.eof()) {
CDLootTable entry; CDLootTable entry;
entry.id = tableData.getIntField("id", -1); uint32_t lootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.itemid = tableData.getIntField("itemid", -1);
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.id = tableData.getIntField("id", -1);
entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false;
entry.sortPriority = tableData.getIntField("sortPriority", -1);
this->entries.push_back(entry); this->entries[lootTableIndex].push_back(ReadRow(tableData));
tableData.nextRow(); tableData.nextRow();
} }
for (auto& [id, table] : this->entries) {
tableData.finalize(); SortTable(table);
}
} }
//! Queries the table with a custom "where" clause const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
std::vector<CDLootTable> CDLootTableTable::Query(std::function<bool(CDLootTable)> predicate) { auto itr = this->entries.find(tableId);
if (itr != this->entries.end()) {
return itr->second;
}
std::vector<CDLootTable> data = cpplinq::from(this->entries) auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootTable WHERE LootTableIndex = ?;");
>> cpplinq::where(predicate) query.bind(1, static_cast<int32_t>(tableId));
>> cpplinq::to_vector(); auto tableData = query.execQuery();
return data; while (!tableData.eof()) {
CDLootTable entry;
this->entries[tableId].push_back(ReadRow(tableData));
tableData.nextRow();
}
SortTable(this->entries[tableId]);
return this->entries[tableId];
} }
//! Gets all the entries in the table
const std::vector<CDLootTable>& CDLootTableTable::GetEntries() const {
return this->entries;
}

View File

@@ -6,20 +6,21 @@
struct CDLootTable { struct CDLootTable {
unsigned int itemid; //!< The LOT of the item unsigned int itemid; //!< The LOT of the item
unsigned int LootTableIndex; //!< The Loot Table Index unsigned int LootTableIndex; //!< The Loot Table Index
unsigned int id; //!< The ID
bool MissionDrop; //!< Whether or not this loot table is a mission drop bool MissionDrop; //!< Whether or not this loot table is a mission drop
unsigned int sortPriority; //!< The sorting priority unsigned int sortPriority; //!< The sorting priority
}; };
typedef uint32_t LootTableIndex;
typedef std::vector<CDLootTable> LootTableEntries;
class CDLootTableTable : public CDTable<CDLootTableTable> { class CDLootTableTable : public CDTable<CDLootTableTable> {
private: private:
std::vector<CDLootTable> entries; CDLootTable ReadRow(CppSQLite3Query& tableData) const;
std::unordered_map<LootTableIndex, LootTableEntries> entries;
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
std::vector<CDLootTable> Query(std::function<bool(CDLootTable)> predicate); const LootTableEntries& GetTable(uint32_t tableId);
const std::vector<CDLootTable>& GetEntries() const;
}; };

View File

@@ -1,24 +1,34 @@
#include "CDRarityTableTable.h" #include "CDRarityTableTable.h"
void CDRarityTableTable::LoadValuesFromDatabase() { void CDRarityTableTable::LoadValuesFromDatabase() {
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RarityTable");
while (!tableData.eof()) {
CDRarityTable entry;
uint32_t id = tableData.getIntField("id", -1);
entry.randmax = tableData.getFloatField("randmax", -1);
entry.rarity = tableData.getIntField("rarity", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
this->entries.insert_or_assign(id, entry); // First, get the size of the table
tableData.nextRow(); unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RarityTable");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
} }
tableData.finalize(); tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RarityTable order by randmax desc;");
while (!tableData.eof()) {
uint32_t rarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
CDRarityTable entry;
entry.randmax = tableData.getFloatField("randmax", -1);
entry.rarity = tableData.getIntField("rarity", -1);
entries[rarityTableIndex].push_back(entry);
tableData.nextRow();
}
} }
//! Queries the table with a custom "where" clause const std::vector<CDRarityTable>& CDRarityTableTable::GetRarityTable(uint32_t id) {
const std::optional<CDRarityTable> CDRarityTableTable::Get(uint32_t id) { return entries[id];
auto it = this->entries.find(id);
return it != this->entries.end() ? std::make_optional(it->second) : std::nullopt;
} }

View File

@@ -4,35 +4,20 @@
#include "CDTable.h" #include "CDTable.h"
struct CDRarityTable { struct CDRarityTable {
unsigned int id;
float randmax; float randmax;
unsigned int rarity; unsigned int rarity;
unsigned int RarityTableIndex;
friend bool operator> (const CDRarityTable& c1, const CDRarityTable& c2) {
return c1.rarity > c2.rarity;
}
friend bool operator>= (const CDRarityTable& c1, const CDRarityTable& c2) {
return c1.rarity >= c2.rarity;
}
friend bool operator< (const CDRarityTable& c1, const CDRarityTable& c2) {
return c1.rarity < c2.rarity;
}
friend bool operator<= (const CDRarityTable& c1, const CDRarityTable& c2) {
return c1.rarity <= c2.rarity;
}
}; };
typedef std::vector<CDRarityTable> RarityTable;
class CDRarityTableTable : public CDTable<CDRarityTableTable> { class CDRarityTableTable : public CDTable<CDRarityTableTable> {
private: private:
std::unordered_map<uint32_t, CDRarityTable> entries; typedef uint32_t RarityTableIndex;
std::unordered_map<RarityTableIndex, std::vector<CDRarityTable>> entries;
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
const std::optional<CDRarityTable> Get(uint32_t predicate); const std::vector<CDRarityTable>& GetRarityTable(uint32_t predicate);
}; };

View File

@@ -1,11 +1,26 @@
#include "CDRebuildComponentTable.h" #include "CDRebuildComponentTable.h"
void CDRebuildComponentTable::LoadValuesFromDatabase() { void CDRebuildComponentTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RebuildComponent");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RebuildComponent"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RebuildComponent");
while (!tableData.eof()) { while (!tableData.eof()) {
CDRebuildComponent entry; CDRebuildComponent entry;
uint32_t id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
entry.reset_time = tableData.getFloatField("reset_time", -1.0f); entry.reset_time = tableData.getFloatField("reset_time", -1.0f);
entry.complete_time = tableData.getFloatField("complete_time", -1.0f); entry.complete_time = tableData.getFloatField("complete_time", -1.0f);
entry.take_imagination = tableData.getIntField("take_imagination", -1); entry.take_imagination = tableData.getIntField("take_imagination", -1);
@@ -16,12 +31,23 @@ void CDRebuildComponentTable::LoadValuesFromDatabase() {
entry.post_imagination_cost = tableData.getIntField("post_imagination_cost", -1); entry.post_imagination_cost = tableData.getIntField("post_imagination_cost", -1);
entry.time_before_smash = tableData.getFloatField("time_before_smash", -1.0f); entry.time_before_smash = tableData.getFloatField("time_before_smash", -1.0f);
this->entries.insert_or_assign(id, entry); this->entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
const std::optional<CDRebuildComponent> CDRebuildComponentTable::Get(uint32_t componentId) { std::vector<CDRebuildComponent> CDRebuildComponentTable::Query(std::function<bool(CDRebuildComponent)> predicate) {
auto it = this->entries.find(componentId);
return it != this->entries.end() ? std::make_optional(it->second) : std::nullopt; std::vector<CDRebuildComponent> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
return data;
} }
const std::vector<CDRebuildComponent>& CDRebuildComponentTable::GetEntries() const {
return this->entries;
}

View File

@@ -4,6 +4,7 @@
#include "CDTable.h" #include "CDTable.h"
struct CDRebuildComponent { struct CDRebuildComponent {
unsigned int id; //!< The component Id
float reset_time; //!< The reset time float reset_time; //!< The reset time
float complete_time; //!< The complete time float complete_time; //!< The complete time
unsigned int take_imagination; //!< The amount of imagination it costs unsigned int take_imagination; //!< The amount of imagination it costs
@@ -17,11 +18,13 @@ struct CDRebuildComponent {
class CDRebuildComponentTable : public CDTable<CDRebuildComponentTable> { class CDRebuildComponentTable : public CDTable<CDRebuildComponentTable> {
private: private:
std::unordered_map<uint32_t, CDRebuildComponent> entries; std::vector<CDRebuildComponent> entries;
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
const std::optional<CDRebuildComponent> Get(uint32_t componentId); std::vector<CDRebuildComponent> Query(std::function<bool(CDRebuildComponent)> predicate);
const std::vector<CDRebuildComponent>& GetEntries() const;
}; };

View File

@@ -4,14 +4,14 @@ void CDRewardsTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Rewards"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Rewards");
while (!tableData.eof()) { while (!tableData.eof()) {
CDRewards entry; CDRewards entry;
uint32_t id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
entry.levelID = tableData.getIntField("LevelID", -1); entry.levelID = tableData.getIntField("LevelID", -1);
entry.missionID = tableData.getIntField("MissionID", -1); entry.missionID = tableData.getIntField("MissionID", -1);
entry.rewardType = tableData.getIntField("RewardType", -1); entry.rewardType = tableData.getIntField("RewardType", -1);
entry.value = tableData.getIntField("value", -1); entry.value = tableData.getIntField("value", -1);
entry.count = tableData.getIntField("count", -1); entry.count = tableData.getIntField("count", -1);
m_entries.push_back(entry); m_entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow(); tableData.nextRow();
} }
@@ -19,9 +19,9 @@ void CDRewardsTable::LoadValuesFromDatabase() {
} }
std::vector<CDRewards> CDRewardsTable::GetByLevelID(uint32_t levelID) { std::vector<CDRewards> CDRewardsTable::GetByLevelID(uint32_t levelID) {
std::vector<CDRewards> result; std::vector<CDRewards> result{};
for (const auto& levelData : m_entries) { for (const auto& e : m_entries) {
if (levelData.levelID == levelID) result.push_back(levelData); if (e.second.levelID == levelID) result.push_back(e.second);
} }
return result; return result;

View File

@@ -3,6 +3,7 @@
#include <string> #include <string>
struct CDRewards { struct CDRewards {
int32_t id;
int32_t levelID; int32_t levelID;
int32_t missionID; int32_t missionID;
int32_t rewardType; int32_t rewardType;
@@ -13,7 +14,10 @@ struct CDRewards {
class CDRewardsTable : public CDTable<CDRewardsTable> { class CDRewardsTable : public CDTable<CDRewardsTable> {
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
static const std::string GetTableName() { return "Rewards"; };
std::vector<CDRewards> GetByLevelID(uint32_t levelID); std::vector<CDRewards> GetByLevelID(uint32_t levelID);
private: private:
std::vector<CDRewards> m_entries; std::map<uint32_t, CDRewards> m_entries;
}; };

View File

@@ -1,21 +1,39 @@
#include "CDScriptComponentTable.h" #include "CDScriptComponentTable.h"
void CDScriptComponentTable::LoadValuesFromDatabase() { void CDScriptComponentTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ScriptComponent");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ScriptComponent"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ScriptComponent");
while (!tableData.eof()) { while (!tableData.eof()) {
CDScriptComponent entry; CDScriptComponent entry;
uint32_t id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
entry.script_name = tableData.getStringField("script_name", ""); entry.script_name = tableData.getStringField("script_name", "");
entry.client_script_name = tableData.getStringField("client_script_name", ""); entry.client_script_name = tableData.getStringField("client_script_name", "");
this->entries.insert_or_assign(id, entry); this->entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
const std::optional<CDScriptComponent> CDScriptComponentTable::GetByID(unsigned int id) { const CDScriptComponent& CDScriptComponentTable::GetByID(unsigned int id) {
auto it = this->entries.find(id); std::map<unsigned int, CDScriptComponent>::iterator it = this->entries.find(id);
return (it != this->entries.end()) ? std::make_optional<CDScriptComponent>(it->second) : std::nullopt; if (it != this->entries.end()) {
return it->second;
}
return m_ToReturnWhenNoneFound;
} }

View File

@@ -4,16 +4,19 @@
#include "CDTable.h" #include "CDTable.h"
struct CDScriptComponent { struct CDScriptComponent {
std::string script_name; //!< The script name unsigned int id; //!< The component ID
std::string client_script_name; //!< The client script name std::string script_name; //!< The script name
std::string client_script_name; //!< The client script name
}; };
class CDScriptComponentTable : public CDTable<CDScriptComponentTable> { class CDScriptComponentTable : public CDTable<CDScriptComponentTable> {
private: private:
std::unordered_map<unsigned int, CDScriptComponent> entries; std::map<unsigned int, CDScriptComponent> entries;
CDScriptComponent m_ToReturnWhenNoneFound;
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Gets an entry by scriptID // Gets an entry by scriptID
const std::optional<CDScriptComponent> GetByID(unsigned int id); const CDScriptComponent& GetByID(unsigned int id);
}; };

View File

@@ -1,10 +1,27 @@
#include "CDSkillBehaviorTable.h" #include "CDSkillBehaviorTable.h"
void CDSkillBehaviorTable::LoadValuesFromDatabase() { void CDSkillBehaviorTable::LoadValuesFromDatabase() {
m_empty = CDSkillBehavior();
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM SkillBehavior");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
//this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM SkillBehavior"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM SkillBehavior");
while (!tableData.eof()) { while (!tableData.eof()) {
CDSkillBehavior entry; CDSkillBehavior entry;
uint32_t skillID = tableData.getIntField("skillID", -1); entry.skillID = tableData.getIntField("skillID", -1);
UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1)); UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1));
entry.behaviorID = tableData.getIntField("behaviorID", -1); entry.behaviorID = tableData.getIntField("behaviorID", -1);
entry.imaginationcost = tableData.getIntField("imaginationcost", -1); entry.imaginationcost = tableData.getIntField("imaginationcost", -1);
@@ -24,13 +41,20 @@ void CDSkillBehaviorTable::LoadValuesFromDatabase() {
UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
UNUSED(entry.cancelType = tableData.getIntField("cancelType", -1)); UNUSED(entry.cancelType = tableData.getIntField("cancelType", -1));
this->entries.insert_or_assign(skillID, entry); this->entries.insert(std::make_pair(entry.skillID, entry));
//this->entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
const std::optional<CDSkillBehavior> CDSkillBehaviorTable::GetSkillByID(unsigned int skillID) { const CDSkillBehavior& CDSkillBehaviorTable::GetSkillByID(unsigned int skillID) {
auto it = this->entries.find(skillID); std::map<unsigned int, CDSkillBehavior>::iterator it = this->entries.find(skillID);
return it != this->entries.end() ? std::make_optional(it->second) : std::nullopt; if (it != this->entries.end()) {
return it->second;
}
return m_empty;
} }

View File

@@ -4,6 +4,7 @@
#include "CDTable.h" #include "CDTable.h"
struct CDSkillBehavior { struct CDSkillBehavior {
unsigned int skillID; //!< The Skill ID of the skill
UNUSED(unsigned int locStatus); //!< ?? UNUSED(unsigned int locStatus); //!< ??
unsigned int behaviorID; //!< The Behavior ID of the skill unsigned int behaviorID; //!< The Behavior ID of the skill
unsigned int imaginationcost; //!< The imagination cost of the skill unsigned int imaginationcost; //!< The imagination cost of the skill
@@ -26,11 +27,13 @@ struct CDSkillBehavior {
class CDSkillBehaviorTable : public CDTable<CDSkillBehaviorTable> { class CDSkillBehaviorTable : public CDTable<CDSkillBehaviorTable> {
private: private:
std::unordered_map<uint32_t, CDSkillBehavior> entries; std::map<unsigned int, CDSkillBehavior> entries;
CDSkillBehavior m_empty;
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Gets an entry by skillID // Gets an entry by skillID
const std::optional<CDSkillBehavior> GetSkillByID(unsigned int skillID); const CDSkillBehavior& GetSkillByID(unsigned int skillID);
}; };

View File

@@ -5,7 +5,6 @@
#include "DluAssert.h" #include "DluAssert.h"
#include <functional> #include <functional>
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>

View File

@@ -1,22 +1,50 @@
#include "CDVendorComponentTable.h" #include "CDVendorComponentTable.h"
void CDVendorComponentTable::LoadValuesFromDatabase() { void CDVendorComponentTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM VendorComponent");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM VendorComponent"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM VendorComponent");
while (!tableData.eof()) { while (!tableData.eof()) {
CDVendorComponent entry; CDVendorComponent entry;
uint32_t id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
entry.buyScalar = tableData.getFloatField("buyScalar", -1.0f); entry.buyScalar = tableData.getFloatField("buyScalar", 0.0f);
entry.sellScalar = tableData.getFloatField("sellScalar", -1.0f); entry.sellScalar = tableData.getFloatField("sellScalar", -1.0f);
entry.refreshTimeSeconds = tableData.getFloatField("refreshTimeSeconds", -1.0f); entry.refreshTimeSeconds = tableData.getFloatField("refreshTimeSeconds", -1.0f);
entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1); entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
this->entries.insert_or_assign(id, entry); this->entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
const std::optional<CDVendorComponent> CDVendorComponentTable::Query(uint32_t id) { //! Queries the table with a custom "where" clause
const auto& iter = entries.find(id); std::vector<CDVendorComponent> CDVendorComponentTable::Query(std::function<bool(CDVendorComponent)> predicate) {
return iter != entries.end() ? std::make_optional(iter->second) : std::nullopt;
std::vector<CDVendorComponent> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
return data;
} }
//! Gets all the entries in the table
const std::vector<CDVendorComponent>& CDVendorComponentTable::GetEntries() const {
return this->entries;
}

View File

@@ -4,6 +4,7 @@
#include "CDTable.h" #include "CDTable.h"
struct CDVendorComponent { struct CDVendorComponent {
unsigned int id; //!< The Component ID
float buyScalar; //!< Buy Scalar (what does that mean?) float buyScalar; //!< Buy Scalar (what does that mean?)
float sellScalar; //!< Sell Scalar (what does that mean?) float sellScalar; //!< Sell Scalar (what does that mean?)
float refreshTimeSeconds; //!< The refresh time float refreshTimeSeconds; //!< The refresh time
@@ -12,11 +13,13 @@ struct CDVendorComponent {
class CDVendorComponentTable : public CDTable<CDVendorComponentTable> { class CDVendorComponentTable : public CDTable<CDVendorComponentTable> {
private: private:
std::unordered_map<uint32_t, CDVendorComponent> entries; std::vector<CDVendorComponent> entries;
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
const std::optional<CDVendorComponent> Query(uint32_t id); std::vector<CDVendorComponent> Query(std::function<bool(CDVendorComponent)> predicate);
const std::vector<CDVendorComponent>& GetEntries(void) const;
}; };

View File

@@ -1,6 +1,18 @@
#include "CDZoneTableTable.h" #include "CDZoneTableTable.h"
void CDZoneTableTable::LoadValuesFromDatabase() { void CDZoneTableTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ZoneTable");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ZoneTable"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ZoneTable");
while (!tableData.eof()) { while (!tableData.eof()) {
@@ -36,11 +48,18 @@ void CDZoneTableTable::LoadValuesFromDatabase() {
this->m_Entries.insert(std::make_pair(entry.zoneID, entry)); this->m_Entries.insert(std::make_pair(entry.zoneID, entry));
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
//! Queries the table with a zoneID to find. //! Queries the table with a zoneID to find.
const std::optional<CDZoneTable> CDZoneTableTable::Query(unsigned int zoneID) { const CDZoneTable* CDZoneTableTable::Query(unsigned int zoneID) {
const auto& iter = m_Entries.find(zoneID); const auto& iter = m_Entries.find(zoneID);
return iter != m_Entries.end() ? std::make_optional(iter->second) : std::nullopt;
if (iter != m_Entries.end()) {
return &iter->second;
}
return nullptr;
} }

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
// Custom Classes
#include "CDTable.h" #include "CDTable.h"
struct CDZoneTable { struct CDZoneTable {
@@ -34,11 +35,11 @@ struct CDZoneTable {
class CDZoneTableTable : public CDTable<CDZoneTableTable> { class CDZoneTableTable : public CDTable<CDZoneTableTable> {
private: private:
std::unordered_map<uint32_t, CDZoneTable> m_Entries; std::map<unsigned int, CDZoneTable> m_Entries;
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Queries the table with a zoneID to find. // Queries the table with a zoneID to find.
const std::optional<CDZoneTable> Query(unsigned int zoneID); const CDZoneTable* Query(unsigned int zoneID);
}; };

View File

@@ -30,7 +30,7 @@
#include "Component.h" #include "Component.h"
#include "ControllablePhysicsComponent.h" #include "ControllablePhysicsComponent.h"
#include "RenderComponent.h" #include "RenderComponent.h"
#include "RocketLaunchLupComponent.h" #include "MultiZoneEntranceComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "BuffComponent.h" #include "BuffComponent.h"
@@ -51,6 +51,7 @@
#include "BuildBorderComponent.h" #include "BuildBorderComponent.h"
#include "MovementAIComponent.h" #include "MovementAIComponent.h"
#include "VendorComponent.h" #include "VendorComponent.h"
#include "DonationVendorComponent.h"
#include "RocketLaunchpadControlComponent.h" #include "RocketLaunchpadControlComponent.h"
#include "PropertyComponent.h" #include "PropertyComponent.h"
#include "BaseCombatAIComponent.h" #include "BaseCombatAIComponent.h"
@@ -70,6 +71,7 @@
#include "ShootingGalleryComponent.h" #include "ShootingGalleryComponent.h"
#include "RailActivatorComponent.h" #include "RailActivatorComponent.h"
#include "LUPExhibitComponent.h" #include "LUPExhibitComponent.h"
#include "RacingSoundTriggerComponent.h"
#include "TriggerComponent.h" #include "TriggerComponent.h"
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
@@ -317,6 +319,9 @@ void Entity::Initialize() {
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) {
auto* comp = new SoundTriggerComponent(this); auto* comp = new SoundTriggerComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::SOUND_TRIGGER, comp)); m_Components.insert(std::make_pair(eReplicaComponentType::SOUND_TRIGGER, comp));
} else if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_SOUND_TRIGGER, -1) != -1) {
auto* comp = new RacingSoundTriggerComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::RACING_SOUND_TRIGGER, comp));
} }
//Also check for the collectible id: //Also check for the collectible id:
@@ -340,7 +345,7 @@ void Entity::Initialize() {
int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF); int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
int rebuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD); int rebuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
int componentID = 0; int componentID = -1;
if (collectibleComponentID > 0) componentID = collectibleComponentID; if (collectibleComponentID > 0) componentID = collectibleComponentID;
if (rebuildComponentID > 0) componentID = rebuildComponentID; if (rebuildComponentID > 0) componentID = rebuildComponentID;
if (buffComponentID > 0) componentID = buffComponentID; if (buffComponentID > 0) componentID = buffComponentID;
@@ -348,7 +353,8 @@ void Entity::Initialize() {
CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>(); CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); }); std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
if (buffComponentID > 0 || collectibleComponentID > 0) { bool isSmashable = GetVarAs<int32_t>(u"is_smashable") != 0;
if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) {
DestroyableComponent* comp = new DestroyableComponent(this); DestroyableComponent* comp = new DestroyableComponent(this);
if (m_Character) { if (m_Character) {
comp->LoadFromXml(m_Character->GetXMLDoc()); comp->LoadFromXml(m_Character->GetXMLDoc());
@@ -368,10 +374,12 @@ void Entity::Initialize() {
comp->SetMaxHealth(destCompData[0].life); comp->SetMaxHealth(destCompData[0].life);
comp->SetMaxImagination(destCompData[0].imagination); comp->SetMaxImagination(destCompData[0].imagination);
comp->SetMaxArmor(destCompData[0].armor); comp->SetMaxArmor(destCompData[0].armor);
comp->SetDeathBehavior(destCompData[0].death_behavior);
comp->SetIsSmashable(destCompData[0].isSmashable); comp->SetIsSmashable(destCompData[0].isSmashable);
comp->SetLootMatrixID(destCompData[0].LootMatrixIndex); comp->SetLootMatrixID(destCompData[0].LootMatrixIndex);
Loot::CacheMatrix(destCompData[0].LootMatrixIndex);
// Now get currency information // Now get currency information
uint32_t npcMinLevel = destCompData[0].level; uint32_t npcMinLevel = destCompData[0].level;
@@ -387,7 +395,7 @@ void Entity::Initialize() {
} }
// extraInfo overrides. Client ORs the database smashable and the luz smashable. // extraInfo overrides. Client ORs the database smashable and the luz smashable.
comp->SetIsSmashable(comp->GetIsSmashable() | (GetVarAs<int32_t>(u"is_smashable") != 0)); comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
} }
} else { } else {
comp->SetHealth(1); comp->SetHealth(1);
@@ -420,6 +428,19 @@ void Entity::Initialize() {
} }
} }
// override the factions if needed.
auto setFaction = GetVarAsString(u"set_faction");
if (!setFaction.empty()) {
// TODO also split on space here however we do not have a general util for splitting on multiple characters yet.
std::vector<std::string> factionsToAdd = GeneralUtils::SplitString(setFaction, ';');
int32_t factionToAdd;
for (const auto faction : factionsToAdd) {
if (GeneralUtils::TryParse(faction, factionToAdd)) {
comp->AddFaction(factionToAdd, true);
}
}
}
m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, comp)); m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, comp));
} }
@@ -446,9 +467,9 @@ void Entity::Initialize() {
m_Components.insert(std::make_pair(eReplicaComponentType::INVENTORY, comp)); m_Components.insert(std::make_pair(eReplicaComponentType::INVENTORY, comp));
} }
// if this component exists, then we initialize it. it's value is always 0 // if this component exists, then we initialize it. it's value is always 0
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH_LUP, -1) != -1) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
auto comp = new RocketLaunchLupComponent(this); auto comp = new MultiZoneEntranceComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::ROCKET_LAUNCH_LUP, comp)); m_Components.insert(std::make_pair(eReplicaComponentType::MULTI_ZONE_ENTRANCE, comp));
} }
/** /**
@@ -463,11 +484,9 @@ void Entity::Initialize() {
if (scriptComponentID > 0 || m_Character) { if (scriptComponentID > 0 || m_Character) {
std::string clientScriptName; std::string clientScriptName;
if (!m_Character) { if (!m_Character) {
auto scriptCompData = scriptCompTable->GetByID(scriptComponentID); CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
if (scriptCompData) { scriptName = scriptCompData.script_name;
scriptName = scriptCompData->script_name; clientScriptName = scriptCompData.client_script_name;
clientScriptName = scriptCompData->client_script_name;
}
} else { } else {
scriptName = ""; scriptName = "";
} }
@@ -506,17 +525,16 @@ void Entity::Initialize() {
// ZoneControl script // ZoneControl script
if (m_TemplateID == 2365) { if (m_TemplateID == 2365) {
auto* zoneTable = CDClientManager::Instance().GetTable<CDZoneTableTable>(); CDZoneTableTable* zoneTable = CDClientManager::Instance().GetTable<CDZoneTableTable>();
const auto zoneID = Game::zoneManager->GetZoneID(); const auto zoneID = Game::zoneManager->GetZoneID();
auto zoneData = zoneTable->Query(zoneID.GetMapID()); const CDZoneTable* zoneData = zoneTable->Query(zoneID.GetMapID());
if (zoneData) { if (zoneData != nullptr) {
int zoneScriptID = zoneData->scriptID; int zoneScriptID = zoneData->scriptID;
auto zoneScriptData = scriptCompTable->GetByID(zoneScriptID); CDScriptComponent zoneScriptData = scriptCompTable->GetByID(zoneScriptID);
if (zoneScriptData) {
ScriptComponent* comp = new ScriptComponent(this, zoneScriptData->script_name, true); ScriptComponent* comp = new ScriptComponent(this, zoneScriptData.script_name, true);
m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPT, comp)); m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPT, comp));
}
} }
} }
@@ -536,17 +554,17 @@ void Entity::Initialize() {
m_Components.insert(std::make_pair(eReplicaComponentType::QUICK_BUILD, comp)); m_Components.insert(std::make_pair(eReplicaComponentType::QUICK_BUILD, comp));
CDRebuildComponentTable* rebCompTable = CDClientManager::Instance().GetTable<CDRebuildComponentTable>(); CDRebuildComponentTable* rebCompTable = CDClientManager::Instance().GetTable<CDRebuildComponentTable>();
auto rebCompData = rebCompTable->Get(rebuildComponentID); std::vector<CDRebuildComponent> rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == rebuildComponentID); });
if (rebCompData) { if (rebCompData.size() > 0) {
comp->SetResetTime(rebCompData->reset_time); comp->SetResetTime(rebCompData[0].reset_time);
comp->SetCompleteTime(rebCompData->complete_time); comp->SetCompleteTime(rebCompData[0].complete_time);
comp->SetTakeImagination(rebCompData->take_imagination); comp->SetTakeImagination(rebCompData[0].take_imagination);
comp->SetInterruptible(rebCompData->interruptible); comp->SetInterruptible(rebCompData[0].interruptible);
comp->SetSelfActivator(rebCompData->self_activator); comp->SetSelfActivator(rebCompData[0].self_activator);
comp->SetActivityId(rebCompData->activityID); comp->SetActivityId(rebCompData[0].activityID);
comp->SetPostImaginationCost(rebCompData->post_imagination_cost); comp->SetPostImaginationCost(rebCompData[0].post_imagination_cost);
comp->SetTimeBeforeSmash(rebCompData->time_before_smash); comp->SetTimeBeforeSmash(rebCompData[0].time_before_smash);
const auto rebuildResetTime = GetVar<float>(u"rebuild_reset_time"); const auto rebuildResetTime = GetVar<float>(u"rebuild_reset_time");
@@ -563,6 +581,7 @@ void Entity::Initialize() {
if (activityID > 0) { if (activityID > 0) {
comp->SetActivityId(activityID); comp->SetActivityId(activityID);
Loot::CacheMatrix(activityID);
} }
const auto compTime = GetVar<float>(u"compTime"); const auto compTime = GetVar<float>(u"compTime");
@@ -581,6 +600,9 @@ void Entity::Initialize() {
if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR) > 0)) { if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR) > 0)) {
VendorComponent* comp = new VendorComponent(this); VendorComponent* comp = new VendorComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::VENDOR, comp)); m_Components.insert(std::make_pair(eReplicaComponentType::VENDOR, comp));
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1) != -1)) {
DonationVendorComponent* comp = new DonationVendorComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::DONATION_VENDOR, comp));
} }
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) {
@@ -697,12 +719,12 @@ void Entity::Initialize() {
const Path* path = Game::zoneManager->GetZone()->GetPath(pathName); const Path* path = Game::zoneManager->GetZone()->GetPath(pathName);
//Check to see if we have an attached path and add the appropiate component to handle it: //Check to see if we have an attached path and add the appropiate component to handle it:
if (path){ if (path) {
// if we have a moving platform path, then we need a moving platform component // if we have a moving platform path, then we need a moving platform component
if (path->pathType == PathType::MovingPlatform) { if (path->pathType == PathType::MovingPlatform) {
MovingPlatformComponent* plat = new MovingPlatformComponent(this, pathName); MovingPlatformComponent* plat = new MovingPlatformComponent(this, pathName);
m_Components.insert(std::make_pair(eReplicaComponentType::MOVING_PLATFORM, plat)); m_Components.insert(std::make_pair(eReplicaComponentType::MOVING_PLATFORM, plat));
// else if we are a movement path // else if we are a movement path
} /*else if (path->pathType == PathType::Movement) { } /*else if (path->pathType == PathType::Movement) {
auto movementAIcomp = GetComponent<MovementAIComponent>(); auto movementAIcomp = GetComponent<MovementAIComponent>();
if (movementAIcomp){ if (movementAIcomp){
@@ -917,10 +939,20 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
outBitStream->Write1(); //ldf data outBitStream->Write1(); //ldf data
RakNet::BitStream settingStream; RakNet::BitStream settingStream;
settingStream.Write<uint32_t>(m_Settings.size()); int32_t numberOfValidKeys = m_Settings.size();
// Writing keys value pairs the client does not expect to receive or interpret will result in undefined behavior,
// so we need to filter out any keys that are not valid and fix the number of valid keys to be correct.
// TODO should make this more efficient so that we dont waste loops evaluating the same condition twice
for (LDFBaseData* data : m_Settings) {
if (!data || data->GetValueType() == eLDFType::LDF_TYPE_UNKNOWN) {
numberOfValidKeys--;
}
}
settingStream.Write<uint32_t>(numberOfValidKeys);
for (LDFBaseData* data : m_Settings) { for (LDFBaseData* data : m_Settings) {
if (data) { if (data && data->GetValueType() != eLDFType::LDF_TYPE_UNKNOWN) {
data->WriteToPacket(&settingStream); data->WriteToPacket(&settingStream);
} }
} }
@@ -939,7 +971,6 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
RakNet::BitStream settingStream; RakNet::BitStream settingStream;
settingStream.Write<uint32_t>(ldfData.size()); settingStream.Write<uint32_t>(ldfData.size());
for (LDFBaseData* data : ldfData) { for (LDFBaseData* data : ldfData) {
if (data) { if (data) {
data->WriteToPacket(&settingStream); data->WriteToPacket(&settingStream);
@@ -1015,57 +1046,60 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
*/ */
bool destroyableSerialized = false; bool destroyableSerialized = false;
bool bIsInitialUpdate = false; bool bIsInitialUpdate = packetType == eReplicaPacketType::CONSTRUCTION;
if (packetType == eReplicaPacketType::CONSTRUCTION) bIsInitialUpdate = true;
unsigned int flags = 0;
PossessableComponent* possessableComponent; PossessableComponent* possessableComponent;
if (TryGetComponent(eReplicaComponentType::POSSESSABLE, possessableComponent)) { if (TryGetComponent(eReplicaComponentType::POSSESSABLE, possessableComponent)) {
possessableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); possessableComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
ModuleAssemblyComponent* moduleAssemblyComponent; ModuleAssemblyComponent* moduleAssemblyComponent;
if (TryGetComponent(eReplicaComponentType::MODULE_ASSEMBLY, moduleAssemblyComponent)) { if (TryGetComponent(eReplicaComponentType::MODULE_ASSEMBLY, moduleAssemblyComponent)) {
moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate, flags); moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
ControllablePhysicsComponent* controllablePhysicsComponent; ControllablePhysicsComponent* controllablePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS, controllablePhysicsComponent)) { if (TryGetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS, controllablePhysicsComponent)) {
controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
SimplePhysicsComponent* simplePhysicsComponent; SimplePhysicsComponent* simplePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::SIMPLE_PHYSICS, simplePhysicsComponent)) { if (TryGetComponent(eReplicaComponentType::SIMPLE_PHYSICS, simplePhysicsComponent)) {
simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
RigidbodyPhantomPhysicsComponent* rigidbodyPhantomPhysics; RigidbodyPhantomPhysicsComponent* rigidbodyPhantomPhysics;
if (TryGetComponent(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, rigidbodyPhantomPhysics)) { if (TryGetComponent(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, rigidbodyPhantomPhysics)) {
rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate, flags); rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate);
} }
VehiclePhysicsComponent* vehiclePhysicsComponent; VehiclePhysicsComponent* vehiclePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)) { if (TryGetComponent(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)) {
vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
PhantomPhysicsComponent* phantomPhysicsComponent; PhantomPhysicsComponent* phantomPhysicsComponent;
if (TryGetComponent(eReplicaComponentType::PHANTOM_PHYSICS, phantomPhysicsComponent)) { if (TryGetComponent(eReplicaComponentType::PHANTOM_PHYSICS, phantomPhysicsComponent)) {
phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
SoundTriggerComponent* soundTriggerComponent; SoundTriggerComponent* soundTriggerComponent;
if (TryGetComponent(eReplicaComponentType::SOUND_TRIGGER, soundTriggerComponent)) { if (TryGetComponent(eReplicaComponentType::SOUND_TRIGGER, soundTriggerComponent)) {
soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate, flags); soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate);
}
RacingSoundTriggerComponent* racingSoundTriggerComponent;
if (TryGetComponent(eReplicaComponentType::RACING_SOUND_TRIGGER, racingSoundTriggerComponent)) {
racingSoundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
BuffComponent* buffComponent; BuffComponent* buffComponent;
if (TryGetComponent(eReplicaComponentType::BUFF, buffComponent)) { if (TryGetComponent(eReplicaComponentType::BUFF, buffComponent)) {
buffComponent->Serialize(outBitStream, bIsInitialUpdate, flags); buffComponent->Serialize(outBitStream, bIsInitialUpdate);
DestroyableComponent* destroyableComponent; DestroyableComponent* destroyableComponent;
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) { if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
destroyableSerialized = true; destroyableSerialized = true;
} }
@@ -1073,7 +1107,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
if (HasComponent(eReplicaComponentType::COLLECTIBLE)) { if (HasComponent(eReplicaComponentType::COLLECTIBLE)) {
DestroyableComponent* destroyableComponent; DestroyableComponent* destroyableComponent;
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
destroyableSerialized = true; destroyableSerialized = true;
outBitStream->Write(m_CollectibleID); // Collectable component outBitStream->Write(m_CollectibleID); // Collectable component
@@ -1081,7 +1115,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
PetComponent* petComponent; PetComponent* petComponent;
if (TryGetComponent(eReplicaComponentType::PET, petComponent)) { if (TryGetComponent(eReplicaComponentType::PET, petComponent)) {
petComponent->Serialize(outBitStream, bIsInitialUpdate, flags); petComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
CharacterComponent* characterComponent; CharacterComponent* characterComponent;
@@ -1089,7 +1123,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
PossessorComponent* possessorComponent; PossessorComponent* possessorComponent;
if (TryGetComponent(eReplicaComponentType::POSSESSOR, possessorComponent)) { if (TryGetComponent(eReplicaComponentType::POSSESSOR, possessorComponent)) {
possessorComponent->Serialize(outBitStream, bIsInitialUpdate, flags); possessorComponent->Serialize(outBitStream, bIsInitialUpdate);
} else { } else {
// Should never happen, but just to be safe // Should never happen, but just to be safe
outBitStream->Write0(); outBitStream->Write0();
@@ -1097,7 +1131,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
LevelProgressionComponent* levelProgressionComponent; LevelProgressionComponent* levelProgressionComponent;
if (TryGetComponent(eReplicaComponentType::LEVEL_PROGRESSION, levelProgressionComponent)) { if (TryGetComponent(eReplicaComponentType::LEVEL_PROGRESSION, levelProgressionComponent)) {
levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate, flags); levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate);
} else { } else {
// Should never happen, but just to be safe // Should never happen, but just to be safe
outBitStream->Write0(); outBitStream->Write0();
@@ -1105,13 +1139,13 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
PlayerForcedMovementComponent* playerForcedMovementComponent; PlayerForcedMovementComponent* playerForcedMovementComponent;
if (TryGetComponent(eReplicaComponentType::PLAYER_FORCED_MOVEMENT, playerForcedMovementComponent)) { if (TryGetComponent(eReplicaComponentType::PLAYER_FORCED_MOVEMENT, playerForcedMovementComponent)) {
playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate, flags); playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate);
} else { } else {
// Should never happen, but just to be safe // Should never happen, but just to be safe
outBitStream->Write0(); outBitStream->Write0();
} }
characterComponent->Serialize(outBitStream, bIsInitialUpdate, flags); characterComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
if (HasComponent(eReplicaComponentType::ITEM)) { if (HasComponent(eReplicaComponentType::ITEM)) {
@@ -1120,88 +1154,93 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
InventoryComponent* inventoryComponent; InventoryComponent* inventoryComponent;
if (TryGetComponent(eReplicaComponentType::INVENTORY, inventoryComponent)) { if (TryGetComponent(eReplicaComponentType::INVENTORY, inventoryComponent)) {
inventoryComponent->Serialize(outBitStream, bIsInitialUpdate, flags); inventoryComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
ScriptComponent* scriptComponent; ScriptComponent* scriptComponent;
if (TryGetComponent(eReplicaComponentType::SCRIPT, scriptComponent)) { if (TryGetComponent(eReplicaComponentType::SCRIPT, scriptComponent)) {
scriptComponent->Serialize(outBitStream, bIsInitialUpdate, flags); scriptComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
SkillComponent* skillComponent; SkillComponent* skillComponent;
if (TryGetComponent(eReplicaComponentType::SKILL, skillComponent)) { if (TryGetComponent(eReplicaComponentType::SKILL, skillComponent)) {
skillComponent->Serialize(outBitStream, bIsInitialUpdate, flags); skillComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
BaseCombatAIComponent* baseCombatAiComponent; BaseCombatAIComponent* baseCombatAiComponent;
if (TryGetComponent(eReplicaComponentType::BASE_COMBAT_AI, baseCombatAiComponent)) { if (TryGetComponent(eReplicaComponentType::BASE_COMBAT_AI, baseCombatAiComponent)) {
baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate, flags); baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
RebuildComponent* rebuildComponent; RebuildComponent* rebuildComponent;
if (TryGetComponent(eReplicaComponentType::QUICK_BUILD, rebuildComponent)) { if (TryGetComponent(eReplicaComponentType::QUICK_BUILD, rebuildComponent)) {
DestroyableComponent* destroyableComponent; DestroyableComponent* destroyableComponent;
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
destroyableSerialized = true; destroyableSerialized = true;
rebuildComponent->Serialize(outBitStream, bIsInitialUpdate, flags); rebuildComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
MovingPlatformComponent* movingPlatformComponent; MovingPlatformComponent* movingPlatformComponent;
if (TryGetComponent(eReplicaComponentType::MOVING_PLATFORM, movingPlatformComponent)) { if (TryGetComponent(eReplicaComponentType::MOVING_PLATFORM, movingPlatformComponent)) {
movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate, flags); movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
SwitchComponent* switchComponent; SwitchComponent* switchComponent;
if (TryGetComponent(eReplicaComponentType::SWITCH, switchComponent)) { if (TryGetComponent(eReplicaComponentType::SWITCH, switchComponent)) {
switchComponent->Serialize(outBitStream, bIsInitialUpdate, flags); switchComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
VendorComponent* vendorComponent; VendorComponent* vendorComponent;
if (TryGetComponent(eReplicaComponentType::VENDOR, vendorComponent)) { if (TryGetComponent(eReplicaComponentType::VENDOR, vendorComponent)) {
vendorComponent->Serialize(outBitStream, bIsInitialUpdate, flags); vendorComponent->Serialize(outBitStream, bIsInitialUpdate);
}
DonationVendorComponent* donationVendorComponent;
if (TryGetComponent(eReplicaComponentType::DONATION_VENDOR, donationVendorComponent)) {
donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
BouncerComponent* bouncerComponent; BouncerComponent* bouncerComponent;
if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) { if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) {
bouncerComponent->Serialize(outBitStream, bIsInitialUpdate, flags); bouncerComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
ScriptedActivityComponent* scriptedActivityComponent; ScriptedActivityComponent* scriptedActivityComponent;
if (TryGetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY, scriptedActivityComponent)) { if (TryGetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY, scriptedActivityComponent)) {
scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate, flags); scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
ShootingGalleryComponent* shootingGalleryComponent; ShootingGalleryComponent* shootingGalleryComponent;
if (TryGetComponent(eReplicaComponentType::SHOOTING_GALLERY, shootingGalleryComponent)) { if (TryGetComponent(eReplicaComponentType::SHOOTING_GALLERY, shootingGalleryComponent)) {
shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate, flags); shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
RacingControlComponent* racingControlComponent; RacingControlComponent* racingControlComponent;
if (TryGetComponent(eReplicaComponentType::RACING_CONTROL, racingControlComponent)) { if (TryGetComponent(eReplicaComponentType::RACING_CONTROL, racingControlComponent)) {
racingControlComponent->Serialize(outBitStream, bIsInitialUpdate, flags); racingControlComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
LUPExhibitComponent* lupExhibitComponent; LUPExhibitComponent* lupExhibitComponent;
if (TryGetComponent(eReplicaComponentType::LUP_EXHIBIT, lupExhibitComponent)) { if (TryGetComponent(eReplicaComponentType::LUP_EXHIBIT, lupExhibitComponent)) {
lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate, flags); lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
ModelComponent* modelComponent; ModelComponent* modelComponent;
if (TryGetComponent(eReplicaComponentType::MODEL, modelComponent)) { if (TryGetComponent(eReplicaComponentType::MODEL, modelComponent)) {
modelComponent->Serialize(outBitStream, bIsInitialUpdate, flags); modelComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
RenderComponent* renderComponent; RenderComponent* renderComponent;
if (TryGetComponent(eReplicaComponentType::RENDER, renderComponent)) { if (TryGetComponent(eReplicaComponentType::RENDER, renderComponent)) {
renderComponent->Serialize(outBitStream, bIsInitialUpdate, flags); renderComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
if (modelComponent) { if (modelComponent || !destroyableSerialized) {
DestroyableComponent* destroyableComponent; DestroyableComponent* destroyableComponent;
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
destroyableSerialized = true; destroyableSerialized = true;
} }
} }
@@ -1216,10 +1255,6 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
outBitStream->Write0(); outBitStream->Write0();
} }
void Entity::ResetFlags() {
// Unused
}
void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) { void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) {
//This function should only ever be called from within Character, meaning doc should always exist when this is called. //This function should only ever be called from within Character, meaning doc should always exist when this is called.
//Naturally, we don't include any non-player components in this update function. //Naturally, we don't include any non-player components in this update function.
@@ -1541,7 +1576,17 @@ void Entity::Kill(Entity* murderer) {
} }
if (!IsPlayer()) { if (!IsPlayer()) {
Game::entityManager->DestroyEntity(this); auto* destroyableComponent = GetComponent<DestroyableComponent>();
bool waitForDeathAnimation = false;
if (destroyableComponent) {
waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0;
}
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
constexpr float DelayDeathTime = 12.0f;
if (waitForDeathAnimation) AddCallbackTimer(DelayDeathTime, [this]() { Game::entityManager->DestroyEntity(this); });
else Game::entityManager->DestroyEntity(this);
} }
const auto& grpNameQBShowBricks = GetVar<std::string>(u"grpNameQBShowBricks"); const auto& grpNameQBShowBricks = GetVar<std::string>(u"grpNameQBShowBricks");
@@ -1631,9 +1676,9 @@ void Entity::PickupItem(const LWOOBJID& objectID) {
std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); }); std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); });
for (CDObjectSkills skill : skills) { for (CDObjectSkills skill : skills) {
CDSkillBehaviorTable* skillBehTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>(); CDSkillBehaviorTable* skillBehTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
auto behaviorData = skillBehTable->GetSkillByID(skill.skillID); CDSkillBehavior behaviorData = skillBehTable->GetSkillByID(skill.skillID);
if (!behaviorData) continue;
SkillComponent::HandleUnmanaged(behaviorData->behaviorID, GetObjectID()); SkillComponent::HandleUnmanaged(behaviorData.behaviorID, GetObjectID());
auto* missionComponent = GetComponent<MissionComponent>(); auto* missionComponent = GetComponent<MissionComponent>();

View File

@@ -85,6 +85,7 @@ public:
bool GetPlayerReadyForUpdates() const { return m_PlayerIsReadyForUpdates; } bool GetPlayerReadyForUpdates() const { return m_PlayerIsReadyForUpdates; }
bool GetIsGhostingCandidate() const; bool GetIsGhostingCandidate() const;
void SetIsGhostingCandidate(bool value) { m_IsGhostingCandidate = value; };
int8_t GetObservers() const; int8_t GetObservers() const;
@@ -173,7 +174,6 @@ public:
void WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType); void WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
void WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType); void WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
void ResetFlags();
void UpdateXMLDoc(tinyxml2::XMLDocument* doc); void UpdateXMLDoc(tinyxml2::XMLDocument* doc);
void Update(float deltaTime); void Update(float deltaTime);

View File

@@ -298,6 +298,16 @@ std::vector<Entity*> EntityManager::GetEntitiesByLOT(const LOT& lot) const {
return entities; return entities;
} }
std::vector<Entity*> EntityManager::GetEntitiesByProximity(NiPoint3 reference, float radius) const{
std::vector<Entity*> entities = {};
if (radius > 1000.0f) return entities;
for (const auto& entity : m_Entities) {
if (NiPoint3::Distance(reference, entity.second->GetPosition()) <= radius) entities.push_back(entity.second);
}
return entities;
}
Entity* EntityManager::GetZoneControlEntity() const { Entity* EntityManager::GetZoneControlEntity() const {
return m_ZoneControlEntity; return m_ZoneControlEntity;
} }
@@ -583,12 +593,6 @@ bool EntityManager::GetGhostingEnabled() const {
return m_GhostingEnabled; return m_GhostingEnabled;
} }
void EntityManager::ResetFlags() {
for (const auto& e : m_Entities) {
e.second->ResetFlags();
}
}
void EntityManager::ScheduleForKill(Entity* entity) { void EntityManager::ScheduleForKill(Entity* entity) {
// Deactivate switches if they die // Deactivate switches if they die
if (!entity) if (!entity)

View File

@@ -28,6 +28,7 @@ public:
std::vector<Entity*> GetEntitiesInGroup(const std::string& group); std::vector<Entity*> GetEntitiesInGroup(const std::string& group);
std::vector<Entity*> GetEntitiesByComponent(eReplicaComponentType componentType) const; std::vector<Entity*> GetEntitiesByComponent(eReplicaComponentType componentType) const;
std::vector<Entity*> GetEntitiesByLOT(const LOT& lot) const; std::vector<Entity*> GetEntitiesByLOT(const LOT& lot) const;
std::vector<Entity*> GetEntitiesByProximity(NiPoint3 reference, float radius) const;
Entity* GetZoneControlEntity() const; Entity* GetZoneControlEntity() const;
// Get spawn point entity by spawn name // Get spawn point entity by spawn name
@@ -59,8 +60,6 @@ public:
Entity* GetGhostCandidate(int32_t id); Entity* GetGhostCandidate(int32_t id);
bool GetGhostingEnabled() const; bool GetGhostingEnabled() const;
void ResetFlags();
void ScheduleForKill(Entity* entity); void ScheduleForKill(Entity* entity);
void ScheduleForDeletion(LWOOBJID entity); void ScheduleForDeletion(LWOOBJID entity);

View File

@@ -131,8 +131,8 @@ void Leaderboard::QueryToLdf(std::unique_ptr<sql::ResultSet>& rows) {
// Time:1 // Time:1
break; break;
case Type::Donations: case Type::Donations:
entry.push_back(new LDFData<int32_t>(u"Points", rows->getInt("primaryScore"))); entry.push_back(new LDFData<int32_t>(u"Score", rows->getInt("primaryScore")));
// Score:1 // Score:1
break; break;
case Type::None: case Type::None:
// This type is included here simply to resolve a compiler warning on mac about unused enum types // This type is included here simply to resolve a compiler warning on mac about unused enum types
@@ -170,32 +170,32 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r
resultEnd++; resultEnd++;
// We need everything except 1 column so i'm selecting * from leaderboard // We need everything except 1 column so i'm selecting * from leaderboard
const std::string queryBase = const std::string queryBase =
R"QUERY( R"QUERY(
WITH leaderboardsRanked AS ( WITH leaderboardsRanked AS (
SELECT leaderboard.*, charinfo.name, SELECT leaderboard.*, charinfo.name,
RANK() OVER RANK() OVER
( (
ORDER BY %s, UNIX_TIMESTAMP(last_played) ASC, id DESC ORDER BY %s, UNIX_TIMESTAMP(last_played) ASC, id DESC
) AS ranking ) AS ranking
FROM leaderboard JOIN charinfo on charinfo.id = leaderboard.character_id FROM leaderboard JOIN charinfo on charinfo.id = leaderboard.character_id
WHERE game_id = ? %s WHERE game_id = ? %s
), ),
myStanding AS ( myStanding AS (
SELECT SELECT
ranking as myRank ranking as myRank
FROM leaderboardsRanked FROM leaderboardsRanked
WHERE id = ? WHERE id = ?
), ),
lowestRanking AS ( lowestRanking AS (
SELECT MAX(ranking) AS lowestRank SELECT MAX(ranking) AS lowestRank
FROM leaderboardsRanked FROM leaderboardsRanked
) )
SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
WHERE leaderboardsRanked.ranking WHERE leaderboardsRanked.ranking
BETWEEN BETWEEN
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9) LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9)
AND AND
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank) LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
ORDER BY ranking ASC; ORDER BY ranking ASC;
)QUERY"; )QUERY";
@@ -277,15 +277,15 @@ std::string FormatInsert(const Leaderboard::Type& type, const Score& score, cons
if (useUpdate) { if (useUpdate) {
insertStatement = insertStatement =
R"QUERY( R"QUERY(
UPDATE leaderboard UPDATE leaderboard
SET primaryScore = %f, secondaryScore = %f, tertiaryScore = %f, SET primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?; timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;
)QUERY"; )QUERY";
} else { } else {
insertStatement = insertStatement =
R"QUERY( R"QUERY(
INSERT leaderboard SET INSERT leaderboard SET
primaryScore = %f, secondaryScore = %f, tertiaryScore = %f, primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
character_id = ?, game_id = ?; character_id = ?, game_id = ?;
)QUERY"; )QUERY";
} }
@@ -300,9 +300,8 @@ std::string FormatInsert(const Leaderboard::Type& type, const Score& score, cons
void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) { void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) {
const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId); const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId);
auto* lookup = "SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;";
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt(lookup)); std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;"));
query->setInt(1, playerID); query->setInt(1, playerID);
query->setInt(2, activityId); query->setInt(2, activityId);
std::unique_ptr<sql::ResultSet> myScoreResult(query->executeQuery()); std::unique_ptr<sql::ResultSet> myScoreResult(query->executeQuery());
@@ -337,6 +336,7 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi
case Leaderboard::Type::UnusedLeaderboard4: case Leaderboard::Type::UnusedLeaderboard4:
case Leaderboard::Type::Donations: { case Leaderboard::Type::Donations: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore")); oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
newScore.SetPrimaryScore(oldScore.GetPrimaryScore() + newScore.GetPrimaryScore());
break; break;
} }
case Leaderboard::Type::Racing: { case Leaderboard::Type::Racing: {
@@ -382,7 +382,7 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi
saveStatement->setInt(1, playerID); saveStatement->setInt(1, playerID);
saveStatement->setInt(2, activityId); saveStatement->setInt(2, activityId);
saveStatement->execute(); saveStatement->execute();
// track wins separately // track wins separately
if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) { if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) {
std::unique_ptr<sql::PreparedStatement> winUpdate(Database::CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;")); std::unique_ptr<sql::PreparedStatement> winUpdate(Database::CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;"));

View File

@@ -1,8 +1,14 @@
#include "TeamManager.h" #include "TeamManager.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "Game.h"
#include "dConfig.h"
TeamManager* TeamManager::m_Address = nullptr; //For singleton method TeamManager* TeamManager::m_Address = nullptr; //For singleton method
Team::Team() {
lootOption = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
}
TeamManager::TeamManager() { TeamManager::TeamManager() {
} }

View File

@@ -2,16 +2,15 @@
#include "Entity.h" #include "Entity.h"
struct Team struct Team {
{ Team();
LWOOBJID teamID = LWOOBJID_EMPTY; LWOOBJID teamID = LWOOBJID_EMPTY;
char lootOption = 0; char lootOption = 0;
std::vector<LWOOBJID> members{}; std::vector<LWOOBJID> members{};
char lootRound = 0; char lootRound = 0;
}; };
class TeamManager class TeamManager {
{
public: public:
static TeamManager* Instance() { static TeamManager* Instance() {
if (!m_Address) { if (!m_Address) {

View File

@@ -28,6 +28,8 @@
#include "eRenameResponse.h" #include "eRenameResponse.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h" #include "eChatInternalMessageType.h"
#include "BitStreamUtils.h"
#include "CheatDetection.h"
UserManager* UserManager::m_Address = nullptr; UserManager* UserManager::m_Address = nullptr;
@@ -203,40 +205,38 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
stmt->setUInt(1, u->GetAccountID()); stmt->setUInt(1, u->GetAccountID());
sql::ResultSet* res = stmt->executeQuery(); sql::ResultSet* res = stmt->executeQuery();
if (res->rowsCount() > 0) { std::vector<Character*>& chars = u->GetCharacters();
std::vector<Character*>& chars = u->GetCharacters();
for (size_t i = 0; i < chars.size(); ++i) {
if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save
{
delete chars[i];
continue;
}
auto* skillComponent = chars[i]->GetEntity()->GetComponent<SkillComponent>();
if (skillComponent != nullptr) {
skillComponent->Reset();
}
Game::entityManager->DestroyEntity(chars[i]->GetEntity());
chars[i]->SaveXMLToDatabase();
chars[i]->GetEntity()->SetCharacter(nullptr);
for (size_t i = 0; i < chars.size(); ++i) {
if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save
{
delete chars[i]; delete chars[i];
continue;
} }
chars.clear(); auto* skillComponent = chars[i]->GetEntity()->GetComponent<SkillComponent>();
while (res->next()) { if (skillComponent != nullptr) {
LWOOBJID objID = res->getUInt64(1); skillComponent->Reset();
Character* character = new Character(uint32_t(objID), u);
character->SetIsNewLogin();
chars.push_back(character);
} }
Game::entityManager->DestroyEntity(chars[i]->GetEntity());
chars[i]->SaveXMLToDatabase();
chars[i]->GetEntity()->SetCharacter(nullptr);
delete chars[i];
}
chars.clear();
while (res->next()) {
LWOOBJID objID = res->getUInt64(1);
Character* character = new Character(uint32_t(objID), u);
character->SetIsNewLogin();
chars.push_back(character);
} }
delete res; delete res;
@@ -251,21 +251,21 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
std::string name = PacketUtils::ReadString(8, packet, true); std::string name = PacketUtils::ReadString(8, packet, true);
uint32_t firstNameIndex = PacketUtils::ReadPacketU32(74, packet); uint32_t firstNameIndex = PacketUtils::ReadU32(74, packet);
uint32_t middleNameIndex = PacketUtils::ReadPacketU32(78, packet); uint32_t middleNameIndex = PacketUtils::ReadU32(78, packet);
uint32_t lastNameIndex = PacketUtils::ReadPacketU32(82, packet); uint32_t lastNameIndex = PacketUtils::ReadU32(82, packet);
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex); std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
uint32_t shirtColor = PacketUtils::ReadPacketU32(95, packet); uint32_t shirtColor = PacketUtils::ReadU32(95, packet);
uint32_t shirtStyle = PacketUtils::ReadPacketU32(99, packet); uint32_t shirtStyle = PacketUtils::ReadU32(99, packet);
uint32_t pantsColor = PacketUtils::ReadPacketU32(103, packet); uint32_t pantsColor = PacketUtils::ReadU32(103, packet);
uint32_t hairStyle = PacketUtils::ReadPacketU32(107, packet); uint32_t hairStyle = PacketUtils::ReadU32(107, packet);
uint32_t hairColor = PacketUtils::ReadPacketU32(111, packet); uint32_t hairColor = PacketUtils::ReadU32(111, packet);
uint32_t lh = PacketUtils::ReadPacketU32(115, packet); uint32_t lh = PacketUtils::ReadU32(115, packet);
uint32_t rh = PacketUtils::ReadPacketU32(119, packet); uint32_t rh = PacketUtils::ReadU32(119, packet);
uint32_t eyebrows = PacketUtils::ReadPacketU32(123, packet); uint32_t eyebrows = PacketUtils::ReadU32(123, packet);
uint32_t eyes = PacketUtils::ReadPacketU32(127, packet); uint32_t eyes = PacketUtils::ReadU32(127, packet);
uint32_t mouth = PacketUtils::ReadPacketU32(131, packet); uint32_t mouth = PacketUtils::ReadU32(131, packet);
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle); LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
LOT pantsLOT = FindCharPantsID(pantsColor); LOT pantsLOT = FindCharPantsID(pantsColor);
@@ -387,20 +387,19 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
return; return;
} }
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet); LWOOBJID objectID = PacketUtils::ReadS64(8, packet);
uint32_t charID = static_cast<uint32_t>(objectID); uint32_t charID = static_cast<uint32_t>(objectID);
Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)", objectID, charID); Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)", objectID, charID);
//Check if this user has this character: bool hasCharacter = CheatDetection::VerifyLwoobjidIsSender(
bool hasCharacter = false; objectID,
std::vector<Character*>& characters = u->GetCharacters(); sysAddr,
for (size_t i = 0; i < characters.size(); ++i) { CheckType::User,
if (characters[i]->GetID() == charID) { hasCharacter = true; } "User %i tried to delete a character that it does not own!",
} u->GetAccountID());
if (!hasCharacter) { if (!hasCharacter) {
Game::logger->Log("UserManager", "User %i tried to delete a character that it does not own!", u->GetAccountID());
WorldPackets::SendCharacterDeleteResponse(sysAddr, false); WorldPackets::SendCharacterDeleteResponse(sysAddr, false);
} else { } else {
Game::logger->Log("UserManager", "Deleting character %i", charID); Game::logger->Log("UserManager", "Deleting character %i", charID);
@@ -423,7 +422,7 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
stmt->execute(); stmt->execute();
delete stmt; delete stmt;
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
bitStream.Write(objectID); bitStream.Write(objectID);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} }
@@ -483,7 +482,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
return; return;
} }
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet); LWOOBJID objectID = PacketUtils::ReadS64(8, packet);
GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER); GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER);
GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT); GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT);
@@ -495,16 +494,24 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
Character* character = nullptr; Character* character = nullptr;
//Check if this user has this character: //Check if this user has this character:
bool hasCharacter = false; bool ownsCharacter = CheatDetection::VerifyLwoobjidIsSender(
std::vector<Character*>& characters = u->GetCharacters(); objectID,
for (size_t i = 0; i < characters.size(); ++i) { sysAddr,
if (characters[i]->GetID() == charID) { hasCharacter = true; character = characters[i]; } CheckType::User,
} "User %i tried to rename a character that it does not own!",
u->GetAccountID());
if (!hasCharacter || !character) { std::find_if(u->GetCharacters().begin(), u->GetCharacters().end(), [&](Character* c) {
Game::logger->Log("UserManager", "User %i tried to rename a character that it does not own!", u->GetAccountID()); if (c->GetID() == charID) {
character = c;
return true;
}
return false;
});
if (!ownsCharacter || !character) {
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR); WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
} else if (hasCharacter && character) { } else if (ownsCharacter && character) {
if (newName == character->GetName()) { if (newName == character->GetName()) {
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE); WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE);
return; return;

View File

@@ -20,134 +20,114 @@ void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* b
return; return;
} }
if (targetCount > this->m_maxTargets) { if (this->m_useTargetPosition && branch.target == LWOOBJID_EMPTY) return;
if (targetCount == 0){
PlayFx(u"miss", context->originator);
return; return;
} }
std::vector<LWOOBJID> targets; if (targetCount > this->m_maxTargets) {
Game::logger->Log("AreaOfEffectBehavior", "Serialized size is greater than max targets! Size: %i, Max: %i", targetCount, this->m_maxTargets);
return;
}
auto caster = context->caster;
if (this->m_useTargetAsCaster) context->caster = branch.target;
std::vector<LWOOBJID> targets;
targets.reserve(targetCount); targets.reserve(targetCount);
for (auto i = 0u; i < targetCount; ++i) { for (auto i = 0u; i < targetCount; ++i) {
LWOOBJID target{}; LWOOBJID target{};
if (!bitStream->Read(target)) { if (!bitStream->Read(target)) {
Game::logger->Log("AreaOfEffectBehavior", "failed to read in target %i from bitStream, aborting target Handle!", i); Game::logger->Log("AreaOfEffectBehavior", "failed to read in target %i from bitStream, aborting target Handle!", i);
return;
}; };
targets.push_back(target); targets.push_back(target);
} }
for (auto target : targets) { for (auto target : targets) {
branch.target = target; branch.target = target;
this->m_action->Handle(context, bitStream, branch); this->m_action->Handle(context, bitStream, branch);
} }
context->caster = caster;
PlayFx(u"cast", context->originator);
} }
void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
auto* self = Game::entityManager->GetEntity(context->caster); auto* caster = Game::entityManager->GetEntity(context->caster);
if (self == nullptr) { if (!caster) return;
Game::logger->Log("AreaOfEffectBehavior", "Invalid self for (%llu)!", context->originator);
return; // determine the position we are casting the AOE from
auto reference = branch.isProjectile ? branch.referencePosition : caster->GetPosition();
if (this->m_useTargetPosition) {
if (branch.target == LWOOBJID_EMPTY) return;
auto branchTarget = Game::entityManager->GetEntity(branch.target);
if (branchTarget) reference = branchTarget->GetPosition();
} }
auto reference = branch.isProjectile ? branch.referencePosition : self->GetPosition(); reference += this->m_offset;
std::vector<Entity*> targets; std::vector<Entity*> targets {};
targets = Game::entityManager->GetEntitiesByProximity(reference, this->m_radius);
auto* presetTarget = Game::entityManager->GetEntity(branch.target); context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
if (presetTarget != nullptr) {
if (this->m_radius * this->m_radius >= Vector3::DistanceSquared(reference, presetTarget->GetPosition())) {
targets.push_back(presetTarget);
}
}
int32_t includeFaction = m_includeFaction;
if (self->GetLOT() == 14466) // TODO: Fix edge case
{
includeFaction = 1;
}
// Gets all of the valid targets, passing in if should target enemies and friends
for (auto validTarget : context->GetValidTargets(m_ignoreFaction, includeFaction, m_TargetSelf == 1, m_targetEnemy == 1, m_targetFriend == 1)) {
auto* entity = Game::entityManager->GetEntity(validTarget);
if (entity == nullptr) {
Game::logger->Log("AreaOfEffectBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
continue;
}
if (std::find(targets.begin(), targets.end(), entity) != targets.end()) {
continue;
}
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr) {
continue;
}
if (destroyableComponent->HasFaction(m_ignoreFaction)) {
continue;
}
const auto distance = Vector3::DistanceSquared(reference, entity->GetPosition());
if (this->m_radius * this->m_radius >= distance && (this->m_maxTargets == 0 || targets.size() < this->m_maxTargets)) {
targets.push_back(entity);
}
}
// sort by distance
std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b) { std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b) {
const auto aDistance = Vector3::DistanceSquared(a->GetPosition(), reference); const auto aDistance = NiPoint3::Distance(a->GetPosition(), reference);
const auto bDistance = Vector3::DistanceSquared(b->GetPosition(), reference); const auto bDistance = NiPoint3::Distance(b->GetPosition(), reference);
return aDistance < bDistance;
}
);
return aDistance > bDistance; // resize if we have more than max targets allows
}); if (targets.size() > this->m_maxTargets) targets.resize(this->m_maxTargets);
const uint32_t size = targets.size(); bitStream->Write<uint32_t>(targets.size());
bitStream->Write(size); if (targets.size() == 0) {
PlayFx(u"miss", context->originator);
if (size == 0) {
return; return;
} } else {
context->foundTarget = true;
// write all the targets to the bitstream
for (auto* target : targets) {
bitStream->Write(target->GetObjectID());
}
context->foundTarget = true; // then cast all the actions
for (auto* target : targets) {
for (auto* target : targets) { branch.target = target->GetObjectID();
bitStream->Write(target->GetObjectID()); this->m_action->Calculate(context, bitStream, branch);
}
PlayFx(u"cast", context->originator, target->GetObjectID()); PlayFx(u"cast", context->originator);
}
for (auto* target : targets) {
branch.target = target->GetObjectID();
this->m_action->Calculate(context, bitStream, branch);
} }
} }
void AreaOfEffectBehavior::Load() { void AreaOfEffectBehavior::Load() {
this->m_action = GetAction("action"); this->m_action = GetAction("action"); // required
this->m_radius = GetFloat("radius", 0.0f); // required
this->m_maxTargets = GetInt("max targets", 100);
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
this->m_useTargetPosition = GetBoolean("use_target_position", false);
this->m_useTargetAsCaster = GetBoolean("use_target_as_caster", false);
this->m_offset = NiPoint3(
GetFloat("offset_x", 0.0f),
GetFloat("offset_y", 0.0f),
GetFloat("offset_z", 0.0f)
);
this->m_radius = GetFloat("radius"); // params after this are needed for filter targets
const auto parameters = GetParameterNames();
this->m_maxTargets = GetInt("max targets"); for (const auto& parameter : parameters) {
if (parameter.first.rfind("include_faction", 0) == 0) {
this->m_ignoreFaction = GetInt("ignore_faction"); this->m_includeFactionList.push_front(parameter.second);
} else if (parameter.first.rfind("ignore_faction", 0) == 0) {
this->m_includeFaction = GetInt("include_faction"); this->m_ignoreFactionList.push_front(parameter.second);
}
this->m_TargetSelf = GetInt("target_self"); }
this->m_targetSelf = GetBoolean("target_self", false);
this->m_targetEnemy = GetInt("target_enemy"); this->m_targetEnemy = GetBoolean("target_enemy", false);
this->m_targetFriend = GetBoolean("target_friend", false);
this->m_targetFriend = GetInt("target_friend"); this->m_targetTeam = GetBoolean("target_team", false);
} }

View File

@@ -1,34 +1,26 @@
#pragma once #pragma once
#include "Behavior.h" #include "Behavior.h"
#include <forward_list>
class AreaOfEffectBehavior final : public Behavior class AreaOfEffectBehavior final : public Behavior
{ {
public: public:
Behavior* m_action; explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {}
uint32_t m_maxTargets;
float m_radius;
int32_t m_ignoreFaction;
int32_t m_includeFaction;
int32_t m_TargetSelf;
int32_t m_targetEnemy;
int32_t m_targetFriend;
/*
* Inherited
*/
explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override; void Load() override;
private:
Behavior* m_action;
uint32_t m_maxTargets;
float m_radius;
bool m_useTargetPosition;
bool m_useTargetAsCaster;
NiPoint3 m_offset;
std::forward_list<int32_t> m_ignoreFactionList {};
std::forward_list<int32_t> m_includeFactionList {};
bool m_targetSelf;
bool m_targetEnemy;
bool m_targetFriend;
bool m_targetTeam;
}; };

View File

@@ -175,7 +175,7 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
case BehaviorTemplates::BEHAVIOR_SPEED: case BehaviorTemplates::BEHAVIOR_SPEED:
behavior = new SpeedBehavior(behaviorId); behavior = new SpeedBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION:
behavior = new DarkInspirationBehavior(behaviorId); behavior = new DarkInspirationBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF: case BehaviorTemplates::BEHAVIOR_LOOT_BUFF:

View File

@@ -6,7 +6,7 @@
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "dLogger.h"
#include "dServer.h" #include "dServer.h"
#include "PacketUtils.h" #include "BitStreamUtils.h"
#include <sstream> #include <sstream>
@@ -15,6 +15,7 @@
#include "PhantomPhysicsComponent.h" #include "PhantomPhysicsComponent.h"
#include "RebuildComponent.h" #include "RebuildComponent.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "TeamManager.h"
#include "eConnectionType.h" #include "eConnectionType.h"
BehaviorSyncEntry::BehaviorSyncEntry() { BehaviorSyncEntry::BehaviorSyncEntry() {
@@ -253,7 +254,7 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
// Write message // Write message
RakNet::BitStream message; RakNet::BitStream message;
PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
message.Write(this->originator); message.Write(this->originator);
echo.Serialize(&message); echo.Serialize(&message);
@@ -307,46 +308,123 @@ void BehaviorContext::Reset() {
this->scheduledUpdates.clear(); this->scheduledUpdates.clear();
} }
std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf, bool targetEnemy, bool targetFriend) const { void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_list<int32_t>& ignoreFactionList, std::forward_list<int32_t>& includeFactionList, bool targetSelf, bool targetEnemy, bool targetFriend, bool targetTeam) const {
auto* entity = Game::entityManager->GetEntity(this->caster);
std::vector<LWOOBJID> targets; // if we aren't targeting anything, then clear the targets vector
if (!targetSelf && !targetEnemy && !targetFriend && !targetTeam && ignoreFactionList.empty() && includeFactionList.empty()) {
if (entity == nullptr) { targets.clear();
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator); return;
return targets;
} }
if (!ignoreFaction && !includeFaction) { // if the caster is not there, return empty targets list
for (auto entry : entity->GetTargetsInPhantom()) { auto* caster = Game::entityManager->GetEntity(this->caster);
auto* instance = Game::entityManager->GetEntity(entry); if (!caster) {
Game::logger->LogDebug("BehaviorContext", "Invalid caster for (%llu)!", this->originator);
if (instance == nullptr) { targets.clear();
continue; return;
}
targets.push_back(entry);
}
} }
if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) { auto index = targets.begin();
DestroyableComponent* destroyableComponent; while (index != targets.end()) {
if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) { auto candidate = *index;
return targets;
// make sure we don't have a nullptr
if (!candidate) {
index = targets.erase(index);
continue;
} }
auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS); // handle targeting the caster
for (auto* candidate : entities) { if (candidate == caster){
const auto id = candidate->GetObjectID(); // if we aren't targeting self, erase, otherise increment and continue
if (!targetSelf) index = targets.erase(index);
else index++;
continue;
}
if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction, targetEnemy, targetFriend)) { // make sure that the entity is targetable
targets.push_back(id); if (!CheckTargetingRequirements(candidate)) {
index = targets.erase(index);
continue;
}
// get factions to check against
// CheckTargetingRequirements checks for a destroyable component
// but we check again because bounds check are necessary
auto candidateDestroyableComponent = candidate->GetComponent<DestroyableComponent>();
if (!candidateDestroyableComponent) {
index = targets.erase(index);
continue;
}
// if they are dead, then earse and continue
if (candidateDestroyableComponent->GetIsDead()){
index = targets.erase(index);
continue;
}
// if their faction is explicitly included, increment and continue
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
if (CheckFactionList(includeFactionList, candidateFactions)){
index++;
continue;
}
// check if they are a team member
if (targetTeam){
auto* team = TeamManager::Instance()->GetTeam(this->caster);
if (team){
// if we find a team member keep it and continue to skip enemy checks
if(std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()){
index++;
continue;
}
} }
} }
}
return targets; // if the caster doesn't have a destroyable component, return an empty targets list
auto* casterDestroyableComponent = caster->GetComponent<DestroyableComponent>();
if (!casterDestroyableComponent) {
targets.clear();
return;
}
// if we arent targeting a friend, and they are a friend OR
// if we are not targeting enemies and they are an enemy OR.
// if we are ignoring their faction is explicitly ignored
// erase and continue
auto isEnemy = casterDestroyableComponent->IsEnemy(candidate);
if ((!targetFriend && !isEnemy) ||
(!targetEnemy && isEnemy) ||
CheckFactionList(ignoreFactionList, candidateFactions)) {
index = targets.erase(index);
continue;
}
index++;
}
return;
}
// some basic checks as well as the check that matters for this: if the quickbuild is complete
bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const {
// if the target is a nullptr, then it's not valid
if (!target) return false;
// ignore quickbuilds that aren't completed
auto* targetQuickbuildComponent = target->GetComponent<RebuildComponent>();
if (targetQuickbuildComponent && targetQuickbuildComponent->GetState() != eRebuildState::COMPLETED) return false;
return true;
}
// returns true if any of the object factions are in the faction list
bool BehaviorContext::CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const {
if (factionList.empty() || objectsFactions.empty()) return false;
for (auto faction : factionList){
if(std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
}
return false;
} }

View File

@@ -6,6 +6,7 @@
#include "GameMessages.h" #include "GameMessages.h"
#include <vector> #include <vector>
#include <forward_list>
class Behavior; class Behavior;
@@ -106,7 +107,11 @@ struct BehaviorContext
void Reset(); void Reset();
std::vector<LWOOBJID> GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false) const; void FilterTargets(std::vector<Entity*>& targetsReference, std::forward_list<int32_t>& ignoreFaction, std::forward_list<int32_t>& includeFaction, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false, const bool targetTeam = false) const;
bool CheckTargetingRequirements(const Entity* target) const;
bool CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const;
explicit BehaviorContext(LWOOBJID originator, bool calculation = false); explicit BehaviorContext(LWOOBJID originator, bool calculation = false);

View File

@@ -2,9 +2,9 @@
#ifndef BEHAVIORSLOT_H #ifndef BEHAVIORSLOT_H
#define BEHAVIORSLOT_H #define BEHAVIORSLOT_H
#include <cstdint>
enum class BehaviorSlot enum class BehaviorSlot : int32_t {
{
Invalid = -1, Invalid = -1,
Primary, Primary,
Offhand, Offhand,

View File

@@ -42,8 +42,7 @@ void OverTimeBehavior::Load() {
// Since m_Action is a skillID and not a behavior, get is correlated behaviorID. // Since m_Action is a skillID and not a behavior, get is correlated behaviorID.
CDSkillBehaviorTable* skillTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>(); CDSkillBehaviorTable* skillTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
auto skillData = skillTable->GetSkillByID(m_Action); m_ActionBehaviorId = skillTable->GetSkillByID(m_Action).behaviorID;
if (skillData) m_ActionBehaviorId = skillData->behaviorID;
m_Delay = GetFloat("delay"); m_Delay = GetFloat("delay");
m_NumIntervals = GetInt("num_intervals"); m_NumIntervals = GetInt("num_intervals");

View File

@@ -12,16 +12,24 @@
#include <vector> #include <vector>
void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
if (this->m_targetEnemy && this->m_usePickedTarget && branch.target > 0) { std::vector<Entity*> targets = {};
this->m_action->Handle(context, bitStream, branch);
return; if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
auto target = Game::entityManager->GetEntity(branch.target);
if (!target) Game::logger->Log("TacArcBehavior", "target %llu is null", branch.target);
else {
targets.push_back(target);
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
if (!targets.empty()) {
this->m_action->Handle(context, bitStream, branch);
return;
}
}
} }
bool hit = false; bool hasTargets = false;
if (!bitStream->Read(hasTargets)) {
if (!bitStream->Read(hit)) { Game::logger->Log("TacArcBehavior", "Unable to read hasTargets from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
Game::logger->Log("TacArcBehavior", "Unable to read hit from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
return; return;
}; };
@@ -35,26 +43,23 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
if (blocked) { if (blocked) {
this->m_blockedAction->Handle(context, bitStream, branch); this->m_blockedAction->Handle(context, bitStream, branch);
return; return;
} }
} }
if (hit) { if (hasTargets) {
uint32_t count = 0; uint32_t count = 0;
if (!bitStream->Read(count)) { if (!bitStream->Read(count)) {
Game::logger->Log("TacArcBehavior", "Unable to read count from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); Game::logger->Log("TacArcBehavior", "Unable to read count from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
return; return;
}; };
if (count > m_maxTargets && m_maxTargets > 0) { if (count > m_maxTargets) {
count = m_maxTargets; Game::logger->Log("TacArcBehavior", "Bitstream has too many targets Max:%i Recv:%i", this->m_maxTargets, count);
return;
} }
std::vector<LWOOBJID> targets; for (auto i = 0u; i < count; i++) {
for (auto i = 0u; i < count; ++i) {
LWOOBJID id{}; LWOOBJID id{};
if (!bitStream->Read(id)) { if (!bitStream->Read(id)) {
@@ -62,17 +67,19 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
return; return;
}; };
targets.push_back(id); if (id != LWOOBJID_EMPTY) {
auto* canidate = Game::entityManager->GetEntity(id);
if (canidate) targets.push_back(canidate);
} else {
Game::logger->Log("TacArcBehavior", "Bitstream has LWOOBJID_EMPTY as a target!");
}
} }
for (auto target : targets) { for (auto target : targets) {
branch.target = target; branch.target = target->GetObjectID();
this->m_action->Handle(context, bitStream, branch); this->m_action->Handle(context, bitStream, branch);
} }
} else { } else this->m_missAction->Handle(context, bitStream, branch);
this->m_missAction->Handle(context, bitStream, branch);
}
} }
void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
@@ -82,23 +89,15 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
return; return;
} }
const auto* destroyableComponent = self->GetComponent<DestroyableComponent>(); std::vector<Entity*> targets = {};
if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
if ((this->m_usePickedTarget || context->clientInitalized) && branch.target > 0) { auto target = Game::entityManager->GetEntity(branch.target);
const auto* target = Game::entityManager->GetEntity(branch.target); targets.push_back(target);
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
if (target == nullptr) { if (!targets.empty()) {
this->m_action->Handle(context, bitStream, branch);
return; return;
} }
// If the game is specific about who to target, check that
if (destroyableComponent == nullptr || ((!m_targetFriend && !m_targetEnemy
|| m_targetFriend && destroyableComponent->IsFriend(target)
|| m_targetEnemy && destroyableComponent->IsEnemy(target)))) {
this->m_action->Calculate(context, bitStream, branch);
}
return;
} }
auto* combatAi = self->GetComponent<BaseCombatAIComponent>(); auto* combatAi = self->GetComponent<BaseCombatAIComponent>();
@@ -107,50 +106,25 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
auto reference = self->GetPosition(); //+ m_offset; auto reference = self->GetPosition(); //+ m_offset;
std::vector<Entity*> targets; targets.clear();
std::vector<LWOOBJID> validTargets; std::vector<Entity*> validTargets = Game::entityManager->GetEntitiesByProximity(reference, this->m_maxRange);
if (combatAi != nullptr) { // filter all valid targets, based on whether we target enemies or friends
if (combatAi->GetTarget() != LWOOBJID_EMPTY) { context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
validTargets.push_back(combatAi->GetTarget());
}
}
// Find all valid targets, based on whether we target enemies or friends
for (const auto& contextTarget : context->GetValidTargets()) {
if (destroyableComponent != nullptr) {
const auto* targetEntity = Game::entityManager->GetEntity(contextTarget);
if (m_targetEnemy && destroyableComponent->IsEnemy(targetEntity)
|| m_targetFriend && destroyableComponent->IsFriend(targetEntity)) {
validTargets.push_back(contextTarget);
}
} else {
validTargets.push_back(contextTarget);
}
}
for (auto validTarget : validTargets) { for (auto validTarget : validTargets) {
if (targets.size() >= this->m_maxTargets) { if (targets.size() >= this->m_maxTargets) {
break; break;
} }
auto* entity = Game::entityManager->GetEntity(validTarget); if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) {
if (entity == nullptr) {
Game::logger->Log("TacArcBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
continue; continue;
} }
if (std::find(targets.begin(), targets.end(), entity) != targets.end()) { if (validTarget->GetIsDead()) continue;
continue;
}
if (entity->GetIsDead()) continue; const auto otherPosition = validTarget->GetPosition();
const auto otherPosition = entity->GetPosition();
const auto heightDifference = std::abs(otherPosition.y - casterPosition.y); const auto heightDifference = std::abs(otherPosition.y - casterPosition.y);
@@ -180,8 +154,8 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180); const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180);
if (distance >= this->m_minDistance && this->m_maxDistance >= distance && degreeAngle <= 2 * this->m_angle) { if (distance >= this->m_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) {
targets.push_back(entity); targets.push_back(validTarget);
} }
} }
@@ -228,43 +202,48 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
} }
void TacArcBehavior::Load() { void TacArcBehavior::Load() {
this->m_usePickedTarget = GetBoolean("use_picked_target"); this->m_maxRange = GetFloat("max range");
this->m_height = GetFloat("height", 2.2f);
this->m_distanceWeight = GetFloat("distance_weight", 0.0f);
this->m_angleWeight = GetFloat("angle_weight", 0.0f);
this->m_angle = GetFloat("angle", 45.0f);
this->m_minRange = GetFloat("min range", 0.0f);
this->m_offset = NiPoint3(
GetFloat("offset_x", 0.0f),
GetFloat("offset_y", 0.0f),
GetFloat("offset_z", 0.0f)
);
this->m_method = GetInt("method", 1);
this->m_upperBound = GetFloat("upper_bound", 4.4f);
this->m_lowerBound = GetFloat("lower_bound", 0.4f);
this->m_usePickedTarget = GetBoolean("use_picked_target", false);
this->m_useTargetPostion = GetBoolean("use_target_position", false);
this->m_checkEnv = GetBoolean("check_env", false);
this->m_useAttackPriority = GetBoolean("use_attack_priority", false);
this->m_action = GetAction("action"); this->m_action = GetAction("action");
this->m_missAction = GetAction("miss action"); this->m_missAction = GetAction("miss action");
this->m_checkEnv = GetBoolean("check_env");
this->m_blockedAction = GetAction("blocked action"); this->m_blockedAction = GetAction("blocked action");
this->m_minDistance = GetFloat("min range"); this->m_maxTargets = GetInt("max targets", 100);
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
this->m_maxDistance = GetFloat("max range"); this->m_farHeight = GetFloat("far_height", 5.0f);
this->m_farWidth = GetFloat("far_width", 5.0f);
this->m_nearHeight = GetFloat("near_height", 5.0f);
this->m_nearWidth = GetFloat("near_width", 5.0f);
this->m_maxTargets = GetInt("max targets"); // params after this are needed for filter targets
const auto parameters = GetParameterNames();
this->m_targetEnemy = GetBoolean("target_enemy"); for (const auto& parameter : parameters) {
if (parameter.first.rfind("include_faction", 0) == 0) {
this->m_targetFriend = GetBoolean("target_friend"); this->m_includeFactionList.push_front(parameter.second);
} else if (parameter.first.rfind("ignore_faction", 0) == 0) {
this->m_targetTeam = GetBoolean("target_team"); this->m_ignoreFactionList.push_front(parameter.second);
}
this->m_angle = GetFloat("angle"); }
this->m_targetSelf = GetBoolean("target_caster", false);
this->m_upperBound = GetFloat("upper_bound"); this->m_targetEnemy = GetBoolean("target_enemy", false);
this->m_targetFriend = GetBoolean("target_friend", false);
this->m_lowerBound = GetFloat("lower_bound"); this->m_targetTeam = GetBoolean("target_team", false);
this->m_farHeight = GetFloat("far_height");
this->m_farWidth = GetFloat("far_width");
this->m_method = GetInt("method");
this->m_offset = {
GetFloat("offset_x"),
GetFloat("offset_y"),
GetFloat("offset_z")
};
} }

View File

@@ -2,56 +2,42 @@
#include "Behavior.h" #include "Behavior.h"
#include "dCommonVars.h" #include "dCommonVars.h"
#include "NiPoint3.h" #include "NiPoint3.h"
#include <forward_list>
class TacArcBehavior final : public Behavior class TacArcBehavior final : public Behavior {
{
public: public:
bool m_usePickedTarget; explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id) {}
Behavior* m_action;
bool m_checkEnv;
Behavior* m_missAction;
Behavior* m_blockedAction;
float m_minDistance;
float m_maxDistance;
uint32_t m_maxTargets;
bool m_targetEnemy;
bool m_targetFriend;
bool m_targetTeam;
float m_angle;
float m_upperBound;
float m_lowerBound;
float m_farHeight;
float m_farWidth;
uint32_t m_method;
NiPoint3 m_offset;
/*
* Inherited
*/
explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id) {
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override; void Load() override;
private:
float m_maxRange;
float m_height;
float m_distanceWeight;
float m_angleWeight;
float m_angle;
float m_minRange;
NiPoint3 m_offset;
uint32_t m_method;
float m_upperBound;
float m_lowerBound;
bool m_usePickedTarget;
bool m_useTargetPostion;
bool m_checkEnv;
bool m_useAttackPriority;
Behavior* m_action;
Behavior* m_missAction;
Behavior* m_blockedAction;
uint32_t m_maxTargets;
float m_farHeight;
float m_farWidth;
float m_nearHeight;
float m_nearWidth;
std::forward_list<int32_t> m_ignoreFactionList {};
std::forward_list<int32_t> m_includeFactionList {};
bool m_targetSelf;
bool m_targetEnemy;
bool m_targetFriend;
bool m_targetTeam;
}; };

View File

@@ -318,7 +318,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
// Speed towards start position // Speed towards start position
if (m_MovementAI != nullptr) { if (m_MovementAI != nullptr) {
m_MovementAI->SetHaltDistance(0); m_MovementAI->SetHaltDistance(0);
m_MovementAI->SetSpeed(m_PursuitSpeed); m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(m_StartPosition); m_MovementAI->SetDestination(m_StartPosition);
} }
@@ -382,8 +382,6 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
} }
LWOOBJID BaseCombatAIComponent::FindTarget() { LWOOBJID BaseCombatAIComponent::FindTarget() {
//const auto reference = m_MovementAI == nullptr ? m_StartPosition : m_MovementAI->ApproximateLocation();
NiPoint3 reference = m_StartPosition; NiPoint3 reference = m_StartPosition;
if (m_MovementAI) reference = m_MovementAI->ApproximateLocation(); if (m_MovementAI) reference = m_MovementAI->ApproximateLocation();
@@ -522,7 +520,7 @@ bool BaseCombatAIComponent::IsMech() {
} }
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate); outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate);
if (m_DirtyStateOrTarget || bIsInitialUpdate) { if (m_DirtyStateOrTarget || bIsInitialUpdate) {
outBitStream->Write(uint32_t(m_State)); outBitStream->Write(uint32_t(m_State));
@@ -660,17 +658,17 @@ void BaseCombatAIComponent::Wander() {
destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination); destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination);
} }
if (Vector3::DistanceSquared(destination, m_MovementAI->GetCurrentPosition()) < 2 * 2) { if (Vector3::DistanceSquared(destination, m_MovementAI->GetParent()->GetPosition()) < 2 * 2) {
m_MovementAI->Stop(); m_MovementAI->Stop();
return; return;
} }
m_MovementAI->SetSpeed(m_TetherSpeed); m_MovementAI->SetMaxSpeed(m_TetherSpeed);
m_MovementAI->SetDestination(destination); m_MovementAI->SetDestination(destination);
m_Timer += (m_MovementAI->GetCurrentPosition().x - destination.x) / m_TetherSpeed; m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_TetherSpeed;
} }
void BaseCombatAIComponent::OnAggro() { void BaseCombatAIComponent::OnAggro() {
@@ -685,21 +683,21 @@ void BaseCombatAIComponent::OnAggro() {
m_MovementAI->SetHaltDistance(m_AttackRadius); m_MovementAI->SetHaltDistance(m_AttackRadius);
NiPoint3 targetPos = target->GetPosition(); NiPoint3 targetPos = target->GetPosition();
NiPoint3 currentPos = m_MovementAI->GetCurrentPosition(); NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition();
// If the player's position is within range, attack // If the player's position is within range, attack
if (Vector3::DistanceSquared(currentPos, targetPos) <= m_AttackRadius * m_AttackRadius) { if (Vector3::DistanceSquared(currentPos, targetPos) <= m_AttackRadius * m_AttackRadius) {
m_MovementAI->Stop(); m_MovementAI->Stop();
} else if (Vector3::DistanceSquared(m_StartPosition, targetPos) > m_HardTetherRadius * m_HardTetherRadius) //Return to spawn if we're too far } else if (Vector3::DistanceSquared(m_StartPosition, targetPos) > m_HardTetherRadius * m_HardTetherRadius) //Return to spawn if we're too far
{ {
m_MovementAI->SetSpeed(m_PursuitSpeed); m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(m_StartPosition); m_MovementAI->SetDestination(m_StartPosition);
} else //Chase the player's new position } else //Chase the player's new position
{ {
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return; if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
m_MovementAI->SetSpeed(m_PursuitSpeed); m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(targetPos); m_MovementAI->SetDestination(targetPos);
@@ -725,7 +723,7 @@ void BaseCombatAIComponent::OnTether() {
m_MovementAI->Stop(); m_MovementAI->Stop();
} else if (Vector3::DistanceSquared(m_StartPosition, targetPos) > m_HardTetherRadius * m_HardTetherRadius) //Return to spawn if we're too far } else if (Vector3::DistanceSquared(m_StartPosition, targetPos) > m_HardTetherRadius * m_HardTetherRadius) //Return to spawn if we're too far
{ {
m_MovementAI->SetSpeed(m_PursuitSpeed); m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(m_StartPosition); m_MovementAI->SetDestination(m_StartPosition);
@@ -733,7 +731,7 @@ void BaseCombatAIComponent::OnTether() {
} else { } else {
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return; if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
m_MovementAI->SetSpeed(m_PursuitSpeed); m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(targetPos); m_MovementAI->SetDestination(targetPos);
} }

View File

@@ -53,7 +53,7 @@ public:
~BaseCombatAIComponent() override; ~BaseCombatAIComponent() override;
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/** /**
* Get the current behavioral state of the enemy * Get the current behavioral state of the enemy

View File

@@ -22,7 +22,7 @@ BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
BouncerComponent::~BouncerComponent() { BouncerComponent::~BouncerComponent() {
} }
void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(m_PetEnabled); outBitStream->Write(m_PetEnabled);
if (m_PetEnabled) { if (m_PetEnabled) {
outBitStream->Write(m_PetBouncerEnabled); outBitStream->Write(m_PetBouncerEnabled);

View File

@@ -17,7 +17,7 @@ public:
BouncerComponent(Entity* parentEntity); BouncerComponent(Entity* parentEntity);
~BouncerComponent() override; ~BouncerComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
Entity* GetParentEntity() const; Entity* GetParentEntity() const;

View File

@@ -20,7 +20,7 @@ BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
BuffComponent::~BuffComponent() { BuffComponent::~BuffComponent() {
} }
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (!bIsInitialUpdate) return; if (!bIsInitialUpdate) return;
if (m_Buffs.empty()) { if (m_Buffs.empty()) {
outBitStream->Write0(); outBitStream->Write0();
@@ -104,12 +104,7 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
if (parameter.name == "overtime") { if (parameter.name == "overtime") {
auto* behaviorTemplateTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>(); auto* behaviorTemplateTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
auto skillInfo = behaviorTemplateTable->GetSkillByID(parameter.values[0]); behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID;
if (skillInfo) {
behaviorID = skillInfo->behaviorID;
} else {
Game::logger->Log("BuffComponent", "Failed to find skill info for skill ID %d!", parameter.values[0]);
}
stacks = static_cast<int32_t>(parameter.values[1]); stacks = static_cast<int32_t>(parameter.values[1]);
tick = parameter.values[2]; tick = parameter.values[2];
const auto unknown2 = parameter.values[3]; // Always 0 const auto unknown2 = parameter.values[3]; // Always 0

View File

@@ -54,7 +54,7 @@ public:
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument* doc) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void Update(float deltaTime) override; void Update(float deltaTime) override;

View File

@@ -6,6 +6,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"Component.cpp" "Component.cpp"
"ControllablePhysicsComponent.cpp" "ControllablePhysicsComponent.cpp"
"DestroyableComponent.cpp" "DestroyableComponent.cpp"
"DonationVendorComponent.cpp"
"InventoryComponent.cpp" "InventoryComponent.cpp"
"LevelProgressionComponent.cpp" "LevelProgressionComponent.cpp"
"LUPExhibitComponent.cpp" "LUPExhibitComponent.cpp"
@@ -17,6 +18,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"MovingPlatformComponent.cpp" "MovingPlatformComponent.cpp"
"PetComponent.cpp" "PetComponent.cpp"
"PhantomPhysicsComponent.cpp" "PhantomPhysicsComponent.cpp"
"PhysicsComponent.cpp"
"PlayerForcedMovementComponent.cpp" "PlayerForcedMovementComponent.cpp"
"PossessableComponent.cpp" "PossessableComponent.cpp"
"PossessorComponent.cpp" "PossessorComponent.cpp"
@@ -30,7 +32,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"RebuildComponent.cpp" "RebuildComponent.cpp"
"RenderComponent.cpp" "RenderComponent.cpp"
"RigidbodyPhantomPhysicsComponent.cpp" "RigidbodyPhantomPhysicsComponent.cpp"
"RocketLaunchLupComponent.cpp" "MultiZoneEntranceComponent.cpp"
"RocketLaunchpadControlComponent.cpp" "RocketLaunchpadControlComponent.cpp"
"ScriptedActivityComponent.cpp" "ScriptedActivityComponent.cpp"
"ShootingGalleryComponent.cpp" "ShootingGalleryComponent.cpp"

View File

@@ -70,7 +70,7 @@ bool CharacterComponent::LandingAnimDisabled(int zoneID) {
CharacterComponent::~CharacterComponent() { CharacterComponent::~CharacterComponent() {
} }
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (bIsInitialUpdate) { if (bIsInitialUpdate) {
outBitStream->Write0(); outBitStream->Write0();

View File

@@ -70,7 +70,7 @@ public:
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument* doc) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/** /**
* Updates the rocket configuration using a LOT string separated by commas * Updates the rocket configuration using a LOT string separated by commas
@@ -276,6 +276,10 @@ public:
*/ */
void UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const; void UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const;
void SetCurrentInteracting(LWOOBJID objectID) {m_CurrentInteracting = objectID;};
LWOOBJID GetCurrentInteracting() {return m_CurrentInteracting;};
/** /**
* Character info regarding this character, including clothing styles, etc. * Character info regarding this character, including clothing styles, etc.
*/ */
@@ -560,6 +564,8 @@ private:
* ID of the last rocket used * ID of the last rocket used
*/ */
LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY; LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY;
LWOOBJID m_CurrentInteracting = LWOOBJID_EMPTY;
}; };
#endif // CHARACTERCOMPONENT_H #endif // CHARACTERCOMPONENT_H

View File

@@ -28,3 +28,7 @@ void Component::UpdateXml(tinyxml2::XMLDocument* doc) {
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) { void Component::LoadFromXml(tinyxml2::XMLDocument* doc) {
} }
void Component::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
}

View File

@@ -43,6 +43,8 @@ public:
*/ */
virtual void LoadFromXml(tinyxml2::XMLDocument* doc); virtual void LoadFromXml(tinyxml2::XMLDocument* doc);
virtual void Serialize(RakNet::BitStream* outBitStream, bool isConstruction);
protected: protected:
/** /**

View File

@@ -15,15 +15,12 @@
#include "LevelProgressionComponent.h" #include "LevelProgressionComponent.h"
#include "eStateChangeType.h" #include "eStateChangeType.h"
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Component(entity) { ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : PhysicsComponent(entity) {
m_Position = {};
m_Rotation = NiQuaternion::IDENTITY;
m_Velocity = {}; m_Velocity = {};
m_AngularVelocity = {}; m_AngularVelocity = {};
m_InJetpackMode = false; m_InJetpackMode = false;
m_IsOnGround = true; m_IsOnGround = true;
m_IsOnRail = false; m_IsOnRail = false;
m_DirtyPosition = true;
m_DirtyVelocity = true; m_DirtyVelocity = true;
m_DirtyAngularVelocity = true; m_DirtyAngularVelocity = true;
m_dpEntity = nullptr; m_dpEntity = nullptr;
@@ -74,7 +71,7 @@ void ControllablePhysicsComponent::Update(float deltaTime) {
} }
void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
//If this is a creation, then we assume the position is dirty, even when it isn't. //If this is a creation, then we assume the position is dirty, even when it isn't.
//This is because new clients will still need to receive the position. //This is because new clients will still need to receive the position.
//if (bIsInitialUpdate) m_DirtyPosition = true; //if (bIsInitialUpdate) m_DirtyPosition = true;
@@ -181,12 +178,6 @@ void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_DirtyPosition = true; m_DirtyPosition = true;
} }
void ControllablePhysicsComponent::ResetFlags() {
m_DirtyAngularVelocity = false;
m_DirtyPosition = false;
m_DirtyVelocity = false;
}
void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
@@ -208,26 +199,14 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
} }
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) { void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
if (m_Static) { if (m_Static) return;
return; PhysicsComponent::SetPosition(pos);
}
m_Position.x = pos.x;
m_Position.y = pos.y;
m_Position.z = pos.z;
m_DirtyPosition = true;
if (m_dpEntity) m_dpEntity->SetPosition(pos); if (m_dpEntity) m_dpEntity->SetPosition(pos);
} }
void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) { void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (m_Static) { if (m_Static) return;
return; PhysicsComponent::SetRotation(rot);
}
m_Rotation = rot;
m_DirtyPosition = true;
if (m_dpEntity) m_dpEntity->SetRotation(rot); if (m_dpEntity) m_dpEntity->SetRotation(rot);
} }

View File

@@ -6,7 +6,7 @@
#include "NiPoint3.h" #include "NiPoint3.h"
#include "NiQuaternion.h" #include "NiQuaternion.h"
#include "tinyxml2.h" #include "tinyxml2.h"
#include "Component.h" #include "PhysicsComponent.h"
#include "dpCollisionChecks.h" #include "dpCollisionChecks.h"
#include "PhantomPhysicsComponent.h" #include "PhantomPhysicsComponent.h"
#include "eBubbleType.h" #include "eBubbleType.h"
@@ -19,7 +19,7 @@ enum class eStateChangeType : uint32_t;
/** /**
* Handles the movement of controllable Entities, e.g. enemies and players * Handles the movement of controllable Entities, e.g. enemies and players
*/ */
class ControllablePhysicsComponent : public Component { class ControllablePhysicsComponent : public PhysicsComponent {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS; static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
@@ -27,9 +27,8 @@ public:
~ControllablePhysicsComponent() override; ~ControllablePhysicsComponent() override;
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void ResetFlags();
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument* doc) override;
/** /**
@@ -37,26 +36,14 @@ public:
* If the entity is static, this is a no-op. * If the entity is static, this is a no-op.
* @param pos The position to set * @param pos The position to set
*/ */
void SetPosition(const NiPoint3& pos); void SetPosition(const NiPoint3& pos) override;
/**
* Returns the current position of the entity
* @return The current position of the entity
*/
const NiPoint3& GetPosition() const { return m_Position; }
/** /**
* Sets the rotation of this entity, ensures this change is serialized next tick. If the entity is static, this is * Sets the rotation of this entity, ensures this change is serialized next tick. If the entity is static, this is
* a no-op. * a no-op.
* @param rot the rotation to set * @param rot the rotation to set
*/ */
void SetRotation(const NiQuaternion& rot); void SetRotation(const NiQuaternion& rot) override;
/**
* Returns the current rotation of this entity
* @return the current rotation of this entity
*/
const NiQuaternion& GetRotation() const { return m_Rotation; }
/** /**
* Sets the current velocity of this entity, ensures that this change is serialized next tick. If the entity is * Sets the current velocity of this entity, ensures that this change is serialized next tick. If the entity is
@@ -323,21 +310,6 @@ private:
*/ */
dpEntity* m_dpEntity; dpEntity* m_dpEntity;
/**
* Whether or not the position is dirty, forcing a serialization update of the position
*/
bool m_DirtyPosition;
/**
* The current position of the entity
*/
NiPoint3 m_Position;
/**
* The current rotation of the entity
*/
NiQuaternion m_Rotation;
/** /**
* Whether or not the velocity is dirty, forcing a serialization of the velocity * Whether or not the velocity is dirty, forcing a serialization of the velocity
*/ */

View File

@@ -72,6 +72,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
m_ImmuneToImaginationLossCount = 0; m_ImmuneToImaginationLossCount = 0;
m_ImmuneToQuickbuildInterruptCount = 0; m_ImmuneToQuickbuildInterruptCount = 0;
m_ImmuneToPullToPointCount = 0; m_ImmuneToPullToPointCount = 0;
m_DeathBehavior = -1;
} }
DestroyableComponent::~DestroyableComponent() { DestroyableComponent::~DestroyableComponent() {
@@ -119,7 +120,7 @@ void DestroyableComponent::Reinitialize(LOT templateID) {
} }
} }
void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) { void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (bIsInitialUpdate) { if (bIsInitialUpdate) {
outBitStream->Write1(); // always write these on construction outBitStream->Write1(); // always write these on construction
outBitStream->Write(m_ImmuneToBasicAttackCount); outBitStream->Write(m_ImmuneToBasicAttackCount);
@@ -363,9 +364,10 @@ void DestroyableComponent::SetIsShielded(bool value) {
void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignoreChecks) { void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignoreChecks) {
// Ignore factionID -1 // Ignore factionID -1
if (factionID == -1 && !ignoreChecks) { if (factionID == -1 && !ignoreChecks) return;
return;
} // if we already have that faction, don't add it again
if (std::find(m_FactionIDs.begin(), m_FactionIDs.end(), factionID) != m_FactionIDs.end()) return;
m_FactionIDs.push_back(factionID); m_FactionIDs.push_back(factionID);
m_DirtyHealth = true; m_DirtyHealth = true;
@@ -407,6 +409,14 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
} }
bool DestroyableComponent::IsEnemy(const Entity* other) const { bool DestroyableComponent::IsEnemy(const Entity* other) const {
if (m_Parent->IsPlayer() && other->IsPlayer()){
auto* thisCharacterComponent = m_Parent->GetComponent<CharacterComponent>();
if (!thisCharacterComponent) return false;
auto* otherCharacterComponent = other->GetComponent<CharacterComponent>();
if (!otherCharacterComponent) return false;
if (thisCharacterComponent->GetPvpEnabled() && otherCharacterComponent->GetPvpEnabled()) return true;
return false;
}
const auto* otherDestroyableComponent = other->GetComponent<DestroyableComponent>(); const auto* otherDestroyableComponent = other->GetComponent<DestroyableComponent>();
if (otherDestroyableComponent != nullptr) { if (otherDestroyableComponent != nullptr) {
for (const auto enemyFaction : m_EnemyFactionIDs) { for (const auto enemyFaction : m_EnemyFactionIDs) {
@@ -485,43 +495,6 @@ Entity* DestroyableComponent::GetKiller() const {
return Game::entityManager->GetEntity(m_KillerID); return Game::entityManager->GetEntity(m_KillerID);
} }
bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignoreFactions, const bool targetEnemy, const bool targetFriend) const {
auto* targetEntity = Game::entityManager->GetEntity(target);
if (targetEntity == nullptr) {
Game::logger->Log("DestroyableComponent", "Invalid entity for checking validity (%llu)!", target);
return false;
}
auto* targetDestroyable = targetEntity->GetComponent<DestroyableComponent>();
if (targetDestroyable == nullptr) {
return false;
}
auto* targetQuickbuild = targetEntity->GetComponent<RebuildComponent>();
if (targetQuickbuild != nullptr) {
const auto state = targetQuickbuild->GetState();
if (state != eRebuildState::COMPLETED) {
return false;
}
}
if (ignoreFactions) {
return true;
}
// Get if the target entity is an enemy and friend
bool isEnemy = IsEnemy(targetEntity);
bool isFriend = IsFriend(targetEntity);
// Return true if the target type matches what we are targeting
return (isEnemy && targetEnemy) || (isFriend && targetFriend);
}
void DestroyableComponent::Heal(const uint32_t health) { void DestroyableComponent::Heal(const uint32_t health) {
auto current = static_cast<uint32_t>(GetHealth()); auto current = static_cast<uint32_t>(GetHealth());
const auto max = static_cast<uint32_t>(GetMaxHealth()); const auto max = static_cast<uint32_t>(GetMaxHealth());
@@ -763,18 +736,18 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
auto* member = Game::entityManager->GetEntity(specificOwner); auto* member = Game::entityManager->GetEntity(specificOwner);
if (member) LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins()); if (member) Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
} else { } else {
for (const auto memberId : team->members) { // Free for all for (const auto memberId : team->members) { // Free for all
auto* member = Game::entityManager->GetEntity(memberId); auto* member = Game::entityManager->GetEntity(memberId);
if (member == nullptr) continue; if (member == nullptr) continue;
LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins()); Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
} }
} }
} else { // drop loot for non team user } else { // drop loot for non team user
LootGenerator::Instance().DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins()); Loot::DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
} }
} }
} else { } else {
@@ -792,7 +765,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
coinsTotal -= coinsToLose; coinsTotal -= coinsToLose;
LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose); Loot::DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
character->SetCoins(coinsTotal, eLootSourceType::PICKUP); character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
} }
} }

View File

@@ -24,7 +24,7 @@ public:
DestroyableComponent(Entity* parentEntity); DestroyableComponent(Entity* parentEntity);
~DestroyableComponent() override; ~DestroyableComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument* doc) override;
@@ -371,14 +371,6 @@ public:
*/ */
Entity* GetKiller() const; Entity* GetKiller() const;
/**
* Checks if the target ID is a valid enemy of this entity
* @param target the target ID to check for
* @param ignoreFactions whether or not check for the factions, e.g. just return true if the entity cannot be smashed
* @return if the target ID is a valid enemy
*/
bool CheckValidity(LWOOBJID target, bool ignoreFactions = false, bool targetEnemy = true, bool targetFriend = false) const;
/** /**
* Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks. * Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks.
* @param damage the damage to attempt to apply * @param damage the damage to attempt to apply
@@ -424,6 +416,9 @@ public:
const bool GetImmuneToQuickbuildInterrupt() {return m_ImmuneToQuickbuildInterruptCount > 0;}; const bool GetImmuneToQuickbuildInterrupt() {return m_ImmuneToQuickbuildInterruptCount > 0;};
const bool GetImmuneToPullToPoint() {return m_ImmuneToPullToPointCount > 0;}; const bool GetImmuneToPullToPoint() {return m_ImmuneToPullToPointCount > 0;};
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
/** /**
* Utility to reset all stats to the default stats based on items and completed missions * Utility to reset all stats to the default stats based on items and completed missions
*/ */
@@ -605,6 +600,11 @@ private:
uint32_t m_ImmuneToImaginationLossCount; uint32_t m_ImmuneToImaginationLossCount;
uint32_t m_ImmuneToQuickbuildInterruptCount; uint32_t m_ImmuneToQuickbuildInterruptCount;
uint32_t m_ImmuneToPullToPointCount; uint32_t m_ImmuneToPullToPointCount;
/**
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
*/
int32_t m_DeathBehavior;
}; };
#endif // DESTROYABLECOMPONENT_H #endif // DESTROYABLECOMPONENT_H

View File

@@ -0,0 +1,50 @@
#include "DonationVendorComponent.h"
#include "Database.h"
DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) {
//LoadConfigData
m_PercentComplete = 0.0;
m_TotalDonated = 0;
m_TotalRemaining = 0;
// custom attribute to calculate other values
m_Goal = m_Parent->GetVar<int32_t>(u"donationGoal");
if (m_Goal == 0) m_Goal = INT32_MAX;
// Default to the nexus tower jawbox activity and setup settings
m_ActivityId = m_Parent->GetVar<uint32_t>(u"activityID");
if ((m_ActivityId == 0) || (m_ActivityId == 117)) {
m_ActivityId = 117;
m_PercentComplete = 1.0;
m_TotalDonated = INT32_MAX;
m_TotalRemaining = 0;
m_Goal = INT32_MAX;
return;
}
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;"));
query->setInt(1, m_ActivityId);
std::unique_ptr<sql::ResultSet> donation_total(query->executeQuery());
if (donation_total->next()) m_TotalDonated = donation_total->getInt("donation_total");
m_TotalRemaining = m_Goal - m_TotalDonated;
m_PercentComplete = m_TotalDonated/static_cast<float>(m_Goal);
}
void DonationVendorComponent::SubmitDonation(uint32_t count) {
if (count <= 0 && ((m_TotalDonated + count) > 0)) return;
m_TotalDonated += count;
m_TotalRemaining = m_Goal - m_TotalDonated;
m_PercentComplete = m_TotalDonated/static_cast<float>(m_Goal);
m_DirtyDonationVendor = true;
}
void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
VendorComponent::Serialize(outBitStream, bIsInitialUpdate);
outBitStream->Write(bIsInitialUpdate || m_DirtyDonationVendor);
if (bIsInitialUpdate || m_DirtyDonationVendor) {
outBitStream->Write(m_PercentComplete);
outBitStream->Write(m_TotalDonated);
outBitStream->Write(m_TotalRemaining);
if (!bIsInitialUpdate) m_DirtyDonationVendor = false;
}
}

View File

@@ -0,0 +1,27 @@
#ifndef __DONATIONVENDORCOMPONENT__H__
#define __DONATIONVENDORCOMPONENT__H__
#include "VendorComponent.h"
#include "eReplicaComponentType.h"
class Entity;
class DonationVendorComponent final : public VendorComponent {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
DonationVendorComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
uint32_t GetActivityID() {return m_ActivityId;};
void SubmitDonation(uint32_t count);
private:
bool m_DirtyDonationVendor = false;
float m_PercentComplete = 0.0;
int32_t m_TotalDonated = 0;
int32_t m_TotalRemaining = 0;
uint32_t m_ActivityId = 0;
int32_t m_Goal = 0;
};
#endif //!__DONATIONVENDORCOMPONENT__H__

View File

@@ -116,6 +116,9 @@ Inventory* InventoryComponent::GetInventory(const eInventoryType type) {
case eInventoryType::VENDOR_BUYBACK: case eInventoryType::VENDOR_BUYBACK:
size = 27u; size = 27u;
break; break;
case eInventoryType::DONATION:
size = 24u;
break;
default: default:
break; break;
} }
@@ -709,7 +712,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
} }
} }
void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool bIsInitialUpdate, unsigned& flags) { void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool bIsInitialUpdate) {
if (bIsInitialUpdate || m_Dirty) { if (bIsInitialUpdate || m_Dirty) {
outBitStream->Write(true); outBitStream->Write(true);
@@ -767,10 +770,6 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
outBitStream->Write(false); outBitStream->Write(false);
} }
void InventoryComponent::ResetFlags() {
m_Dirty = false;
}
void InventoryComponent::Update(float deltaTime) { void InventoryComponent::Update(float deltaTime) {
for (auto* set : m_Itemsets) { for (auto* set : m_Itemsets) {
set->Update(deltaTime); set->Update(deltaTime);
@@ -933,9 +932,8 @@ void InventoryComponent::EquipScripts(Item* equippedItem) {
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1); int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1);
if (scriptComponentID > -1) { if (scriptComponentID > -1) {
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable<CDScriptComponentTable>(); CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable<CDScriptComponentTable>();
auto scriptCompData = scriptCompTable->GetByID(scriptComponentID); CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
if (!scriptCompData) return; auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData->script_name);
if (!itemScript) { if (!itemScript) {
Game::logger->Log("InventoryComponent", "null script?"); Game::logger->Log("InventoryComponent", "null script?");
} }
@@ -949,9 +947,8 @@ void InventoryComponent::UnequipScripts(Item* unequippedItem) {
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1); int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1);
if (scriptComponentID > -1) { if (scriptComponentID > -1) {
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable<CDScriptComponentTable>(); CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable<CDScriptComponentTable>();
auto scriptCompData = scriptCompTable->GetByID(scriptComponentID); CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
if (!scriptCompData) return; auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData->script_name);
if (!itemScript) { if (!itemScript) {
Game::logger->Log("InventoryComponent", "null script?"); Game::logger->Log("InventoryComponent", "null script?");
} }
@@ -1161,19 +1158,7 @@ void InventoryComponent::AddItemSkills(const LOT lot) {
const auto skill = FindSkill(lot); const auto skill = FindSkill(lot);
if (skill == 0) { SetSkill(slot, skill);
return;
}
if (index != m_Skills.end()) {
const auto old = index->second;
GameMessages::SendRemoveSkill(m_Parent, old);
}
GameMessages::SendAddSkill(m_Parent, skill, static_cast<int>(slot));
m_Skills.insert_or_assign(slot, skill);
} }
void InventoryComponent::RemoveItemSkills(const LOT lot) { void InventoryComponent::RemoveItemSkills(const LOT lot) {
@@ -1200,7 +1185,7 @@ void InventoryComponent::RemoveItemSkills(const LOT lot) {
if (slot == BehaviorSlot::Primary) { if (slot == BehaviorSlot::Primary) {
m_Skills.insert_or_assign(BehaviorSlot::Primary, 1); m_Skills.insert_or_assign(BehaviorSlot::Primary, 1);
GameMessages::SendAddSkill(m_Parent, 1, static_cast<int>(BehaviorSlot::Primary)); GameMessages::SendAddSkill(m_Parent, 1, BehaviorSlot::Primary);
} }
} }
@@ -1343,12 +1328,8 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip
for (const auto& result : results) { for (const auto& result : results) {
if (result.castOnType == 1) { if (result.castOnType == 1) {
const auto entry = behaviors->GetSkillByID(result.skillID); const auto entry = behaviors->GetSkillByID(result.skillID);
if (!entry) {
Game::logger->Log("InventoryComponent", "Buff %i not in database!", result.skillID);
continue; if (entry.skillID == 0) {
}
if (entry->skillID == 0) {
Game::logger->Log("InventoryComponent", "Failed to find buff behavior for skill (%i)!", result.skillID); Game::logger->Log("InventoryComponent", "Failed to find buff behavior for skill (%i)!", result.skillID);
continue; continue;
@@ -1359,7 +1340,7 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip
} }
// If item is not a proxy, add its buff to the added buffs. // If item is not a proxy, add its buff to the added buffs.
if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast<uint32_t>(entry->behaviorID)); if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast<uint32_t>(entry.behaviorID));
} }
} }
@@ -1634,3 +1615,29 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) {
petInventoryElement->LinkEndChild(petElement); petInventoryElement->LinkEndChild(petElement);
} }
} }
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary;
else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand;
else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck;
else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head;
else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable;
else return false;
return SetSkill(behaviorSlot, skillId);
}
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
if (skillId == 0) return false;
const auto index = m_Skills.find(slot);
if (index != m_Skills.end()) {
const auto old = index->second;
GameMessages::SendRemoveSkill(m_Parent, old);
}
GameMessages::SendAddSkill(m_Parent, skillId, slot);
m_Skills.insert_or_assign(slot, skillId);
return true;
}

View File

@@ -42,10 +42,9 @@ public:
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr); explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr);
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void LoadXml(tinyxml2::XMLDocument* document); void LoadXml(tinyxml2::XMLDocument* document);
void UpdateXml(tinyxml2::XMLDocument* document) override; void UpdateXml(tinyxml2::XMLDocument* document) override;
void ResetFlags();
/** /**
* Returns an inventory of the specified type, if it exists * Returns an inventory of the specified type, if it exists
@@ -368,6 +367,11 @@ public:
*/ */
void UnequipScripts(Item* unequippedItem); void UnequipScripts(Item* unequippedItem);
std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
bool SetSkill(int slot, uint32_t skillId);
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
~InventoryComponent() override; ~InventoryComponent() override;
private: private:

View File

@@ -38,7 +38,7 @@ void LUPExhibitComponent::NextExhibit() {
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }
void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) { void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write1(); // Dirty flag? outBitStream->Write1(); // Dirty flag?
outBitStream->Write(m_Exhibit); outBitStream->Write(m_Exhibit);
} }

View File

@@ -16,7 +16,7 @@ public:
LUPExhibitComponent(Entity* parent); LUPExhibitComponent(Entity* parent);
~LUPExhibitComponent(); ~LUPExhibitComponent();
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/** /**
* After the timer runs out, this changes the currently exhibited LOT to the next one * After the timer runs out, this changes the currently exhibited LOT to the next one

View File

@@ -37,7 +37,7 @@ void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_CharacterVersion = static_cast<eCharacterVersion>(characterVersion); m_CharacterVersion = static_cast<eCharacterVersion>(characterVersion);
} }
void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo); outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo);
if (bIsInitialUpdate || m_DirtyLevelInfo) outBitStream->Write(m_Level); if (bIsInitialUpdate || m_DirtyLevelInfo) outBitStream->Write(m_Level);
m_DirtyLevelInfo = false; m_DirtyLevelInfo = false;

View File

@@ -21,7 +21,7 @@ public:
*/ */
LevelProgressionComponent(Entity* parent); LevelProgressionComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/** /**
* Save data from this componennt to character XML * Save data from this componennt to character XML

View File

@@ -8,7 +8,7 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID"); m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
} }
void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
// ItemComponent Serialization. Pets do not get this serialization. // ItemComponent Serialization. Pets do not get this serialization.
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) { if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
outBitStream->Write1(); outBitStream->Write1();

View File

@@ -17,7 +17,7 @@ public:
ModelComponent(Entity* parent); ModelComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/** /**
* Returns the original position of the model * Returns the original position of the model

View File

@@ -46,7 +46,7 @@ const std::u16string& ModuleAssemblyComponent::GetAssemblyPartsLOTs() const {
return m_AssemblyPartsLOTs; return m_AssemblyPartsLOTs;
} }
void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (bIsInitialUpdate) { if (bIsInitialUpdate) {
outBitStream->Write1(); outBitStream->Write1();

View File

@@ -17,7 +17,7 @@ public:
ModuleAssemblyComponent(Entity* parent); ModuleAssemblyComponent(Entity* parent);
~ModuleAssemblyComponent() override; ~ModuleAssemblyComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void Update(float deltaTime) override; void Update(float deltaTime) override;
/** /**

View File

@@ -10,85 +10,77 @@
#include "EntityManager.h" #include "EntityManager.h"
#include "SimplePhysicsComponent.h" #include "SimplePhysicsComponent.h"
#include "CDClientManager.h" #include "CDClientManager.h"
#include "Game.h"
#include "dZoneManager.h"
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
#include "CDPhysicsComponentTable.h" #include "CDPhysicsComponentTable.h"
std::map<LOT, float> MovementAIComponent::m_PhysicsSpeedCache = {}; namespace {
/**
* Cache of all lots and their respective speeds
*/
std::map<LOT, float> m_PhysicsSpeedCache;
}
MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) { MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) {
m_Info = std::move(info); m_Info = info;
m_Done = true; m_AtFinalWaypoint = true;
m_BaseCombatAI = nullptr; m_BaseCombatAI = nullptr;
m_BaseCombatAI = reinterpret_cast<BaseCombatAIComponent*>(m_Parent->GetComponent(eReplicaComponentType::BASE_COMBAT_AI)); m_BaseCombatAI = m_Parent->GetComponent<BaseCombatAIComponent>();
//Try and fix the insane values: //Try and fix the insane values:
if (m_Info.wanderRadius > 5.0f) m_Info.wanderRadius = m_Info.wanderRadius * 0.5f; if (m_Info.wanderRadius > 5.0f) m_Info.wanderRadius *= 0.5f;
if (m_Info.wanderRadius > 8.0f) m_Info.wanderRadius = 8.0f; if (m_Info.wanderRadius > 8.0f) m_Info.wanderRadius = 8.0f;
if (m_Info.wanderSpeed > 0.5f) m_Info.wanderSpeed = m_Info.wanderSpeed * 0.5f; if (m_Info.wanderSpeed > 0.5f) m_Info.wanderSpeed *= 0.5f;
m_BaseSpeed = GetBaseSpeed(m_Parent->GetLOT()); m_BaseSpeed = GetBaseSpeed(m_Parent->GetLOT());
m_NextWaypoint = GetCurrentPosition(); m_NextWaypoint = m_Parent->GetPosition();
m_Acceleration = 0.4f; m_Acceleration = 0.4f;
m_Interrupted = false; m_PullingToPoint = false;
m_PullPoint = {}; m_PullPoint = NiPoint3::ZERO;
m_HaltDistance = 0; m_HaltDistance = 0;
m_Timer = 0; m_TimeToTravel = 0;
m_TimeTravelled = 0;
m_CurrentSpeed = 0; m_CurrentSpeed = 0;
m_Speed = 0; m_MaxSpeed = 0;
m_TotalTime = 0;
m_LockRotation = false; m_LockRotation = false;
} }
MovementAIComponent::~MovementAIComponent() = default;
void MovementAIComponent::Update(const float deltaTime) { void MovementAIComponent::Update(const float deltaTime) {
if (m_Interrupted) { if (m_PullingToPoint) {
const auto source = GetCurrentWaypoint(); const auto source = GetCurrentWaypoint();
const auto speed = deltaTime * 2.5f; const auto speed = deltaTime * 2.5f;
NiPoint3 velocity; NiPoint3 velocity = (m_PullPoint - source) * speed;
velocity.x = (m_PullPoint.x - source.x) * speed;
velocity.y = (m_PullPoint.y - source.y) * speed;
velocity.z = (m_PullPoint.z - source.z) * speed;
SetPosition(source + velocity); SetPosition(source + velocity);
if (Vector3::DistanceSquared(GetCurrentPosition(), m_PullPoint) < 2 * 2) { if (Vector3::DistanceSquared(m_Parent->GetPosition(), m_PullPoint) < std::pow(2, 2)) {
m_Interrupted = false; m_PullingToPoint = false;
} }
return; return;
} }
if (AtFinalWaypoint()) // Are we done? // Are we done?
{ if (AtFinalWaypoint()) return;
return;
}
if (m_HaltDistance > 0) { if (m_HaltDistance > 0) {
if (Vector3::DistanceSquared(ApproximateLocation(), GetDestination()) < m_HaltDistance * m_HaltDistance) // Prevent us from hugging the target // Prevent us from hugging the target
{ if (Vector3::DistanceSquared(ApproximateLocation(), GetDestination()) < std::pow(m_HaltDistance, 2)) {
Stop(); Stop();
return; return;
} }
} }
if (m_Timer > 0) { m_TimeTravelled += deltaTime;
m_Timer -= deltaTime; if (m_TimeTravelled < m_TimeToTravel) return;
m_TimeTravelled = 0.0f;
if (m_Timer > 0) {
return;
}
m_Timer = 0;
}
const auto source = GetCurrentWaypoint(); const auto source = GetCurrentWaypoint();
@@ -101,48 +93,44 @@ void MovementAIComponent::Update(const float deltaTime) {
m_NextWaypoint = GetCurrentWaypoint(); m_NextWaypoint = GetCurrentWaypoint();
if (m_NextWaypoint == source) { if (m_NextWaypoint == source) {
m_Timer = 0; m_TimeToTravel = 0.0f;
goto nextAction; goto nextAction;
} }
if (m_CurrentSpeed < m_Speed) { if (m_CurrentSpeed < m_MaxSpeed) {
m_CurrentSpeed += m_Acceleration; m_CurrentSpeed += m_Acceleration;
} }
if (m_CurrentSpeed > m_Speed) { if (m_CurrentSpeed > m_MaxSpeed) {
m_CurrentSpeed = m_Speed; m_CurrentSpeed = m_MaxSpeed;
} }
const auto speed = m_CurrentSpeed * m_BaseSpeed; const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed
const auto delta = m_NextWaypoint - source; const auto delta = m_NextWaypoint - source;
// Normalize the vector // Normalize the vector
const auto length = sqrtf(delta.x * delta.x + delta.y * delta.y + delta.z * delta.z); const auto length = delta.Length();
if (length > 0) { if (length > 0) {
velocity.x = (delta.x / length) * speed; velocity = (delta / length) * speed;
velocity.y = (delta.y / length) * speed;
velocity.z = (delta.z / length) * speed;
} }
// Calclute the time it will take to reach the next waypoint with the current speed // Calclute the time it will take to reach the next waypoint with the current speed
m_TotalTime = m_Timer = length / speed; m_TimeTravelled = 0.0f;
m_TimeToTravel = length / speed;
SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint)); SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint));
} else { } else {
// Check if there are more waypoints in the queue, if so set our next destination to the next waypoint // Check if there are more waypoints in the queue, if so set our next destination to the next waypoint
if (!m_Queue.empty()) { if (m_CurrentPath.empty()) {
SetDestination(m_Queue.top());
m_Queue.pop();
} else {
// We have reached our final waypoint
Stop(); Stop();
return; return;
} }
SetDestination(m_CurrentPath.top());
m_CurrentPath.pop();
} }
nextAction: nextAction:
@@ -157,7 +145,7 @@ const MovementAIInfo& MovementAIComponent::GetInfo() const {
} }
bool MovementAIComponent::AdvanceWaypointIndex() { bool MovementAIComponent::AdvanceWaypointIndex() {
if (m_PathIndex >= m_CurrentPath.size()) { if (m_PathIndex >= m_InterpolatedWaypoints.size()) {
return false; return false;
} }
@@ -167,37 +155,19 @@ bool MovementAIComponent::AdvanceWaypointIndex() {
} }
NiPoint3 MovementAIComponent::GetCurrentWaypoint() const { NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
if (m_PathIndex >= m_CurrentPath.size()) { return m_PathIndex >= m_InterpolatedWaypoints.size() ? m_Parent->GetPosition() : m_InterpolatedWaypoints[m_PathIndex];
return GetCurrentPosition();
}
return m_CurrentPath[m_PathIndex];
}
NiPoint3 MovementAIComponent::GetNextWaypoint() const {
return m_NextWaypoint;
}
NiPoint3 MovementAIComponent::GetCurrentPosition() const {
return m_Parent->GetPosition();
} }
NiPoint3 MovementAIComponent::ApproximateLocation() const { NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = GetCurrentPosition(); auto source = m_Parent->GetPosition();
if (m_Done) { if (AtFinalWaypoint()) return source;
return source;
}
auto destination = m_NextWaypoint; auto destination = m_NextWaypoint;
auto factor = m_TotalTime > 0 ? (m_TotalTime - m_Timer) / m_TotalTime : 0; auto percentageToWaypoint = m_TimeToTravel > 0 ? m_TimeTravelled / m_TimeToTravel : 0;
auto x = source.x + factor * (destination.x - source.x); auto approximation = source + ((destination - source) * percentageToWaypoint);
auto y = source.y + factor * (destination.y - source.y);
auto z = source.z + factor * (destination.z - source.z);
NiPoint3 approximation = NiPoint3(x, y, z);
if (dpWorld::Instance().IsLoaded()) { if (dpWorld::Instance().IsLoaded()) {
approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation); approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation);
@@ -226,28 +196,20 @@ bool MovementAIComponent::Warp(const NiPoint3& point) {
return true; return true;
} }
float MovementAIComponent::GetTimer() const {
return m_Timer;
}
bool MovementAIComponent::AtFinalWaypoint() const {
return m_Done;
}
void MovementAIComponent::Stop() { void MovementAIComponent::Stop() {
if (m_Done) { if (AtFinalWaypoint()) return;
return;
}
SetPosition(ApproximateLocation()); SetPosition(ApproximateLocation());
SetVelocity(NiPoint3::ZERO); SetVelocity(NiPoint3::ZERO);
m_TotalTime = m_Timer = 0; m_TimeToTravel = 0;
m_TimeTravelled = 0;
m_Done = true; m_AtFinalWaypoint = true;
m_CurrentPath = {}; m_InterpolatedWaypoints.clear();
while (!m_CurrentPath.empty()) m_CurrentPath.pop();
m_PathIndex = 0; m_PathIndex = 0;
@@ -259,20 +221,17 @@ void MovementAIComponent::Stop() {
void MovementAIComponent::PullToPoint(const NiPoint3& point) { void MovementAIComponent::PullToPoint(const NiPoint3& point) {
Stop(); Stop();
m_Interrupted = true; m_PullingToPoint = true;
m_PullPoint = point; m_PullPoint = point;
} }
void MovementAIComponent::SetPath(std::vector<NiPoint3> path) { void MovementAIComponent::SetPath(std::vector<NiPoint3> path) {
std::reverse(path.begin(), path.end()); if (path.empty()) return;
std::for_each(path.rbegin(), path.rend() - 1, [this](const NiPoint3& point) {
this->m_CurrentPath.push(point);
});
for (const auto& point : path) { SetDestination(path.front());
m_Queue.push(point);
}
SetDestination(m_Queue.top());
m_Queue.pop();
} }
float MovementAIComponent::GetBaseSpeed(LOT lot) { float MovementAIComponent::GetBaseSpeed(LOT lot) {
@@ -291,26 +250,14 @@ float MovementAIComponent::GetBaseSpeed(LOT lot) {
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::CONTROLLABLE_PHYSICS, -1); componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::CONTROLLABLE_PHYSICS, -1);
if (componentID != -1) { if (componentID == -1) {
physicsComponent = physicsComponentTable->GetByID(componentID); componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::SIMPLE_PHYSICS, -1);
goto foundComponent;
} }
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::SIMPLE_PHYSICS, -1); physicsComponent = physicsComponentTable->GetByID(componentID);
if (componentID != -1) {
physicsComponent = physicsComponentTable->GetByID(componentID);
goto foundComponent;
}
foundComponent:
// Client defaults speed to 10 and if the speed is also null in the table, it defaults to 10. // Client defaults speed to 10 and if the speed is also null in the table, it defaults to 10.
float speed = 10.0f; float speed = physicsComponent != nullptr ? physicsComponent->speed : 10.0f;
if (physicsComponent) speed = physicsComponent->speed;
float delta = fabs(speed) - 1.0f; float delta = fabs(speed) - 1.0f;
@@ -322,39 +269,11 @@ foundComponent:
} }
void MovementAIComponent::SetPosition(const NiPoint3& value) { void MovementAIComponent::SetPosition(const NiPoint3& value) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>(); m_Parent->SetPosition(value);
if (controllablePhysicsComponent != nullptr) {
controllablePhysicsComponent->SetPosition(value);
return;
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
simplePhysicsComponent->SetPosition(value);
}
} }
void MovementAIComponent::SetRotation(const NiQuaternion& value) { void MovementAIComponent::SetRotation(const NiQuaternion& value) {
if (m_LockRotation) { if (!m_LockRotation) m_Parent->SetRotation(value);
return;
}
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
controllablePhysicsComponent->SetRotation(value);
return;
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
simplePhysicsComponent->SetRotation(value);
}
} }
void MovementAIComponent::SetVelocity(const NiPoint3& value) { void MovementAIComponent::SetVelocity(const NiPoint3& value) {
@@ -373,15 +292,8 @@ void MovementAIComponent::SetVelocity(const NiPoint3& value) {
} }
} }
void MovementAIComponent::SetDestination(const NiPoint3& value) { void MovementAIComponent::SetDestination(const NiPoint3& destination) {
if (m_Interrupted) { if (m_PullingToPoint) return;
return;
}
/*if (Vector3::DistanceSquared(value, GetDestination()) < 2 * 2)
{
return;
}*/
const auto location = ApproximateLocation(); const auto location = ApproximateLocation();
@@ -390,97 +302,53 @@ void MovementAIComponent::SetDestination(const NiPoint3& value) {
} }
std::vector<NiPoint3> computedPath; std::vector<NiPoint3> computedPath;
if (dpWorld::Instance().IsLoaded()) { if (dpWorld::Instance().IsLoaded()) {
computedPath = dpWorld::Instance().GetNavMesh()->GetPath(GetCurrentPosition(), value, m_Info.wanderSpeed); computedPath = dpWorld::Instance().GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed);
} else { }
// Somehow failed
if (computedPath.empty()) {
// Than take 10 points between the current position and the destination and make that the path // Than take 10 points between the current position and the destination and make that the path
auto point = location; auto start = location;
auto delta = value - point; auto delta = destination - start;
auto step = delta / 10; auto step = delta / 10.0f;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
point = point + step; // TODO: Replace this with += when the NiPoint3::operator+= is fixed
start = start + step;
computedPath.push_back(point); computedPath.push_back(start);
} }
} }
if (computedPath.empty()) // Somehow failed m_InterpolatedWaypoints.clear();
{
return;
}
m_CurrentPath.clear();
m_CurrentPath.push_back(location);
// Simply path // Simply path
for (auto point : computedPath) { for (auto& point : computedPath) {
if (dpWorld::Instance().IsLoaded()) { if (dpWorld::Instance().IsLoaded()) {
point.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point); point.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point);
} }
m_CurrentPath.push_back(point); m_InterpolatedWaypoints.push_back(point);
} }
m_CurrentPath.push_back(computedPath[computedPath.size() - 1]);
m_PathIndex = 0; m_PathIndex = 0;
m_TotalTime = m_Timer = 0; m_TimeTravelled = 0;
m_TimeToTravel = 0;
m_Done = false; m_AtFinalWaypoint = false;
} }
NiPoint3 MovementAIComponent::GetDestination() const { NiPoint3 MovementAIComponent::GetDestination() const {
if (m_CurrentPath.empty()) { return m_InterpolatedWaypoints.empty() ? m_Parent->GetPosition() : m_InterpolatedWaypoints.back();
return GetCurrentPosition();
}
return m_CurrentPath[m_CurrentPath.size() - 1];
} }
void MovementAIComponent::SetSpeed(const float value) { void MovementAIComponent::SetMaxSpeed(const float value) {
m_Speed = value; if (value == m_MaxSpeed) return;
m_MaxSpeed = value;
m_Acceleration = value / 5; m_Acceleration = value / 5;
} }
float MovementAIComponent::GetSpeed() const {
return m_Speed;
}
void MovementAIComponent::SetAcceleration(const float value) {
m_Acceleration = value;
}
float MovementAIComponent::GetAcceleration() const {
return m_Acceleration;
}
void MovementAIComponent::SetHaltDistance(const float value) {
m_HaltDistance = value;
}
float MovementAIComponent::GetHaltDistance() const {
return m_HaltDistance;
}
void MovementAIComponent::SetCurrentSpeed(float value) {
m_CurrentSpeed = value;
}
float MovementAIComponent::GetCurrentSpeed() const {
return m_CurrentSpeed;
}
void MovementAIComponent::SetLockRotation(bool value) {
m_LockRotation = value;
}
bool MovementAIComponent::GetLockRotation() const {
return m_LockRotation;
}

View File

@@ -1,6 +1,6 @@
/* /*
* Darkflame Universe * Darkflame Universe
* Copyright 2018 * Copyright 2023
*/ */
#ifndef MOVEMENTAICOMPONENT_H #ifndef MOVEMENTAICOMPONENT_H
@@ -60,7 +60,6 @@ public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI; static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI;
MovementAIComponent(Entity* parentEntity, MovementAIInfo info); MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
~MovementAIComponent() override;
void Update(float deltaTime) override; void Update(float deltaTime) override;
@@ -86,61 +85,55 @@ public:
* Sets the max speed at which this entity may run * Sets the max speed at which this entity may run
* @param value the speed value to set * @param value the speed value to set
*/ */
void SetSpeed(float value); void SetMaxSpeed(float value);
/**
* Returns the max speed at which this entity may run
* @return the max speed at which this entity may run
*/
float GetSpeed() const;
/** /**
* Sets how fast the entity will accelerate when not running at full speed * Sets how fast the entity will accelerate when not running at full speed
* @param value the acceleration to set * @param value the acceleration to set
*/ */
void SetAcceleration(float value); void SetAcceleration(float value) { m_Acceleration = value; };
/** /**
* Returns the current speed at which this entity accelerates when not running at full speed * Returns the current speed at which this entity accelerates when not running at full speed
* @return the current speed at which this entity accelerates when not running at full speed * @return the current speed at which this entity accelerates when not running at full speed
*/ */
float GetAcceleration() const; float GetAcceleration() const { return m_Acceleration; };
/** /**
* Sets the halting distance (the distance at which we consider the target to be reached) * Sets the halting distance (the distance at which we consider the target to be reached)
* @param value the halting distance to set * @param value the halting distance to set
*/ */
void SetHaltDistance(float value); void SetHaltDistance(float value) { m_HaltDistance = value; }
/** /**
* Returns the current halting distance (the distance at which we consider the target to be reached) * Returns the current halting distance (the distance at which we consider the target to be reached)
* @return the current halting distance * @return the current halting distance
*/ */
float GetHaltDistance() const; float GetHaltDistance() const { return m_HaltDistance; }
/** /**
* Sets the speed the entity is currently running at * Sets the speed the entity is currently running at
* @param value the speed value to set * @param value the speed value to set
*/ */
void SetCurrentSpeed(float value); void SetCurrentSpeed(float value) { m_CurrentSpeed = value; }
/** /**
* Returns the speed the entity is currently running at * Returns the speed the entity is currently running at
* @return the speed the entity is currently running at * @return the speed the entity is currently running at
*/ */
float GetCurrentSpeed() const; float GetCurrentSpeed() const { return m_CurrentSpeed; }
/** /**
* Locks the rotation of this entity in place, depending on the argument * Locks the rotation of this entity in place, depending on the argument
* @param value if true, the entity will be rotationally locked * @param value if true, the entity will be rotationally locked
*/ */
void SetLockRotation(bool value); void SetLockRotation(bool value) { m_LockRotation = value; }
/** /**
* Returns whether this entity is currently rotationally locked * Returns whether this entity is currently rotationally locked
* @return true if the entity is rotationally locked, false otherwise * @return true if the entity is rotationally locked, false otherwise
*/ */
bool GetLockRotation() const; bool GetLockRotation() const { return m_LockRotation; };
/** /**
* Attempts to update the waypoint index, making the entity move to the next waypoint * Attempts to update the waypoint index, making the entity move to the next waypoint
@@ -158,13 +151,7 @@ public:
* Returns the waypoint this entity is supposed to move towards next * Returns the waypoint this entity is supposed to move towards next
* @return the waypoint this entity is supposed to move towards next * @return the waypoint this entity is supposed to move towards next
*/ */
NiPoint3 GetNextWaypoint() const; NiPoint3 GetNextWaypoint() const { return m_NextWaypoint; }
/**
* Returns the current position of this entity
* @return the current position of this entity
*/
NiPoint3 GetCurrentPosition() const;
/** /**
* Returns the approximate current location of the entity, including y coordinates * Returns the approximate current location of the entity, including y coordinates
@@ -180,17 +167,11 @@ public:
*/ */
bool Warp(const NiPoint3& point); bool Warp(const NiPoint3& point);
/**
* Returns the time it will take to reach the final waypoint according to the current speed
* @return the time it will take to reach the final waypoint according to the current speed
*/
float GetTimer() const;
/** /**
* Returns if the entity is at its final waypoint * Returns if the entity is at its final waypoint
* @return if the entity is at its final waypoint * @return if the entity is at its final waypoint
*/ */
bool AtFinalWaypoint() const; bool AtFinalWaypoint() const { return m_AtFinalWaypoint; }
/** /**
* Renders the entity stationary * Renders the entity stationary
@@ -250,17 +231,12 @@ private:
/** /**
* The max speed this entity may move at * The max speed this entity may move at
*/ */
float m_Speed; float m_MaxSpeed;
/** /**
* The time it will take to reach the next waypoint using the current speed * The time it will take to reach the next waypoint using the current speed
*/ */
float m_Timer; float m_TimeTravelled;
/**
* The total time it will take to reach the waypoint form its starting point
*/
float m_TotalTime;
/** /**
* The path this entity is currently traversing * The path this entity is currently traversing
@@ -270,7 +246,7 @@ private:
/** /**
* If the entity has reached it last waypoint * If the entity has reached it last waypoint
*/ */
bool m_Done; bool m_AtFinalWaypoint;
/** /**
* The speed the entity is currently moving at * The speed the entity is currently moving at
@@ -287,6 +263,11 @@ private:
*/ */
float m_HaltDistance; float m_HaltDistance;
/**
* The total time it will take to reach the waypoint form its starting point
*/
float m_TimeToTravel;
/** /**
* The base speed this entity has * The base speed this entity has
*/ */
@@ -295,7 +276,7 @@ private:
/** /**
* If the AI is currently turned of (e.g. when teleporting to some location) * If the AI is currently turned of (e.g. when teleporting to some location)
*/ */
bool m_Interrupted; bool m_PullingToPoint;
/** /**
* A position that the entity is currently moving towards while being interrupted * A position that the entity is currently moving towards while being interrupted
@@ -315,17 +296,12 @@ private:
/** /**
* The path the entity is currently following * The path the entity is currently following
*/ */
std::vector<NiPoint3> m_CurrentPath; std::vector<NiPoint3> m_InterpolatedWaypoints;
/** /**
* Queue of positions to traverse * The path from the current position to the destination.
*/ */
std::stack<NiPoint3> m_Queue; std::stack<NiPoint3> m_CurrentPath;
/**
* Cache of all lots and their respective speeds
*/
static std::map<LOT, float> m_PhysicsSpeedCache;
}; };
#endif // MOVEMENTAICOMPONENT_H #endif // MOVEMENTAICOMPONENT_H

View File

@@ -32,7 +32,7 @@ MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) {
MoverSubComponent::~MoverSubComponent() = default; MoverSubComponent::~MoverSubComponent() = default;
void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const { void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write<bool>(true); outBitStream->Write<bool>(true);
outBitStream->Write<uint32_t>(static_cast<uint32_t>(mState)); outBitStream->Write<uint32_t>(static_cast<uint32_t>(mState));
@@ -71,7 +71,7 @@ MovingPlatformComponent::~MovingPlatformComponent() {
delete static_cast<MoverSubComponent*>(m_MoverSubComponent); delete static_cast<MoverSubComponent*>(m_MoverSubComponent);
} }
void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
// Here we don't serialize the moving platform to let the client simulate the movement // Here we don't serialize the moving platform to let the client simulate the movement
if (!m_Serialize) { if (!m_Serialize) {
@@ -112,7 +112,7 @@ void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
if (m_MoverSubComponentType == eMoverSubComponentType::simpleMover) { if (m_MoverSubComponentType == eMoverSubComponentType::simpleMover) {
// TODO // TODO
} else { } else {
mover->Serialize(outBitStream, bIsInitialUpdate, flags); mover->Serialize(outBitStream, bIsInitialUpdate);
} }
} }
} }

View File

@@ -38,7 +38,7 @@ public:
MoverSubComponent(const NiPoint3& startPos); MoverSubComponent(const NiPoint3& startPos);
~MoverSubComponent(); ~MoverSubComponent();
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate);
/** /**
* The state the platform is currently in * The state the platform is currently in
@@ -111,7 +111,7 @@ public:
MovingPlatformComponent(Entity* parent, const std::string& pathName); MovingPlatformComponent(Entity* parent, const std::string& pathName);
~MovingPlatformComponent() override; ~MovingPlatformComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/** /**
* Stops all pathing, called when an entity starts a quick build associated with this platform * Stops all pathing, called when an entity starts a quick build associated with this platform

View File

@@ -1,9 +1,9 @@
#include "RocketLaunchLupComponent.h" #include "MultiZoneEntranceComponent.h"
#include "RocketLaunchpadControlComponent.h" #include "RocketLaunchpadControlComponent.h"
#include "InventoryComponent.h" #include "InventoryComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) { MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent) : Component(parent) {
m_Parent = parent; m_Parent = parent;
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs")); std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs"));
std::stringstream ss(zoneString); std::stringstream ss(zoneString);
@@ -14,17 +14,17 @@ RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(p
} }
} }
RocketLaunchLupComponent::~RocketLaunchLupComponent() {} MultiZoneEntranceComponent::~MultiZoneEntranceComponent() {}
void RocketLaunchLupComponent::OnUse(Entity* originator) { void MultiZoneEntranceComponent::OnUse(Entity* originator) {
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator); auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
if (!rocket) return; if (!rocket) return;
// the LUP world menu is just the property menu, the client knows how to handle it // the LUP world menu is just the property menu, the client knows how to handle it
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress()); GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), originator->GetSystemAddress());
} }
void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) { void MultiZoneEntranceComponent::OnSelectWorld(Entity* originator, uint32_t index) {
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>(); auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
if (!rocketLaunchpadControlComponent) return; if (!rocketLaunchpadControlComponent) return;

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include "Entity.h" #include "Entity.h"
#include "GameMessages.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
@@ -9,16 +8,16 @@
* Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds. * Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds.
* *
*/ */
class RocketLaunchLupComponent : public Component { class MultiZoneEntranceComponent : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH_LUP; static const eReplicaComponentType ComponentType = eReplicaComponentType::MULTI_ZONE_ENTRANCE;
/** /**
* Constructor for this component, builds the m_LUPWorlds vector * Constructor for this component, builds the m_LUPWorlds vector
* @param parent parent that contains this component * @param parent parent that contains this component
*/ */
RocketLaunchLupComponent(Entity* parent); MultiZoneEntranceComponent(Entity* parent);
~RocketLaunchLupComponent() override; ~MultiZoneEntranceComponent() override;
/** /**
* Handles an OnUse event from some entity, preparing it for launch to some other world * Handles an OnUse event from some entity, preparing it for launch to some other world

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