Add support for more attack-animations
Exploratory code to determine how to architect the engine for increasing requirements in game play.
This commit is contained in:
		
							parent
							
								
									fc6f6e086f
								
							
						
					
					
						commit
						5c4f493979
					
				| @ -62,9 +62,6 @@ inline char *debug_entityattack_string(i32 val) | ||||
| 	char *string; | ||||
| 	switch(val) | ||||
| 	{ | ||||
| 		case entityattack_tackle: | ||||
| 			string = "EntityAttack_Tackle"; | ||||
| 			break; | ||||
| 		case entityattack_count: | ||||
| 			string = "EntityAttack_Count (Error!)"; | ||||
| 			break; | ||||
|  | ||||
							
								
								
									
										24
									
								
								src/Entity.c
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/Entity.c
									
									
									
									
									
								
							| @ -3,12 +3,14 @@ | ||||
| #include "Dengine/Platform.h" | ||||
| #include "Dengine/WorldTraveller.h" | ||||
| 
 | ||||
| void entity_setActiveAnim(Entity *const entity, const char *const animName) | ||||
| void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity, | ||||
|                           const char *const animName) | ||||
| { | ||||
| 	/* Reset current anim data */ | ||||
| 	EntityAnim *currEntityAnim   = &entity->animList[entity->currAnimId]; | ||||
| 	currEntityAnim->currDuration = currEntityAnim->anim->frameDuration; | ||||
| 	currEntityAnim->currFrame    = 0; | ||||
| 	EntityAnim *currEntityAnim     = &entity->animList[entity->currAnimId]; | ||||
| 	currEntityAnim->currDuration   = currEntityAnim->anim->frameDuration; | ||||
| 	currEntityAnim->currFrame      = 0; | ||||
| 	currEntityAnim->timesCompleted = 0; | ||||
| 
 | ||||
| 	/* Set entity active animation */ | ||||
| 	for (i32 i = 0; i < ARRAY_COUNT(entity->animList); i++) | ||||
| @ -24,6 +26,9 @@ void entity_setActiveAnim(Entity *const entity, const char *const animName) | ||||
| 				newEntityAnim->currDuration = | ||||
| 				    newEntityAnim->anim->frameDuration; | ||||
| 				newEntityAnim->currFrame = 0; | ||||
| 
 | ||||
| 				worldTraveller_registerEvent(eventQueue, eventtype_start_anim, | ||||
| 				                             newEntityAnim); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| @ -32,7 +37,8 @@ void entity_setActiveAnim(Entity *const entity, const char *const animName) | ||||
| 	DEBUG_LOG("Entity does not have access to desired anim"); | ||||
| } | ||||
| 
 | ||||
| void entity_updateAnim(Entity *const entity, const f32 dt) | ||||
| void entity_updateAnim(EventQueue *eventQueue, Entity *const entity, | ||||
|                        const f32 dt) | ||||
| { | ||||
| 	if (!entity->tex) | ||||
| 		return; | ||||
| @ -45,6 +51,13 @@ void entity_updateAnim(Entity *const entity, const f32 dt) | ||||
| 	{ | ||||
| 		currEntityAnim->currFrame++; | ||||
| 		currEntityAnim->currFrame = currEntityAnim->currFrame % anim->numFrames; | ||||
| 		if (currEntityAnim->currFrame == 0) | ||||
| 		{ | ||||
| 			worldTraveller_registerEvent(eventQueue, eventtype_end_anim, | ||||
| 			                             currEntityAnim); | ||||
| 			currEntityAnim->timesCompleted++; | ||||
| 		} | ||||
| 
 | ||||
| 		currEntityAnim->currDuration = anim->frameDuration; | ||||
| 	} | ||||
| 
 | ||||
| @ -138,6 +151,7 @@ Entity *const entity_add(MemoryArena *const arena, World *const world, | ||||
| 		break; | ||||
| 	} | ||||
| 	case entitytype_projectile: | ||||
| 	case entitytype_attackObject: | ||||
| 		entity.stats               = PLATFORM_MEM_ALLOC(arena, 1, EntityStats); | ||||
| 		entity.stats->maxHealth    = 100; | ||||
| 		entity.stats->health       = entity.stats->maxHealth; | ||||
|  | ||||
| @ -13,37 +13,14 @@ enum State | ||||
| 	state_win, | ||||
| }; | ||||
| 
 | ||||
| enum EventType | ||||
| { | ||||
| 	eventtype_null = 0, | ||||
| 	eventtype_start_attack, | ||||
| 	eventtype_end_attack, | ||||
| 	eventtype_start_anim, | ||||
| 	eventtype_end_anim, | ||||
| 	eventtype_entity_died, | ||||
| 	eventtype_count, | ||||
| }; | ||||
| 
 | ||||
| typedef struct Event | ||||
| { | ||||
| 	enum EventType type; | ||||
| 	void *data; | ||||
| } Event; | ||||
| 
 | ||||
| typedef struct EventQueue | ||||
| { | ||||
| 	Event queue[1024]; | ||||
| 	i32 numEvents; | ||||
| } EventQueue; | ||||
| 
 | ||||
