After further reading, re-combined the interfaces

= There wasn't really any point in trying to split the interfaces all
the way up to the top level - this is a game; it is inevitable that some object data
will change and some will stay the same
This commit is contained in:
loplkc loplkc 2022-07-12 11:40:04 -04:00
parent 8a9d5414f8
commit 88129a87ba
3 changed files with 106 additions and 70 deletions

View file

@ -1,3 +1,4 @@
--An attempt to profile the speed of TweenService:GetValue
local TweenService = game:GetService("TweenService") local TweenService = game:GetService("TweenService")
local clock = os.clock local clock = os.clock
local first = clock() local first = clock()

View file

@ -1,12 +1,19 @@
// "EntityManager": Create entity objects and apply transformations to them. // "EntityManager": Create entity objects and apply transformations to them.
// A bold attempt at functional programming in a videogame entity system // A bold (or just foolish) attempt at functional programming in a videogame entity system
import { makePuppet, puppet, puppetEntry } from "./Puppetmaster"; import { makePuppet, puppet, puppetEntry } from "./Puppetmaster";
// import { ability } from "./AbilityManager"; // import { ability } from "./AbilityManager";
const entityTags = ["minion"] as const const entityTags = ["minion"] as const;
type modifiers = readonly [power: number, speed: number, defense: number, resistance: number]; // values used for calculation, only modified by buffs and debuffs type modifiers = readonly [power: number, speed: number, defense: number, resistance: number]; // values used for calculation, only modified by buffs and debuffs
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, entityTransformTemplate, number]; type statusEffect = readonly [
type entityTag = typeof entityTags[number] name: string,
type: string,
strength: number,
modifiers?: modifiers,
effect?: entityTransformTemplate,
]; // Should also be able to cause a visual effect somehow - maybe rather than having an entityTransformTemplate, it should broadcast a message back to the top
// ...that can affect other entities or communicate with the client
type entityTag = typeof entityTags[number];
export interface entityId { export interface entityId {
readonly name: string; readonly name: string;
readonly team: "players" | "enemies"; readonly team: "players" | "enemies";
@ -15,20 +22,21 @@ export interface entityId {
}; };
} }
export interface entityProperties { export interface entityProperties {
readonly id: entityId, readonly id: entityId;
readonly baseModifiers: modifiers, readonly maxStatuses: statuses;
readonly maxStatuses: statuses, readonly baseModifiers: modifiers;
readonly baseStatusEffects: statusEffect[],// Permanent immunities/status effects readonly baseStatusEffects: statusEffect[]; // Permanent immunities/status effects
} }
export interface entityStatuses { export interface entity {
readonly statuses: statuses; // Health and stuff that change constantly statuses: statuses;
readonly statusEffects: statusEffect[]; // Burn, poison, etc. statusEffects: statusEffect[];
readonly properties: entityProperties;
} }
type entityTransformType = "support" | "attack"; type entityTransformType = "support" | "attack";
type healthTransformValue = [magnitude: number, affectsHealth: boolean, affectsBarrier: boolean] type healthTransformValue = [magnitude: number, affectsHealth: boolean, affectsBarrier: boolean];
interface entityTransformTemplate { interface entityTransformTemplate {
healthTransformValue: healthTransformValue, healthTransformValue: healthTransformValue;
statusEffectsGranted?: placeholder[]; // TBA (Stuff like burn, slow, stun, etc.) statusEffectsGranted?: placeholder[]; // TBA (Stuff like burn, slow, stun, etc.)
} }
type entityTransformDeterminer = ( type entityTransformDeterminer = (
@ -43,7 +51,8 @@ interface ability {
// Immunities are just status effects that take the place of a damaging status effect (so ones of that type can't be added) but don't do anything // Immunities are just status effects that take the place of a damaging status effect (so ones of that type can't be added) but don't do anything
// Status effects have a strength value that determines whether they can override another status effect of the same type (holy inferno can replace burn but burn can't replace holy inferno) // Status effects have a strength value that determines whether they can override another status effect of the same type (holy inferno can replace burn but burn can't replace holy inferno)
export interface entityController {//Deprecated export interface entityController {
//Deprecated
setPosition: (location: Vector3) => void; setPosition: (location: Vector3) => void;
useAbility: (abilityName: string, activating: boolean) => void; useAbility: (abilityName: string, activating: boolean) => void;
} }
@ -124,7 +133,12 @@ function applyAttackToEntityStatuses(
): statuses { ): statuses {
// + 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 modifiedDamage = applyModifiersToDamage(entityTransformTemplate.magnitude, entityModifiers); const modifiedDamage = applyModifiersToDamage(entityTransformTemplate.magnitude, entityModifiers);
const newStatuses = applyDamageToStatuses(entityStatuses, modifiedDamage, entityTransformTemplate.affectsHealth, entityTransformTemplate.affectsBarrier); const newStatuses = applyDamageToStatuses(
entityStatuses,
modifiedDamage,
entityTransformTemplate.affectsHealth,
entityTransformTemplate.affectsBarrier,
);
return entityStatuses; return entityStatuses;
} }
/*function applyHealToEntityStats() {//if (transformType === "attack") { /*function applyHealToEntityStats() {//if (transformType === "attack") {
@ -150,7 +164,13 @@ function applyHealToEntityStatuses(
entityStatusEffects: statusEffect[], entityStatusEffects: statusEffect[],
): statuses { ): statuses {
// + Apply status effects of receiving entity to damage (e.g. heal up) // + Apply status effects of receiving entity to damage (e.g. heal up)
const newStatuses = applyHealToStatuses(entityStatuses, entityTransformTemplate.magnitude, entityTransformTemplate.affectsHealth, entityTransformTemplate.affectsBarrier, entityMaxStatuses); const newStatuses = applyHealToStatuses(
entityStatuses,
entityTransformTemplate.magnitude,
entityTransformTemplate.affectsHealth,
entityTransformTemplate.affectsBarrier,
entityMaxStatuses,
);
return entityStatuses; return entityStatuses;
} }
// ? Are you meant to pipe determineEntityTransform into the resulting function? // ? Are you meant to pipe determineEntityTransform into the resulting function?
@ -178,7 +198,7 @@ function applyEntityToHeal(
entityTransformTemplate: entityTransformTemplate, entityTransformTemplate: entityTransformTemplate,
): entityTransformTemplate { ): entityTransformTemplate {
// + Apply user's status effects to outgoing heal // + Apply user's status effects to outgoing heal
return entityTransformTemplate // There could be a heal modifier later return entityTransformTemplate; // There could be a heal modifier later
} }
/*function transformEntityStats( /*function transformEntityStats(
entityStats: entityStats, entityStats: entityStats,
@ -307,17 +327,19 @@ function getEntityByName(entityList: entity[], entityName: string): success<enti
return [false, "Entity not found"]; return [false, "Entity not found"];
} }
function getAbility() {} function getAbility() {}
function applyEntityTransformToEntityStatuses(entityTransform: entityTransformTemplate, statuses: statuses, maxStatuses: statuses) { function applyEntityTransformToEntityStatuses(
entityTransform: entityTransformTemplate,
} statuses: statuses,
maxStatuses: statuses,
) {}
function applyEntityTransformToEntityList( function applyEntityTransformToEntityList(
entityList: entity[], entityList: entity[],
entityTransformDeterminer: entityTransformDeterminer, entityTransformDeterminer: entityTransformDeterminer,
entityPerformingTransform: entity, entityPerformingTransform: entity,
aim: Vector3, aim: Vector3,
): [entity[], placeholder[]?] { ): [entity[], placeholder[]?] {
const entityPerformingTransformModifiers = entityPerformingTransform.baseModifiers const entityPerformingTransformModifiers = entityPerformingTransform.baseModifiers;
const entityPerformingTransformStatusEffects = entityPerformingTransform.stats.statusEffects // Not good const entityPerformingTransformStatusEffects = entityPerformingTransform.stats.statusEffects; // Not good
const newEntityList = entityList.map(function (entityReceivingTransform: entity): entity { const newEntityList = entityList.map(function (entityReceivingTransform: entity): entity {
const entityTransform = entityTransformDeterminer( const entityTransform = entityTransformDeterminer(
entityPerformingTransform.id, entityPerformingTransform.id,
@ -326,13 +348,13 @@ function applyEntityTransformToEntityList(
entityReceivingTransform.puppet, entityReceivingTransform.puppet,
); );
if (entityTransform) { if (entityTransform) {
const entityStatuses = entityPerformingTransform.stats.statuses const entityStatuses = entityPerformingTransform.stats.statuses;
const [entityTransformType, entityTransformTemplate] = entityTransform const [entityTransformType, entityTransformTemplate] = entityTransform;
if (entityTransformType === "attack") { if (entityTransformType === "attack") {
const outgoingTransformTemplate = applyEntityToAttack( const outgoingTransformTemplate = applyEntityToAttack(
entityPerformingTransformModifiers, entityPerformingTransformModifiers,
entityPerformingTransformStatusEffects, entityPerformingTransformStatusEffects,
entityTransformTemplate entityTransformTemplate,
); );
const newEntityStatuses = outgoingTransformTemplate const newEntityStatuses = outgoingTransformTemplate
? applyAttackToEntityStatuses( ? applyAttackToEntityStatuses(
@ -343,11 +365,12 @@ function applyEntityTransformToEntityList(
) )
: entityStatuses; // Also cringe : entityStatuses; // Also cringe
} else { } else {
assert(entityTransformType === "heal") assert(entityTransformType === "heal");
const outgoingTransformTemplate = applyEntityToHeal( // Lots of DRY violations here const outgoingTransformTemplate = applyEntityToHeal(
// Lots of DRY violations here
entityPerformingTransformModifiers, entityPerformingTransformModifiers,
entityPerformingTransformStatusEffects, entityPerformingTransformStatusEffects,
entityTransformTemplate entityTransformTemplate,
); );
const newEntityStatuses = outgoingTransformTemplate const newEntityStatuses = outgoingTransformTemplate
? applyHealToEntityStatuses( ? applyHealToEntityStatuses(

View file

@ -1,28 +1,37 @@
// "The": Handle events. // "The": Handle events.
import { entityId, entityProperties, entityStatuses/*entityManagerRequest /*makeEntity, entityController*/ } from "./EntityManager"; import { entity } from "./EntityManager";
import { applyRequestsToPlayerStorage, playerManagerRequest } from "./PlayerManager" import { applyRequestsToPlayerStorage, playerManagerRequest } from "./PlayerManager";
export type sceneManagerRequest = [Player, "useAbility", Vector3] | [Player, "foo", "bar"] export type sceneManagerRequest = [Player, "useAbility", Vector3] | [Player, "foo", "bar"];
type endConditionFunction = (containedScenes: scene[], containedEntities: entity[], timeElapsed: number) => boolean type endConditionFunction = (containedEntities: entity[], timeElapsed: number) => boolean;
export interface sceneTemplate { export interface sceneTemplate {
readonly sceneComplete: endConditionFunction // Checks conditions that need to pass for the scene to end (e.g. entityX.Alive == false || timeSpent > 1000) readonly sceneComplete: endConditionFunction; // Checks conditions that need to pass for the scene to end (e.g. entityX.Alive == false || timeSpent > 1000)
readonly onCompletion: readonly playerManagerRequest[] // Requests to get sent out when the scene ends readonly onCompletion: readonly playerManagerRequest[]; // Requests to get sent out when the scene ends
} }
export interface scene extends sceneTemplate { export interface scene {
entities: entity[];
}
/*export interface sceneBackstage {
readonly entityProperties: {
[glumph: string]: entityProperties;
}
}*/
// export interface scene extends sceneTemplate {
/*containedScenes?: { /*containedScenes?: {
[sceneName: string]: scene | undefined [sceneName: string]: scene | undefined
}; // Scenes within this scene that are isolated from each other and can be run in parallel }; // Scenes within this scene that are isolated from each other and can be run in parallel
// Not necessarily "A list of events that need to return true (in sequence) to complete this event", but such events would go there // Not necessarily "A list of events that need to return true (in sequence) to complete this event", but such events would go there
*/ //Scene nesting is currently cancelled */ //Scene nesting is currently cancelled
containedEntities: string[]; // "A list of entities that need to die to complete the event" (not really but kinda) // containedEntities: string[]; // "A list of entities that need to die to complete the event" (not really but kinda)
containedEntityProperties: { // containedEntityProperties: {
[entityName: string]: entityProperties // [entityName: string]: entityProperties
} // }
containedEntityStatuses // containedEntityStatuses
players: { // players: {
[userId: number]: [inThisScene: true] | [inThisScene: false, subScene: string] | undefined // [userId: number]: [inThisScene: true] | [inThisScene: false, subScene: string] | undefined
} // }
//timeout?: number; // A timeout for the event; passes a lose condition if there are other completion requirements that have not been satisfied //timeout?: number; // A timeout for the event; passes a lose condition if there are other completion requirements that have not been satisfied
} // }
export function runScene(scene: scene, now: number): success<[scene, playerManagerRequest[]]> { export function runScene(scene: scene, now: number): success<[scene, playerManagerRequest[]]> {
return [true, [scene, []]]; return [true, [scene, []]];
} }
@ -60,19 +69,22 @@ function applyRequestToScene(scene: scene, now: number, request: sceneManagerReq
return [scene, sceneRequestResult[1]] return [scene, sceneRequestResult[1]]
}*/ }*/
} }
export function applyRequestsToScene(scene: scene, now: number, requests: sceneManagerRequest[]): success<[scene, playerManagerRequest[]]> { export function applyRequestsToScene(
scene: scene,
now: number,
requests: sceneManagerRequest[],
): success<[scene, playerManagerRequest[]]> {
try { try {
let newScene: scene = scene let newScene: scene = scene;
let outgoingRequests: playerManagerRequest[] = [] let outgoingRequests: playerManagerRequest[] = [];
requests.forEach(function (request: sceneManagerRequest) { requests.forEach(function (request: sceneManagerRequest) {
const sceneRequestResult = applyRequestToScene(newScene, now, request); const sceneRequestResult = applyRequestToScene(newScene, now, request);
newScene = sceneRequestResult[0] newScene = sceneRequestResult[0];
outgoingRequests = [...outgoingRequests, ...sceneRequestResult[1]] outgoingRequests = [...outgoingRequests, ...sceneRequestResult[1]];
}) });
return [true, [newScene, outgoingRequests]]; return [true, [newScene, outgoingRequests]];
} } catch (error) {
catch(error) { return [false, error];
return [false, error]
} }
} }
export function initScene(sceneTemplate: sceneTemplate): scene { export function initScene(sceneTemplate: sceneTemplate): scene {
@ -82,6 +94,6 @@ export function initScene(sceneTemplate: sceneTemplate): scene {
players: [], players: [],
sceneComplete: sceneTemplate.sceneComplete, sceneComplete: sceneTemplate.sceneComplete,
onCompletion: sceneTemplate.onCompletion, onCompletion: sceneTemplate.onCompletion,
}
return newScene;
}; };
return newScene;
}