Compare commits
No commits in common. "8a9d5414f83c5d202b36c8ac0d1266eb359a1ee3" and "894def692c87051c3ac1ba108a4ef5ed2668215a" have entirely different histories.
8a9d5414f8
...
894def692c
2 changed files with 117 additions and 165 deletions
|
@ -1,54 +1,18 @@
|
|||
// "EntityManager": Create entity objects and apply transformations to them.
|
||||
// A bold attempt at functional programming in a videogame entity system
|
||||
// + Functions are here, as to avoid storing unecessary data in the server store.
|
||||
import { makePuppet, puppet, puppetEntry } from "./Puppetmaster";
|
||||
// import { ability } from "./AbilityManager";
|
||||
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
|
||||
// I spent a lot of time thinking about the names for these types and they still suck
|
||||
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 statusEffect = readonly [string, modifiers, entityTransformTemplate, number];
|
||||
type entityTag = typeof entityTags[number]
|
||||
export interface entityId {
|
||||
readonly name: string;
|
||||
readonly team: "players" | "enemies";
|
||||
readonly tags: {
|
||||
[tagName in entityTag]: boolean;
|
||||
};
|
||||
}
|
||||
export interface entityProperties {
|
||||
readonly id: entityId,
|
||||
readonly baseModifiers: modifiers,
|
||||
readonly maxStatuses: statuses,
|
||||
readonly baseStatusEffects: statusEffect[],// Permanent immunities/status effects
|
||||
}
|
||||
export interface entityStatuses {
|
||||
readonly statuses: statuses; // Health and stuff that change constantly
|
||||
readonly statusEffects: statusEffect[]; // Burn, poison, etc.
|
||||
}
|
||||
|
||||
type entityTransformType = "support" | "attack";
|
||||
type healthTransformValue = [magnitude: number, affectsHealth: boolean, affectsBarrier: boolean]
|
||||
interface entityTransformTemplate {
|
||||
healthTransformValue: healthTransformValue,
|
||||
statusEffectsGranted?: placeholder[]; // TBA (Stuff like burn, slow, stun, etc.)
|
||||
}
|
||||
type entityTransformDeterminer = (
|
||||
entityPerformingId: entityId,
|
||||
entityReceivingId: entityId,
|
||||
entityPerformingPuppet: puppet,
|
||||
entityReceivingPuppet: puppet,
|
||||
) => [entityTransformType, entityTransformTemplate] | false;
|
||||
interface ability {
|
||||
placeholder: entityTransformDeterminer; // TBA (Most abilities will not be a single instant of damage/heal)
|
||||
}
|
||||
// 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)
|
||||
|
||||
export interface entityController {//Deprecated
|
||||
export interface entityController {
|
||||
setPosition: (location: Vector3) => void;
|
||||
useAbility: (abilityName: string, activating: boolean) => void;
|
||||
}
|
||||
|
||||
export interface entityModifier {}// Deprecated
|
||||
export interface entityModifier {}
|
||||
/* Prototype
|
||||
entityTransform(setOfAllEntities: entity[], actingEntity: entity, ability destructured to {hitBoxList, modificationList})
|
||||
const entitiesEligible = getAffectedEntities(setOfAllEntities, actingEntity)
|
||||
|
@ -67,6 +31,25 @@ export interface entityModifier {}// Deprecated
|
|||
const ableToUse = completeAbility ? canUseAbility(now, completeAbility) : completeAbility // canUseAbility should return the first abilityPart
|
||||
return resultOfFirstAbilityPart = ableToUse ? useAbilityPart(ability.firstPart, setOfAllEntities, thisEntity) : [ableToUse]
|
||||
*/
|
||||
interface entityStats {
|
||||
statuses: statuses; // Health and stuff that change constantly
|
||||
/* immunities: {
|
||||
[statusEffectName: string]: boolean | undefined;
|
||||
};*/
|
||||
statusEffects: statusEffect[]; // Burn, poison, etc.
|
||||
}
|
||||
interface entityId {
|
||||
readonly name: string;
|
||||
readonly team: "players" | "enemies";
|
||||
readonly isMinion: boolean;
|
||||
}
|
||||
export interface entity {
|
||||
readonly id: entityId;
|
||||
stats: entityStats;
|
||||
baseModifiers: modifiers; // Base modifiers that change only when the player switches weapons or something
|
||||
maxStatuses: statuses;
|
||||
puppet: puppet;
|
||||
}
|
||||
|
||||
// type entityTransform = (entity: entity) => entity;
|
||||
// interface entityTransformTemplate {
|
||||
|
@ -82,6 +65,23 @@ export interface entityModifier {}// Deprecated
|
|||
excludesSelf: boolean,
|
||||
affectsSameTeam: boolean,
|
||||
*/ //}
|
||||
type entityTransformType = "heal" | "attack";
|
||||
interface entityTransformTemplate {
|
||||
thingus: entityTransformType;
|
||||
magnitude: number;
|
||||
affectsHealth: boolean;
|
||||
affectsBarrier: boolean;
|
||||
statusEffectsGranted?: placeholder[]; // TBA (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]
|
||||
/*function isEligibleForTransform(entityPerformingTransform: entity, entityReceivingTransform: entity, eligibilityTemplate: entityTransformEligibilityTemplate): false | [boolean] { // This function sucks
|
||||
const entityReceivingId = entityReceivingTransform.id
|
||||
|
@ -100,57 +100,27 @@ export interface entityModifier {}// Deprecated
|
|||
}
|
||||
return [onSameTeam]
|
||||
}*/
|
||||
|
||||
/*function applyAttackToEntityStats() {//else if (transformType === "heal") {
|
||||
// Apply receiver's status effects to incoming heal
|
||||
const newEntityStatuses = applyHealToStatuses(
|
||||
entityStats.statuses, // DRY alert
|
||||
magnitude,
|
||||
entityTransformTemplate.affectsHealth,
|
||||
entityTransformTemplate.affectsBarrier,
|
||||
maxStatuses,
|
||||
);
|
||||
// + Add or remove status effects (don't compare against immunities)
|
||||
return {
|
||||
statuses: newEntityStatuses,
|
||||
statusEffects: entityStats.statusEffects, // Placeholder
|
||||
};
|
||||
}*/
|
||||
function applyEntityToAttack(
|
||||
entityModifiers: modifiers,
|
||||
entityStatusEffects: statusEffect[],
|
||||
magnitude: number,
|
||||
): number {
|
||||
const attack = applyModifiersToAttack(magnitude, entityModifiers);
|
||||
// + Apply status effects of performing entity to attack (e.g. weaken)
|
||||
return attack;
|
||||
}
|
||||
function applyAttackToEntityStatuses(
|
||||
entityStatuses: statuses,
|
||||
entityTransformTemplate: entityTransformTemplate,
|
||||
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)
|
||||
const modifiedDamage = applyModifiersToDamage(entityTransformTemplate.magnitude, entityModifiers);
|
||||
const newStatuses = applyDamageToStatuses(entityStatuses, modifiedDamage, entityTransformTemplate.affectsHealth, entityTransformTemplate.affectsBarrier);
|
||||
return entityStatuses;
|
||||
}
|
||||
/*function applyHealToEntityStats() {//if (transformType === "attack") {
|
||||
// Apply receiver's status effects to incoming damage
|
||||
const incomingDamage = applyModifiersToDamage(magnitude, baseModifiers);
|
||||
const newEntityStatuses = applyDamageToStatuses(
|
||||
entityStats.statuses,
|
||||
incomingDamage,
|
||||
entityTransformTemplate.affectsHealth,
|
||||
entityTransformTemplate.affectsBarrier,
|
||||
);
|
||||
// + Add or remove status effects (compare against immunities)
|
||||
return {
|
||||
statuses: newEntityStatuses,
|
||||
statusEffects: entityStats.statusEffects, // Placeholder
|
||||
};
|
||||
} */
|
||||
function applyHealToEntityStatuses(
|
||||
entityStatuses: statuses,
|
||||
entityTransformTemplate: entityTransformTemplate,
|
||||
entityModifiers: modifiers,
|
||||
entityMaxStatuses: statuses,
|
||||
entityStatusEffects: statusEffect[],
|
||||
): statuses {
|
||||
// + Apply status effects of receiving entity to damage (e.g. heal up)
|
||||
const newStatuses = applyHealToStatuses(entityStatuses, entityTransformTemplate.magnitude, entityTransformTemplate.affectsHealth, entityTransformTemplate.affectsBarrier, entityMaxStatuses);
|
||||
const damage = applyModifiersToAttack(attack, entityModifiers);
|
||||
const newStatuses = applyDamageToStatuses(entityStatuses, damage, affectsHealth, affectsBarrier);
|
||||
return entityStatuses;
|
||||
}
|
||||
// ? Are you meant to pipe determineEntityTransform into the resulting function?
|
||||
|
@ -161,33 +131,53 @@ function applyHealToEntityStatuses(
|
|||
return false; // Placeholder
|
||||
}
|
||||
*/
|
||||
function applyEntityToAttack(
|
||||
entityModifiers: modifiers,
|
||||
entityStatusEffects: statusEffect[],
|
||||
entityTransformTemplate: entityTransformTemplate,
|
||||
): entityTransformTemplate {
|
||||
const outgoingAttack = applyModifiersToAttack(entityTransformTemplate.magnitude, entityModifiers);
|
||||
// + Apply user's status effects to outgoing attack
|
||||
// (Old; same thing) + Apply status effects of performing entity to attack (e.g. weaken)
|
||||
entityTransformTemplate.magnitude = outgoingAttack; // Mutating is cringe, but the other parts of the transform need to be defined first
|
||||
return entityTransformTemplate;
|
||||
}
|
||||
function applyEntityToHeal(
|
||||
entityModifiers: modifiers,
|
||||
entityStatusEffects: statusEffect[],
|
||||
entityTransformTemplate: entityTransformTemplate,
|
||||
): entityTransformTemplate {
|
||||
// + Apply user's status effects to outgoing heal
|
||||
return entityTransformTemplate // There could be a heal modifier later
|
||||
}
|
||||
/*function transformEntityStats(
|
||||
function transformEntityStats(
|
||||
entityPerformingTransform: entity,
|
||||
entityStats: entityStats,
|
||||
entityTransformTemplate: entityTransformTemplate,
|
||||
baseModifiers: modifiers,
|
||||
maxStatuses: statuses,
|
||||
): entityStats {
|
||||
const magnitude = entityTransformTemplate.magnitude*/
|
||||
//}
|
||||
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 {
|
||||
specificEntity?: string,
|
||||
team: "players" | "enemies",
|
||||
|
@ -307,61 +297,28 @@ function getEntityByName(entityList: entity[], entityName: string): success<enti
|
|||
return [false, "Entity not found"];
|
||||
}
|
||||
function getAbility() {}
|
||||
function applyEntityTransformToEntityStatuses(entityTransform: entityTransformTemplate, statuses: statuses, maxStatuses: statuses) {
|
||||
|
||||
}
|
||||
function applyEntityTransformToEntityList(
|
||||
entityList: entity[],
|
||||
entityTransformDeterminer: entityTransformDeterminer,
|
||||
entityPerformingTransform: entity,
|
||||
aim: Vector3,
|
||||
): [entity[], placeholder[]?] {
|
||||
const entityPerformingTransformModifiers = entityPerformingTransform.baseModifiers
|
||||
const entityPerformingTransformStatusEffects = entityPerformingTransform.stats.statusEffects // Not good
|
||||
const newEntityList = entityList.map(function (entityReceivingTransform: entity): entity {
|
||||
const entityTransform = entityTransformDeterminer(
|
||||
const entityTransformTemplate = entityTransformDeterminer(
|
||||
entityPerformingTransform.id,
|
||||
entityReceivingTransform.id,
|
||||
entityPerformingTransform.puppet,
|
||||
entityReceivingTransform.puppet,
|
||||
);
|
||||
if (entityTransform) {
|
||||
const entityStatuses = entityPerformingTransform.stats.statuses
|
||||
const [entityTransformType, entityTransformTemplate] = entityTransform
|
||||
if (entityTransformType === "attack") {
|
||||
const outgoingTransformTemplate = applyEntityToAttack(
|
||||
entityPerformingTransformModifiers,
|
||||
entityPerformingTransformStatusEffects,
|
||||
entityTransformTemplate
|
||||
);
|
||||
const newEntityStatuses = outgoingTransformTemplate
|
||||
? applyAttackToEntityStatuses(
|
||||
entityStatuses,
|
||||
entityTransformTemplate,
|
||||
entityReceivingTransform.baseModifiers,
|
||||
entityReceivingTransform.stats.statusEffects, // This is also cringe
|
||||
)
|
||||
: entityStatuses; // Also cringe
|
||||
} else {
|
||||
assert(entityTransformType === "heal")
|
||||
const outgoingTransformTemplate = applyEntityToHeal( // Lots of DRY violations here
|
||||
entityPerformingTransformModifiers,
|
||||
entityPerformingTransformStatusEffects,
|
||||
entityTransformTemplate
|
||||
);
|
||||
const newEntityStatuses = outgoingTransformTemplate
|
||||
? applyHealToEntityStatuses(
|
||||
entityStatuses,
|
||||
entityTransformTemplate,
|
||||
entityReceivingTransform.baseModifiers,
|
||||
entityReceivingTransform.maxStatuses,
|
||||
entityReceivingTransform.stats.statusEffects, // So much cringe
|
||||
)
|
||||
: entityStatuses;
|
||||
}
|
||||
} else {
|
||||
return entityReceivingTransform;
|
||||
}
|
||||
const newEntityStats = entityTransformTemplate
|
||||
? transformEntityStats(
|
||||
entityPerformingTransform,
|
||||
entityReceivingTransform.stats,
|
||||
entityTransformTemplate,
|
||||
entityReceivingTransform.baseModifiers,
|
||||
entityReceivingTransform.maxStatuses,
|
||||
)
|
||||
: entityReceivingTransform.stats;
|
||||
});
|
||||
return [newEntityList];
|
||||
}
|
||||
|
@ -429,4 +386,4 @@ class entityManager extends actorClass<entityManagerRequest> {
|
|||
export function initEntityManager() {
|
||||
return new entityManager();
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// "The": Handle events.
|
||||
import { entityId, entityProperties, entityStatuses/*entityManagerRequest /*makeEntity, entityController*/ } from "./EntityManager";
|
||||
import { entity, getEntityByName/*entityManagerRequest /*makeEntity, entityController*/ } from "./EntityManager";
|
||||
import { applyRequestsToPlayerStorage, playerManagerRequest } from "./PlayerManager"
|
||||
export type sceneManagerRequest = [Player, "useAbility", Vector3] | [Player, "foo", "bar"]
|
||||
type endConditionFunction = (containedScenes: scene[], containedEntities: entity[], timeElapsed: number) => boolean
|
||||
|
@ -8,16 +8,11 @@ export interface sceneTemplate {
|
|||
readonly onCompletion: readonly playerManagerRequest[] // Requests to get sent out when the scene ends
|
||||
}
|
||||
export interface scene extends sceneTemplate {
|
||||
/*containedScenes?: {
|
||||
containedScenes?: {
|
||||
[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
|
||||
*///Scene nesting is currently cancelled
|
||||
containedEntities: string[]; // "A list of entities that need to die to complete the event" (not really but kinda)
|
||||
containedEntityProperties: {
|
||||
[entityName: string]: entityProperties
|
||||
}
|
||||
containedEntityStatuses
|
||||
containedEntities: entity[]; // A list of entities that need to die to complete the event
|
||||
players: {
|
||||
[userId: number]: [inThisScene: true] | [inThisScene: false, subScene: string] | undefined
|
||||
}
|
||||
|
@ -26,7 +21,7 @@ export interface scene extends sceneTemplate {
|
|||
export function runScene(scene: scene, now: number): success<[scene, playerManagerRequest[]]> {
|
||||
return [true, [scene, []]];
|
||||
}
|
||||
/*function getPlayerSceneName(scene: scene, userId: number): success<string | false> {
|
||||
function getPlayerSceneName(scene: scene, userId: number): success<string | false> {
|
||||
let playerSceneLocation = scene.players[userId];
|
||||
if (!playerSceneLocation) {
|
||||
return [false, "Player does not exist"]; // Some kind of error needs to go here
|
||||
|
@ -35,9 +30,9 @@ export function runScene(scene: scene, now: number): success<[scene, playerManag
|
|||
} else {
|
||||
return [true, playerSceneLocation[1]]
|
||||
}
|
||||
}*/
|
||||
}
|
||||
function applyRequestToScene(scene: scene, now: number, request: sceneManagerRequest): [scene, playerManagerRequest[]] {
|
||||
/*const playerSceneResult = getPlayerSceneName(scene, request[0].UserId)
|
||||
const playerSceneResult = getPlayerSceneName(scene, request[0].UserId)
|
||||
if (!playerSceneResult[0]) {
|
||||
return [scene, []]; // Some kind of error needs to go here
|
||||
}
|
||||
|
@ -58,7 +53,7 @@ function applyRequestToScene(scene: scene, now: number, request: sceneManagerReq
|
|||
containedScenes[playerSceneName] = sceneRequestResult[0] // This is questionably object-oriented
|
||||
scene.containedScenes = containedScenes
|
||||
return [scene, sceneRequestResult[1]]
|
||||
}*/
|
||||
}
|
||||
}
|
||||
export function applyRequestsToScene(scene: scene, now: number, requests: sceneManagerRequest[]): success<[scene, playerManagerRequest[]]> {
|
||||
try {
|
||||
|
|
Loading…
Reference in a new issue