| INTERNAL Entity *getHeroEntity(World *world) | ||||
| { | ||||
| 	Entity *result = &world->entities[entity_getIndex(world, world->heroId)]; | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| INTERNAL void addGenericMob(MemoryArena *arena, AssetManager *assetManager, | ||||
|                             World *world, v2 pos) | ||||
| INTERNAL void addGenericMob(EventQueue *eventQueue, MemoryArena *arena, | ||||
|                             AssetManager *assetManager, World *world, v2 pos) | ||||
| { | ||||
| #ifdef DENGINE_DEBUG | ||||
| 	DEBUG_LOG("Mob entity spawned"); | ||||
| @ -71,8 +48,17 @@ INTERNAL void addGenericMob(MemoryArena *arena, AssetManager *assetManager, | ||||
| 	entity_addAnim(assetManager, mob, "claudeIdle"); | ||||
| 	entity_addAnim(assetManager, mob, "claudeRun"); | ||||
| 	entity_addAnim(assetManager, mob, "claudeBattleIdle"); | ||||
| 
 | ||||
| 	entity_addAnim(assetManager, mob, "claudeAttack"); | ||||
| 	entity_setActiveAnim(mob, "claudeIdle"); | ||||
| 	entity_addAnim(assetManager, mob, "claudeAttackUp"); | ||||
| 	entity_addAnim(assetManager, mob, "claudeAttackDown"); | ||||
| 
 | ||||
| 	entity_addAnim(assetManager, mob, "claudeAirSlash"); | ||||
| 	entity_addAnim(assetManager, mob, "claudeDragonHowl"); | ||||
| 	entity_addAnim(assetManager, mob, "claudeEnergySword"); | ||||
| 	entity_addAnim(assetManager, mob, "claudeRipperBlast"); | ||||
| 
 | ||||
| 	entity_setActiveAnim(eventQueue, mob, "claudeIdle"); | ||||
| } | ||||
| 
 | ||||
| INTERNAL void rendererInit(GameState *state, v2 windowSize) | ||||
| @ -244,6 +230,95 @@ INTERNAL void assetInit(GameState *state) | ||||
| 			asset_addAnimation(assetManager, arena, "claudeAttack", claudeAtlas, | ||||
| 			                   claudeAttack, numRects, duration); | ||||
| 
 | ||||
| 			char *claudeAttackDown[7] = { | ||||
| 			    "ClaudeSprite_Attack_Down_01", | ||||
| 			    "ClaudeSprite_Attack_Down_02", | ||||
| 			    "ClaudeSprite_Attack_Down_03", | ||||
| 			    "ClaudeSprite_Attack_Down_04", | ||||
| 			    "ClaudeSprite_Attack_Down_05", | ||||
| 			    "ClaudeSprite_Attack_Down_06", | ||||
| 			    "ClaudeSprite_Attack_Down_07", | ||||
| 			}; | ||||
| 
 | ||||
| 			numRects = ARRAY_COUNT(claudeAttackDown); | ||||
| 			duration = 0.1f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeAttackDown", | ||||
| 			                   claudeAtlas, claudeAttackDown, numRects, | ||||
| 			                   duration); | ||||
| 
 | ||||
| 			char *claudeAttackUp[3] = { | ||||
| 			    "ClaudeSprite_Attack_Up_01", | ||||
| 			    "ClaudeSprite_Attack_Up_02", | ||||
| 			    "ClaudeSprite_Attack_Up_03", | ||||
| 			}; | ||||
| 
 | ||||
| 			numRects = ARRAY_COUNT(claudeAttackUp); | ||||
| 			duration = 0.2f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeAttackUp", | ||||
| 			                   claudeAtlas, claudeAttackUp, numRects, | ||||
| 			                   duration); | ||||
| 
 | ||||
| 			char *claudeDragonHowl[3] = { | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_01", | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_02", | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_03", | ||||
| 			}; | ||||
| 
 | ||||
| 			numRects = ARRAY_COUNT(claudeDragonHowl); | ||||
| 			duration = 0.2f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeDragonHowl", | ||||
| 			                   claudeAtlas, claudeDragonHowl, numRects, | ||||
| 			                   duration); | ||||
| 
 | ||||
| 			char *claudeDragonHowlVfx[7] = { | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_Vfx_01", | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_Vfx_02", | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_Vfx_03", | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_Vfx_04", | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_Vfx_05", | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_Vfx_06", | ||||
| 			    "ClaudeSprite_Attack_DragonHowl_Vfx_07", | ||||
| 			}; | ||||
| 
 | ||||
| 			numRects = ARRAY_COUNT(claudeDragonHowlVfx); | ||||
| 			duration = 0.1f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeDragonHowlVfx", | ||||
| 			                   claudeAtlas, claudeDragonHowlVfx, numRects, | ||||
| 			                   duration); | ||||
| 
 | ||||
| 			char *claudeRipperBlast[6] = { | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_01", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_02", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_03", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_04", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_05", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_06", | ||||
| 			}; | ||||
| 
 | ||||
| 			numRects = ARRAY_COUNT(claudeRipperBlast); | ||||
| 			duration = 0.1f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeRipperBlast", | ||||
| 			                   claudeAtlas, claudeRipperBlast, numRects, | ||||
| 			                   duration); | ||||
| 
 | ||||
| 			char *claudeRipperBlastVfx[9] = { | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_04", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_05", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_06", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_07", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_08", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_09", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_10", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_11", | ||||
| 			    "ClaudeSprite_Attack_RipperBlast_Vfx_12", | ||||
| 			}; | ||||
| 
 | ||||
| 			numRects = ARRAY_COUNT(claudeRipperBlastVfx); | ||||
| 			duration = 0.1f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeRipperBlastVfx", | ||||
| 			                   claudeAtlas, claudeRipperBlastVfx, numRects, | ||||
| 			                   duration); | ||||
| 
 | ||||
