Further progress with Entity Manager

~ Made some changes to try to make it compile (doesn't compile yet)
This commit is contained in:
loplkc loplkc 2022-05-27 18:57:52 -04:00
parent 052a37b36f
commit 894def692c
4 changed files with 965 additions and 209 deletions

View file

@ -8,8 +8,11 @@
"$className": "DataModel", "$className": "DataModel",
"ServerScriptService": { "ServerScriptService": {
"$className": "ServerScriptService", "$className": "ServerScriptService",
"TS": { "Server": {
"$path": "out/server" "$path": "out/server"
},
"Game": {
"$path": "out/game"
} }
}, },
"StarterPlayer": { "StarterPlayer": {

861
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -21,5 +21,8 @@
"eslint-plugin-roblox-ts": "^0.0.31", "eslint-plugin-roblox-ts": "^0.0.31",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"typescript": "^4.4.2" "typescript": "^4.4.2"
},
"dependencies": {
"roblox-ts": "^1.3.3"
} }
} }

View file

@ -3,10 +3,10 @@
import { makePuppet, puppet, puppetEntry } from "./Puppetmaster"; import { makePuppet, puppet, puppetEntry } from "./Puppetmaster";
// import { ability } from "./AbilityManager"; // import { ability } from "./AbilityManager";
// I spent a lot of time thinking about the names for these types and they still suck // I spent a lot of time thinking about the names for these types and they still suck
type modifiers = readonly [[maxHealth: number, defense: number], [power: number, speed: number, resistance: number]]; // values used for calculation, only modified by buffs and debuffs (the first group is additive, the second multiplicative) type modifiers = readonly [power: number, speed: number, defense: number, resistance: number]; // values used for calculation, only modified by buffs and debuffs (the first group is additive, the second multiplicative)
type statuses = readonly [health: number, barrier: number]; // values used to store an entity's current status (more existential than stats) type statuses = readonly [health: number, barrier: number]; // values used to store an entity's current status (more existential than stats)
type statusEffect = readonly [string, modifiers, (entity: entity) => entity]; type statusEffect = readonly [string, modifiers, entityTransformTemplate, number];
export interface entityController { export interface entityController {
setPosition: (location: Vector3) => void; setPosition: (location: Vector3) => void;
useAbility: (abilityName: string, activating: boolean) => void; useAbility: (abilityName: string, activating: boolean) => void;
@ -32,27 +32,27 @@ export interface entityModifier {}
return resultOfFirstAbilityPart = ableToUse ? useAbilityPart(ability.firstPart, setOfAllEntities, thisEntity) : [ableToUse] return resultOfFirstAbilityPart = ableToUse ? useAbilityPart(ability.firstPart, setOfAllEntities, thisEntity) : [ableToUse]
*/ */
interface entityStats { interface entityStats {
statuses: statuses // Health and stuff that change constantly statuses: statuses; // Health and stuff that change constantly
baseModifiers: modifiers // Base modifiers that change only when the player switches weapons or something /* immunities: {
immunities: { [statusEffectName: string]: boolean | undefined;
[statusEffectName: string]: boolean | undefined };*/
} statusEffects: statusEffect[]; // Burn, poison, etc.
statusEffects: statusEffect[] // Burn, poison, etc.
} }
interface entityId { interface entityId {
readonly name: string readonly name: string;
readonly team: "players" | "enemies", readonly team: "players" | "enemies";
readonly isMinion: boolean readonly isMinion: boolean;
} }
export interface entity { export interface entity {
readonly id: entityId readonly id: entityId;
stats: entityStats stats: entityStats;
puppet: puppet baseModifiers: modifiers; // Base modifiers that change only when the player switches weapons or something
maxStatuses: statuses;
puppet: puppet;
} }
type entityTransform = (entity: entity) => entity // type entityTransform = (entity: entity) => entity;
// interface entityTransformTemplate { // interface entityTransformTemplate {
type abuiloga = (entityPerformingId: entityId, entityReceivingId: entityId) => entityTransformTemplate | false
/* /*
allies: { allies: {
minions: { minions: {
@ -64,15 +64,23 @@ type abuiloga = (entityPerformingId: entityId, entityReceivingId: entityId) => e
affectsMinions: boolean, affectsMinions: boolean,
excludesSelf: boolean, excludesSelf: boolean,
affectsSameTeam: boolean, affectsSameTeam: boolean,
*///} */ //}
type entityTransformType = "heal" | "attack" type entityTransformType = "heal" | "attack";
interface entityTransformTemplate { interface entityTransformTemplate {
extraFunction: (entityPerformingTransform: entity, entityReceivingTransform: entity) => boolean thingus: entityTransformType;
thingus: entityTransformType magnitude: number;
magnitude: number, // Positive for heal, negative for damage affectsHealth: boolean;
affectsHealth: boolean affectsBarrier: boolean;
affectsBarrier: boolean statusEffectsGranted?: placeholder[]; // TBA (Stuff like burn, slow, stun, etc.)
statusEffectsGranted: placeholder[], // Stuff like burn, slow, stun, etc.*/ }
type entityTransformDeterminer = (
entityPerformingId: entityId,
entityReceivingId: entityId,
entityPerformingPuppet: puppet,
entityReceivingPuppet: puppet,
) => entityTransformTemplate | false;
interface ability {
placeholder: entityTransformDeterminer; // TBA (Most abilities will not be a single instant of damage/heal)
} }
// type entityTransformTemplate = [entityTransformEligibilityTemplate, entityTransformApplicationTemplate] // type entityTransformTemplate = [entityTransformEligibilityTemplate, entityTransformApplicationTemplate]
/*function isEligibleForTransform(entityPerformingTransform: entity, entityReceivingTransform: entity, eligibilityTemplate: entityTransformEligibilityTemplate): false | [boolean] { // This function sucks /*function isEligibleForTransform(entityPerformingTransform: entity, entityReceivingTransform: entity, eligibilityTemplate: entityTransformEligibilityTemplate): false | [boolean] { // This function sucks
@ -92,31 +100,82 @@ interface entityTransformTemplate {
} }
return [onSameTeam] return [onSameTeam]
}*/ }*/
function applyEntityToAttack(entityModifiers: modifiers, entityStatusEffects: statusEffect[], magnitude: number): number { function applyEntityToAttack(
entityModifiers: modifiers,
entityStatusEffects: statusEffect[],
magnitude: number,
): number {
const attack = applyModifiersToAttack(magnitude, entityModifiers); const attack = applyModifiersToAttack(magnitude, entityModifiers);
// + Apply status effects of performing entity to attack (e.g. weaken) // + Apply status effects of performing entity to attack (e.g. weaken)
return attack return attack;
} }
function applyAttackToEntityStatuses(entityStatuses: statuses, entityModifiers: modifiers, entityStatusEffects: statusEffect[], attack: number, affectsHealth: boolean, affectsBarrier: boolean): statuses { // Not sure if this should return a whole entity function applyAttackToEntityStatuses(
entityStatuses: statuses,
entityModifiers: modifiers,
entityStatusEffects: statusEffect[],
attack: number,
affectsHealth: boolean,
affectsBarrier: boolean,
): statuses {
// Not sure if this should return a whole entity
// + Apply status effects of receiving entity to damage (e.g. armor break) // + Apply status effects of receiving entity to damage (e.g. armor break)
const damage = applyModifiersToAttack(attack, entityModifiers); const damage = applyModifiersToAttack(attack, entityModifiers);
const newStatuses = applyDamageToStatuses(entityStatuses, damage, affectsHealth, affectsBarrier); const newStatuses = applyDamageToStatuses(entityStatuses, damage, affectsHealth, affectsBarrier);
return entityStatuses return entityStatuses;
} }
function makeEntityTransform(entityPerformingTransformId: entityId, abuiloga: abuiloga): (entityReceivingTransform: entity) => entityStats { // ? Are you meant to pipe determineEntityTransform into the resulting function?
return function(entityReceivingTransform: entity): entityStats { /*function determineEntityTransform(
const entityTransformTemplate = abuiloga(entityPerformingTransformId, entityReceivingTransform.id); entityPerformingTransformId: entityId,
entityReceivingTransform: entityId,
const newStats = transformEntityStatuses( // All of this stuff should be packed into one object, maybe a new entityTransformTemplate ): entityTransformTemplate | false {
entityReceivingTransform.stats.statuses, return false; // Placeholder
finalCalculatedMaxHealth, }
finalCalculatedMaxBarrier, */
entityTransformTemplate.thingus, function transformEntityStats(
finalCalculatedMagnitude, entityPerformingTransform: entity,
finalCalculatedAffectsHealth, entityStats: entityStats,
finalCalculatedAffectsBarrier) // L entityTransformTemplate: entityTransformTemplate,
// const newEntity = entityTransformTemplate ? applyEntityTransform(entityPerformingTransform, entityReceivingTransform, onSameTeam, template[1]) : entityReceivingTransform; baseModifiers: modifiers,
return newStats maxStatuses: statuses,
): entityStats {
const transformType = entityTransformTemplate.thingus;
if (transformType === "attack") {
const outgoingAttack = applyModifiersToAttack(
entityTransformTemplate.magnitude,
entityPerformingTransform.baseModifiers,
);
// Apply user's status effects to outgoing attack
// Apply receiver's status effects to incoming damage
const incomingDamage = applyModifiersToDamage(outgoingAttack, baseModifiers);
const newEntityStatuses = applyDamageToStatuses(
entityStats.statuses,
incomingDamage,
entityTransformTemplate.affectsHealth,
entityTransformTemplate.affectsBarrier,
);
// Add or remove status effects
return {
statuses: newEntityStatuses,
statusEffects: entityStats.statusEffects, // Placeholder
};
} else if (transformType === "heal") {
const outgoingHeal = entityTransformTemplate.magnitude; // There could be a heal modifier later
// Apply user's status effects to outgoing heal
// Apply receiver's status effects to incoming heal
const newEntityStatuses = applyHealToStatuses(
entityStats.statuses, // DRY alert
outgoingHeal,
entityTransformTemplate.affectsHealth,
entityTransformTemplate.affectsBarrier,
maxStatuses,
);
// Add or remove status effects
return {
statuses: newEntityStatuses,
statusEffects: entityStats.statusEffects, // Placeholder
};
} else {
throw "Unknown entity transform type " + transformType; // Should be never
} }
} }
/*interface entityTransform extends entityTransformTemplate { /*interface entityTransform extends entityTransformTemplate {
@ -124,8 +183,8 @@ function makeEntityTransform(entityPerformingTransformId: entityId, abuiloga: ab
team: "players" | "enemies", team: "players" | "enemies",
}*/ }*/
type abilityTemplate = [ type abilityTemplate = [
[entityTransformTemplate[], number] // I guess the number is a delay until the next part or something [entityTransformTemplate[], number], // I guess the number is a delay until the next part or something
] ];
/*type ability = [ /*type ability = [
[entityTransform[], number] [entityTransform[], number]
]*/ ]*/
@ -133,95 +192,135 @@ type abilityTemplate = [
// const ability = getAbility // const ability = getAbility
// const entities = applyAbilityToScene(scene, ability) // const entities = applyAbilityToScene(scene, ability)
function applyDamageToHealth(health: number, damage: number): [newHealth: number, excessDamage: number] { // Needs testing function applyDamageToHealth(health: number, damage: number): [newHealth: number, excessDamage: number] {
const newHealth = health - damage // Needs testing
const newHealth = health - damage;
if (newHealth < 0) { if (newHealth < 0) {
return [0, -newHealth] return [0, -newHealth];
} else { } else {
return [newHealth, 0] return [newHealth, 0];
} }
} }
const applyDamageToBarrier = applyDamageToHealth const applyDamageToBarrier = applyDamageToHealth;
function applyDamageToStatuses(statuses: statuses, damage: number, affectsHealth: boolean, affectsBarrier: boolean): statuses { function applyDamageToStatuses(
statuses: statuses,
damage: number,
affectsHealth: boolean,
affectsBarrier: boolean,
): statuses {
if (affectsBarrier) { if (affectsBarrier) {
const [newBarrier, excessBarrierDamage] = applyDamageToBarrier(statuses[1], damage) const [newBarrier, excessBarrierDamage] = applyDamageToBarrier(statuses[1], damage);
const [newHealth, excessHealthDamage] = applyDamageToHealth(statuses[0], affectsHealth? excessBarrierDamage : 0) const [newHealth, excessHealthDamage] = applyDamageToHealth(
return [newHealth, newBarrier] statuses[0],
affectsHealth ? excessBarrierDamage : 0,
);
return [newHealth, newBarrier];
} else if (affectsHealth) { } else if (affectsHealth) {
const [newHealth, excessHealthDamage] = applyDamageToHealth(statuses[0], damage) const [newHealth, excessHealthDamage] = applyDamageToHealth(statuses[0], damage);
return [newHealth, statuses[1]] return [newHealth, statuses[1]];
} else { } else {
return statuses return statuses;
} }
} }
function applyHealToHealth(currentHealth: number, heal: number, maxHealth: number): [newHealth: number, excessHeal: number] { function applyHealToHealth(
const newHealth = currentHealth + heal currentHealth: number,
heal: number,
maxHealth: number,
): [newHealth: number, excessHeal: number] {
const newHealth = currentHealth + heal;
if (newHealth > maxHealth) { if (newHealth > maxHealth) {
return [maxHealth, newHealth - maxHealth] return [maxHealth, newHealth - maxHealth];
} else { } else {
return [newHealth, 0] return [newHealth, 0];
} }
} }
const applyHealToBarrier = applyHealToHealth const applyHealToBarrier = applyHealToHealth;
function applyHealToStatuses(statuses: statuses, heal: number, maxHealth: number, maxBarrier: number, affectsHealth: boolean, affectsBarrier: boolean): statuses { function applyHealToStatuses(
statuses: statuses,
heal: number,
affectsHealth: boolean,
affectsBarrier: boolean,
maxStatuses: statuses,
): statuses {
if (affectsHealth) { if (affectsHealth) {
const [newHealth, excessHealth] = applyHealToHealth(statuses[0], heal, maxHealth) const [newHealth, excessHealth] = applyHealToHealth(statuses[0], heal, maxStatuses[0]);
const [newBarrier, excessBarrier] = applyHealToBarrier(statuses[1], affectsBarrier ? excessHealth : 0, maxBarrier) const [newBarrier, excessBarrier] = applyHealToBarrier(
return [newHealth, newBarrier] statuses[1],
} else if (affectsBarrier) { // Using a branch isn't optimal, but as of writing I can't think of a better solution affectsBarrier ? excessHealth : 0,
const [newBarrier, excessBarrier] = applyHealToBarrier(statuses[1], heal, maxBarrier) maxStatuses[1],
return [statuses[0], newBarrier] );
return [newHealth, newBarrier];
} else if (affectsBarrier) {
// Using a branch isn't optimal, but as of writing I can't think of a better solution
const [newBarrier, excessBarrier] = applyHealToBarrier(statuses[1], heal, maxStatuses[1]);
return [statuses[0], newBarrier];
} else { } else {
return statuses return statuses;
} }
} }
function transformEntityStatuses(entityStatuses: statuses, maxHealth: number, maxBarrier: number, transformType: string, magnitude: number, affectsHealth: boolean, affectsBarrier: boolean): statuses { function applyPowerToAttack(attack: number, power: number) {
if (transformType == "heal") { return attack * power;
const newStatuses = applyHealToStatuses(entityStatuses, magnitude, maxHealth, maxBarrier, affectsHealth, affectsBarrier) // More chaining method calls...
return newStatuses
} else if (transformType == "attack") {
const newStatuses = applyDamageToStatuses(entityStatuses, magnitude, affectsHealth, affectsBarrier)
return newStatuses
} else {
throw "Unimplemented transformType " + transformType
}
}// Damage should come before status effects are applied
function applyPowerToAttack(attack: number, power: number) {
return attack*power
} }
function applyModifiersToAttack(attack: number, modifiers: modifiers): number { // Get arguments they use (a bit sketchy) function applyModifiersToAttack(attack: number, modifiers: modifiers): number {
return applyPowerToAttack(attack, modifiers[1][0]) // Get arguments they use (a bit sketchy)
return applyPowerToAttack(attack, modifiers[0]);
} }
function applyResistanceToDamage(damage: number, resistance: number): number { function applyResistanceToDamage(damage: number, resistance: number): number {
return damage*(1 - resistance) return damage * (1 - resistance);
} }
function applyDefenseToDamage(damage: number, defense: number): number { function applyDefenseToDamage(damage: number, defense: number): number {
return damage - defense return damage - defense;
} }
function applyModifiersToDamage(damage: number, modifiers: modifiers): number { function applyModifiersToDamage(damage: number, modifiers: modifiers): number {
return applyResistanceToDamage(applyDefenseToDamage(damage, modifiers[0][1]), modifiers[1][2]) return applyResistanceToDamage(applyDefenseToDamage(damage, modifiers[2]), modifiers[3]);
} }
function modifiersArrayFunction(applyModifiersToNumber: (number: number, modifiers: modifiers) => number): (number: number, modifiers: modifiers[]) => number { function modifiersArrayFunction(
return function(number: number, modifiersArray: modifiers[]) { applyModifiersToNumber: (number: number, modifiers: modifiers) => number,
let newNumber = number ): (number: number, modifiers: modifiers[]) => number {
modifiersArray.forEach(function(modifiers: modifiers) { return function (number: number, modifiersArray: modifiers[]) {
newNumber = applyModifiersToNumber(newNumber, modifiers) let newNumber = number;
}) modifiersArray.forEach(function (modifiers: modifiers) {
return newNumber newNumber = applyModifiersToNumber(newNumber, modifiers);
} });
return newNumber;
};
} }
const applyModifiersArrayToAttack = modifiersArrayFunction(applyModifiersToAttack); const applyModifiersArrayToAttack = modifiersArrayFunction(applyModifiersToAttack);
const applyModifiersArrayToDamage = modifiersArrayFunction(applyModifiersToDamage); const applyModifiersArrayToDamage = modifiersArrayFunction(applyModifiersToDamage);
function getEntityByName(entityList: entity[], entityName: string): success<entity> { function getEntityByName(entityList: entity[], entityName: string): success<entity> {
entityList.forEach(function(entity: entity) { entityList.forEach(function (entity: entity) {
if (entity.id.name == entityName) { // Chained method calls are cringe if (entity.id.name === entityName) {
return [true, entity] // Chained method calls are cringe
return [true, entity];
} }
}) });
return [false, "Entity not found"] return [false, "Entity not found"];
} }
function getAbility() {} function getAbility() {}
function useAbility(entityList: entity[], entityUsing: entity, aim: Vector3): [entity[], placeholder[]] { function applyEntityTransformToEntityList(
entityList: entity[],
entityTransformDeterminer: entityTransformDeterminer,
entityPerformingTransform: entity,
aim: Vector3,
): [entity[], placeholder[]?] {
const newEntityList = entityList.map(function (entityReceivingTransform: entity): entity {
const entityTransformTemplate = entityTransformDeterminer(
entityPerformingTransform.id,
entityReceivingTransform.id,
entityPerformingTransform.puppet,
entityReceivingTransform.puppet,
);
const newEntityStats = entityTransformTemplate
? transformEntityStats(
entityPerformingTransform,
entityReceivingTransform.stats,
entityTransformTemplate,
entityReceivingTransform.baseModifiers,
entityReceivingTransform.maxStatuses,
)
: entityReceivingTransform.stats;
});
return [newEntityList];
} }
/*class entityHandler implements entityController { /*class entityHandler implements entityController {
constructor(baseStats: stats, baseAmounts: amounts, puppetEntry: puppetEntry) { constructor(baseStats: stats, baseAmounts: amounts, puppetEntry: puppetEntry) {
@ -287,4 +386,4 @@ class entityManager extends actorClass<entityManagerRequest> {
export function initEntityManager() { export function initEntityManager() {
return new entityManager(); return new entityManager();
} }
*/ */