More corner case checks for battle interaction
This commit is contained in:
		
							parent
							
								
									2707c46df1
								
							
						
					
					
						commit
						7b13615614
					
				@ -330,6 +330,8 @@ void debug_drawUi(GameState *state, f32 dt)
 | 
				
			|||||||
	DEBUG_PUSH_VAR("glDrawArray Calls: %d",
 | 
						DEBUG_PUSH_VAR("glDrawArray Calls: %d",
 | 
				
			||||||
	               GLOBAL_debug.callCount[debugcallcount_drawArrays], "i32");
 | 
						               GLOBAL_debug.callCount[debugcallcount_drawArrays], "i32");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i32 debug_bAllocated = state->arena.bytesAllocated;
 | 
				
			||||||
 | 
						DEBUG_PUSH_VAR("TotalMemoryAllocated: %db", debug_bAllocated, "i32");
 | 
				
			||||||
	i32 debug_kbAllocated = state->arena.bytesAllocated / 1024;
 | 
						i32 debug_kbAllocated = state->arena.bytesAllocated / 1024;
 | 
				
			||||||
	DEBUG_PUSH_VAR("TotalMemoryAllocated: %dkb", debug_kbAllocated, "i32");
 | 
						DEBUG_PUSH_VAR("TotalMemoryAllocated: %dkb", debug_kbAllocated, "i32");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -45,7 +45,7 @@ INTERNAL Entity *addEntity(MemoryArena *arena, World *world, v2 pos, v2 size,
 | 
				
			|||||||
		    entity.stats->health           = entity.stats->maxHealth;
 | 
							    entity.stats->health           = entity.stats->maxHealth;
 | 
				
			||||||
		    entity.stats->actionRate       = 100;
 | 
							    entity.stats->actionRate       = 100;
 | 
				
			||||||
		    entity.stats->actionTimer      = entity.stats->actionRate;
 | 
							    entity.stats->actionTimer      = entity.stats->actionRate;
 | 
				
			||||||
		    entity.stats->actionSpdMul     = 100;
 | 
							    entity.stats->actionSpdMul     = 1000;
 | 
				
			||||||
		    entity.stats->entityIdToAttack = -1;
 | 
							    entity.stats->entityIdToAttack = -1;
 | 
				
			||||||
		    entity.stats->queuedAttack     = entityattack_invalid;
 | 
							    entity.stats->queuedAttack     = entityattack_invalid;
 | 
				
			||||||
		    entity.state                   = entitystate_idle;
 | 
							    entity.state                   = entitystate_idle;
 | 
				
			||||||
@ -450,7 +450,7 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// TODO(doyle): Revisit key input with state checking for last ended down
 | 
							// TODO(doyle): Revisit key input with state checking for last ended down
 | 
				
			||||||
		if (state->keys[GLFW_KEY_SPACE] && !spaceBarWasDown)
 | 
							if (state->keys[GLFW_KEY_SPACE])
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Renderer *renderer = &state->renderer;
 | 
								Renderer *renderer = &state->renderer;
 | 
				
			||||||
			f32 yPos = CAST(f32)(rand() % CAST(i32)renderer->size.h);
 | 
								f32 yPos = CAST(f32)(rand() % CAST(i32)renderer->size.h);
 | 
				
			||||||
@ -460,10 +460,6 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
 | 
				
			|||||||
			addGenericMob(&state->arena, &state->assetManager, world, pos);
 | 
								addGenericMob(&state->arena, &state->assetManager, world, pos);
 | 
				
			||||||
			spaceBarWasDown = TRUE;
 | 
								spaceBarWasDown = TRUE;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else if (!state->keys[GLFW_KEY_SPACE])
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			spaceBarWasDown = FALSE;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NOTE(doyle): Clipping threshold for snapping velocity to 0
 | 
						// NOTE(doyle): Clipping threshold for snapping velocity to 0
 | 
				
			||||||
@ -507,10 +503,13 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
 | 
				
			|||||||
	b32 heroCollided = FALSE;
 | 
						b32 heroCollided = FALSE;
 | 
				
			||||||
	if (hero->collides == TRUE)
 | 
						if (hero->collides == TRUE)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		for (i32 i = 0; i < world->freeEntityIndex; i++)
 | 
							for (i32 i = 0; i < world->maxEntities; i++)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (i == world->heroIndex) continue;
 | 
								if (i == world->heroIndex) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Entity entity = world->entities[i];
 | 
								Entity entity = world->entities[i];
 | 
				
			||||||
 | 
								if (entity.state == entitystate_dead) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (entity.collides)
 | 
								if (entity.collides)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				v4 heroRect =
 | 
									v4 heroRect =
 | 
				
			||||||
@ -623,7 +622,10 @@ INTERNAL i32 findBestEntityToAttack(World *world, Entity attacker)
 | 
				
			|||||||
	if (attacker.type == entitytype_mob)
 | 
						if (attacker.type == entitytype_mob)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Entity hero = world->entities[world->heroIndex];
 | 
							Entity hero = world->entities[world->heroIndex];
 | 
				
			||||||
		result = hero.id;
 | 
					
 | 
				
			||||||
 | 
							if (hero.state == entitystate_dead) result = ENTITY_NULL_ID;
 | 
				
			||||||
 | 
							else result = hero.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -666,6 +668,16 @@ INTERNAL inline void updateWorldBattleEntities(World *world, Entity *entity,
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INTERNAL inline void resetEntityState(World *world, Entity *entity)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
 | 
				
			||||||
 | 
						setActiveEntityAnim(entity, animlist_hero_idle);
 | 
				
			||||||
 | 
						entity->stats->busyDuration     = 0;
 | 
				
			||||||
 | 
						entity->stats->actionTimer      = entity->stats->actionRate;
 | 
				
			||||||
 | 
						entity->stats->queuedAttack     = entityattack_invalid;
 | 
				
			||||||
 | 
						entity->stats->entityIdToAttack = ENTITY_NULL_ID;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
INTERNAL void entityStateSwitch(World *world, Entity *entity,
 | 
					INTERNAL void entityStateSwitch(World *world, Entity *entity,
 | 
				
			||||||
                                enum EntityState newState)
 | 
					                                enum EntityState newState)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -691,8 +703,8 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
 | 
				
			|||||||
		// attacking it since there's no check before attack if entity is idle
 | 
							// attacking it since there's no check before attack if entity is idle
 | 
				
			||||||
		// or not (i.e. has moved out of frame last frame).
 | 
							// or not (i.e. has moved out of frame last frame).
 | 
				
			||||||
		case entitystate_dead:
 | 
							case entitystate_dead:
 | 
				
			||||||
			updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
 | 
					 | 
				
			||||||
			setActiveEntityAnim(entity, animlist_hero_idle);
 | 
								setActiveEntityAnim(entity, animlist_hero_idle);
 | 
				
			||||||
 | 
								entity->stats->busyDuration     = 0;
 | 
				
			||||||
			entity->stats->actionTimer      = entity->stats->actionRate;
 | 
								entity->stats->actionTimer      = entity->stats->actionRate;
 | 
				
			||||||
			entity->stats->queuedAttack     = entityattack_invalid;
 | 
								entity->stats->queuedAttack     = entityattack_invalid;
 | 
				
			||||||
			entity->stats->entityIdToAttack = ENTITY_NULL_ID;
 | 
								entity->stats->entityIdToAttack = ENTITY_NULL_ID;
 | 
				
			||||||
@ -709,14 +721,16 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
 | 
				
			|||||||
		switch (newState)
 | 
							switch (newState)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
		case entitystate_attack:
 | 
							case entitystate_attack:
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								EntityAnim_ attackAnim = entity->anim[entity->stats->queuedAttack];
 | 
				
			||||||
 | 
								f32 busyDuration       = attackAnim.anim->frameDuration *
 | 
				
			||||||
 | 
								                   CAST(f32) attackAnim.anim->numFrames;
 | 
				
			||||||
 | 
								entity->stats->busyDuration = busyDuration;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		case entitystate_idle:
 | 
							case entitystate_idle:
 | 
				
			||||||
		case entitystate_dead:
 | 
							case entitystate_dead:
 | 
				
			||||||
			updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
 | 
								resetEntityState(world, entity);
 | 
				
			||||||
			setActiveEntityAnim(entity, animlist_hero_idle);
 | 
					 | 
				
			||||||
			entity->stats->actionTimer      = entity->stats->actionRate;
 | 
					 | 
				
			||||||
			entity->stats->queuedAttack     = entityattack_invalid;
 | 
					 | 
				
			||||||
			entity->stats->entityIdToAttack = ENTITY_NULL_ID;
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
#ifdef DENGINE_DEBUG
 | 
					#ifdef DENGINE_DEBUG
 | 
				
			||||||
@ -728,27 +742,14 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
 | 
				
			|||||||
		switch (newState)
 | 
							switch (newState)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
		case entitystate_battle:
 | 
							case entitystate_battle:
 | 
				
			||||||
 | 
								setActiveEntityAnim(entity, animlist_hero_battlePose);
 | 
				
			||||||
			entity->stats->actionTimer  = entity->stats->actionRate;
 | 
								entity->stats->actionTimer  = entity->stats->actionRate;
 | 
				
			||||||
			entity->stats->busyDuration = 0;
 | 
								entity->stats->busyDuration = 0;
 | 
				
			||||||
			setActiveEntityAnim(entity, animlist_hero_battlePose);
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case entitystate_dead:
 | 
					 | 
				
			||||||
			// TODO(doyle): Repeated logic with battle -> dead
 | 
					 | 
				
			||||||
			updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
 | 
					 | 
				
			||||||
			setActiveEntityAnim(entity, animlist_hero_wave);
 | 
					 | 
				
			||||||
			entity->stats->actionTimer      = entity->stats->actionRate;
 | 
					 | 
				
			||||||
			entity->stats->queuedAttack     = entityattack_invalid;
 | 
					 | 
				
			||||||
			entity->stats->entityIdToAttack = ENTITY_NULL_ID;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// NOTE(doyle): Entity has been forced out of an attack (out of range)
 | 
							// NOTE(doyle): Entity has been forced out of an attack (out of range)
 | 
				
			||||||
		case entitystate_idle:
 | 
							case entitystate_idle:
 | 
				
			||||||
			updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
 | 
							case entitystate_dead:
 | 
				
			||||||
			setActiveEntityAnim(entity, animlist_hero_idle);
 | 
								resetEntityState(world, entity);
 | 
				
			||||||
			entity->stats->busyDuration     = 0;
 | 
					 | 
				
			||||||
			entity->stats->actionTimer      = entity->stats->actionRate;
 | 
					 | 
				
			||||||
			entity->stats->queuedAttack     = entityattack_invalid;
 | 
					 | 
				
			||||||
			entity->stats->entityIdToAttack = ENTITY_NULL_ID;
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
#ifdef DENGINE_DEBUG
 | 
					#ifdef DENGINE_DEBUG
 | 
				
			||||||
@ -777,24 +778,18 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
 | 
				
			|||||||
	entity->state = newState;
 | 
						entity->state = newState;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
INTERNAL void beginAttack(Entity *attacker)
 | 
					INTERNAL void beginAttack(World *world, Entity *attacker)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef DENGINE_DEBUG
 | 
					#ifdef DENGINE_DEBUG
 | 
				
			||||||
	ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
 | 
						ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						entityStateSwitch(world, attacker, entitystate_attack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	attacker->state = entitystate_attack;
 | 
					 | 
				
			||||||
	switch (attacker->stats->queuedAttack)
 | 
						switch (attacker->stats->queuedAttack)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
	case entityattack_tackle:
 | 
						case entityattack_tackle:
 | 
				
			||||||
		EntityAnim_ attackAnim = attacker->anim[animlist_hero_tackle];
 | 
					 | 
				
			||||||
		f32 busyDuration = attackAnim.anim->frameDuration *
 | 
					 | 
				
			||||||
		                   CAST(f32) attackAnim.anim->numFrames;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		attacker->stats->busyDuration = busyDuration;
 | 
					 | 
				
			||||||
		setActiveEntityAnim(attacker, animlist_hero_tackle);
 | 
							setActiveEntityAnim(attacker, animlist_hero_tackle);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (attacker->direction == direction_east)
 | 
							if (attacker->direction == direction_east)
 | 
				
			||||||
			attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
 | 
								attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
@ -836,6 +831,7 @@ INTERNAL void endAttack(World *world, Entity *attacker)
 | 
				
			|||||||
	Entity *defender = NULL;
 | 
						Entity *defender = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO(doyle): Implement faster lookup for entity id in entity table
 | 
						// TODO(doyle): Implement faster lookup for entity id in entity table
 | 
				
			||||||
 | 
						b32 noMoreValidTargets = FALSE;
 | 
				
			||||||
	do
 | 
						do
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		/* Get target entity to attack */
 | 
							/* Get target entity to attack */
 | 
				
			||||||
@ -845,31 +841,32 @@ INTERNAL void endAttack(World *world, Entity *attacker)
 | 
				
			|||||||
			if (world->entities[i].id == entityIdToAttack)
 | 
								if (world->entities[i].id == entityIdToAttack)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				defender = &world->entities[i];
 | 
									defender = &world->entities[i];
 | 
				
			||||||
 | 
					#ifdef DENGINE_DEBUG
 | 
				
			||||||
 | 
									ASSERT(defender->type == entitytype_mob ||
 | 
				
			||||||
 | 
									       defender->type == entitytype_hero);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* If no longer exists, find next best */
 | 
							/* If no longer exists, find next best */
 | 
				
			||||||
		if (!defender)
 | 
							if (!defender)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (world->numEntitiesInBattle > 1)
 | 
								i32 entityIdToAttack = findBestEntityToAttack(world, *attacker);
 | 
				
			||||||
 | 
								if (entityIdToAttack == ENTITY_NULL_ID)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									noMoreValidTargets = TRUE;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				attacker->stats->entityIdToAttack =
 | 
									attacker->stats->entityIdToAttack =
 | 
				
			||||||
				    findBestEntityToAttack(world, *attacker);
 | 
									    findBestEntityToAttack(world, *attacker);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
#ifdef DENGINE_DEBUG
 | 
					 | 
				
			||||||
				ASSERT(INVALID_CODE_PATH);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		}
 | 
						} while (!defender && noMoreValidTargets == TRUE);
 | 
				
			||||||
	}	while (!defender);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO(doyle): Very susceptible to bugs- ensure if defender is dead the
 | 
						enum EntityState newAttackerState = entitystate_invalid;
 | 
				
			||||||
	// attacker immediately locates a new target or exits battle mode. But this
 | 
						if (!noMoreValidTargets)
 | 
				
			||||||
	// is necessary since it's possible that by the time the hero is ready to
 | 
					 | 
				
			||||||
	// apply attack damage another person may of killed it (i.e. party member)
 | 
					 | 
				
			||||||
	if (defender)
 | 
					 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		// TODO(doyle): Use attacker stats in battle equations
 | 
							// TODO(doyle): Use attacker stats in battle equations
 | 
				
			||||||
		if (attacker->type == entitytype_hero)
 | 
							if (attacker->type == entitytype_hero)
 | 
				
			||||||