| 			//  Victory animation
 | ||||
| 			char *claudeVictory[8] = {"ClaudeSprite_Battle_Victory_01", | ||||
| 			                          "ClaudeSprite_Battle_Victory_02", | ||||
| @ -318,6 +393,7 @@ INTERNAL void assetInit(GameState *state) | ||||
| 			asset_addAnimation(assetManager, arena, "claudeAttackSlashLeft", | ||||
| 			                   claudeAtlas, claudeAttackSlashLeft, numRects, | ||||
| 			                   duration); | ||||
| 
 | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| @ -451,6 +527,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) | ||||
| 	        CAST(i32) windowSize.h / state->tileSize); | ||||
| #endif | ||||
| 
 | ||||
| 	EventQueue *eventQueue = &state->eventQueue; | ||||
| 	for (i32 i = 0; i < ARRAY_COUNT(state->world); i++) | ||||
| 	{ | ||||
| 		World *const world = &state->world[i]; | ||||
| @ -488,7 +565,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) | ||||
| 				                          dir, tex, collides); | ||||
| 
 | ||||
| 				entity_addAnim(assetManager, tile, "terrainGrass"); | ||||
| 				entity_setActiveAnim(tile, "terrainGrass"); | ||||
| 				entity_setActiveAnim(eventQueue, tile, "terrainGrass"); | ||||
| 			} | ||||
| 		} | ||||
| #endif | ||||
| @ -552,10 +629,17 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) | ||||
| 	entity_addAnim(assetManager, hero, "claudeIdle"); | ||||
| 	entity_addAnim(assetManager, hero, "claudeRun"); | ||||
| 	entity_addAnim(assetManager, hero, "claudeBattleIdle"); | ||||
| 
 | ||||
| 	entity_addAnim(assetManager, hero, "claudeAttack"); | ||||
| 	entity_addAnim(assetManager, hero, "claudeAttackUp"); | ||||
| 	entity_addAnim(assetManager, hero, "claudeAttackDown"); | ||||
| 
 | ||||
| 	entity_addAnim(assetManager, hero, "claudeDragonHowl"); | ||||
| 	entity_addAnim(assetManager, hero, "claudeEnergySword"); | ||||
| 	entity_addAnim(assetManager, hero, "claudeRipperBlast"); | ||||
| 	entity_addAnim(assetManager, hero, "claudeAirSlash"); | ||||
| 	entity_setActiveAnim(hero, "claudeIdle"); | ||||
| 
 | ||||
| 	entity_setActiveAnim(eventQueue, hero, "claudeIdle"); | ||||
| 
 | ||||
| 	/* Create a NPC */ | ||||
| 	pos         = V2(hero->pos.x * 3, CAST(f32) state->tileSize); | ||||
| @ -569,12 +653,12 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) | ||||
| 
 | ||||
| 	/* Populate npc animation references */ | ||||
| 	entity_addAnim(assetManager, npc, "claudeVictory"); | ||||
| 	entity_setActiveAnim(npc, "claudeVictory"); | ||||
| 	entity_setActiveAnim(eventQueue, npc, "claudeVictory"); | ||||
| 
 | ||||
| 	/* Create a Mob */ | ||||
| 	pos = V2(renderer->size.w - (renderer->size.w / 3.0f), | ||||
| 	         CAST(f32) state->tileSize); | ||||
| 	addGenericMob(arena, assetManager, world, pos); | ||||
| 	addGenericMob(eventQueue, arena, assetManager, world, pos); | ||||
| 
 | ||||
| #ifdef DENGINE_DEBUG | ||||
| 	DEBUG_LOG("World populated"); | ||||
| @ -923,17 +1007,18 @@ INTERNAL inline void updateWorldBattleEntities(World *world, Entity *entity, | ||||
| } | ||||
| 
 | ||||
| // TODO(doyle): Function too vague
 | ||||
| INTERNAL inline void resetEntityState(World *world, Entity *entity) | ||||
| INTERNAL inline void resetEntityState(EventQueue *eventQueue, World *world, | ||||
|                                       Entity *entity) | ||||
| { | ||||
| 	updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE); | ||||
| 	entity_setActiveAnim(entity, "claudeIdle"); | ||||
| 	entity_setActiveAnim(eventQueue, entity, "claudeIdle"); | ||||
| 	entity->stats->busyDuration     = 0; | ||||
| 	entity->stats->actionTimer      = entity->stats->actionRate; | ||||
| 	entity->stats->queuedAttack     = entityattack_invalid; | ||||
| 	entity->stats->entityIdToAttack = ENTITY_NULL_ID; | ||||
| } | ||||
| 
 | ||||
| INTERNAL registerEvent(EventQueue *eventQueue, enum EventType type, void *data) | ||||
| void worldTraveller_registerEvent(EventQueue *eventQueue, enum EventType type, void *data) | ||||
| { | ||||
| #ifdef DENGINE_DEBUG | ||||
| 	ASSERT(eventQueue && type < eventtype_count); | ||||
| @ -969,8 +1054,9 @@ INTERNAL void entityStateSwitch(EventQueue *eventQueue, World *world, | ||||
| 		// attacking it since there's no check before attack if entity is idle
 | ||||
| 		// or not (i.e. has moved out of frame last frame).
 | ||||
| 		case entitystate_dead: | ||||
| 			registerEvent(eventQueue, eventtype_entity_died, CAST(void *)entity); | ||||
| 			entity_setActiveAnim(entity, "claudeIdle"); | ||||
| 			worldTraveller_registerEvent(eventQueue, eventtype_entity_died, | ||||
| 			                             CAST(void *) entity); | ||||
| 			entity_setActiveAnim(eventQueue, entity, "claudeIdle"); | ||||
| 			entity->stats->busyDuration     = 0; | ||||
| 			entity->stats->actionTimer      = entity->stats->actionRate; | ||||
| 			entity->stats->queuedAttack     = entityattack_invalid; | ||||
| @ -993,11 +1079,12 @@ INTERNAL void entityStateSwitch(EventQueue *eventQueue, World *world, | ||||
| 			break; | ||||
| 		} | ||||
| 		case entitystate_idle: | ||||
| 			resetEntityState(world, entity); | ||||
| 			resetEntityState(eventQueue, world, entity); | ||||
| 			break; | ||||
| 		case entitystate_dead: | ||||
| 			registerEvent(eventQueue, eventtype_entity_died, CAST(void *)entity); | ||||
| 			resetEntityState(world, entity); | ||||
| 			worldTraveller_registerEvent(eventQueue, eventtype_entity_died, | ||||
| 			                             CAST(void *) entity); | ||||
| 			resetEntityState(eventQueue, world, entity); | ||||
| 			break; | ||||
| 		default: | ||||
| #ifdef DENGINE_DEBUG | ||||
| @ -1010,17 +1097,18 @@ INTERNAL void entityStateSwitch(EventQueue *eventQueue, World *world, | ||||
| 		switch (newState) | ||||
| 		{ | ||||
| 		case entitystate_battle: | ||||
| 			entity_setActiveAnim(entity, "claudeBattleIdle"); | ||||
| 			entity_setActiveAnim(eventQueue, entity, "claudeBattleIdle"); | ||||
| 			entity->stats->actionTimer  = entity->stats->actionRate; | ||||
| 			entity->stats->busyDuration = 0; | ||||
| 			break; | ||||
| 		// NOTE(doyle): Entity has been forced out of an attack (out of range)
 | ||||
| 		case entitystate_idle: | ||||
| 			resetEntityState(world, entity); | ||||
| 			resetEntityState(eventQueue, world, entity); | ||||
| 			break; | ||||
| 		case entitystate_dead: | ||||
| 			registerEvent(eventQueue, eventtype_entity_died, CAST(void *)entity); | ||||
| 			resetEntityState(world, entity); | ||||
| 			worldTraveller_registerEvent(eventQueue, eventtype_entity_died, | ||||
| 			                             CAST(void *) entity); | ||||
| 			resetEntityState(eventQueue, world, entity); | ||||
| 			break; | ||||
| 		default: | ||||
| #ifdef DENGINE_DEBUG | ||||
| @ -1071,13 +1159,13 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena, | ||||
| 	entityStateSwitch(eventQueue, world, attacker, entitystate_attack); | ||||
| 	switch (attacker->stats->queuedAttack) | ||||
| 	{ | ||||
| 	case entityattack_tackle: | ||||
| 	case entityattack_claudeAttack: | ||||
| 	{ | ||||
| 		entity_setActiveAnim(attacker, "claudeAttack"); | ||||
| 		entity_setActiveAnim(eventQueue, attacker, "claudeAttack"); | ||||
| 		if (attacker->stats->weapon) | ||||
| 		{ | ||||
| 			attacker->stats->weapon->invisible = FALSE; | ||||
| 			entity_setActiveAnim(attacker->stats->weapon, | ||||
| 			entity_setActiveAnim(eventQueue, attacker->stats->weapon, | ||||
| 			                     "claudeAttackSlashLeft"); | ||||
| 		} | ||||
| 		if (attacker->direction == direction_east) | ||||
| @ -1087,27 +1175,106 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena, | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	case entityattack_energySword: | ||||
| 	case entityattack_claudeAttackUp: | ||||
| 	{ | ||||
| 		entity_setActiveAnim(attacker, "claudeEnergySword"); | ||||
| 		entity_setActiveAnim(eventQueue, attacker, "claudeAttackUp"); | ||||
| 		if (attacker->stats->weapon) | ||||
| 		{ | ||||
| 			attacker->stats->weapon->invisible = FALSE; | ||||
| 			entity_setActiveAnim(eventQueue, attacker->stats->weapon, | ||||
| 			                     "claudeAttackSlashLeft"); | ||||
| 		} | ||||
| 		if (attacker->direction == direction_east) | ||||
| 			attacker->dPos.x += (1.0f * METERS_TO_PIXEL); | ||||
| 		else | ||||
| 			attacker->dPos.x -= (1.0f * METERS_TO_PIXEL); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	case entityattack_airSlash: | ||||
| 	case entityattack_claudeAttackDown: | ||||
| 	{ | ||||
| 		entity_setActiveAnim(attacker, "claudeAirSlash"); | ||||
| 		entity_setActiveAnim(eventQueue, attacker, "claudeAttackDown"); | ||||
| 		if (attacker->stats->weapon) | ||||
| 		{ | ||||
| 			attacker->stats->weapon->invisible = FALSE; | ||||
| 			entity_setActiveAnim(eventQueue, attacker->stats->weapon, | ||||
| 			                     "claudeAttackSlashLeft"); | ||||
| 		} | ||||
| 		if (attacker->direction == direction_east) | ||||
| 			attacker->dPos.x += (1.0f * METERS_TO_PIXEL); | ||||
| 		else | ||||
| 			attacker->dPos.x -= (1.0f * METERS_TO_PIXEL); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	case entityattack_claudeDragonHowl: | ||||
| 	{ | ||||
| 		entity_setActiveAnim(eventQueue, attacker, "claudeDragonHowl"); | ||||
| 		f32 scale = 1.5f; | ||||
| 		v2 size = V2(40, 40); | ||||
| 		Entity *projectile = entity_add( | ||||
| 		    arena, world, attacker->pos, size, scale, | ||||
| 		    entitytype_projectile, attacker->direction, attacker->tex, TRUE); | ||||
| 
 | ||||
| 		projectile->collidesWith[entitytype_hero] = TRUE; | ||||
| 		projectile->collidesWith[entitytype_mob]  = TRUE; | ||||
| 		projectile->collidesWith[attacker->type]  = FALSE; | ||||
| 
 | ||||
| 		projectile->stats->entityIdToAttack = attacker->stats->entityIdToAttack; | ||||
| 		entity_addAnim(assetManager, projectile, "claudeDragonHowlVfx"); | ||||
| 		entity_setActiveAnim(eventQueue, projectile, "claudeDragonHowlVfx"); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	case entityattack_claudeEnergySword: | ||||
| 	{ | ||||
| 		entity_setActiveAnim(eventQueue, attacker, "claudeEnergySword"); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	case entityattack_claudeRipperBlast: | ||||
| 	{ | ||||
| 		entity_setActiveAnim(eventQueue, attacker, "claudeRipperBlast"); | ||||
| 		f32 scale = 5.0f; | ||||
| 		v2 size = V2(20, 20); | ||||
| 
 | ||||
| 		Entity *target = entity_get(world, attacker->stats->entityIdToAttack); | ||||
| 		v2 targetPos = v2_add(attacker->pos, V2(100, 0)); | ||||
| 		if (target) | ||||
| 		{ | ||||
| 			targetPos = target->pos; | ||||
| 		} | ||||
| 
 | ||||
| 		Entity *projectile = entity_add( | ||||
| 		    arena, world, targetPos, size, scale, | ||||
| 		    entitytype_attackObject, attacker->direction, attacker->tex, TRUE); | ||||
| 
 | ||||
| 		projectile->collidesWith[entitytype_hero] = TRUE; | ||||
| 		projectile->collidesWith[entitytype_mob]  = TRUE; | ||||
| 		projectile->collidesWith[attacker->type]  = FALSE; | ||||
| 
 | ||||
| 		projectile->stats->entityIdToAttack = attacker->stats->entityIdToAttack; | ||||
| 		entity_addAnim(assetManager, projectile, "claudeRipperBlastVfx"); | ||||
| 		entity_setActiveAnim(eventQueue, projectile, "claudeRipperBlastVfx"); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	case entityattack_claudeAirSlash: | ||||
| 	{ | ||||
| 		entity_setActiveAnim(eventQueue, attacker, "claudeAirSlash"); | ||||
| 		f32 scale = 1.5f; | ||||
| 		v2 size = V2(20, 20); | ||||
| 		Entity *projectile = entity_add( | ||||
| 		    arena, world, attacker->pos, size, scale, | ||||
| 		    entitytype_projectile, attacker->direction, attacker->tex, TRUE); | ||||
| 
 | ||||
| 		projectile->collidesWith[entitytype_hero] = FALSE; | ||||
| 		projectile->collidesWith[entitytype_hero] = TRUE; | ||||
| 		projectile->collidesWith[entitytype_mob]  = TRUE; | ||||
| 		projectile->collidesWith[attacker->type]  = FALSE; | ||||
| 
 | ||||
| 		projectile->stats->entityIdToAttack = attacker->stats->entityIdToAttack; | ||||
| 		entity_addAnim(assetManager, projectile, "claudeAirSlashVfx"); | ||||
| 		entity_setActiveAnim(projectile, "claudeAirSlashVfx"); | ||||
| 		entity_setActiveAnim(eventQueue, projectile, "claudeAirSlashVfx"); | ||||
| 
 | ||||
| 		v2 initialOffset      = V2(size.x * 0.5f, 0); | ||||
| 		f32 deltaScale        = 0.3f; | ||||
| @ -1128,7 +1295,7 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena, | ||||
| 			child->stats->entityIdToAttack = | ||||
| 			    projectile->stats->entityIdToAttack; | ||||
| 			entity_addAnim(assetManager, child, "claudeAirSlashVfx"); | ||||
| 			entity_setActiveAnim(child, "claudeAirSlashVfx"); | ||||
| 			entity_setActiveAnim(eventQueue, child, "claudeAirSlashVfx"); | ||||
| 			projectile->childIds[i] = child->id; | ||||
| 		} | ||||
| 		break; | ||||
| @ -1159,7 +1326,9 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue, | ||||
| 	entityStateSwitch(eventQueue, world, attacker, entitystate_battle); | ||||
| 	switch (attacker->stats->queuedAttack) | ||||
| 	{ | ||||
| 	case entityattack_tackle: | ||||
| 	case entityattack_claudeAttack: | ||||
| 	case entityattack_claudeAttackUp: | ||||
| 	case entityattack_claudeAttackDown: | ||||
| 		// TODO(doyle): Move animation offsets out and into animation type
 | ||||
| 
 | ||||
| 		if (attacker->stats->weapon) | ||||
| @ -1174,16 +1343,19 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue, | ||||
| 
 | ||||
| 		break; | ||||
| 
 | ||||
| 	case entityattack_airSlash: | ||||
| 	case entityattack_claudeAirSlash: | ||||
| 	case entityattack_claudeRipperBlast: | ||||
| 	case entityattack_claudeDragonHowl: | ||||
| 		break; | ||||
| 
 | ||||
| 	case entityattack_energySword: | ||||
| 	case entityattack_claudeEnergySword: | ||||
| 		attacker->stats->health += 80; | ||||
| 		AttackSpec *attackSpec = PLATFORM_MEM_ALLOC(arena, 1, AttackSpec); | ||||
| 		attackSpec->attacker   = attacker; | ||||
| 		attackSpec->defender   = attacker; | ||||
| 		attackSpec->damage     = 30; | ||||
| 		registerEvent(eventQueue, eventtype_end_attack, attackSpec); | ||||
| 		worldTraveller_registerEvent(eventQueue, eventtype_end_attack, | ||||
| 		                             attackSpec); | ||||
| 		return; | ||||
| 
 | ||||
| 	default: | ||||
| @ -1236,7 +1408,8 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue, | ||||
| 		attackSpec->defender   = defender; | ||||
| 		attackSpec->damage     = damage; | ||||
| 
 | ||||
| 		registerEvent(eventQueue, eventtype_end_attack, attackSpec); | ||||
| 		worldTraveller_registerEvent(eventQueue, eventtype_end_attack, | ||||
| 		                             attackSpec); | ||||
| 		// TODO(doyle): Use attacker stats in battle equations
 | ||||
| 		if (attacker->type == entitytype_hero) | ||||
| 		{ | ||||
| @ -1286,17 +1459,22 @@ INTERNAL enum EntityAttack selectBestAttack(Entity *entity) | ||||
| { | ||||
| 	if (entity->stats->health <= 50.0f && entity->type == entitytype_hero) | ||||
| 	{ | ||||
| 		return entityattack_energySword; | ||||
| 		return entityattack_claudeEnergySword; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		enum EntityAttack attack = entityattack_tackle;; | ||||
| 		if (entity->type == entitytype_hero) | ||||
| 		enum EntityAttack attack = entityattack_claudeAttack; | ||||
| 		i32 choice               = rand() % 7; | ||||
| 		switch(choice) | ||||
| 		{ | ||||
| 			b32 choice = rand() % 2; | ||||
| 			attack = | ||||
| 			    (choice == TRUE) ? entityattack_airSlash : entityattack_tackle; | ||||
| 			//attack = entityattack_tackle;
 | ||||
| 			case 0: attack = entityattack_claudeAttack; break; | ||||
| 			case 1: attack = entityattack_claudeAttackUp; break; | ||||
| 			case 2: attack = entityattack_claudeAttackDown; break; | ||||
| 			case 3: attack = entityattack_claudeDragonHowl; break; | ||||
| 			case 4: attack = entityattack_claudeEnergySword; break; | ||||
| 			case 5: attack = entityattack_claudeRipperBlast; break; | ||||
| 			case 6: attack = entityattack_claudeAirSlash; break; | ||||
| 			default: break; | ||||
| 		} | ||||
| 
 | ||||
| 		return attack; | ||||
| @ -1447,6 +1625,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 	World *const world         = &state->world[state->currWorldIndex]; | ||||
| 	Font *font                 = &assetManager->font; | ||||
| 	MemoryArena *arena         = &state->arena; | ||||
| 	EventQueue *eventQueue     = &state->eventQueue; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 ********************** | ||||
| @ -1526,7 +1705,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 
 | ||||
| 			v2 pos = | ||||
| 			    V2(renderer->size.w - (renderer->size.w / xModifier), yPos); | ||||
| 			addGenericMob(&state->arena, &state->assetManager, world, pos); | ||||
| 			addGenericMob(eventQueue, &state->arena, &state->assetManager, world, pos); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -1535,7 +1714,6 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 	 * Update entities and render | ||||
| 	 ****************************** | ||||
| 	 */ | ||||
| 	EventQueue eventQueue = {0}; | ||||
| 	Rect camera           = createWorldBoundedCamera(world, renderer->size); | ||||
| 	AudioManager *audioManager = &state->audioManager; | ||||
| 
 | ||||
| @ -1655,7 +1833,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 			} | ||||
| 
 | ||||
| 			i32 numEntitiesInBattleBefore = world->numEntitiesInBattle; | ||||
| 			entityStateSwitch(&eventQueue, world, entity, newState); | ||||
| 			entityStateSwitch(eventQueue, world, entity, newState); | ||||
| 
 | ||||
| 			if (numEntitiesInBattleBefore == 0 && | ||||
| 			    world->numEntitiesInBattle > 0) | ||||
| @ -1767,14 +1945,14 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 			{ | ||||
| 				if (common_strcmp(currAnimName, "claudeIdle") == 0) | ||||
| 				{ | ||||
| 					entity_setActiveAnim(hero, "claudeRun"); | ||||
| 					entity_setActiveAnim(eventQueue, hero, "claudeRun"); | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				if (common_strcmp(currAnimName, "claudeRun") == 0) | ||||
| 				{ | ||||
| 					entity_setActiveAnim(hero, "claudeIdle"); | ||||
| 					entity_setActiveAnim(eventQueue, hero, "claudeIdle"); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| @ -1813,7 +1991,18 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 				// TODO(doyle): Unify concept of dead entity for mobs and
 | ||||
| 				// projectiles
 | ||||
| 				projectile->state = entitystate_dead; | ||||
| 				registerEvent(&eventQueue, eventtype_entity_died, projectile); | ||||
| 				// TODO(doyle): register endattack event
 | ||||
| 				worldTraveller_registerEvent(eventQueue, eventtype_entity_died, | ||||
| 				                             projectile); | ||||
| 			} | ||||
| 		} | ||||
| 		else if (entity->type == entitytype_attackObject) | ||||
| 		{ | ||||
| 			if (entity->animList[entity->currAnimId].timesCompleted == 1) | ||||
| 			{ | ||||
| 				entity->state = entitystate_dead; | ||||
| 				worldTraveller_registerEvent(eventQueue, eventtype_entity_died, | ||||
| 				                             entity); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| @ -1846,7 +2035,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 					} | ||||
| 
 | ||||
| 					/* Launch up attack animation */ | ||||
| 					beginAttack(assetManager, &state->arena, &eventQueue, world, | ||||
| 					beginAttack(assetManager, &state->arena, eventQueue, world, | ||||
| 					            entity); | ||||
| 				} | ||||
| 			} | ||||
| @ -1858,7 +2047,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 				if (stats->busyDuration <= 0) | ||||
| 				{ | ||||
| 					/* Apply attack damage */ | ||||
| 					endAttack(&state->arena, &eventQueue, world, entity); | ||||
| 					endAttack(&state->arena, eventQueue, world, entity); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @ -1876,7 +2065,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 
 | ||||
| 		if (entity->tex) | ||||
| 		{ | ||||
| 			entity_updateAnim(entity, dt); | ||||
| 			entity_updateAnim(eventQueue, entity, dt); | ||||
| 			/* Calculate region to render */ | ||||
| 			renderer_entity(renderer, camera, entity, | ||||
| 			                v2_scale(entity->renderSize, 0.5f), 0, | ||||
| @ -1890,9 +2079,13 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 	 ***************************************** | ||||
| 	 */ | ||||
| 	i32 numDeadEntities = 0; | ||||
| 	for (i32 i = 0; i < eventQueue.numEvents; i++) | ||||
| 	for (i32 i = 0; i < eventQueue->numEvents; i++) | ||||
| 	{ | ||||
| 		Event event = eventQueue.queue[i]; | ||||
| 		Event event = eventQueue->queue[i]; | ||||
| 
 | ||||
| 		eventQueue->queue[i].type = eventtype_null; | ||||
| 		eventQueue->queue[i].data = NULL; | ||||
| 
 | ||||
| 		switch(event.type) | ||||
| 		{ | ||||
| 		case eventtype_end_attack: | ||||
| @ -1903,7 +2096,9 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 			Entity *attacker       = attackSpec->attacker; | ||||
| 			Entity *defender       = attackSpec->defender; | ||||
| 
 | ||||
| 			if (attacker->stats->queuedAttack == entityattack_tackle) | ||||
| 			if (attacker->stats->queuedAttack == entityattack_claudeAttack || | ||||
| 			    attacker->stats->queuedAttack == entityattack_claudeAttackUp || | ||||
| 			    attacker->stats->queuedAttack == entityattack_claudeAttackDown) | ||||
| 			{ | ||||
| 				i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker); | ||||
| 				if (freeAudioIndex != -1) | ||||
| @ -1940,7 +2135,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 					    1); | ||||
| 				} | ||||
| 			} | ||||
| 			else if (attacker->stats->queuedAttack == entityattack_energySword) | ||||
| 			else if (attacker->stats->queuedAttack == | ||||
| 			         entityattack_claudeEnergySword) | ||||
| 			{ | ||||
| 				i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker); | ||||
| 				if (freeAudioIndex != -1) | ||||
| @ -1952,7 +2148,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 					    1); | ||||
| 				} | ||||
| 			} | ||||
| 			else if (attacker->stats->queuedAttack == entityattack_airSlash) | ||||
| 			else if (attacker->stats->queuedAttack == | ||||
| 			         entityattack_claudeAirSlash) | ||||
| 			{ | ||||
| 				i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker); | ||||
| 				if (freeAudioIndex != -1) | ||||
| @ -1964,6 +2161,32 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 					    1); | ||||
| 				} | ||||
| 			} | ||||
| 			else if (attacker->stats->queuedAttack == | ||||
| 			         entityattack_claudeDragonHowl) | ||||
| 			{ | ||||
| 				i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker); | ||||
| 				if (freeAudioIndex != -1) | ||||
| 				{ | ||||
| 					audio_playVorbis( | ||||
| 					    arena, audioManager, | ||||
| 					    &attacker->audioRenderer[freeAudioIndex], | ||||
| 					    asset_getVorbis(assetManager, "Attack_Dragon_howl"), | ||||
| 					    1); | ||||
| 				} | ||||
| 			} | ||||
| 			else if (attacker->stats->queuedAttack == | ||||
| 			         entityattack_claudeRipperBlast) | ||||
| 			{ | ||||
| 				i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker); | ||||
| 				if (freeAudioIndex != -1) | ||||
| 				{ | ||||
| 					audio_playVorbis( | ||||
| 					    arena, audioManager, | ||||
| 					    &attacker->audioRenderer[freeAudioIndex], | ||||
| 					    asset_getVorbis(assetManager, "Attack_tear_into_pieces"), | ||||
| 					    1); | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				//ASSERT(INVALID_CODE_PATH);
 | ||||
| @ -2039,6 +2262,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 		} | ||||
| 		} | ||||
| 	} | ||||
| 	eventQueue->numEvents = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 **************************** | ||||
| @ -2053,11 +2277,11 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 		if (hero->state == entitystate_battle && | ||||
| 		    world->numEntitiesInBattle == 1) | ||||
| 		{ | ||||
| 			entityStateSwitch(&eventQueue, world, hero, entitystate_idle); | ||||
| 			entityStateSwitch(eventQueue, world, hero, entitystate_idle); | ||||
| 		} | ||||
| 		else if (hero->state != entitystate_attack) | ||||
| 		{ | ||||
| 			entityStateSwitch(&eventQueue, world, hero, entitystate_battle); | ||||
| 			entityStateSwitch(eventQueue, world, hero, entitystate_battle); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| @ -2066,7 +2290,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 		{ | ||||
| 			hero->state                       = entitystate_idle; | ||||
| 			world->entityIdInBattle[hero->id] = FALSE; | ||||
| 			entity_setActiveAnim(hero, "claudeIdle"); | ||||
| 			entity_setActiveAnim(eventQueue, hero, "claudeIdle"); | ||||
| 		} | ||||
| 		hero->stats->entityIdToAttack = -1; | ||||
| 		hero->stats->actionTimer      = hero->stats->actionRate; | ||||
|  | ||||
| @ -10,6 +10,7 @@ typedef struct MemoryArena MemoryArena; | ||||
| typedef struct Texture Texture; | ||||
| typedef struct Animation Animation; | ||||
| typedef struct World World; | ||||
| typedef struct EventQueue EventQueue; | ||||
| 
 | ||||
| typedef struct Entity Entity; | ||||
| 
 | ||||
| @ -29,6 +30,7 @@ enum EntityType | ||||
| 	entitytype_hero, | ||||
| 	entitytype_weapon, | ||||
| 	entitytype_projectile, | ||||
| 	entitytype_attackObject, | ||||
| 	entitytype_npc, | ||||
| 	entitytype_mob, | ||||
| 	entitytype_tile, | ||||
| @ -48,9 +50,13 @@ enum EntityState | ||||
| 
 | ||||
| enum EntityAttack | ||||
| { | ||||
| 	entityattack_tackle, | ||||
| 	entityattack_airSlash, | ||||
| 	entityattack_energySword, | ||||
| 	entityattack_claudeAttack, | ||||
| 	entityattack_claudeAttackUp, | ||||
| 	entityattack_claudeAttackDown, | ||||
| 	entityattack_claudeDragonHowl, | ||||
| 	entityattack_claudeEnergySword, | ||||
| 	entityattack_claudeRipperBlast, | ||||
| 	entityattack_claudeAirSlash, | ||||
| 	entityattack_count, | ||||
| 	entityattack_invalid, | ||||
| }; | ||||
| @ -76,6 +82,8 @@ typedef struct EntityAnim | ||||
| 	Animation *anim; | ||||
| 	i32 currFrame; | ||||
| 	f32 currDuration; | ||||
| 
 | ||||
| 	u32 timesCompleted; | ||||
| } EntityAnim; | ||||
| 
 | ||||
| struct Entity | ||||
| @ -118,8 +126,10 @@ struct Entity | ||||
| 	i32 numAudioRenderers; | ||||
| }; | ||||
| 
 | ||||
| void entity_setActiveAnim(Entity *const entity, const char *const animName); | ||||
| void entity_updateAnim(Entity *const entity, const f32 dt); | ||||
| void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity, | ||||
|                           const char *const animName); | ||||
| void entity_updateAnim(EventQueue *eventQueue, Entity *const entity, | ||||
|                        const f32 dt); | ||||
| void entity_addAnim(AssetManager *const assetManager, Entity *const entity, | ||||
|                     const char *const animName); | ||||
| Entity *const entity_add(MemoryArena *const arena, World *const world, | ||||
|  | ||||
| @ -16,6 +16,29 @@ | ||||
| /* Forward declaration */ | ||||
| typedef struct Entity Entity; | ||||
| 
 | ||||
| enum EventType | ||||
| { | ||||
| 	eventtype_null = 0, | ||||
| 	eventtype_start_attack, | ||||
| 	eventtype_end_attack, | ||||
| 	eventtype_start_anim, | ||||
| 	eventtype_end_anim, | ||||
| 	eventtype_entity_died, | ||||
| 	eventtype_count, | ||||
| }; | ||||
| 
 | ||||
| typedef struct Event | ||||
| { | ||||
| 	enum EventType type; | ||||
| 	void *data; | ||||
| } Event; | ||||
| 
 | ||||
| typedef struct EventQueue | ||||
| { | ||||
| 	Event queue[1024]; | ||||
| 	i32 numEvents; | ||||
| } EventQueue; | ||||
| 
 | ||||
| enum RectBaseline | ||||
| { | ||||
| 	rectbaseline_top, | ||||
| @ -73,8 +96,11 @@ typedef struct GameState | ||||
| 	Config config; | ||||
| 	MemoryArena arena; | ||||
| 	UiState uiState; | ||||
| 	EventQueue eventQueue; | ||||
| } GameState; | ||||
| 
 | ||||
| void worldTraveller_gameInit(GameState *state, v2 windowSize); | ||||
| void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt); | ||||
| void worldTraveller_registerEvent(EventQueue *eventQueue, enum EventType type, | ||||
|                                   void *data); | ||||
| #endif | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user