@ -888,10 +885,16 @@ INTERNAL void endAttack(World *world, Entity *attacker)
 | 
				
			|||||||
			attacker->stats->entityIdToAttack =
 | 
								attacker->stats->entityIdToAttack =
 | 
				
			||||||
			    findBestEntityToAttack(world, *attacker);
 | 
								    findBestEntityToAttack(world, *attacker);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newAttackerState = entitystate_battle;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							newAttackerState = entitystate_idle;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Return attacker back to non-attacking state */
 | 
						/* Return attacker back to non-attacking state */
 | 
				
			||||||
	entityStateSwitch(world, attacker, entitystate_battle);
 | 
						entityStateSwitch(world, attacker, newAttackerState);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -933,7 +936,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
 | 
				
			|||||||
			// TODO(doyle): Accumulate all dead entities and delete at the
 | 
								// TODO(doyle): Accumulate all dead entities and delete at the
 | 
				
			||||||
			// end. Hence resort/organise entity array once, not every time
 | 
								// end. Hence resort/organise entity array once, not every time
 | 
				
			||||||
			// an entity dies
 | 
								// an entity dies
 | 
				
			||||||
#if 0
 | 
					#if 1
 | 
				
			||||||
			i32 entityIndexInArray = i;
 | 
								i32 entityIndexInArray = i;
 | 
				
			||||||
			deleteEntity(&state->arena, world, entityIndexInArray);
 | 
								deleteEntity(&state->arena, world, entityIndexInArray);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -996,7 +999,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
 | 
				
			|||||||
					if (stats->queuedAttack == entityattack_invalid)
 | 
										if (stats->queuedAttack == entityattack_invalid)
 | 
				
			||||||
						stats->queuedAttack = entityattack_tackle;
 | 
											stats->queuedAttack = entityattack_tackle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					beginAttack(entity);
 | 
										beginAttack(world, entity);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			else if (entity->state == entitystate_attack)
 | 
								else if (entity->state == entitystate_attack)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user