Files
2025-09-29 00:52:08 +02:00

6153 lines
217 KiB
Scheme
Executable File

//-------------------------------------------------
// File: ggsm_arcade_entity.sch
// Purpose: general functions required by system
//-------------------------------------------------
//-------------------------------------------------
// INCLUDES
//-------------------------------------------------
USING "ggsm_structs.sch"
USING "ggsm_maths.sch"
USING "ggsm_data.sch"
USING "ggsm_helpers.sch"
//-------------------------------------------------
// DEPTH SORTING FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Reset Z Sort Array
/// PARAMS:
/// sArray -
PROC GGSM_RESET_ENTITY_DEPTHSORT_ARRAY()
INT i
REPEAT GGSM_MAX_ENTITIES i
sGGSMData.sZSortData[i].iEntityIndex = GGSM_INVALID_ENTITY
sGGSMData.sZSortData[i].iZDepth = -1
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Sort Z Sort Array
/// PARAMS:
/// sArray - Array to sort
/// iLeft - left index
/// iRight - right index
PROC GGSM_QUICK_SORT_ZDEPTH_ARRAY(GGSM_ZSORTDATA &sArray[], INT iLeft, INT iRight)
INT i, j
INT p = sArray[((iLeft + iRight) / 2)].iZDepth
GGSM_ZSORTDATA q
i = iLeft
j = iRight
WHILE (i <= j)
WHILE ((sArray[i].iZDepth > p) AND (i < iRight))
i++
ENDWHILE
WHILE ((p > sArray[j].iZDepth) AND (j > iLeft))
j--
ENDWHILE
IF (i <= j)
q = sArray[i]
sArray[i] = sArray[j]
sArray[j] = q
i++
j--
ENDIF
ENDWHILE
IF (i < iRight)
GGSM_QUICK_SORT_ZDEPTH_ARRAY(sArray, i, iRight)
ENDIF
IF (iLeft < j)
GGSM_QUICK_SORT_ZDEPTH_ARRAY(sArray, iLeft, j)
ENDIF
ENDPROC
//-------------------------------------------------
// ENTITY FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Sets Z Depth for zsorting
/// PARAMS:
/// ent -
/// iZDepth -
PROC GGSM_ENTITY_SET_Z_DEPTH(GGSM_ENTITY &ent, INT iZDepth)
INT i
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_FORCE_Z_SORT)
REPEAT GGSM_MAX_ENTITIES i
IF (sGGSMData.sZSortData[i].iEntityIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent))
sGGSMData.sZSortData[i].iZDepth = iZDepth
EXIT
ENDIF
ENDREPEAT
REPEAT GGSM_MAX_ENTITIES i
IF (sGGSMData.sZSortData[i].iEntityIndex = GGSM_INVALID_ENTITY)
sGGSMData.sZSortData[i].iZDepth = iZDepth
sGGSMData.sZSortData[i].iEntityIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent)
EXIT
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_ENTITY_CLEAR_Z_DEPTH(GGSM_ENTITY &ent)
INT i
REPEAT GGSM_MAX_ENTITIES i
IF (sGGSMData.sZSortData[i].iEntityIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent))
sGGSMData.sZSortData[i].iEntityIndex = GGSM_INVALID_ENTITY
sGGSMData.sZSortData[i].iZDepth = -1
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_FORCE_Z_SORT)
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_ENTITY_SET_PATH_INDEX(GGSM_ENTITY &ent, INT i)
ent.iPathNumber = i
ENDPROC
PROC GGSM_ENTITY_RESET_BASE_SPEED(GGSM_ENTITY &ent)
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
ent.fSpeed = dat.fBaseSpeed
ENDPROC
PROC GGSM_ENTITY_SCALE_BASE_SPEED(GGSM_ENTITY &ent, FLOAT fScale)
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
ent.fSpeed = (dat.fBaseSpeed * fScale)
ENDPROC
/// PURPOSE:
/// Gets the speed multiplied by timescale
/// PARAMS:
/// ent -
/// RETURNS:
///
FUNC FLOAT GGSM_ENTITY_GET_SPEED(GGSM_ENTITY &ent)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED)
RETURN ent.fSpeed
ENDIF
RETURN ent.fSpeed * sGGSMData.fTimeScale
ENDFUNC
PROC GGSM_ENTITY_SET_PACKED_ORIGIN(GGSM_ENTITY &ent, VECTOR_2D vec)
ent.iPackedLocalOrigin = GGSM_PACK_VECTOR_2D_TO_INT(vec)
ENDPROC
FUNC VECTOR_2D GGSM_GET_RANDOM_POINT_ON_ENTITY(GGSM_ENTITY &ent)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(ent.eType)
VECTOR_2D size = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
VECTOR_2D pos = ent.sWorld.vPosition
size.x /= 2.0
size.y /= 2.0
pos.x += GET_RANDOM_FLOAT_IN_RANGE(-size.x, size.x)
pos.y += GET_RANDOM_FLOAT_IN_RANGE(-size.y, size.y)
RETURN pos
ENDFUNC
FUNC BOOL GGSM_ENTITY_IS_MOVEMENT_COMPLETE(GGSM_ENTITY &ent)
IF (ent.eMoveType = GGSM_MOVEMENT_NONE)
RETURN TRUE
ENDIF
RETURN IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
ENDFUNC
//-------------------------------------------------
// ENTITY ARRAY FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Counts # of entities free in array
/// PARAMS:
/// array -
/// RETURNS:
///
FUNC INT GGSM_COUNT_FREE_ENTITIES(GGSM_ENTITY &array[])
INT i
INT cnt = 0
REPEAT COUNT_OF(array) i
IF (array[i].eState = GGSM_ENTITY_STATE_NONE)
cnt ++
ENDIF
ENDREPEAT
RETURN cnt
ENDFUNC
/// PURPOSE:
/// Gets the next free entity index
/// PARAMS:
/// array - entity array
/// ind - Index of the next free index
/// iSearchIndex - Index to start searching from
/// RETURNS:
/// TRUE if entity can be found
FUNC BOOL GGSM_GET_FREE_ENTITY(GGSM_ENTITY &array[], INT &ind, INT &iSearchIndex)
INT i
INT cnt = COUNT_OF(array)
// stops any overflow nonsense
iSearchIndex = ABSI(iSearchIndex) % cnt
ind = GGSM_INVALID_ENTITY
REPEAT cnt i
IF (array[iSearchIndex].eState = GGSM_ENTITY_STATE_NONE)
ind = iSearchIndex
RETURN TRUE
ENDIF
iSearchIndex = (iSearchIndex + 1) % cnt
ENDREPEAT
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Gets a number of free entity
/// PARAMS:
/// array - array of entity
/// iNumProjectiles - # of desired entity
/// iArray - Array of ints with entity indexes in
/// iSearchIndex - Index in array to search for
/// RETURNS:
/// # of entity it could get
FUNC INT GGSM_GET_FREE_ENTITIES(GGSM_ENTITY &array[], INT iNumEntities, INT &iArray[], INT &iSearchIndex)
INT i
INT iEntitiesFound
INT cnt = COUNT_OF(array)
// stops any overflow nonsense
iSearchIndex = ABSI(iSearchIndex) % cnt
REPEAT cnt i
IF (array[iSearchIndex].eState = GGSM_ENTITY_STATE_NONE)
iArray[iEntitiesFound] = iSearchIndex
iEntitiesFound ++
ENDIF
iSearchIndex = (iSearchIndex + 1) % cnt
IF (iNumEntities = iEntitiesFound) OR (iEntitiesFound = COUNT_OF(iArray))
RETURN iEntitiesFound
ENDIF
ENDREPEAT
RETURN iEntitiesFound
ENDFUNC
/// PURPOSE:
/// Gets the index of the closest entity to a position
/// PARAMS:
/// array - array
/// vPos - position to check
/// RETURNS:
///
FUNC INT GGSM_GET_CLOSEST_ENTITY_INDEX(GGSM_ENTITY &array[], VECTOR_2D vPos)
INT i
INT ind = GGSM_INVALID_ENTITY
FLOAT fDist
FLOAT fMaxDist = MAX_FLOAT
REPEAT COUNT_OF(array) i
IF (array[i].eState = GGSM_ENTITY_STATE_ALIVE)
fDist = VECTOR_2D_DIST2(array[i].sWorld.vPosition, vPos)
IF (fDist < fMaxDist)
fMaxDist = fDist
ind = i
ENDIF
ENDIF
ENDREPEAT
RETURN ind
ENDFUNC
/// PURPOSE:
/// Gets the index of the closest entity to a position
/// PARAMS:
/// array - array
/// vPos - position to check
/// RETURNS:
///
FUNC INT GGSM_GET_CLOSEST_ENEMY_ENTITY_INDEX(GGSM_ENTITY &array[], VECTOR_2D vPos, BOOL bAllied)
INT i
INT ind = GGSM_INVALID_ENTITY
FLOAT fDist
FLOAT fMaxDist = MAX_FLOAT
REPEAT COUNT_OF(array) i
IF (array[i].eState = GGSM_ENTITY_STATE_ALIVE) AND (IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED) != bAllied)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_ENTITY_BIT_NO_COLLISION)
fDist = VECTOR_2D_DIST2(array[i].sCollider.sAABB.vCenter, vPos)
IF (fDist < fMaxDist)
fMaxDist = fDist
ind = i
ENDIF
ENDIF
ENDIF
ENDREPEAT
RETURN ind
ENDFUNC
//-------------------------------------------------
// ENTITY GROUP FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Resets Entity Group
/// PARAMS:
/// group -
PROC GGSM_RESET_ENTITY_GROUP(GGSM_ENTITY_GROUP &group)
group.eFlags = GGSM_GROUP_BIT_NONE
group.iLeader = GGSM_INVALID_ENTITY
group.iMemberBitSet = 0
group.iMembersToCreate = 0
group.iFormSpawnBitSet = 0
group.iMembersOnStageBitSet = 0
group.iActiveCount = 0
group.fDistCounter = 0.0
GGSM_FORMATION_SET_TYPE(group.sFormSettings, GGSM_FORMATION_NONE)
ENDPROC
/// PURPOSE:
/// Checks if group is running
/// PARAMS:
/// group -
/// RETURNS:
///
FUNC BOOL GGSM_ENTITY_GROUP_IS_RUNNING(GGSM_ENTITY_GROUP &group)
RETURN IS_BITMASK_ENUM_AS_ENUM_SET(group.eFlags, GGSM_GROUP_BIT_ACTIVE)
ENDFUNC
/// PURPOSE:
/// Checks if group has finished (all members are dead)
/// PARAMS:
/// group -
/// RETURNS:
///
FUNC BOOL GGSM_ENTITY_GROUP_HAS_GROUP_FINISHED(GGSM_ENTITY_GROUP &group)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(group.eFlags, GGSM_GROUP_BIT_ACTIVE)
RETURN TRUE
ENDIF
RETURN (group.iMembersToCreate = 0) AND (group.iActiveCount = 0)
ENDFUNC
PROC GGSM_DEACTIVATE_ENTITY_GROUP(GGSM_ENTITY_GROUP &group)
#IF IS_DEBUG_BUILD
CDEBUG1LN(DEBUG_MINIGAME, "GGSM_DEACTIVATE_ENTITY_GROUP - GROUP:", group.iGroupID)
#ENDIF
GGSM_RESET_ENTITY_GROUP(group)
ENDPROC
PROC GGSM_SET_ENTITY_GROUP_FLAGS(GGSM_ENTITY_GROUP &group, GGSM_GROUP_BIT eFlags)
SET_BITMASK_ENUM_AS_ENUM(group.eFlags, eFlags)
ENDPROC
/// PURPOSE:
/// Activates and entity group
/// PARAMS:
/// group - group
/// iFlags - flags entity to create
/// iMembersLeftToCreate -
PROC GGSM_ACTIVATE_ENTITY_GROUP(GGSM_ENTITY_GROUP &group, GGSM_GROUP_BIT eFlags = GGSM_GROUP_BIT_NONE, INT iMembersLeftToCreate = 0)
GGSM_RESET_ENTITY_GROUP(group)
group.eFlags = eFlags
group.iMembersToCreate = iMembersLeftToCreate
SET_BITMASK_ENUM_AS_ENUM(group.eFlags, GGSM_GROUP_BIT_ACTIVE)
ENDPROC
/// PURPOSE:
/// Activates groups as formation
/// PARAMS:
/// group -
/// eSettings -
PROC GGSM_ACTIVATE_ENTITY_GROUP_AS_FORMATION(GGSM_ENTITY_GROUP &group, GGSM_FORMATION_DATA &eSettings)
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
GGSM_RESET_ENTITY_GROUP(group)
group.sFormSettings = eSettings
group.iMembersToCreate = eSettings.iFormationMembers
group.fDistCounter = FMAX(ABSF(spriteSize.x), ABSF(spriteSize.y)) * 1.5
SET_BITMASK_ENUM_AS_ENUM(group.eFlags, GGSM_GROUP_BIT_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(group.eFlags, GGSM_GROUP_BIT_IS_FORMATION)
SET_BITMASK_ENUM_AS_ENUM(group.eFlags, GGSM_GROUP_BIT_END_WHEN_ALL_OFFSCREEN)
#IF IS_DEBUG_BUILD
CDEBUG1LN(DEBUG_MINIGAME, "GGSM_ACTIVATE_ENTITY_GROUP_AS_FORMATION - GROUP:", group.iGroupID,
" TYPE:", GGSM_FORMATION_TYPE_TO_STRING(GGSM_FORMATION_GET_TYPE(group.sFormSettings)),
" MOVE PATTERN:", GGSM_MOVEMENT_TYPE_TO_STRING(GGSM_FORMATION_GET_MOVEMENT_TYPE(group.sFormSettings)),
" #:", GGSM_FORMATION_GET_MEMBER_COUNT(group.sFormSettings))
#ENDIF
ENDPROC
/// PURPOSE:
/// Resets a group array
/// PARAMS:
/// array -
PROC GGSM_RESET_ENTITY_GROUP_ARRAY(GGSM_ENTITY_GROUP &array[])
INT i
REPEAT COUNT_OF(array) i
array[i].iGroupID = i
GGSM_RESET_ENTITY_GROUP(array[i])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Searchs for a free entity group
/// PARAMS:
/// array -
/// RETURNS:
/// Index into group array - if no free group this returns -1
FUNC INT GGSM_GET_FREE_ENTITY_GROUP(GGSM_ENTITY_GROUP &array[])
INT i
REPEAT COUNT_OF(array) i
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_GROUP_BIT_ACTIVE)
RETURN i
ENDIF
ENDREPEAT
RETURN GGSM_INVALID_GROUP_ID
ENDFUNC
/// PURPOSE:
/// Adds an Entity to a group
/// PARAMS:
/// ent - entity to add
/// iGroupID - group id to add to
PROC GGSM_ADD_ENTITY_TO_GROUP(GGSM_ENTITY &ent, INT iGroupID)
INT iArrayIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_ACTIVE)
ENDIF
IF (sGGSMData.sEntityGroup[iGroupID].iLeader = GGSM_INVALID_ENTITY)
sGGSMData.sEntityGroup[iGroupID].iLeader = iArrayIndex
ENDIF
IF NOT IS_BIT_SET(sGGSMData.sEntityGroup[iGroupID].iMemberBitSet, iArrayIndex)
SET_BIT(sGGSMData.sEntityGroup[iGroupID].iMemberBitSet, iArrayIndex)
SET_BIT(sGGSMData.sEntityGroup[iGroupID].iMembersOnStageBitSet, iArrayIndex)
sGGSMData.sEntityGroup[iGroupID].iActiveCount ++
sGGSMData.sEntityGroup[iGroupID].iMembersToCreate --
IF (sGGSMData.sEntityGroup[iGroupID].iMembersToCreate < 0)
sGGSMData.sEntityGroup[iGroupID].iMembersToCreate = 0
ENDIF
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_END_WHEN_ALL_OFFSCREEN)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_REMOVE_WHEN_OFFSCREEN)
ENDIF
CPRINTLN(DEBUG_MINIGAME, "GGSM_ADD_ENTITY_TO_GROUP #", iGroupID, " - TYPE:", GGSM_ENTITY_TYPE_TO_STRING(ent.eType), " ARRAY INDEX:", iArrayIndex)
GGSM_ENTITY_SET_GROUP_ID(ent, iGroupID)
ENDPROC
/// PURPOSE:
/// Removes entity from a group - This will shutdown the group if there are no members left
/// PARAMS:
/// ent -
PROC GGSM_REMOVE_ENTITY_FROM_GROUP(GGSM_ENTITY &ent)
INT grp = GGSM_ENTITY_GET_GROUP_ID(ent)
IF (grp = GGSM_INVALID_GROUP_ID)
EXIT
ENDIF
INT iArrayIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent)
IF IS_BIT_SET(sGGSMData.sEntityGroup[grp].iMemberBitSet, iArrayIndex)
CLEAR_BIT(sGGSMData.sEntityGroup[grp].iMemberBitSet, iArrayIndex)
CLEAR_BIT(sGGSMData.sEntityGroup[grp].iMembersOnStageBitSet, iArrayIndex)
sGGSMData.sEntityGroup[grp].iActiveCount --
ENDIF
CPRINTLN(DEBUG_MINIGAME, "GGSM_REMOVE_ENTITY_FROM_GROUP #", grp, " - TYPE:", GGSM_ENTITY_TYPE_TO_STRING(ent.eType), " ARRAY INDEX:", iArrayIndex)
GGSM_ENTITY_SET_GROUP_ID(ent, GGSM_INVALID_GROUP_ID)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntityGroup[grp].eFlags, GGSM_GROUP_BIT_IS_FORMATION)
IF (sGGSMData.sEntityGroup[grp].iActiveCount = 0)
GGSM_DEACTIVATE_ENTITY_GROUP(sGGSMData.sEntityGroup[grp])
ENDIF
ELIF (sGGSMData.sEntityGroup[grp].iMembersToCreate = 0)
IF (sGGSMData.sEntityGroup[grp].iActiveCount = 0)
GGSM_DEACTIVATE_ENTITY_GROUP(sGGSMData.sEntityGroup[grp])
ENDIF
ENDIF
ENDPROC
//-------------------------------------------------
// QUERY FUNCTION
//-------------------------------------------------
FUNC BOOL GGSM_DOES_PLAYER_SHIP_EXIST()
IF (sGGSMData.iPlayerShipIndex = GGSM_INVALID_ENTITY)
RETURN FALSE
ENDIF
RETURN (sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].eState != GGSM_ENTITY_STATE_NONE)
ENDFUNC
FUNC BOOL GGSM_IS_PLAYER_SHIP_ALIVE()
IF (sGGSMData.iPlayerShipIndex = GGSM_INVALID_ENTITY)
RETURN FALSE
ENDIF
RETURN (sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].eState = GGSM_ENTITY_STATE_ALIVE)
ENDFUNC
FUNC BOOL GGSM_DOES_PLAYER_DECOY_EXIST()
IF (sGGSMData.iPlayerDecoyIndex = GGSM_INVALID_ENTITY)
RETURN FALSE
ENDIF
RETURN (sGGSMData.sEntities[sGGSMData.iPlayerDecoyIndex].eState != GGSM_ENTITY_STATE_NONE)
ENDFUNC
FUNC BOOL GGSM_DOES_PLAYER_SHIELD_EXIST()
IF (sGGSMData.iPlayerShieldIndex = GGSM_INVALID_ENTITY)
RETURN FALSE
ENDIF
RETURN (sGGSMData.sEntities[sGGSMData.iPlayerShieldIndex].eState != GGSM_ENTITY_STATE_NONE)
ENDFUNC
FUNC BOOL GGSM_IS_PLAYER_TOTALLY_DEFEATED()
IF GGSM_DOES_PLAYER_SHIP_EXIST()
RETURN FALSE
ENDIF
RETURN (sGGSMData.iPlayerLives = 0)
ENDFUNC
PROC GGSM_SET_PLAYER_INVUNERABLE()
IF GGSM_DOES_PLAYER_SHIP_EXIST()
sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].iInvTimeMS = GET_GAME_TIMER() + GGSM_PLAYER_INITIAL_INV_TIME
ENDIF
ENDPROC
//-------------------------------------------------
// PROJECTILE ARRAY FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Sets all projectiles to be inactive
/// PARAMS:
/// array - array of projectiles
PROC GGSM_RESET_PROJECTILE_ARRAY(GGSM_PROJECTILE &array[])
INT i
INT cnt = COUNT_OF(array)
REPEAT cnt i
array[i].eFlags = GGSM_PROJECTILE_BIT_NONE
array[i].fTimer = 0
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Gets the next free projectile index
/// PARAMS:
/// array - projecile array
/// ind - Index of the next free index
/// iSearchIndex - Index to start searching from
/// RETURNS:
/// TRUE if projectile can be found
FUNC BOOL GGSM_GET_FREE_PROJECTILE(GGSM_PROJECTILE &array[], INT &ind, INT &iSearchIndex)
INT i
INT cnt = COUNT_OF(array)
// stops any overflow nonsense
iSearchIndex = ABSI(iSearchIndex) % cnt
ind = GGSM_INVALID_PROJECTILE
REPEAT cnt i
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(array[iSearchIndex].eFlags, GGSM_PROJECTILE_BIT_ACTIVE)
ind = iSearchIndex
RETURN TRUE
ENDIF
iSearchIndex = (iSearchIndex + 1) % cnt
ENDREPEAT
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Gets a number of free projectiles
/// PARAMS:
/// array - array of projectiles
/// iNumProjectiles - # of desired projectiles
/// iArray - Array of ints with projectile indexes in
/// iSearchIndex - Index in array to search for
/// RETURNS:
/// # of projectiles it could get
FUNC INT GGSM_GET_FREE_PROJECTILES(GGSM_PROJECTILE &array[], INT iNumProjectiles, INT &iArray[], INT &iSearchIndex)
INT i
INT iProjectilesFound
INT cnt = COUNT_OF(array)
// stops any overflow nonsense
iSearchIndex = ABSI(iSearchIndex) % cnt
REPEAT cnt i
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(array[iSearchIndex].eFlags, GGSM_PROJECTILE_BIT_ACTIVE)
iArray[iProjectilesFound] = iSearchIndex
iProjectilesFound ++
ENDIF
iSearchIndex = (iSearchIndex + 1) % cnt
IF (iProjectilesFound = iNumProjectiles) OR (iProjectilesFound = cnt)
RETURN iProjectilesFound
ENDIF
ENDREPEAT
RETURN 0
ENDFUNC
//-------------------------------------------------
// PROJECTILE FUNCTIONS
//-------------------------------------------------
FUNC VECTOR_2D GGSM_GET_TARGET_AT_PLAYER_POSITION()
IF (sGGSMData.iPlayerDecoyIndex != GGSM_INVALID_ENTITY)
RETURN sGGSMData.sEntities[sGGSMData.iPlayerDecoyIndex].sWorld.vPosition
ENDIF
IF (sGGSMData.iPlayerShipIndex != GGSM_INVALID_ENTITY)
RETURN sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].sWorld.vPosition
ENDIF
RETURN INIT_VECTOR_2D(0, 0)
ENDFUNC
FUNC INT GGSM_PROJECTILE_GET_TARGET_INDEX(GGSM_PROJECTILE &ent)
RETURN ent.iTargetIndex
ENDFUNC
PROC GGSM_PROJECTILE_SET_TARGET_INDEX(GGSM_PROJECTILE &ent, INT i)
ent.iTargetIndex = i
ENDPROC
FUNC BOOL GGSM_PROJECTILE_HAS_COLLISION(GGSM_PROJECTILE &ent)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_ACTIVE)
RETURN FALSE
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_HAS_NO_COLLISION)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Initializes projectile based on data
/// PARAMS:
/// ent - projectile to initialize
/// eType - projectile type
/// vPos - world position of projectile
/// fRot - world rotation of projectile
PROC GGSM_PROJECTILE_INIT(GGSM_PROJECTILE &ent, GGSM_PROJECTILE_TYPE eType, VECTOR_2D vPos, FLOAT fRot, BOOL bIsFriendly = FALSE)
GGSM_PROJECTILE_DATA dat = GGSM_PROJECTILE_DATA_GET(eType)
VECTOR_2D dir
ent.eFlags = GGSM_PROJECTILE_BIT_NONE
ent.eType = eType
ent.fTimer = 0.0
ent.fDistTravelled = 0
ent.fLength = dat.fLength
ent.fSpeed = dat.fBaseSpeed
ent.sCollider.sData.fColRadius = dat.fRadius
ent.vDirection = GGSM_GET_VECTOR_FROM_HEADING(fRot)
ent.iPackedOrigin = GGSM_PACK_VECTOR_2D_TO_INT(vPos)
ent.iTargetIndex = GGSM_INVALID_ENTITY
IF (ent.fLength = 0.0)
ent.sCollider.sData.eType = GGSM_COLLISION_CIRCLE
ent.sCollider.sData.vCollisionVectors[0] = vPos
ent.sCollider.sData.vCollisionVectors[1] = vPos
ELSE
dir.x = ent.vDirection.x * ent.fLength
dir.y = ent.vDirection.y * ent.fLength
ent.sCollider.sData.eType = GGSM_COLLISION_CAPSULE
ent.sCollider.sData.vCollisionVectors[0] = vPos
ent.sCollider.sData.vCollisionVectors[1].x = vPos.x + dir.x
ent.sCollider.sData.vCollisionVectors[1].y = vPos.y + dir.y
ENDIF
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_ACTIVE)
GGSM_COLLIDER_CALCULATE_AABB(ent.sCollider)
IF (bIsFriendly)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED)
ENDIF
IF (eType = GGSM_PROJECTILE_HOMING_ROCKET)
ent.iTargetIndex = GGSM_GET_CLOSEST_ENEMY_ENTITY_INDEX(sGGSMData.sEntities, vPos, bIsFriendly)
IF (ent.iTargetIndex != GGSM_INVALID_ENTITY)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_IS_HOMING)
ENDIF
ENDIF
IF (eType = GGSM_PROJECTILE_ENEMY_CLUSTER_BOMB) OR (eType = GGSM_PROJECTILE_DANK_CLUSTERBOMB)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_IS_CLUSTER_BOMB)
ELIF (eType = GGSM_PROJECTILE_ENEMY_BEAM)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_LOCKED_TO_PARENT)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_BEAM_WEAPON)
ELIF (eType = GGSM_PROJECTILE_DANK_HOMING_ROCKET)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_IS_DELAYED_HOMING)
ENDIF
ENDPROC
PROC GGSM_PROJECTILE_CREATE_ENEMY_RADIAL_SPREAD(GGSM_PROJECTILE_TYPE eType, VECTOR_2D vPos, INT iCount, FLOAT fRot = 0.0, BOOL bTargetPlayer = FALSE)
INT ind, cnt
INT iProjList[16]
FLOAT fAngle = fRot
FLOAT fAngDiff = 360.0 / TO_FLOAT(iCount)
VECTOR_2D vec
cnt = GGSM_GET_FREE_PROJECTILES(sGGSMData.sProjectiles, iCount, iProjList, sGGSMData.iProjectileSearchIndex)
IF (cnt = 0)
EXIT
ENDIF
IF (bTargetPlayer)
vec = SUBTRACT_VECTOR_2D(GGSM_GET_TARGET_AT_PLAYER_POSITION(), vPos)
fAngle = GET_HEADING_FROM_VECTOR_2D(vec.x, vec.y)
ENDIF
REPEAT cnt ind
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[ind]], eType, vPos, fAngle, FALSE)
fAngle += fAngDiff
ENDREPEAT
GGSM_PROJECTILE_DATA dat = GGSM_PROJECTILE_DATA_GET(eType)
GGSM_PLAY_SOUND_FROM_POSITION(dat.eProjSound, vPos)
ENDPROC
PROC GGSM_PROJECTILE_CREATE_ENEMY_CONE_SPREAD(GGSM_PROJECTILE_TYPE eType, VECTOR_2D vPos, FLOAT fSpreadAngle, INT iCount, BOOL bTargetPlayer = FALSE)
INT ind, cnt
INT iProjList[16]
FLOAT fBaseAngle, fAngle
FLOAT fAngDiff = 360.0 / TO_FLOAT(iCount)
VECTOR_2D vec
cnt = GGSM_GET_FREE_PROJECTILES(sGGSMData.sProjectiles, iCount, iProjList, sGGSMData.iProjectileSearchIndex)
IF (cnt = 0)
EXIT
ENDIF
IF (bTargetPlayer)
vec = SUBTRACT_VECTOR_2D(GGSM_GET_TARGET_AT_PLAYER_POSITION(), vPos)
fBaseAngle = GET_HEADING_FROM_VECTOR_2D(vec.x, vec.y)
ENDIF
fAngDiff = fSpreadAngle / TO_FLOAT(iCount - 1)
fBaseAngle -= (fSpreadAngle / 2.0)
REPEAT cnt ind
fAngle = fBaseAngle + (TO_FLOAT(ind) * fAngDiff)
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[ind]], eType, vPos, fAngle, FALSE)
ENDREPEAT
GGSM_PROJECTILE_DATA dat = GGSM_PROJECTILE_DATA_GET(eType)
GGSM_PLAY_SOUND_FROM_POSITION(dat.eProjSound, vPos)
ENDPROC
/// PURPOSE:
/// Gets the speed multiplied by timescale
/// PARAMS:
/// ent -
/// RETURNS:
///
FUNC FLOAT GGSM_GET_PROJECTILE_SPEED(GGSM_PROJECTILE &ent)
GGSM_PROJECTILE_DATA dat = GGSM_PROJECTILE_DATA_GET(ent.eType)
FLOAT spd = dat.fBaseSpeed
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED)
spd *= sGGSMData.fTimeScale
ENDIF
RETURN spd
ENDFUNC
PROC GGSM_REFLECT_PROJECTILE(GGSM_PROJECTILE &ent)
VECTOR_2D tmp
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_ACTIVE)
EXIT
ENDIF
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_FIRED_BY_PLAYER)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED)
ELSE
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED)
ENDIF
// remove any homing capability
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_PROJECTILE_BIT_IS_HOMING)
ent.iTargetIndex = GGSM_INVALID_ENTITY
// make it face the other way
tmp = ent.sCollider.sData.vCollisionVectors[0]
ent.sCollider.sData.vCollisionVectors[0] = ent.sCollider.sData.vCollisionVectors[1]
ent.sCollider.sData.vCollisionVectors[1] = tmp
FLOAT ang = GET_HEADING_FROM_VECTOR_2D(ent.vDirection.x, ent.vDirection.y)
IF GET_RANDOM_BOOL()
ang += GET_RANDOM_FLOAT_IN_RANGE(-190.0, -181.0)
ELSE
ang += GET_RANDOM_FLOAT_IN_RANGE(-179.0, -160.0)
ENDIF
ent.vDirection = GGSM_GET_VECTOR_FROM_HEADING(ang)
ent.fDistTravelled = 0.0
ENDPROC
PROC GGSM_REFLECT_ENEMY_PROJECTILES()
INT i
REPEAT GGSM_MAX_PROJECTILES i
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sProjectiles[i].eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED)
GGSM_REFLECT_PROJECTILE(sGGSMData.sProjectiles[i])
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Returns true if the bullet was destroyed and not turned into something else
/// PARAMS:
/// ent -
/// RETURNS:
///
FUNC BOOL GGSM_PROJECTILE_TAKE_DAMAGE(GGSM_PROJECTILE &ent)
BOOL bIsAllied = IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED)
GGSM_PROJECTILE_DATA dat
IF (ent.eType = GGSM_PROJECTILE_EXP_SHELL)
GGSM_PROJECTILE_INIT(ent, GGSM_PROJECTILE_BOMB_EXPLOSION, ent.sCollider.sAABB.vCenter, 0, bIsAllied)
dat = GGSM_PROJECTILE_DATA_GET(GGSM_PROJECTILE_BOMB_EXPLOSION)
GGSM_PLAY_SOUND_FROM_POSITION(dat.eProjSound, ent.sCollider.sAABB.vCenter)
RETURN FALSE
ENDIF
IF (ent.eType = GGSM_PROJECTILE_BOMB_EXPLOSION)
RETURN FALSE
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_CLUSTER_BOMB) AND NOT (bIsAllied)
GGSM_PROJECTILE_CREATE_ENEMY_RADIAL_SPREAD(GGSM_PROJECTILE_ENEMY_SHOT, ent.sCollider.sAABB.vCenter, 5, 0, TRUE)
ENDIF
ent.eFlags = GGSM_PROJECTILE_BIT_NONE
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Update projectile and transform collision
/// PARAMS:
/// ent - projectile to update
PROC GGSM_PROJECTILE_UPDATE_ACID(GGSM_PROJECTILE &ent)
FLOAT spd = 0 +@ GGSM_GET_PROJECTILE_SPEED(ent)
IF (ent.fDistTravelled > 300.0)
ent.vDirection = LERP_VECTOR_2D(ent.vDirection, INIT_VECTOR_2D(-1, 0), 0.25)
ENDIF
ent.fDistTravelled += spd
ent.sCollider.sData.vCollisionVectors[0].x += (ent.vDirection.x * spd)
ent.sCollider.sData.vCollisionVectors[0].y += (ent.vDirection.y * spd)
ent.sCollider.sData.vCollisionVectors[1] = ent.sCollider.sData.vCollisionVectors[0]
IF (ent.sCollider.sData.eType = GGSM_COLLISION_CAPSULE)
ent.sCollider.sData.vCollisionVectors[1].x += (ent.vDirection.x * ent.fLength)
ent.sCollider.sData.vCollisionVectors[1].y += (ent.vDirection.y * ent.fLength)
ENDIF
ENDPROC
/// PURPOSE:
/// Update projectile and transform collision
/// PARAMS:
/// ent - projectile to update
PROC GGSM_PROJECTILE_UPDATE_HOMING(GGSM_PROJECTILE &ent)
FLOAT spd = 0 +@ GGSM_GET_PROJECTILE_SPEED(ent)
VECTOR_2D dir, ndir
IF (ent.iTargetIndex != GGSM_INVALID_ENTITY)
IF (sGGSMData.sEntities[ent.iTargetIndex].eState = GGSM_ENTITY_STATE_ALIVE)
dir = SUBTRACT_VECTOR_2D(sGGSMData.sEntities[ent.iTargetIndex].sCollider.sAABB.vCenter, ent.sCollider.sData.vCollisionVectors[0])
ndir = NORMALISE_VECTOR_2D(dir)
ent.vDirection = LERP_VECTOR_2D(ent.vDirection, ndir, 0.25)
ELSE
ent.iTargetIndex = GGSM_INVALID_ENTITY
ENDIF
ENDIF
ent.fDistTravelled += spd
ent.sCollider.sData.vCollisionVectors[0].x += (ent.vDirection.x * spd)
ent.sCollider.sData.vCollisionVectors[0].y += (ent.vDirection.y * spd)
ent.sCollider.sData.vCollisionVectors[1] = ent.sCollider.sData.vCollisionVectors[0]
IF (ent.sCollider.sData.eType = GGSM_COLLISION_CAPSULE)
ent.sCollider.sData.vCollisionVectors[1].x += (ent.vDirection.x * ent.fLength)
ent.sCollider.sData.vCollisionVectors[1].y += (ent.vDirection.y * ent.fLength)
ENDIF
ENDPROC
/// PURPOSE:
/// Update projectile and transform collision
/// PARAMS:
/// ent - projectile to update
PROC GGSM_PROJECTILE_UPDATE_DELAYED_HOMING(GGSM_PROJECTILE &ent)
FLOAT spd = 0 +@ GGSM_GET_PROJECTILE_SPEED(ent)
VECTOR_2D dir, ndir
INT ind
IF (ent.fDistTravelled > 150.0) AND (ent.iTargetIndex = GGSM_INVALID_ENTITY)
ent.iTargetIndex = GGSM_GET_CLOSEST_ENEMY_ENTITY_INDEX(sGGSMData.sEntities, ent.sCollider.sData.vCollisionVectors[0], IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED))
ENDIF
IF (ent.iTargetIndex != GGSM_INVALID_ENTITY)
IF (sGGSMData.sEntities[ent.iTargetIndex].eState = GGSM_ENTITY_STATE_ALIVE)
dir = SUBTRACT_VECTOR_2D(sGGSMData.sEntities[ent.iTargetIndex].sCollider.sAABB.vCenter, ent.sCollider.sData.vCollisionVectors[0])
ndir = NORMALISE_VECTOR_2D(dir)
ent.vDirection = LERP_VECTOR_2D(ent.vDirection, ndir, 0.125)
ELSE
ent.iTargetIndex = GGSM_INVALID_ENTITY
ENDIF
ENDIF
ent.fDistTravelled += spd
ent.sCollider.sData.vCollisionVectors[0].x += (ent.vDirection.x * spd)
ent.sCollider.sData.vCollisionVectors[0].y += (ent.vDirection.y * spd)
ent.sCollider.sData.vCollisionVectors[1] = ent.sCollider.sData.vCollisionVectors[0]
IF (ent.sCollider.sData.eType = GGSM_COLLISION_CAPSULE)
ent.sCollider.sData.vCollisionVectors[1].x += (ent.vDirection.x * ent.fLength)
ent.sCollider.sData.vCollisionVectors[1].y += (ent.vDirection.y * ent.fLength)
ENDIF
IF (ent.fDistTravelled > 1200.0)
IF GGSM_GET_FREE_FXSPRITE(sGGSMData.sFXSprite, ind, sGGSMData.iExplosionSearchIndex)
GGSM_FXSPRITE_INIT_ANIMATED(sGGSMData.sFXSprite[ind], ent.sCollider.sAABB.vCenter, GGSM_SPRITE_ANIM_EXPLOSION, 2.0)
GGSM_FXSPRITE_SET_PHYSICAL_RADIUS(sGGSMData.sFXSprite[ind], ent.fLength * 2.0)
ENDIF
ent.eFlags = GGSM_PROJECTILE_BIT_NONE
ENDIF
ENDPROC
/// PURPOSE:
/// Update projectile and transform collision
/// PARAMS:
/// ent - projectile to update
PROC GGSM_PROJECTILE_UPDATE_SINWAVE(GGSM_PROJECTILE &ent)
FLOAT fPeriod, fWaveHeight = 44.0
FLOAT spd = 0 +@ GGSM_GET_PROJECTILE_SPEED(ent)
// move forward
ent.fDistTravelled += spd
ent.sCollider.sData.vCollisionVectors[0] = GGSM_UNPACK_VECTOR_2D(ent.iPackedOrigin)
ent.sCollider.sData.vCollisionVectors[0].x += (ent.vDirection.x * ent.fDistTravelled)
ent.sCollider.sData.vCollisionVectors[0].y += (ent.vDirection.y * ent.fDistTravelled)
// horizontal displacement - get the perpendicular
fPeriod = TO_FLOAT(FLOOR(ent.fDistTravelled) % FLOOR(600.0)) / 600.0
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_NEG_SINEWAVE)
fWaveHeight *= -1.0
ENDIF
spd = SIN(fPeriod * 360.0) * fWaveHeight
ent.sCollider.sData.vCollisionVectors[0].x += (ent.vDirection.y * spd)
ent.sCollider.sData.vCollisionVectors[0].y += (ent.vDirection.x * spd)
ent.sCollider.sData.vCollisionVectors[1] = ent.sCollider.sData.vCollisionVectors[0]
IF (ent.sCollider.sData.eType = GGSM_COLLISION_CAPSULE)
ent.sCollider.sData.vCollisionVectors[1].x += (ent.vDirection.x * ent.fLength)
ent.sCollider.sData.vCollisionVectors[1].y += (ent.vDirection.y * ent.fLength)
ENDIF
ENDPROC
/// PURPOSE:
/// Update projectile and transform collision
/// PARAMS:
/// ent - projectile to update
PROC GGSM_PROJECTILE_UPDATE_STRAIGHT(GGSM_PROJECTILE &ent)
FLOAT spd = 0 +@ GGSM_GET_PROJECTILE_SPEED(ent)
ent.fDistTravelled += spd
ent.sCollider.sData.vCollisionVectors[0].x += (ent.vDirection.x * spd)
ent.sCollider.sData.vCollisionVectors[0].y += (ent.vDirection.y * spd)
ent.sCollider.sData.vCollisionVectors[1] = ent.sCollider.sData.vCollisionVectors[0]
IF (ent.sCollider.sData.eType = GGSM_COLLISION_CAPSULE)
ent.sCollider.sData.vCollisionVectors[1].x += (ent.vDirection.x * ent.fLength)
ent.sCollider.sData.vCollisionVectors[1].y += (ent.vDirection.y * ent.fLength)
ENDIF
ENDPROC
/// PURPOSE:
/// Update projectile and transform collision
/// PARAMS:
/// ent - projectile to update
PROC GGSM_PROJECTILE_UPDATE_MARINE_BOMB(GGSM_PROJECTILE &ent)
INT ind
FLOAT spd = 0 +@ GGSM_GET_PROJECTILE_SPEED(ent)
ent.fDistTravelled += spd
ent.sCollider.sData.vCollisionVectors[0].x += (ent.vDirection.x * spd)
ent.sCollider.sData.vCollisionVectors[0].y += (ent.vDirection.y * spd)
ent.sCollider.sData.vCollisionVectors[1] = ent.sCollider.sData.vCollisionVectors[0]
IF (ent.sCollider.sData.eType = GGSM_COLLISION_CAPSULE)
ent.sCollider.sData.vCollisionVectors[1].x += (ent.vDirection.x * ent.fLength)
ent.sCollider.sData.vCollisionVectors[1].y += (ent.vDirection.y * ent.fLength)
ENDIF
IF (ent.sCollider.sData.vCollisionVectors[0].x < 350.0) OR (ent.sCollider.sData.vCollisionVectors[0].x > 1580.0) OR
(ent.sCollider.sData.vCollisionVectors[0].y < 110.0) OR (ent.sCollider.sData.vCollisionVectors[0].y > 990.0)
GGSM_PROJECTILE_CREATE_ENEMY_CONE_SPREAD(GGSM_PROJECTILE_ENEMY_SHOT, ent.sCollider.sAABB.vCenter, 90.0, GET_RANDOM_INT_IN_RANGE(3, 5), TRUE)
IF GGSM_GET_FREE_FXSPRITE(sGGSMData.sFXSprite, ind, sGGSMData.iExplosionSearchIndex)
GGSM_FXSPRITE_INIT_ANIMATED(sGGSMData.sFXSprite[ind], ent.sCollider.sAABB.vCenter, GGSM_SPRITE_ANIM_EXPLOSION, 2.0)
GGSM_FXSPRITE_SET_PHYSICAL_RADIUS(sGGSMData.sFXSprite[ind], ent.fLength * 2.0)
ENDIF
ent.eFlags = GGSM_PROJECTILE_BIT_NONE
ENDIF
ENDPROC
PROC GGSM_PROJECTILE_UPDATE_BOMB_EXPLOSION(GGSM_PROJECTILE &ent)
UNUSED_PARAMETER(ent)
ENDPROC
/// PURPOSE:
/// Update projectile and transform collision
/// PARAMS:
/// ent - projectile to update
PROC GGSM_PROJECTILE_UPDATE(GGSM_PROJECTILE &ent)
// update timer
IF (IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED))
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_PLAYER_PROJECTILES_ACTIVE)
ent.fTimer += sGGSMData.fDeltaTime
ELSE
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_ENEMY_PROJECTILES_ACTIVE)
ent.fTimer += (sGGSMData.fDeltaTime * sGGSMData.fTimeScale)
IF GGSM_IS_PLAYER_SHIP_ALIVE()
IF (GET_GAME_TIMER() > sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].iInvTimeMS)
IF VECTOR_2D_DIST2(ent.sCollider.sAABB.vCenter, sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].sWorld.vPosition) < (sGGSMData.fGrazeRadius * sGGSMData.fGrazeRadius)
sGGSMData.bGrazed = TRUE
ENDIF
ENDIF
ENDIF
sGGSMData.iEnemyProjectilesActive ++
ENDIF
IF (ent.eType = GGSM_PROJECTILE_ACID)
GGSM_PROJECTILE_UPDATE_ACID(ent)
ELIF (ent.eType = GGSM_PROJECTILE_BOMB_EXPLOSION)
GGSM_PROJECTILE_UPDATE_BOMB_EXPLOSION(ent)
ELIF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_SINEWAVE)
GGSM_PROJECTILE_UPDATE_SINWAVE(ent)
ELIF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_HOMING)
GGSM_PROJECTILE_UPDATE_HOMING(ent)
ELIF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_DELAYED_HOMING)
GGSM_PROJECTILE_UPDATE_DELAYED_HOMING(ent)
ELIF (ent.eType = GGSM_PROJECTILE_MARINE_LAUNCHER)
GGSM_PROJECTILE_UPDATE_MARINE_BOMB(ent)
ELSE
GGSM_PROJECTILE_UPDATE_STRAIGHT(ent)
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_PROJECTILE_BIT_IS_CLUSTER_BOMB)
IF (VECTOR_2D_DIST2(ent.sCollider.sData.vCollisionVectors[0], GGSM_GET_TARGET_AT_PLAYER_POSITION()) < (400.0 * 400.0)) OR (ent.fDistTravelled > 700.0)
GGSM_PROJECTILE_TAKE_DAMAGE(ent)
ENDIF
ENDIF
sGGSMData.iProjectilesActive ++
GGSM_COLLIDER_CALCULATE_AABB(ent.sCollider)
IF NOT GGSM_IS_COLLIDER_AABB_ON_SCREEN(ent.sCollider)
ent.eFlags = GGSM_PROJECTILE_BIT_NONE
EXIT
ENDIF
ENDPROC
/// PURPOSE:
/// Update the projectile array
/// PARAMS:
/// array - array
/// RETURNS:
/// # of active projectiles
FUNC BOOL GGSM_UPDATE_PROJECTILE_ARRAY(GGSM_PROJECTILE &array[])
INT i
INT cnt = COUNT_OF(array)
BOOL bActiveCheck = FALSE
sGGSMData.iProjectilesActive = 0
sGGSMData.iEnemyProjectilesActive = 0
REPEAT cnt i
IF IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_PROJECTILE_BIT_ACTIVE)
GGSM_PROJECTILE_UPDATE(array[i])
bActiveCheck = TRUE
ENDIF
ENDREPEAT
IF (sGGSMData.iProjectilesActive > sGGSMData.iMaxProjectilesActive)
sGGSMData.iMaxProjectilesActive = sGGSMData.iProjectilesActive
ENDIF
RETURN bActiveCheck
ENDFUNC
/// PURPOSE:
/// Update the projectile array
/// PARAMS:
/// array - array
/// RETURNS:
/// # of active projectiles
PROC GGSM_DESTROY_ALL_FACTION_BULLETS(GGSM_PROJECTILE &array[], BOOL bAllied)
INT i
INT cnt = COUNT_OF(array)
REPEAT cnt i
IF IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_PROJECTILE_BIT_ACTIVE)
IF (IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED) = bAllied)
array[i].eFlags = GGSM_PROJECTILE_BIT_NONE
ENDIF
ENDIF
ENDREPEAT
ENDPROC
//-------------------------------------------------
// ENTITY QUERY FUNCTIONS
//-------------------------------------------------
FUNC BOOL GGSM_ENTITY_IS_MAIN_BOSS_SECTION(GGSM_ENTITY &ent)
IF (ent.eType = GGSM_ENTITY_BOSS_BREAD_FACE)
RETURN TRUE
ENDIF
IF (ent.eType = GGSM_ENTITY_BOSS_GRANANA)
RETURN TRUE
ENDIF
IF (ent.eType = GGSM_ENTITY_BOSS_MARINE)
RETURN TRUE
ENDIF
IF (ent.eType = GGSM_ENTITY_BOSS_SMOOTHIE)
RETURN TRUE
ENDIF
IF (ent.eType = GGSM_ENTITY_BOSS_DR_DANK_TOP)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL GGSM_ENTITY_IS_STRONG(GGSM_ENTITY &ent)
IF GGSM_ENTITY_IS_MAIN_BOSS_SECTION(ent)
RETURN TRUE
ENDIF
SWITCH (ent.eType)
CASE GGSM_ENTITY_ARMOUR_FRUITBOWL
CASE GGSM_ENTITY_BANANASLICE_FRONT
CASE GGSM_ENTITY_BANANASLICE
CASE GGSM_ENTITY_BANANASLICE_BACK
CASE GGSM_ENTITY_BOSS_BREAD_FACE
CASE GGSM_ENTITY_BOSS_BREAD_BOTTOM_LEFT
CASE GGSM_ENTITY_BOSS_BREAD_BOTTOM_RIGHT
CASE GGSM_ENTITY_BOSS_BREAD_TOP_LEFT
CASE GGSM_ENTITY_BOSS_BREAD_TOP_RIGHT
CASE GGSM_ENTITY_BANANASPLIT_1
CASE GGSM_ENTITY_BANANASPLIT_2
CASE GGSM_ENTITY_BANANASPLIT_3
CASE GGSM_ENTITY_BANANASPLIT_4
CASE GGSM_ENTITY_BOSS_MARINE
CASE GGSM_ENTITY_BOSS_GRANANA
CASE GGSM_ENTITY_BOSS_GRANANA_HAIR
CASE GGSM_ENTITY_BOSS_DR_DANK_TOP
CASE GGSM_ENTITY_BOSS_DR_DANK_MIDDLE
CASE GGSM_ENTITY_BOSS_DR_DANK_BOTTOM
CASE GGSM_ENTITY_BOSS_SMOOTHIE
RETURN TRUE
ENDSWITCH
RETURN FALSE
ENDFUNC
FUNC INT GGSM_ENTITY_GET_PERCENTAGE_OF_HEALTH(GGSM_ENTITY &ent, FLOAT fPc)
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
RETURN CEIL(TO_FLOAT(dat.iMaxHP) * (fpc / 100.0))
ENDFUNC
FUNC BOOL GGSM_ENTITY_HAS_COLLISION(GGSM_ENTITY &ent)
IF (ent.eState != GGSM_ENTITY_STATE_ALIVE)
RETURN FALSE
ENDIF
RETURN NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_NO_COLLISION)
ENDFUNC
FUNC BOOL GGSM_ENTITY_CAN_COLLIDE_WITH(GGSM_ENTITY &ent, GGSM_ENTITY &ent2)
IF (IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED) = IS_BITMASK_ENUM_AS_ENUM_SET(ent2.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED))
RETURN FALSE
ENDIF
IF NOT GGSM_ENTITY_HAS_COLLISION(ent)
RETURN FALSE
ENDIF
RETURN GGSM_ENTITY_HAS_COLLISION(ent2)
ENDFUNC
FUNC BOOL GGSM_ENTITY_CAN_COLLIDE_WITH_PROJECTILE(GGSM_ENTITY &ent, GGSM_PROJECTILE &proj)
IF (IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED) = IS_BITMASK_ENUM_AS_ENUM_SET(proj.eFlags, GGSM_PROJECTILE_BIT_IS_PLAYER_ALLIED))
RETURN FALSE
ENDIF
IF NOT GGSM_ENTITY_HAS_COLLISION(ent)
RETURN FALSE
ENDIF
RETURN GGSM_PROJECTILE_HAS_COLLISION(proj)
ENDFUNC
FUNC FLOAT GGSM_ENTITY_GET_HP_PERCENTAGE(GGSM_ENTITY &ent)
IF (ent.eState != GGSM_ENTITY_STATE_ALIVE)
RETURN 0.0
ENDIF
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
RETURN TO_FLOAT(ent.iHP) / TO_FLOAT(dat.iMaxHP)
ENDFUNC
PROC GGSM_ENTITY_SET_ANIM_FRAME(GGSM_ENTITY &ent, INT iAnimFrame = -1, BOOL bStopAnim = FALSE)
IF (iAnimFrame = -1)
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
ent.fAnimFrame = TO_FLOAT(dat.iDefaultAnimFrame)
ELSE
ent.fAnimFrame = TO_FLOAT(iAnimFrame)
ENDIF
IF (bStopAnim)
ent.eSpriteAnim = GGSM_SPRITE_ANIM_NONE
ENDIF
ENDPROC
FUNC BOOL GGSM_ENTITY_SET_ANIMATION(GGSM_ENTITY &ent, GGSM_SPRITE_ANIM eType)
GGSM_SPRITE_ANIM_DATA data
IF (eType = GGSM_SPRITE_ANIM_NONE)
ent.eSpriteAnim = eType
RETURN FALSE
ENDIF
IF NOT GGSM_GET_SPRITE_ANIM_DATA(eType, data)
RETURN FALSE
ENDIF
ent.eSpriteAnim = eType
ent.fAnimFrame = data.fStartFrame
RETURN TRUE
ENDFUNC
PROC GGSM_ENTITY_DETACH_FROM_PARENT(GGSM_ENTITY &ent)
GGSM_ENTITY_SET_PARENT_INDEX(ent, GGSM_INVALID_ENTITY)
ent.sLocal = ent.sWorld
ENDPROC
//-------------------------------------------------
// ENTITY WEAPON FUNCTIONS
//-------------------------------------------------
PROC GGSM_ENTITY_SET_WEAPON_STATE(GGSM_ENTITY &ent, GGSM_WEAPON_STATE eState, BOOL bResetTimer = TRUE)
ent.eWeaponState = eState
IF (bResetTimer)
ent.fWeaponTimer = 0
ENDIF
ENDPROC
PROC GGSM_ENTITY_SET_WEAPON(GGSM_ENTITY &ent, GGSM_WEAPON_TYPE eType)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(ent.eType)
IF (eType = GGSM_WEAPON_DEFAULT)
eType = entData.eWeaponType
ENDIF
ent.eWeaponType = eType
ent.iWeaponShotCounter = 0
ent.iWeaponBurstCounter = 0
IF (ent.eWeaponType = GGSM_WEAPON_NONE)
EXIT
ENDIF
ENDPROC
FUNC VECTOR_2D GGSM_ENTITY_GET_GUN_WORLD_POS(GGSM_ENTITY &ent)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(ent.eType)
RETURN GGSM_TRANSFORM_LOCAL_POS_TO_WORLD_POS(ent.sWorld, entData.sGunPos.vPosition)
ENDFUNC
FUNC BOOL GGSM_ENTITY_FIRE_WEAPON(GGSM_ENTITY &ent)
IF (ent.eWeaponType = GGSM_WEAPON_NONE) OR (ent.eWeaponType = GGSM_WEAPON_INVALID)
RETURN FALSE
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.eStageFlags, GGSM_STAGE_BIT_DISABLE_WEAPONS)
RETURN FALSE
ENDIF
INT ind, cnt
FLOAT fBaseAngle
VECTOR_2D tgtPos, dir, shotPos
BOOL bIsAllied = IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED)
BOOL bIsRandomSpread = IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_RANDOM_SPREAD)
// handle targeting
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(ent.eType)
VECTOR_2D gunPos = GGSM_TRANSFORM_LOCAL_POS_TO_WORLD_POS(ent.sWorld, entData.sGunPos.vPosition)
fBaseAngle = ent.sWorld.fRotation + entData.sGunPos.fRotation
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED) AND NOT bIsAllied
tgtPos = GGSM_GET_TARGET_AT_PLAYER_POSITION()
dir = SUBTRACT_VECTOR_2D(tgtPos, gunPos)
fBaseAngle = GET_HEADING_FROM_VECTOR_2D(dir.x, dir.y)
ENDIF
// get data from weapon
GGSM_WEAPON_DATA wpnData = GGSM_WEAPON_DATA_GET(ent.eWeaponType)
INT iNumShots = wpnData.iNumShots
BOOL bBurstDone
IF (ent.iNumShotsOverride > 0)
iNumShots = ent.iNumShotsOverride
ENDIF
IF (sGGSMData.bHardMode) AND (NOT bIsAllied) AND (iNumShots > 1)
iNumShots ++
ENDIF
FLOAT wSpread
INT iProjList[16]
FLOAT fAngle, fAngDiff = 360.0 / TO_FLOAT(iNumShots)
SWITCH (wpnData.eSpreadType)
CASE GGSM_SPREAD_LINEAR
IF GGSM_GET_FREE_PROJECTILE(sGGSMData.sProjectiles, ind, sGGSMData.iProjectileSearchIndex)
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[ind], wpnData.eProjectileType, gunPos, fBaseAngle, bIsAllied)
ent.iWeaponShotCounter ++
bBurstDone = TRUE
ENDIF
BREAK
CASE GGSM_SPREAD_RADIAL
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_ALT_SPREAD)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRED_ALT_SPREAD)
fBaseAngle += (fAngDiff / 2.0)
ENDIF
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
cnt = GGSM_GET_FREE_PROJECTILES(sGGSMData.sProjectiles, 1, iProjList, sGGSMData.iProjectileSearchIndex)
fAngle = fBaseAngle + (TO_FLOAT(ent.iWeaponShotCounter) * fAngDiff)
IF (bIsRandomSpread)
fAngle = GET_RANDOM_FLOAT_IN_RANGE(0.0, 360.0)
ENDIF
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[0]], wpnData.eProjectileType, gunPos, fAngle, bIsAllied)
ent.iWeaponShotCounter ++
IF (ent.iWeaponShotCounter >= iNumShots)
bBurstDone = TRUE
ent.iWeaponShotCounter = 0
ENDIF
ELSE
cnt = GGSM_GET_FREE_PROJECTILES(sGGSMData.sProjectiles, iNumShots, iProjList, sGGSMData.iProjectileSearchIndex)
REPEAT cnt ind
fAngle = fBaseAngle + (TO_FLOAT(ind) * fAngDiff)
IF (bIsRandomSpread)
fAngle = GET_RANDOM_FLOAT_IN_RANGE(0.0, 360.0)
ENDIF
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[ind]], wpnData.eProjectileType, gunPos, fAngle, bIsAllied)
ENDREPEAT
ent.iWeaponShotCounter = 0
bBurstDone = TRUE
ENDIF
BREAK
CASE GGSM_SPREAD_CONE
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_ALT_SPREAD)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRED_ALT_SPREAD)
iNumShots ++
ENDIF
ENDIF
fAngDiff = wpnData.fSpreadValue / TO_FLOAT(iNumShots - 1)
fBaseAngle -= (wpnData.fSpreadValue / 2.0)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
cnt = GGSM_GET_FREE_PROJECTILES(sGGSMData.sProjectiles, 1, iProjList, sGGSMData.iProjectileSearchIndex)
fAngle = fBaseAngle + (TO_FLOAT(ent.iWeaponShotCounter) * fAngDiff)
IF (bIsRandomSpread)
fAngle = fBaseAngle + (GET_RANDOM_FLOAT_IN_RANGE(0.0, TO_FLOAT(iNumShots)) * fAngDiff)
ENDIF
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[0]], wpnData.eProjectileType, gunPos, fAngle, bIsAllied)
ent.iWeaponShotCounter ++
IF (ent.iWeaponShotCounter >= iNumShots)
bBurstDone = TRUE
ent.iWeaponShotCounter = 0
ENDIF
ELSE
cnt = GGSM_GET_FREE_PROJECTILES(sGGSMData.sProjectiles, iNumShots, iProjList, sGGSMData.iProjectileSearchIndex)
REPEAT cnt ind
fAngle = fBaseAngle + (TO_FLOAT(ind) * fAngDiff)
IF (bIsRandomSpread)
fAngle = fBaseAngle + (GET_RANDOM_FLOAT_IN_RANGE(0.0, TO_FLOAT(iNumShots)) * fAngDiff)
ENDIF
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[ind]], wpnData.eProjectileType, gunPos, fAngle, bIsAllied)
ent.iWeaponShotCounter = 0
bBurstDone = TRUE
ENDREPEAT
ENDIF
BREAK
CASE GGSM_SPREAD_WAVE
IF GGSM_GET_FREE_PROJECTILE(sGGSMData.sProjectiles, ind, sGGSMData.iProjectileSearchIndex)
wSpread = (wpnData.fSpreadValue / 2.0)
dir = GGSM_GET_VECTOR_FROM_HEADING(fBaseAngle)
shotPos = gunpos
IF (ent.iWeaponShotCounter % 3 = 2)
shotPos.x -= dir.y * wSpread
shotPos.y -= dir.x * wSpread
ELIF (ent.iWeaponShotCounter % 3 = 0)
shotPos.x += dir.y * wSpread
shotPos.y += dir.x * wSpread
ENDIF
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[ind], wpnData.eProjectileType, shotPos, fBaseAngle, bIsAllied)
ent.iWeaponShotCounter ++
bBurstDone = TRUE
ENDIF
BREAK
CASE GGSM_SPREAD_DUALARCH
cnt = GGSM_GET_FREE_PROJECTILES(sGGSMData.sProjectiles, 2, iProjList, sGGSMData.iProjectileSearchIndex)
IF (cnt = 2)
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[0]], wpnData.eProjectileType, gunpos, fBaseAngle, bIsAllied)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sProjectiles[iProjList[0]].eFlags, GGSM_PROJECTILE_BIT_IS_SINEWAVE)
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[1]], wpnData.eProjectileType, gunpos, fBaseAngle, bIsAllied)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sProjectiles[iProjList[1]].eFlags, GGSM_PROJECTILE_BIT_IS_SINEWAVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sProjectiles[iProjList[1]].eFlags, GGSM_PROJECTILE_BIT_IS_NEG_SINEWAVE)
bBurstDone = TRUE
ENDIF
BREAK
CASE GGSM_SPREAD_BACKVULCAN
cnt = GGSM_GET_FREE_PROJECTILES(sGGSMData.sProjectiles, 3, iProjList, sGGSMData.iProjectileSearchIndex)
wSpread = (wpnData.fSpreadValue / 2.0)
dir = GGSM_GET_VECTOR_FROM_HEADING(fBaseAngle)
IF (cnt = 3)
shotPos = gunpos
shotPos.x -= dir.y * wSpread
shotPos.y -= dir.x * wSpread
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[0]], wpnData.eProjectileType, shotPos, fBaseAngle - 180.0, bIsAllied)
shotPos = gunpos
shotPos.x += dir.y * wSpread
shotPos.y += dir.x * wSpread
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[1]], wpnData.eProjectileType, shotPos, fBaseAngle - 180.0, bIsAllied)
GGSM_PROJECTILE_INIT(sGGSMData.sProjectiles[iProjList[2]], wpnData.eProjectileType, gunpos, fBaseAngle, bIsAllied)
bBurstDone = TRUE
ENDIF
BREAK
ENDSWITCH
IF (bBurstDone = TRUE)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRED_ALT_SPREAD)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRED_ALT_SPREAD)
ELSE
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRED_ALT_SPREAD)
ENDIF
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_BURST_FIRED)
ent.iWeaponBurstCounter ++
ENDIF
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
GGSM_ENTITY_SET_WEAPON_STATE(ent, GGSM_WEAPON_STATE_FIRED)
GGSM_PROJECTILE_DATA projData = GGSM_PROJECTILE_DATA_GET(wpnData.eProjectileType)
GGSM_PLAY_SOUND_FROM_POSITION(projData.eProjSound, gunpos)
RETURN TRUE
ENDFUNC
PROC GGSM_ENTITY_UPDATE_WEAPON(GGSM_ENTITY &ent)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_BURST_FIRED)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_BURST_RELOAD_THIS_FRAME)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_ENTERED_PLAYFIELD)
EXIT
ENDIF
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_ON_SCREEN)
EXIT
ENDIF
IF GGSM_IS_DIALOG_ACTIVE()
EXIT
ENDIF
// handle slow down
GGSM_PROJECTILE_DATA projdata
GGSM_WEAPON_DATA wpnData = GGSM_WEAPON_DATA_GET(ent.eWeaponType)
FLOAT fDelta = GET_FRAME_TIME()
BOOL bIsAllied = IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED)
IF (bIsAllied)
ent.fWeaponTimer += fDelta
ELSE
ent.fWeaponTimer += (fDelta * sGGSMData.fTimeScale)
ENDIF
// update state
SWITCH (ent.eWeaponState)
CASE GGSM_WEAPON_STATE_READY
projdata = GGSM_PROJECTILE_DATA_GET(wpnData.eProjectileType)
IF (ent.fWeaponTimer >= projdata.fShotReloadTime)
GGSM_ENTITY_SET_WEAPON_STATE(ent, GGSM_WEAPON_STATE_FIRING)
ENDIF
BREAK
CASE GGSM_WEAPON_STATE_FIRING
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_FIRE_WEAPON(ent)
ENDIF
BREAK
CASE GGSM_WEAPON_STATE_FIRED
IF (wpnData.iNumBursts <= 0) OR (ent.iWeaponBurstCounter < wpnData.iNumBursts)
GGSM_ENTITY_SET_WEAPON_STATE(ent, GGSM_WEAPON_STATE_RELOAD)
ELSE
GGSM_ENTITY_SET_WEAPON_STATE(ent, GGSM_WEAPON_STATE_BURST_RELOAD)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_BURST_RELOAD_THIS_FRAME)
ENDIF
BREAK
CASE GGSM_WEAPON_STATE_RELOAD
projdata = GGSM_PROJECTILE_DATA_GET(wpnData.eProjectileType)
IF (ent.fWeaponTimer >= projdata.fShotReloadTime)
GGSM_ENTITY_SET_WEAPON_STATE(ent, GGSM_WEAPON_STATE_FIRING)
ENDIF
BREAK
CASE GGSM_WEAPON_STATE_BURST_RELOAD
IF (ent.fWeaponTimer >= wpnData.fBurstReloadTime)
ent.iWeaponBurstCounter = 0
GGSM_ENTITY_SET_WEAPON_STATE(ent, GGSM_WEAPON_STATE_SPINUP)
ENDIF
BREAK
CASE GGSM_WEAPON_STATE_SPINUP
GGSM_ENTITY_SET_WEAPON_STATE(ent, GGSM_WEAPON_STATE_FIRING)
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------
// ENTITY MOVEMENT FUNCTIONS
//-------------------------------------------------
PROC GGSM_ENTITY_SET_MOVEMENT_STATE(GGSM_ENTITY &ent, GGSM_MOVESTATE_TYPE st)
ent.eMoveState = st
ent.fMoveTimer = 0.0
CDEBUG1LN(DEBUG_MINIGAME, "GGSM_ENTITY_SET_MOVEMENT_STATE - TYPE:", GGSM_ENTITY_TYPE_TO_STRING(ent.eType), " ARRAY INDEX:", GGSM_ENTITY_GET_ARRAY_INDEX(ent), " MOVETYPE:", GGSM_MOVEMENT_TYPE_TO_STRING(ent.eMoveType), " STATE:", GGSM_MOVESTATE_TYPE_TO_STRING(st))
ENDPROC
PROC GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(GGSM_ENTITY &ent, VECTOR_2D localTgtPos)
ent.iPackedLocalTargetPosition = GGSM_PACK_VECTOR_2D_TO_INT(localTgtPos)
ent.vLocalDirection = SUBTRACT_VECTOR_2D(localTgtPos, ent.sLocal.vPosition)
ent.vLocalDirection = NORMALISE_VECTOR_2D(ent.vLocalDirection)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_TARGET_POINT_REACHED)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_TARGET_POINT_REACHED_THIS_FRAME)
IF GGSM_IS_POINT_BEHIND_PLANE(localTgtPos, ent.vLocalDirection, ent.sLocal.vPosition)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_LAST_PLANE_SIDE)
ELSE
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_LAST_PLANE_SIDE)
ENDIF
ENDPROC
PROC GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(GGSM_ENTITY &ent, VECTOR_2D vWorldPos)
VECTOR_2D localTgtPos = vWorldPos
INT iParentIndex = GGSM_ENTITY_GET_PARENT_INDEX(ent)
// convert world position into target space
IF (iParentIndex != GGSM_INVALID_ENTITY)
localTgtPos = GGSM_TRANSFORM_WORLD_POS_TO_LOCAL_POS(sGGSMData.sEntities[iParentIndex].sWorld, vWorldPos)
ENDIF
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, localTgtPos)
ENDPROC
/// PURPOSE:
/// Sets the movement type
/// PARAMS:
/// ent - entity to set
/// eType - movement
/// fMParam1 - param 1
/// fMparam2 - param 2
/// NOTE: For different movetypes the params are different
/// GGSM_MOVEMENT_BG_ATTACK - mParam1 = how to move before scaling in (default is 800.0)
/// GGSM_MOVEMENT_FORWARD_THEN_BACK - mParam1 = how far to move forward
/// GGSM_MOVEMENT_ORBIT - mParam1 = dist from center, mParam2 = start angle, mParam3 = revs / sec
/// GGSM_MOVEMENT_SINWAVE - mParam1 = period length (dist of 1 wave), mParam2 = height of wave
/// GGSM_MOVEMENT_TARGETTED_WANDER - mParam1 = how long to wait at target,
PROC GGSM_ENTITY_SET_MOVEMENT_TYPE(GGSM_ENTITY &ent, GGSM_MOVEMENT_TYPE eType, FLOAT fMParam1 = 0.0, FLOAT fMparam2 = 0.0, FLOAT fMparam3 = 0.0)
ent.eMoveType = eType
ent.eMoveState = GGSM_MOVESTATE_START
ent.fMoveParam[0] = fMParam1
ent.fMoveParam[1] = fMParam2
ent.fMoveParam[2] = fMParam3
ent.eMoveFlags = GGSM_MOVE_BIT_NONE
ent.fMoveTimer = 0.0
// do setups based on type
SWITCH (eType)
CASE GGSM_MOVEMENT_BG_ATTACK
IF (ent.fMoveParam[0] = 0.0)
ent.fMoveParam[0] = 950.0
ENDIF
BREAK
CASE GGSM_MOVEMENT_FORWARD_THEN_BACK
IF (ent.fMoveParam[0] = 0.0)
ent.fMoveParam[0] = 700.0
ENDIF
BREAK
CASE GGSM_MOVEMENT_SIN_WAVE
ent.iPackedLocalOrigin = GGSM_PACK_VECTOR_2D_TO_INT(ent.sLocal.vPosition)
BREAK
CASE GGSM_MOVEMENT_SNAKE_SECTION
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_POSITION_OVERRIDE)
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_SET_MOVEMENT_ORBIT(GGSM_ENTITY &ent, FLOAT fRange, FLOAT fStartAngle = 0.0, FLOAT fRevsPerSec = 1.0)
GGSM_ENTITY_SET_MOVEMENT_TYPE(ent, GGSM_MOVEMENT_ORBIT, fRange, fStartAngle, fRevsPerSec)
ENDPROC
PROC GGSM_ENTITY_RETURN_TO_ORIGIN(GGSM_ENTITY &ent)
VECTOR_2D tmp = GGSM_UNPACK_VECTOR_2D(ent.iPackedLocalOrigin)
GGSM_ENTITY_SET_MOVEMENT_TYPE(ent, GGSM_MOVEMENT_MOVE_TO_VECTOR)
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, tmp)
ENDPROC
PROC GGSM_ENTITY_SET_MOVEMENT_TO_LOCAL_VECTOR(GGSM_ENTITY &ent, VECTOR_2D vec, FLOAT fDelay = 0.0)
GGSM_ENTITY_SET_MOVEMENT_TYPE(ent, GGSM_MOVEMENT_MOVE_TO_VECTOR, fDelay)
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, vec)
ENDPROC
PROC GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(GGSM_ENTITY &ent, VECTOR_2D vec, FLOAT fDelay = 0.0)
GGSM_ENTITY_SET_MOVEMENT_TYPE(ent, GGSM_MOVEMENT_MOVE_TO_VECTOR, fDelay)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, vec)
ENDPROC
PROC GGSM_ENTITY_SET_MOVEMENT_TO_FACE_LOCAL_VECTOR(GGSM_ENTITY &ent, VECTOR_2D vec, FLOAT fDelay = 0.0)
GGSM_ENTITY_SET_MOVEMENT_TYPE(ent, GGSM_MOVEMENT_ROTATE_TO_FACE_VECTOR, fDelay)
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, vec)
ENDPROC
PROC GGSM_ENTITY_SET_MOVEMENT_TO_FACE_VECTOR(GGSM_ENTITY &ent, VECTOR_2D vec, FLOAT fDelay = 0.0)
GGSM_ENTITY_SET_MOVEMENT_TYPE(ent, GGSM_MOVEMENT_ROTATE_TO_FACE_VECTOR, fDelay)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, vec)
ENDPROC
PROC GGSM_ENTITY_SET_LOCAL_TRANSFORM(GGSM_ENTITY &ent, FLOAT x, FLOAT y, FLOAT rot)
ent.sLocal.vPosition.x = x
ent.sLocal.vPosition.y = y
ent.sLocal.fRotation = rot
ENDPROC
FUNC BOOL GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(GGSM_ENTITY &ent, FLOAT spd, BOOL bMultByFrameTime = FALSE)
BOOL bCurCheck
VECTOR_2D dir
VECTOR_2D localTgtPos = GGSM_UNPACK_VECTOR_2D(ent.iPackedLocalTargetPosition)
FLOAT fAngle
IF (bMultByFrameTime)
spd = 0 +@ spd
ENDIF
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += (ent.vLocalDirection.x * spd)
ent.sLocal.vPosition.y += (ent.vLocalDirection.y * spd)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_ROTATE_TO_FACE_DIRECTION)
dir = SUBTRACT_VECTOR_2D(localTgtPos, ent.sLocal.vPosition)
fAngle = GET_HEADING_FROM_VECTOR_2D(dir.x, dir.y) - ent.sLocal.fRotation
IF (fAngle > 180.0)
fAngle -= 360.0
ENDIF
IF (fAngle < -180.0)
fAngle += 360.0
ENDIF
ent.sLocal.fRotation += (0 +@ fAngle)
ENDIF
// we've crossed the plane
bCurCheck = GGSM_IS_POINT_BEHIND_PLANE(localTgtPos, ent.vLocalDirection, ent.sLocal.vPosition)
IF (bCurCheck != IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_LAST_PLANE_SIDE)) OR (VECTOR_2D_DIST2(localTgtPos, ent.sLocal.vPosition) <= 1)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_TARGET_POINT_REACHED)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_TARGET_POINT_REACHED_THIS_FRAME)
ent.sLocal.vPosition = localTgtPos
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRE_ON_ARRIVE)
GGSM_ENTITY_FIRE_WEAPON(ent)
ENDIF
RETURN TRUE
ENDIF
IF (bCurCheck)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_LAST_PLANE_SIDE)
ELSE
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_LAST_PLANE_SIDE)
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET_SCALER(GGSM_ENTITY &ent, FLOAT spd, FLOAT fScaleDist = 400.0, BOOL bMultByFrameTime = FALSE, BOOL bScaleOut = FALSE)
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, spd, bMultByFrameTime)
ent.sLocal.vScale = INIT_VECTOR_2D(1.0, 1.0)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_NO_COLLISION)
RETURN TRUE
ENDIF
VECTOR_2D localTgtPos = GGSM_UNPACK_VECTOR_2D(ent.iPackedLocalTargetPosition)
FLOAT fDist = VECTOR_2D_DIST(localTgtPos, ent.sLocal.vPosition)
FLOAT fScale = ((fScaleDist - fDist) / fScaleDist)
fScale = CLAMP(fScale, 0.25, 1.0)
ent.sLocal.vScale = INIT_VECTOR_2D(fScale, fScale)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_NO_COLLISION)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
UNUSED_PARAMETER(bScaleOut)
RETURN FALSE
ENDFUNC
PROC GGSM_ENTITY_PRE_CALCULATE_FOR_ARC(GGSM_ENTITY &ent, VECTOR_2D arcCenter)
VECTOR_2D temp, tgtPos
// work out position to target to
temp = ent.sLocal.vPosition
temp.x += ent.vLocalDirection.x * 3000.0
temp.y += ent.vLocalDirection.y * 3000.0
tgtPos = GGSM_GET_CLOSEST_POINT_ON_LINE(arcCenter, ent.sLocal.vPosition, temp, FALSE)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, tgtpos)
// work out radius of arc
temp = SUBTRACT_VECTOR_2D(tgtpos, arcCenter)
ent.fMoveParam[0] = VECTOR_2D_DIST(tgtPos, arcCenter)
// work out first angle point of arc
ent.fMoveParam[1] = GET_HEADING_FROM_VECTOR_2D(ent.vLocalDirection.x, ent.vLocalDirection.y)
// work out angular speed of arc
ent.fMoveParam[2] = (GGSM_ENTITY_GET_SPEED(ent) / (2.0 * GGSM_PI * ent.fMoveParam[0]))
SWITCH (ent.eMoveType)
CASE GGSM_MOVEMENT_ARC_BASE_LEFT
CASE GGSM_MOVEMENT_HALF_ARC_BASE_LEFT
ent.fMoveParam[1] += 90.0
ent.fMoveParam[2] *= 1.0
BREAK
CASE GGSM_MOVEMENT_ARC_TOP_LEFT
CASE GGSM_MOVEMENT_HALF_ARC_TOP_LEFT
ent.fMoveParam[1] -= 90.0
ent.fMoveParam[2] *= -1.0
BREAK
CASE GGSM_MOVEMENT_ARC_TOP_RIGHT
CASE GGSM_MOVEMENT_HALF_ARC_TOP_RIGHT
ent.fMoveParam[1] += 90.0
ent.fMoveParam[2] *= 1.0
BREAK
CASE GGSM_MOVEMENT_ARC_BASE_RIGHT
CASE GGSM_MOVEMENT_HALF_ARC_BASE_RIGHT
ent.fMoveParam[1] -= 90.0
ent.fMoveParam[2] *= -1.0
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_ARC(GGSM_ENTITY &ent)
FLOAT fDelta
FLOAT spd = GGSM_ENTITY_GET_SPEED(ent)
VECTOR_2D arcCenter = INIT_VECTOR_2D(cfBASE_SCREEN_HALF_WIDTH, cfBASE_SCREEN_HALF_HEIGHT)
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
arcCenter = INIT_VECTOR_2D(cfBASE_SCREEN_HALF_WIDTH, cfBASE_SCREEN_HALF_HEIGHT)
GGSM_ENTITY_PRE_CALCULATE_FOR_ARC(ent, arcCenter)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_STARTPOS)
BREAK
CASE GGSM_MOVESTATE_GOTO_STARTPOS
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, spd, TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ARC)
ent.fDistTravelled = 0
ENDIF
BREAK
CASE GGSM_MOVESTATE_ARC
fDelta = (0 +@ (ent.fMoveParam[2] * 360.0 * sGGSMData.fTimeScale))
ent.fMoveParam[1] += fDelta
ent.fDistTravelled += ABSF(fDelta)
// rotate round arc center
ent.sLocal.vPosition.x = SIN(ent.fMoveParam[1]) * ent.fMoveParam[0]
ent.sLocal.vPosition.y = COS(ent.fMoveParam[1]) * ent.fMoveParam[0]
// add arc center to local to put it back
ent.sLocal.vPosition.x += arcCenter.x
ent.sLocal.vPosition.y += arcCenter.y
IF (ent.fDistTravelled >= 360.0)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_ATTACK_KAMAKAZE(GGSM_ENTITY &ent)
FLOAT spd
FLOAT fDist = 600.0
VECTOR_2D tgtpos = GGSM_GET_TARGET_AT_PLAYER_POSITION()
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
ent.fDistTravelled = 0.0
ent.iWeaponBurstCounter = 0
ent.fWeaponTimer = 0
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_SET_WEAPON_STATE(ent, GGSM_WEAPON_STATE_READY)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ (GGSM_ENTITY_GET_SPEED(ent) / 2.0)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
IF (ent.fDistTravelled > fDist)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_FIRE_WEAPON(ent)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ATTACK)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ATTACK
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_ENTITY_BIT_WPN_BURST_FIRED)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_DETACH_FROM_PARENT(ent)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, tgtpos)
IF (ent.eType = GGSM_ENTITY_BANANARED_ATTACK)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_FLYBY_KAMIKAZE, ent.sWorld.vPosition)
GGSM_ENTITY_SET_ANIM_FRAME(ent, 1)
ENDIF
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_RAMMING)
ENDIF
BREAK
CASE GGSM_MOVESTATE_RAMMING
spd = GGSM_ENTITY_GET_SPEED(ent) * 2.0
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, spd, TRUE)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_COMPLETE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_BG_ATTACK(GGSM_ENTITY &ent)
FLOAT spd
VECTOR_2D tgtPos
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
ent.fDistTravelled = 0
tgtPos.x = ent.sLocal.vPosition.x + (ent.vLocalDirection.x * ent.fMoveParam[0])
tgtPos.y = ent.sLocal.vPosition.y + (ent.vLocalDirection.y * ent.fMoveParam[0])
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, tgtPos)
ent.sLocal.vScale = INIT_VECTOR_2D(0.5, 0.5)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_NO_COLLISION)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_POS)
BREAK
CASE GGSM_MOVESTATE_GOTO_POS
spd = (GGSM_ENTITY_GET_SPEED(ent) * 0.75)
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET_SCALER(ent, spd, ent.fMoveParam[0] / 2.0, TRUE)
IF (ent.eType = GGSM_ENTITY_BANANARED_ATTACK)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_FLYBY_KAMIKAZE, ent.sWorld.vPosition)
GGSM_ENTITY_SET_ANIM_FRAME(ent, 1)
ELSE
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_FLYBY_UFO, ent.sWorld.vPosition)
ENDIF
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_NO_COLLISION)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, GGSM_GET_TARGET_AT_PLAYER_POSITION())
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += (ent.vLocalDirection.x * spd)
ent.sLocal.vPosition.y += (ent.vLocalDirection.y * spd)
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_DESTRUCT_ON_IMPACT(GGSM_ENTITY &ent)
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
BREAK
CASE GGSM_MOVESTATE_ACTIVE
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_SELF_DESTRUCT)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_COMPLETE
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_FOLLOW_PATH(GGSM_ENTITY &ent)
FLOAT spd
GGSM_PATH_DATA dat
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
ent.fPathValue = 0
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_REMOVE_WHEN_OFFSCREEN)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
BREAK
CASE GGSM_MOVESTATE_SELECT_POS
dat = GGSM_PATH_DATA_GET(ent.iPathNumber)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, GGSM_PATH_DATA_GET_POINT(dat, FLOOR(ent.fPathValue)))
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_POS)
BREAK
CASE GGSM_MOVESTATE_GOTO_POS
spd = GGSM_ENTITY_GET_SPEED(ent)
IF NOT GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, spd, TRUE)
EXIT
ENDIF
ent.fPathValue ++
dat = GGSM_PATH_DATA_GET(ent.iPathNumber)
IF (ent.fPathValue >= GGSM_PATH_DATA_GET_NUM_POINTS(dat))
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ELSE
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_FOLLOW_REL_PATH(GGSM_ENTITY &ent)
FLOAT spd
GGSM_PATH_DATA dat
VECTOR_2D vBasePos, vRootPoint, vRel
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
ent.fPathValue = 0
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_REMOVE_WHEN_OFFSCREEN)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
BREAK
CASE GGSM_MOVESTATE_SELECT_POS
dat = GGSM_PATH_DATA_GET(ent.iPathNumber)
vBasePos = GGSM_UNPACK_VECTOR_2D(ent.iPackedLocalOrigin)
vRootPoint = GGSM_PATH_DATA_GET_POINT(dat, 0)
vRel = SUBTRACT_VECTOR_2D(GGSM_PATH_DATA_GET_POINT(dat, FLOOR(ent.fPathValue)), vRootPoint)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_FLIP_RELATIVE_PATH_X)
vRel.x *= -1.0
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_FLIP_RELATIVE_PATH_Y)
vRel.y *= -1.0
ENDIF
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, ADD_VECTOR_2D(vBasePos, vRel))
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_POS)
BREAK
CASE GGSM_MOVESTATE_GOTO_POS
spd = GGSM_ENTITY_GET_SPEED(ent)
IF NOT GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, spd, TRUE)
EXIT
ENDIF
ent.fPathValue ++
dat = GGSM_PATH_DATA_GET(ent.iPathNumber)
IF (ent.fPathValue >= GGSM_PATH_DATA_GET_NUM_POINTS(dat))
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ELSE
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_FORWARD_THEN_BACK(GGSM_ENTITY &ent)
VECTOR_2D tgtPos
FLOAT spd
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
tgtPos = ent.sLocal.vPosition
tgtPos.x += (ent.vLocalDirection.x * ent.fMoveParam[0])
tgtPos.y += (ent.vLocalDirection.y * ent.fMoveParam[0])
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, tgtpos)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_STARTPOS)
BREAK
CASE GGSM_MOVESTATE_GOTO_STARTPOS
spd = GGSM_ENTITY_GET_SPEED(ent)
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, spd, TRUE)
GGSM_ENTITY_FIRE_WEAPON(ent)
ent.vLocalDirection.x *= -1.0
ent.vLocalDirection.y *= -1.0
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_ON_SCREEN)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_HALF_ARC(GGSM_ENTITY &ent)
FLOAT fDelta
FLOAT spd = GGSM_ENTITY_GET_SPEED(ent)
VECTOR_2D arcCenter
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
arcCenter = INIT_VECTOR_2D(cfBASE_SCREEN_HALF_WIDTH, cfBASE_SCREEN_HALF_HEIGHT)
GGSM_ENTITY_PRE_CALCULATE_FOR_ARC(ent, arcCenter)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_STARTPOS)
BREAK
CASE GGSM_MOVESTATE_GOTO_STARTPOS
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, spd, TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ARC)
ent.fDistTravelled = 0
ENDIF
BREAK
CASE GGSM_MOVESTATE_ARC
fDelta = (0 +@ (ent.fMoveParam[2] * 360.0 * sGGSMData.fTimeScale))
ent.fMoveParam[1] += fDelta
ent.fDistTravelled += ABSF(fDelta)
// rotate round arc center
ent.sLocal.vPosition.x = SIN(ent.fMoveParam[1]) * ent.fMoveParam[0]
ent.sLocal.vPosition.y = COS(ent.fMoveParam[1]) * ent.fMoveParam[0]
// add arc center to local to put it back
ent.sLocal.vPosition.x += arcCenter.x
ent.sLocal.vPosition.y += arcCenter.y
IF (ent.fDistTravelled >= 180.0)
ent.vLocalDirection.x *= -1.0
ent.vLocalDirection.y *= -1.0
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_KAMAKAZE(GGSM_ENTITY &ent)
FLOAT spd
FLOAT fDist = 600.0
VECTOR_2D tgtpos = GGSM_GET_TARGET_AT_PLAYER_POSITION()
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
ent.fDistTravelled = 0.0
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
IF (VECTOR_2D_DIST2(tgtpos, ent.sWorld.vPosition) < (fDist * fDist))
GGSM_ENTITY_DETACH_FROM_PARENT(ent)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, tgtpos)
IF (ent.eType = GGSM_ENTITY_BANANARED_ATTACK)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_FLYBY_KAMIKAZE, ent.sWorld.vPosition)
GGSM_ENTITY_SET_ANIM_FRAME(ent, 1)
ENDIF
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_RAMMING)
ENDIF
BREAK
CASE GGSM_MOVESTATE_RAMMING
spd = GGSM_ENTITY_GET_SPEED(ent) * 2.0
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, spd, TRUE)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_COMPLETE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_ORBIT(GGSM_ENTITY &ent)
VECTOR_2D tmp
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
ent.eMoveState = GGSM_MOVESTATE_ACTIVE
tmp.x = SIN(ent.fMoveParam[1]) * ent.fMoveParam[0]
tmp.y = COS(ent.fMoveParam[1]) * ent.fMoveParam[0]
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, tmp)
IF (VECTOR_2D_DIST(ADD_VECTOR_2D(tmp, ent.sLocal.vPosition), ent.sLocal.vPosition) < 2)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ELSE
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_STARTPOS)
ENDIF
BREAK
CASE GGSM_MOVESTATE_GOTO_STARTPOS
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
// fMoveParam0 is the distance from center
// fMoveParam1 is the angle around the center
// fMoveParam2 is revolutions per second
ent.fMoveParam[1] += (0 +@ (ent.fMoveParam[2] * 360.0 * sGGSMData.fTimeScale))
ent.sLocal.vPosition.x = SIN(ent.fMoveParam[1]) * ent.fMoveParam[0]
ent.sLocal.vPosition.y = COS(ent.fMoveParam[1]) * ent.fMoveParam[0]
ent.vLocalDirection = NORMALISE_VECTOR_2D(ent.sLocal.vPosition)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_ROTATE_TO_FACE_DIRECTION)
ent.sLocal.fRotation = GET_HEADING_FROM_VECTOR_2D(ent.sLocal.vPosition.x, ent.sLocal.vPosition.y) - ent.sLocal.fRotation
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_TO_VECTOR(GGSM_ENTITY &ent)
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_POS)
BREAK
CASE GGSM_MOVESTATE_GOTO_POS
IF (ent.fMoveTimer >= ent.fMoveParam[0])
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_WPN_FIRE_ON_ARRIVE)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
EXIT
ENDIF
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ATTACK)
BREAK
CASE GGSM_MOVESTATE_ATTACK
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_ENTITY_BIT_WPN_BURST_FIRED)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_ROTATE_FACE_TO_VECTOR(GGSM_ENTITY &ent)
VECTOR_2D dir
FLOAT fAngle
VECTOR_2D localTgtPos = GGSM_UNPACK_VECTOR_2D(ent.iPackedLocalTargetPosition)
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_POS)
BREAK
CASE GGSM_MOVESTATE_GOTO_POS
IF (ent.fMoveTimer >= ent.fMoveParam[0])
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
dir = SUBTRACT_VECTOR_2D(localTgtPos, ent.sLocal.vPosition)
fAngle = GET_HEADING_FROM_VECTOR_2D(dir.x, dir.y) - ent.sLocal.fRotation
IF (fAngle > 180.0)
fAngle -= 360.0
ENDIF
IF (fAngle < -180.0)
fAngle += 360.0
ENDIF
ent.sLocal.fRotation += (0 +@ fAngle)
IF (ABSF(fAngle) < 1.0)
ent.sLocal.fRotation = GET_HEADING_FROM_VECTOR_2D(dir.x, dir.y)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Player controlled entity.
/// PARAMS:
/// ent -
PROC GGSM_ENTITY_MOVE_PLAYER_CONTROLLED(GGSM_ENTITY &ent)
FLOAT spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
FLOAT hw, hh
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(ent.eType)
VECTOR_2D sprsize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
VECTOR_2D oldPos = ent.sLocal.vPosition
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.eStageFlags, GGSM_STAGE_BIT_STAGE_STARTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_STAGE_STARTED)
ENDIF
ent.sLocal.vPosition.x = 200.0
ent.sLocal.vPosition.y = 540.0
ent.vLocalDirection.x = 1.0
ent.vLocalDirection.y = 0.0
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_PLYR_INVUNERABLE)
GGSM_ENTITY_SET_ANIM_FRAME(ent, 0)
BREAK
CASE GGSM_MOVESTATE_PLYR_INVUNERABLE // move straight until at ready posF
GGSM_ENTITY_SET_ANIM_FRAME(ent, 0)
ent.iInvTimeMS = GET_GAME_TIMER() + GGSM_PLAYER_INITIAL_INV_TIME
ent.sLocal.vPosition.x += (spd / 2.0)
IF (ent.sLocal.vPosition.x > 600.0)
ent.sLocal.vPosition.x = 600.0
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_PLYR_DIALOG)
ENDIF
BREAK
CASE GGSM_MOVESTATE_PLYR_DIALOG
GGSM_ENTITY_SET_ANIM_FRAME(ent, 0)
ent.iInvTimeMS = GET_GAME_TIMER() + GGSM_PLAYER_INITIAL_INV_TIME
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.eStageFlags, GGSM_STAGE_BIT_INTRO_DONE)
IF (sGGSMData.sGameStats.level = 1)
//GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_ASTEROID_ENTER)
ENDIF
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_INTRO_DONE)
ENDIF
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_PLYR_DIALOG_DONE)
BREAK
CASE GGSM_MOVESTATE_PLYR_DIALOG_DONE // move straight until at ready posF
GGSM_ENTITY_SET_ANIM_FRAME(ent, 0)
ent.iInvTimeMS = GET_GAME_TIMER() + GGSM_PLAYER_INITIAL_INV_TIME
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
BREAK
CASE GGSM_MOVESTATE_ACTIVE // allow player cntrol
// allow for right stick control.
IF GGSM_ALTER_POINT_WITH_INPUT(ent.sLocal.vPosition, GGSM_ENTITY_GET_SPEED(ent))
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eHelpFlags, GGSM_HELP_BIT_MOVE)
ELIF GGSM_ALTER_POINT_WITH_INPUT(ent.sLocal.vPosition, GGSM_ENTITY_GET_SPEED(ent), GGSM_INPUT_RSTICK)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eHelpFlags, GGSM_HELP_BIT_MOVE)
ELIF GGSM_ALTER_POINT_WITH_INPUT(ent.sLocal.vPosition, GGSM_ENTITY_GET_SPEED(ent), GGSM_INPUT_DPAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eHelpFlags, GGSM_HELP_BIT_MOVE)
ENDIF
// clamp movement
hw = sprsize.x / 2.0
hh = sprsize.y / 2.0
ent.sLocal.vPosition.x = CLAMP(ent.sLocal.vPosition.x, 340.0 + hw, 1590.0 - hw)
ent.sLocal.vPosition.y = CLAMP(ent.sLocal.vPosition.y, 90.0 + hh, 990.0 - hh)
// handle movement anim
IF (ent.eType = GGSM_ENTITY_PLAYERSHIP)
IF (ent.sLocal.vPosition.y - oldPos.y < 0)
GGSM_ENTITY_SET_ANIM_FRAME(ent, 1)
ELIF (ent.sLocal.vPosition.y - oldPos.y > 0)
GGSM_ENTITY_SET_ANIM_FRAME(ent, 2)
ELSE
GGSM_ENTITY_SET_ANIM_FRAME(ent, 0)
ENDIF
ENDIF
IF (GET_GAME_TIMER() > sGGSMData.iLastMonkeyFireSoundTimeMS)
IF GGSM_HAS_PLAYER_PRESSED_FIRE()
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.eStageFlags, GGSM_STAGE_BIT_DISABLE_WEAPONS)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_MONKEY_VOICE_FIRE, ent.sWorld.vPosition)
sGGSMData.iLastMonkeyFireSoundTimeMS = (GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(3000, 6000))
ENDIF
ENDIF
ENDIF
IF GGSM_HAS_PLAYER_HOLDING_FIRE()
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eHelpFlags, GGSM_HELP_BIT_SHOOT)
ENDIF
IF GGSM_HAS_PLAYER_PRESSED_DEFENSE_WEAPON()
IF (sGGSMData.eWeaponSlot[GGSM_WEAPON_SLOT_DEFENSE] != GGSM_SPRITE_NONE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_PLAYER_DEFENSE_WPN_USED)
ELSE
GGSM_PLAY_SOUND(ARCADE_GAMES_SOUND_GGSM_PICKUP_UNAVAILABLE)
ENDIF
ENDIF
IF (ent.fTimer > 1.0)
IF GGSM_SHOULD_SHOW_TUTORIAL(GGSM_HELP_BIT_MOVE)
ARCADE_GAMES_HELP_TEXT_PRINT(ARCADE_GAMES_HELP_TEXT_ENUM_GGSM_MOVESHIP)
ENDIF
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eHelpFlags, GGSM_HELP_BIT_MOVE)
ENDIF
IF (ent.fTimer > 4.0)
IF GGSM_SHOULD_SHOW_TUTORIAL(GGSM_HELP_BIT_SHOOT)
ARCADE_GAMES_HELP_TEXT_CLEAR()
ARCADE_GAMES_HELP_TEXT_PRINT(ARCADE_GAMES_HELP_TEXT_ENUM_GGSM_FIREWEAPONS)
ENDIF
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eHelpFlags, GGSM_HELP_BIT_SHOOT)
ENDIF
IF GGSM_HAS_PLAYER_PRESSED_SPECIAL_WEAPON()
IF (sGGSMData.eWeaponSlot[GGSM_WEAPON_SLOT_SPECIAL] != GGSM_SPRITE_NONE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_PLAYER_SPECIAL_WPN_USED)
ELSE
GGSM_PLAY_SOUND(ARCADE_GAMES_SOUND_GGSM_PICKUP_UNAVAILABLE)
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Player controlled entity.
/// PARAMS:
/// ent -
PROC GGSM_ENTITY_MOVE_RANDOM_WANDER(GGSM_ENTITY &ent)
VECTOR_2D tmp
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
ent.eMoveState = GGSM_MOVESTATE_SELECT_POS
BREAK
CASE GGSM_MOVESTATE_SELECT_POS
IF (ent.fMoveTimer >= ent.fMoveParam[0])
tmp.x = GET_RANDOM_FLOAT_IN_RANGE(GGSM_PX_OVERSCAN_MIN_X + 100, GGSM_PX_OVERSCAN_MAX_X - 100)
tmp.y = GET_RANDOM_FLOAT_IN_RANGE(GGSM_PX_OVERSCAN_MIN_Y + 100, GGSM_PX_OVERSCAN_MAX_Y - 100)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, tmp)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
ENDIF
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_FORCE_MOVEMENT_TO_END)
EXIT
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_TO_ORIGIN_ON_MOVE_ABORT)
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, GGSM_UNPACK_VECTOR_2D(ent.iPackedLocalOrigin))
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_ORIGIN)
ELSE
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_GOTO_ORIGIN
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_SINWAVE(GGSM_ENTITY &ent)
FLOAT fPeriod
FLOAT spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
ent.iPackedLocalOrigin = GGSM_PACK_VECTOR_2D_TO_INT(ent.sLocal.vPosition)
ent.fDistTravelled = 0.0
// move param 0 is the period length - movement needed to 1 wave
IF (ent.fMoveParam[0] = 0.0)
ent.fMoveParam[0] = 600.0
ENDIF
// move param 1 is the height of the wave
IF (ent.fMoveParam[1] = 0.0)
ent.fMoveParam[1] = 88.0
ENDIF
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
BREAK
CASE GGSM_MOVESTATE_ACTIVE
ent.fDistTravelled += spd
// move forward
ent.sLocal.vPosition = GGSM_UNPACK_VECTOR_2D(ent.iPackedLocalOrigin)
ent.sLocal.vPosition.x += (ent.vLocalDirection.x * ent.fDistTravelled)
ent.sLocal.vPosition.y += (ent.vLocalDirection.y * ent.fDistTravelled)
// horizontal displacement - get the perpendicular
fPeriod = TO_FLOAT(FLOOR(ent.fDistTravelled) % FLOOR(ent.fMoveParam[0])) / ent.fMoveParam[0]
spd = SIN(fPeriod * 360.0) * ent.fMoveParam[1]
ent.sLocal.vPosition.x += (ent.vLocalDirection.y * spd)
ent.sLocal.vPosition.y += (ent.vLocalDirection.x * spd)
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_SNAKESECTION(GGSM_ENTITY &ent)
VECTOR_2D dir
VECTOR_2D parentPos
VECTOR_2D desiredPos
INT iParent = GGSM_ENTITY_GET_PARENT_INDEX(ent)
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_REMOVE_WHEN_OFFSCREEN)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_POSITION_OVERRIDE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
BREAK
ENDSWITCH
IF (iParent = GGSM_INVALID_ENTITY)
EXIT
ENDIF
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_POSITION_OVERRIDE)
EXIT
ENDIF
parentPos = sGGSMData.sEntities[iParent].sWorld.vPosition
dir.x = parentPos.x - ent.sWorld.vPosition.x
dir.y = parentPos.y - ent.sWorld.vPosition.y
ent.vLocalDirection = NORMALISE_VECTOR_2D(dir)
desiredPos.x = parentPos.x - (ent.sLocal.vPosition.x * ent.vLocalDirection.x)
desiredPos.y = parentPos.y - (ent.sLocal.vPosition.x * ent.vLocalDirection.y)
ent.sWorld.vPosition = LERP_VECTOR_2D(ent.sWorld.vPosition, desiredPos, 0.5)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_ROTATE_TO_FACE_DIRECTION)
ent.sWorld.fRotation = GET_HEADING_FROM_VECTOR_2D(ent.vLocalDirection.x, ent.vLocalDirection.y)
ENDIF
ENDPROC
/// PURPOSE:
/// Simple Movestraight function
/// PARAMS:
/// ent -
/// RETURNS:
/// Returns true when entity has left the play field
PROC GGSM_ENTITY_MOVE_STRAIGHT(GGSM_ENTITY &ent)
FLOAT spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
IF (ent.eMoveState = GGSM_MOVESTATE_START)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_REMOVE_WHEN_OFFSCREEN)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
ENDPROC
/// PURPOSE:
/// Player controlled entity.
/// PARAMS:
/// ent -
PROC GGSM_ENTITY_MOVE_TARGETTED_WANDER(GGSM_ENTITY &ent)
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
BREAK
CASE GGSM_MOVESTATE_SELECT_POS
IF (ent.fMoveTimer >= ent.fMoveParam[0])
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, GGSM_GET_TARGET_AT_PLAYER_POSITION())
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
ENDIF
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_FORCE_MOVEMENT_TO_END)
EXIT
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_TO_ORIGIN_ON_MOVE_ABORT)
GGSM_ENTITY_SET_TARGET_LOCAL_VECTOR(ent, GGSM_UNPACK_VECTOR_2D(ent.iPackedLocalOrigin))
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_ORIGIN)
ELSE
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_GOTO_ORIGIN
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
SET_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_COMPLETE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_MIDDLE_Y_RANGE(GGSM_ENTITY &ent)
VECTOR_2D tgtPos
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_POS)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, INIT_VECTOR_2D(1225.0, 540.0))
BREAK
CASE GGSM_MOVESTATE_GOTO_POS
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
ENDIF
BREAK
CASE GGSM_MOVESTATE_SELECT_POS
tgtPos = ent.sWorld.vPosition
tgtPos.y = GET_RANDOM_FLOAT_IN_RANGE(370.0, 762.0)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, tgtPos)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
BREAK
CASE GGSM_MOVESTATE_ACTIVE
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_SELECT_POS)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_ENTITY_MOVE_TRACK_PLAYER_Y(GGSM_ENTITY &ent)
FLOAT spd
VECTOR_2D tgtPos = GGSM_GET_TARGET_AT_PLAYER_POSITION()
SWITCH (ent.eMoveState)
CASE GGSM_MOVESTATE_START // start
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_GOTO_POS)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(ent, INIT_VECTOR_2D(1225.0, 540.0))
BREAK
CASE GGSM_MOVESTATE_GOTO_POS
IF GGSM_ENTITY_UPDATE_MOVEMENT_TO_TARGET(ent, GGSM_ENTITY_GET_SPEED(ent), TRUE)
GGSM_ENTITY_SET_MOVEMENT_STATE(ent, GGSM_MOVESTATE_ACTIVE)
ENDIF
BREAK
CASE GGSM_MOVESTATE_ACTIVE
spd = 0 +@ GGSM_ENTITY_GET_SPEED(ent)
ent.fDistTravelled += spd
ent.sLocal.vPosition.x += ent.vLocalDirection.x * spd
ent.sLocal.vPosition.y += ent.vLocalDirection.y * spd
IF (ABSF(ent.sWorld.vPosition.y - tgtPos.y) > 30.0)
IF (ent.sWorld.vPosition.y < tgtPos.y)
ent.vLocalDirection = INIT_VECTOR_2D(0, 1)
ELSE
ent.vLocalDirection = INIT_VECTOR_2D(0, -1)
ENDIF
ELSE
ent.vLocalDirection = INIT_VECTOR_2D(0, 0)
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------
// ENTITY FUNCTIONS
//-------------------------------------------------
FUNC BOOL GGSM_ENTITY_IS_AT_FULL_HEALTH(GGSM_ENTITY &ent)
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
RETURN (ent.iHP >= dat.iMaxHP)
ENDFUNC
PROC GGSM_ENTITY_RESET_HEALTH(GGSM_ENTITY &ent)
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
ent.iHP = dat.iMaxHP
ENDPROC
/// PURPOSE:
/// Resets an Entity to default
PROC GGSM_ENTITY_RESET(GGSM_ENTITY &ent)
ent.eType = GGSM_ENTITY_INVALID
ent.eState = GGSM_ENTITY_STATE_NONE
ent.eFlags = GGSM_ENTITY_BIT_NONE
ent.fExplodeTimer = 0.0
ent.iHP = 0
ent.eSpriteAnim = GGSM_SPRITE_ANIM_NONE
ent.fTimer = 0.0
ent.fAnimFrame = 0.0
GGSM_IDENTITY_TRANSFORM(ent.sLocal)
GGSM_IDENTITY_TRANSFORM(ent.sWorld)
GGSM_ENTITY_SET_PARENT_INDEX(ent, GGSM_INVALID_GROUP_ID)
ent.iGroupID = GGSM_INVALID_GROUP_ID
ent.eMoveFlags = GGSM_MOVE_BIT_NONE
ent.iPackedLocalTargetPosition = 0
GGSM_ENTITY_CLEAR_Z_DEPTH(ent)
ent.fDistTravelled = 0
ENDPROC
/// PURPOSE:
/// Resets the entity array
/// PARAMS:
/// eType - Type this is used for collison type things
/// array -
PROC GGSM_RESET_ENTITY_ARRAY(GGSM_ENTITY &array[])
INT i
REPEAT COUNT_OF(array) i
GGSM_ENTITY_RESET(array[i])
GGSM_ENTITY_SET_ARRAY_INDEX(array[i], i)
ENDREPEAT
ENDPROC
PROC GGSM_ENTITY_SET_STATE(GGSM_ENTITY &ent, GGSM_ENTITY_STATE eState)
CDEBUG1LN(DEBUG_MINIGAME, "GGSM_ENTITY_SET_STATE - TYPE:", GGSM_ENTITY_TYPE_TO_STRING(ent.eType), " ARRAY INDEX:", GGSM_ENTITY_GET_ARRAY_INDEX(ent), " STATE:", GGSM_ENTITY_STATE_TO_STRING(eState))
ent.eState = eState
ent.fTimer = 0
ENDPROC
/// PURPOSE:
/// Destroy Entity
/// PARAMS:
/// ent -
PROC GGSM_ENTITY_DESTROY(GGSM_ENTITY &ent)
INT iArrayIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_FORCE_SCROLLING_TO_STOP)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_BRING_CAMERA_TO_STOP)
ENDIF
GGSM_REMOVE_ENTITY_FROM_GROUP(ent)
GGSM_ENTITY_CLEAR_Z_DEPTH(ent)
ent.eFlags = GGSM_ENTITY_BIT_NONE
ent.eState = GGSM_ENTITY_STATE_NONE
CDEBUG1LN(DEBUG_MINIGAME, "GGSM_ENTITY_DESTROY - IND:", iArrayIndex, " TYPE: ", GGSM_ENTITY_TYPE_TO_STRING(ent.eType))
IF (iArrayIndex = sGGSMData.iPlayerShipIndex)
sGGSMData.iPlayerShipIndex = GGSM_INVALID_ENTITY
ENDIF
IF (iArrayIndex = sGGSMData.iPlayerDecoyIndex)
sGGSMData.iPlayerDecoyIndex = GGSM_INVALID_ENTITY
ENDIF
IF (iArrayIndex = sGGSMData.iPlayerShieldIndex)
sGGSMData.iPlayerShieldIndex = GGSM_INVALID_ENTITY
ENDIF
GGSM_ENTITY_RESET(ent)
ENDPROC
PROC GGSM_ENTITY_ATTACH_TO_PARENT(GGSM_ENTITY &ent, GGSM_ENTITY &parent, BOOL bJoinParentGroup = TRUE)
GGSM_ENTITY_SET_PARENT_INDEX(ent, GGSM_ENTITY_GET_ARRAY_INDEX(parent))
GGSM_TRANSFORM_WORLD_TO_LOCAL(parent.sWorld, ent.sWorld, ent.sLocal)
IF (bJoinParentGroup = TRUE)
IF (parent.iGroupID != GGSM_INVALID_GROUP_ID) AND (parent.iGroupID != ent.iGroupID)
GGSM_REMOVE_ENTITY_FROM_GROUP(ent)
GGSM_ADD_ENTITY_TO_GROUP(ent, parent.iGroupID)
ENDIF
ENDIF
UNUSED_PARAMETER(bJoinParentGroup)
ENDPROC
/// PURPOSE:
/// Updates the on screen checks
/// PARAMS:
/// ent -
/// RETURNS:
/// returns true if the offscreen checks say we should be deleted
FUNC BOOL GGSM_ENTITY_UPDATE_SCREEN_CHECKS(GGSM_ENTITY &ent)
INT iArrayIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent)
IF NOT GGSM_IS_COLLIDER_AABB_ON_SCREEN(ent.sCollider)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_ON_SCREEN)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_REMOVE_WHEN_OFFSCREEN)
RETURN FALSE
ENDIF
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_ENTERED_PLAYFIELD)
RETURN FALSE
ENDIF
IF (ent.iGroupID = GGSM_INVALID_GROUP_ID)
RETURN TRUE
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntityGroup[ent.iGroupID].eFlags, GGSM_GROUP_BIT_ALLOW_OFFSCREEN)
RETURN FALSE
ENDIF
CLEAR_BIT(sGGSMData.sEntityGroup[ent.iGroupID].iMembersOnStageBitSet, iArrayIndex)
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntityGroup[ent.iGroupID].eFlags, GGSM_GROUP_BIT_END_WHEN_ALL_OFFSCREEN)
RETURN (sGGSMData.sEntityGroup[ent.iGroupID].iMembersOnStageBitSet = 0)
ENDIF
ELSE
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_ON_SCREEN)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_ENTERED_PLAYFIELD)
IF (ent.iGroupID != GGSM_INVALID_GROUP_ID)
SET_BIT(sGGSMData.sEntityGroup[ent.iGroupID].iMembersOnStageBitSet, iArrayIndex)
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Explode Entity
/// PARAMS:
/// ent -
PROC GGSM_ENTITY_EXPLODE(GGSM_ENTITY &ent)
IF (ent.eState != GGSM_ENTITY_STATE_ALIVE) AND (ent.eState != GGSM_ENTITY_STATE_UNDEAD)
EXIT
ENDIF
INT n, sc
INT iArrayIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent)
INT iGroupID = GGSM_ENTITY_GET_GROUP_ID(ent)
INT iParent = GGSM_ENTITY_GET_PARENT_INDEX(ent)
BOOL bIsAllied = IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED)
CDEBUG1LN(DEBUG_MINIGAME, "GGSM_ENTITY_EXPLODE - TYPE:", GGSM_ENTITY_TYPE_TO_STRING(ent.eType), " ARRAY INDEX:", iArrayIndex)
ent.fTimer = 0
// some enemies shoot when they die
IF NOT bIsAllied
IF (ent.eType = GGSM_ENTITY_CHERRYBOMB)
GGSM_PROJECTILE_CREATE_ENEMY_RADIAL_SPREAD(GGSM_PROJECTILE_ENEMY_SHOT, ent.sWorld.vPosition, GET_RANDOM_INT_IN_RANGE(5, 8), GET_RANDOM_FLOAT_IN_RANGE(0, 360), FALSE)
ELIF (ent.eType = GGSM_ENTITY_PEANUT_METEOR)
GGSM_PROJECTILE_CREATE_ENEMY_CONE_SPREAD(GGSM_PROJECTILE_ENEMY_SHOT, ent.sWorld.vPosition, 90.0, 3, TRUE)
ENDIF
IF (sGGSMData.bHardMode)
GGSM_PROJECTILE_CREATE_ENEMY_RADIAL_SPREAD(GGSM_PROJECTILE_ENEMY_SHOT, ent.sWorld.vPosition, GET_RANDOM_INT_IN_RANGE(1, 6), 0.0, TRUE)
ENDIF
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_NO_TAKEDOWN_BONUS)
sGGSMData.iEnemiesDestroyed ++
sGGSMData.sGameStats.kills ++
sGGSMData.fChainMultiplier ++
IF (sGGSMData.fChainMultiplier > GGSM_CHAIN_MAX)
sGGSMData.fChainMultiplier = GGSM_CHAIN_MAX
ENDIF
sGGSMData.iKillStreak ++
sGGSMData.fKillStreakTimer = GGSM_TIME_FOR_KILL_STREAK
IF GGSM_DOES_PLAYER_SHIP_EXIST()
IF (sGGSMData.iKillStreak >= GGSM_KILLS_NEEDED_FOR_KILL_STREAK)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_MONKEY_VOICE_KILL_STREAK, sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].sWorld.vPosition)
sGGSMData.iKillStreak = 0
ENDIF
ENDIF
ENDIF
sGGSMData.iLastEnemyDeathTimeMS = GET_GAME_TIMER()
IF (FLOOR(sGGSMData.fChainMultiplier) > 1)
sc = FLOOR(TO_FLOAT(GGSM_ENTITY_GET_SCORE_FROM_TYPE(ent.eType)) * FLOOR(sGGSMData.fChainMultiplier))
ELSE
sc = GGSM_ENTITY_GET_SCORE_FROM_TYPE(ent.eType)
ENDIF
GGSM_INCREASE_PLAYER_SCORE(sc)
ENDIF
// relink sections
IF (iGroupID != GGSM_INVALID_GROUP_ID) AND (sGGSMData.sEntityGroup[iGroupID].iLeader = iArrayIndex)
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_DIE_WHEN_LEADER_DIES)
REPEAT GGSM_MAX_ENTITIES n
IF (n != iArrayIndex) AND (GGSM_ENTITY_GET_GROUP_ID(sGGSMData.sEntities[n]) = iGroupID)
IF (sGGSMData.sEntities[n].eState = GGSM_ENTITY_STATE_UNDEAD)
GGSM_ENTITY_SET_STATE(sGGSMData.sEntities[n], GGSM_ENTITY_STATE_ALIVE)
ENDIF
GGSM_ENTITY_EXPLODE(sGGSMData.sEntities[n])
ENDIF
ENDREPEAT
ENDIF
ENDIF
REPEAT GGSM_MAX_ENTITIES n
IF GGSM_ENTITY_GET_PARENT_INDEX(sGGSMData.sEntities[n]) = iArrayIndex
GGSM_ENTITY_SET_PARENT_INDEX(sGGSMData.sEntities[n], iParent)
IF (iParent = GGSM_INVALID_ENTITY)
sGGSMData.sEntities[n].sLocal = sGGSMData.sEntities[n].sWorld
ENDIF
ENDIF
ENDREPEAT
GGSM_REMOVE_ENTITY_FROM_GROUP(ent)
ent.iInvTimeMS = -HIGHEST_INT
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_TOOK_DAMAGE_THIS_FRAME)
// if i'm the player
IF (iArrayIndex = sGGSMData.iPlayerShipIndex)
sGGSMData.iNoMissBonus = 0
sGGSMData.iNoDamageBonus = 0
sGGSMData.fChainMultiplier = 0.0
sGGSMData.fTimeScale = 1.0
GGSM_DESTROY_ALL_FACTION_BULLETS(sGGSMData.sProjectiles, TRUE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_PLAYER_DIED)
SET_CONTROL_SHAKE(FRONTEND_CONTROL, 300, 100)
IF (sGGSMData.iPlayerLives > 0)
sGGSMData.iPlayerLives --
ENDIF
sGGSMData.bGrazed = FALSE
IF (sGGSMData.iPlayerLives = 0)
GGSM_PLAY_AVATAR_GAME_OVER_ANIM()
ELSE
GGSM_PLAY_AVATAR_LOST_LIFE_ANIM()
ENDIF
ENDIF
GGSM_ENTITY_SET_STATE(ent, GGSM_ENTITY_STATE_EXPLODING)
ENDPROC
/// PURPOSE:
/// Damage the entity
/// PARAMS:
/// ent - entity to damage
FUNC BOOL GGSM_ENTITY_DAMAGE(GGSM_ENTITY &ent, INT iDP = 1, BOOL bIgnoreInvunerable = FALSE)
IF (GET_GAME_TIMER() < ent.iInvTimeMS) AND (bIgnoreInvunerable = FALSE)
RETURN FALSE
ENDIF
IF (ent.eState != GGSM_ENTITY_STATE_ALIVE)
RETURN FALSE
ENDIF
// check to see if damage affects parent (this is for bosses who have multiple bits)
INT iArrayIndex = GGSM_ENTITY_GET_ARRAY_INDEX(ent)
INT iGroupID = GGSM_ENTITY_GET_GROUP_ID(ent)
IF (iGroupID != GGSM_INVALID_GROUP_ID)
IF (sGGSMData.sEntityGroup[iGroupID].iLeader != GGSM_INVALID_ENTITY)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_DAMAGE_HURTS_ROOT)
GGSM_ENTITY_DAMAGE(sGGSMData.sEntities[sGGSMData.sEntityGroup[iGroupID].iLeader], iDP)
RETURN FALSE
ENDIF
ENDIF
ENDIF
ent.iHP -= iDP
IF (iArrayIndex = sGGSMData.iPlayerShipIndex)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_PLAYER_DAMAGED)
ent.iInvTimeMS = GET_GAME_TIMER() + GGSM_PLAYER_INV_TIME
SET_CONTROL_SHAKE(FRONTEND_CONTROL, 100, 100)
sGGSMData.fChainMultiplier = 0
sGGSMData.iNoDamageBonus = 0
ENDIF
IF (ent.eType = GGSM_ENTITY_PLAYERSHIELD)
SET_CONTROL_SHAKE(FRONTEND_CONTROL, 100, 100)
ENDIF
IF (ent.iHP <= 0)
ent.iHP = 0
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_DIE_ONLY_FROM_EXPLOSIONS)
GGSM_ENTITY_SET_STATE(ent, GGSM_ENTITY_STATE_FAKE_EXPLODING)
RETURN TRUE
ENDIF
IF (ent.eType = GGSM_ENTITY_PLAYERSHIELD)
GGSM_ENTITY_DESTROY(ent)
ELSE
GGSM_ENTITY_EXPLODE(ent)
ENDIF
RETURN TRUE
ENDIF
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
GGSM_PLAY_SOUND_FROM_POSITION(dat.eDamageSound, ent.sWorld.vPosition)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_TOOK_DAMAGE_THIS_FRAME)
IF (iArrayIndex = sGGSMData.iPlayerShipIndex)
ent.iInvTimeMS = GET_GAME_TIMER() + GGSM_PLAYER_INV_TIME
ELIF GGSM_ENTITY_IS_STRONG(ent)
ent.iInvTimeMS = GET_GAME_TIMER() + GGSM_BOSS_INV_TIME
ELSE
ent.iInvTimeMS = GET_GAME_TIMER() + GGSM_ENTITY_INV_TIME
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Update the projectile array
/// PARAMS:
/// array - array
/// RETURNS:
/// # of active projectiles
FUNC INT GGSM_ENTITY_NUKE_ARRAY(GGSM_ENTITY &array[], BOOL bAllied)
INT i, iDead
INT cnt = COUNT_OF(array)
INT dp
REPEAT cnt i
IF (array[i].eState = GGSM_ENTITY_STATE_ALIVE)
IF (IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED) = bAllied)
IF NOT GGSM_ENTITY_IS_STRONG(array[i])
IF GGSM_ENTITY_DAMAGE(array[i], 30, TRUE)
iDead ++
ENDIF
ELSE
dp = GGSM_ENTITY_GET_PERCENTAGE_OF_HEALTH(array[i], 30)
IF GGSM_ENTITY_DAMAGE(array[i], dp, TRUE)
iDead ++
ENDIF
ENDIF
ENDIF
ENDIF
ENDREPEAT
RETURN iDead
ENDFUNC
/// PURPOSE:
/// Update the projectile array
/// PARAMS:
/// array - array
/// RETURNS:
/// # of active projectiles
FUNC INT GGSM_ENTITY_DAMAGE_ARRAY(GGSM_ENTITY &array[], BOOL bAllied, INT iDP)
INT i, iDead
INT cnt = COUNT_OF(array)
REPEAT cnt i
IF (array[i].eState = GGSM_ENTITY_STATE_ALIVE)
IF (IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED) = bAllied)
IF GGSM_ENTITY_DAMAGE(array[i], iDP, TRUE)
iDead ++
ENDIF
ENDIF
ENDIF
ENDREPEAT
RETURN iDead
ENDFUNC
/// PURPOSE:
/// Update the projectile array
/// PARAMS:
/// array - array
/// RETURNS:
/// # of active projectiles
PROC GGSM_ENTITY_EXPLODE_ARRAY(GGSM_ENTITY &array[], BOOL bAllied)
INT i
INT cnt = COUNT_OF(array)
CPRINTLN(DEBUG_MINIGAME, "GGSM_ENTITY_EXPLODE_ARRAY")
REPEAT cnt i
IF (IS_BITMASK_ENUM_AS_ENUM_SET(array[i].eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED) = bAllied)
IF (array[i].eState != GGSM_ENTITY_STATE_NONE)
CPRINTLN(DEBUG_MINIGAME, "GGSM_ENTITY_EXPLODE_ARRAY - TYPE:", GGSM_ENTITY_TYPE_TO_STRING(array[i].eType))
GGSM_ENTITY_EXPLODE(array[i])
GGSM_ENTITY_SET_STATE(array[i], GGSM_ENTITY_STATE_EXPLODING)
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_ENTITY_UPDATE_MOVEMENT(GGSM_ENTITY &ent)
SWITCH (ent.eMoveType)
CASE GGSM_MOVEMENT_ARC_BASE_LEFT
CASE GGSM_MOVEMENT_ARC_BASE_RIGHT
CASE GGSM_MOVEMENT_ARC_TOP_LEFT
CASE GGSM_MOVEMENT_ARC_TOP_RIGHT
GGSM_ENTITY_MOVE_ARC(ent)
BREAK
CASE GGSM_MOVEMENT_ATTACK_KAMAZAKE
GGSM_ENTITY_MOVE_ATTACK_KAMAKAZE(ent)
BREAK
CASE GGSM_MOVEMENT_BG_ATTACK
GGSM_ENTITY_MOVE_BG_ATTACK(ent)
BREAK
CASE GGSM_MOVEMENT_DESTRUCT_ON_IMPACT
GGSM_ENTITY_MOVE_DESTRUCT_ON_IMPACT(ent)
BREAK
CASE GGSM_MOVEMENT_FOLLOW_PATH
GGSM_ENTITY_MOVE_FOLLOW_PATH(ent)
BREAK
CASE GGSM_MOVEMENT_FOLLOW_REL_PATH
GGSM_ENTITY_MOVE_FOLLOW_REL_PATH(ent)
BREAK
CASE GGSM_MOVEMENT_FORWARD_THEN_BACK
GGSM_ENTITY_MOVE_FORWARD_THEN_BACK(ent)
BREAK
CASE GGSM_MOVEMENT_HALF_ARC_BASE_LEFT
CASE GGSM_MOVEMENT_HALF_ARC_BASE_RIGHT
CASE GGSM_MOVEMENT_HALF_ARC_TOP_LEFT
CASE GGSM_MOVEMENT_HALF_ARC_TOP_RIGHT
GGSM_ENTITY_MOVE_HALF_ARC(ent)
BREAK
CASE GGSM_MOVEMENT_KAMAZAKE
GGSM_ENTITY_MOVE_KAMAKAZE(ent)
BREAK
CASE GGSM_MOVEMENT_MOVE_TO_VECTOR
GGSM_ENTITY_MOVE_TO_VECTOR(ent)
BREAK
CASE GGSM_MOVEMENT_ORBIT
GGSM_ENTITY_MOVE_ORBIT(ent)
BREAK
CASE GGSM_MOVEMENT_PLAYER_CONTROLLED
GGSM_ENTITY_MOVE_PLAYER_CONTROLLED(ent)
BREAK
CASE GGSM_MOVEMENT_RANDOM_WANDER
GGSM_ENTITY_MOVE_RANDOM_WANDER(ent)
BREAK
CASE GGSM_MOVEMENT_ROTATE_TO_FACE_VECTOR
GGSM_ENTITY_MOVE_ROTATE_FACE_TO_VECTOR(ent)
BREAK
CASE GGSM_MOVEMENT_SIN_WAVE
GGSM_ENTITY_MOVE_SINWAVE(ent)
BREAK
CASE GGSM_MOVEMENT_SNAKE_SECTION
GGSM_ENTITY_MOVE_SNAKESECTION(ent)
BREAK
CASE GGSM_MOVEMENT_STRAIGHT
GGSM_ENTITY_MOVE_STRAIGHT(ent)
BREAK
CASE GGSM_MOVEMENT_TARGETTED_WANDER
GGSM_ENTITY_MOVE_TARGETTED_WANDER(ent)
BREAK
CASE GGSM_MOVEMENT_TRACK_PLAYER_Y
GGSM_ENTITY_MOVE_TRACK_PLAYER_Y(ent)
BREAK
CASE GGSM_MOVEMENT_MIDDLE_Y_RANGE
GGSM_ENTITY_MOVE_MIDDLE_Y_RANGE(ent)
BREAK
/*
DEFAULT
CASSERTLN(DEBUG_MINIGAME, "FORGOT TO WRITE THE MOVEMENT FUNCTION - USING DEFAULT - MOVE TYPE:", GGSM_MOVEMENT_TYPE_TO_STRING(ent.eMoveType))
GGSM_ENTITY_MOVE_STRAIGHT(ent)
BREAK
*/
ENDSWITCH
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_FORCE_SCROLLING_TO_STOP)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_BRING_CAMERA_TO_STOP)
ENDIF
ENDPROC
FUNC BOOL GGSM_ENTITY_UPDATE_EXPLOSIONS(GGSM_ENTITY &ent)
INT ind
FLOAT fSize
VECTOR_2D vPos
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
BOOL bIsAllied = IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED)
FLOAT spd = 0 +@ (GGSM_ENTITY_GET_SPEED(ent) / 2.0)
ent.sLocal.vPosition.x += dat.vExplodeVelocity.x * spd
ent.sLocal.vPosition.y += dat.vExplodeVelocity.y * spd
IF bIsAllied
ent.fExplodeTimer -= GET_FRAME_TIME()
ELSE
ent.fExplodeTimer -= (GET_FRAME_TIME() * sGGSMData.fTimeScale)
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_FORCE_SCROLLING_TO_STOP)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_BRING_CAMERA_TO_STOP)
ENDIF
IF (ent.fTimer >= dat.fTotalExplodeTime)
IF GGSM_GET_FREE_FXSPRITE(sGGSMData.sFXSprite, ind, sGGSMData.iExplosionSearchIndex)
GGSM_FXSPRITE_INIT_ANIMATED(sGGSMData.sFXSprite[ind], ent.sWorld.vPosition, GGSM_SPRITE_ANIM_EXPLOSION, 2.0)
GGSM_FXSPRITE_SET_PHYSICAL_RADIUS(sGGSMData.sFXSprite[ind], GGSM_GET_DOMINANT_SPRITE_SIZE(dat.eSpriteType) * 1.5)
IF (dat.eDieSound != ARCADE_GAMES_SOUND_END)
GGSM_PLAY_SOUND_FROM_POSITION(dat.eDieSound, ent.sWorld.vPosition)
ENDIF
IF GGSM_ENTITY_IS_MAIN_BOSS_SECTION(ent)
GGSM_NUKE_WHITE_INSTANT()
ENDIF
GGSM_ENTITY_DESTROY(ent)
RETURN TRUE
ENDIF
ENDIF
IF (ent.fExplodeTimer > 0.0)
RETURN FALSE
ENDIF
IF GGSM_GET_FREE_FXSPRITE(sGGSMData.sFXSprite, ind, sGGSMData.iExplosionSearchIndex)
fSize = GGSM_GET_DOMINANT_SPRITE_SIZE(dat.eSpriteType) * GET_RANDOM_FLOAT_IN_RANGE(0.5, 1.0)
vPos = GGSM_GET_RANDOM_POINT_ON_ENTITY(ent)
GGSM_FXSPRITE_INIT_ANIMATED(sGGSMData.sFXSprite[ind], vPos, GGSM_SPRITE_ANIM_EXPLOSION, 2.0)
GGSM_FXSPRITE_SET_PHYSICAL_RADIUS(sGGSMData.sFXSprite[ind], fSize)
ent.fExplodeTimer = dat.fExplodeDelay
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL GGSM_ENTITY_UPDATE_FAKE_EXPLOSIONS(GGSM_ENTITY &ent)
INT ind
FLOAT fSize
VECTOR_2D vPos
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
BOOL bIsAllied = IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED)
IF bIsAllied
ent.fExplodeTimer -= GET_FRAME_TIME()
ELSE
ent.fExplodeTimer -= (GET_FRAME_TIME() * sGGSMData.fTimeScale)
ENDIF
IF (ent.fTimer >= dat.fTotalExplodeTime)
GGSM_ENTITY_SET_STATE(ent, GGSM_ENTITY_STATE_UNDEAD)
RETURN TRUE
ENDIF
IF (ent.fExplodeTimer > 0.0)
RETURN FALSE
ENDIF
IF GGSM_GET_FREE_FXSPRITE(sGGSMData.sFXSprite, ind, sGGSMData.iExplosionSearchIndex)
fSize = GGSM_GET_DOMINANT_SPRITE_SIZE(dat.eSpriteType) * GET_RANDOM_FLOAT_IN_RANGE(0.5, 1.0)
vPos = GGSM_GET_RANDOM_POINT_ON_ENTITY(ent)
GGSM_FXSPRITE_INIT_ANIMATED(sGGSMData.sFXSprite[ind], vPos, GGSM_SPRITE_ANIM_EXPLOSION, 2.0)
GGSM_FXSPRITE_SET_PHYSICAL_RADIUS(sGGSMData.sFXSprite[ind], fSize)
ent.fExplodeTimer = dat.fExplodeDelay
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL GGSM_ENTITY_UPDATE(GGSM_ENTITY &ent)
INT iParent = GGSM_ENTITY_GET_PARENT_INDEX(ent)
BOOL bIsAllied = IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED)
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(ent.eType)
// update timer
sGGSMData.iEntitiesActive ++
IF bIsAllied
ent.fTimer += GET_FRAME_TIME()
ent.fMoveTimer += GET_FRAME_TIME()
ELSE
ent.fTimer += (GET_FRAME_TIME() * sGGSMData.fTimeScale)
ent.fMoveTimer += (GET_FRAME_TIME() * sGGSMData.fTimeScale)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_ENEMIES_ACTIVE)
ENDIF
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eMoveFlags, GGSM_MOVE_BIT_TARGET_POINT_REACHED_THIS_FRAME)
IF (ent.eState = GGSM_ENTITY_STATE_ALIVE)
GGSM_ENTITY_UPDATE_MOVEMENT(ent)
ELIF (ent.eState = GGSM_ENTITY_STATE_EXPLODING)
IF GGSM_ENTITY_UPDATE_EXPLOSIONS(ent)
GGSM_ENTITY_DESTROY(ent)
RETURN FALSE
ENDIF
ELIF (ent.eState = GGSM_ENTITY_STATE_FAKE_EXPLODING)
GGSM_ENTITY_UPDATE_FAKE_EXPLOSIONS(ent)
ENDIF
// update parenting
IF (iParent != GGSM_INVALID_ENTITY)
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(ent.eMoveFlags, GGSM_MOVE_BIT_POSITION_OVERRIDE)
GGSM_TRANSFORM_LOCAL_TO_WORLD(sGGSMData.sEntities[iParent].sWorld, ent.sLocal, ent.sWorld)
ENDIF
ELSE
ent.sWorld = ent.sLocal
ENDIF
IF (ent.eType = GGSM_ENTITY_PLAYERSHIELD) AND GGSM_DOES_PLAYER_SHIP_EXIST()
ent.sWorld = sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].sWorld
ENDIF
GGSM_COLLIDER_TRANSFORM(ent.sCollider, ent.sWorld, dat.sCollisionData)
IF GGSM_ENTITY_UPDATE_SCREEN_CHECKS(ent)
GGSM_ENTITY_DESTROY(ent)
RETURN FALSE
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_TOOK_DAMAGE_THIS_FRAME)
CLEAR_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_TOOK_DAMAGE_THIS_FRAME)
ENDIF
// only do the stuff below if we are actually alive
IF (ent.eState != GGSM_ENTITY_STATE_ALIVE)
RETURN TRUE
ENDIF
IF (ent.eSpriteAnim != GGSM_SPRITE_ANIM_NONE)
GGSM_SPRITE_UPDATE_ANIMATION(ent.eSpriteAnim, ent.fAnimFrame, NOT bIsAllied)
ENDIF
GGSM_ENTITY_UPDATE_WEAPON(ent)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ent.eFlags, GGSM_ENTITY_BIT_SELF_DESTRUCT)
GGSM_ENTITY_EXPLODE(ent)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
// PURPOSE:
/// Initialize an entity based on data type
/// PARAMS:
/// ent - entity to intialize
/// eType - entity type
/// vLocalPos - Local Position (for most objects this will be the world position
/// fLocalRot - Local Rotation
/// iParentEntity - index into sGGSMData.sEnemyShips for linking boss parts to each other (ONLY USE ON ENEMIES)
PROC GGSM_ENTITY_INIT(GGSM_ENTITY &ent, GGSM_ENTITY_TYPE eType, VECTOR_2D vLocalPos, FLOAT fLocalRot = 0.0, BOOL bIsFriendly = FALSE, INT iParentEntity = GGSM_INVALID_ENTITY, BOOL bCountTowardsTakedown = TRUE)
GGSM_ENTITY_RESET(ent)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(eType)
ent.eType = eType
GGSM_ENTITY_RESET_HEALTH(ent)
ent.eWeaponState = GGSM_WEAPON_STATE_READY
ent.iPackedColor = GGSM_PACK_RGBA_COLOUR_STRUCT(sGGSMData.rgbaSprite)
GGSM_ENTITY_SET_Z_DEPTH(ent, 0)
GGSM_ENTITY_SET_STATE(ent, GGSM_ENTITY_STATE_ALIVE)
GGSM_ENTITY_SET_WEAPON(ent, GGSM_WEAPON_DEFAULT)
GGSM_ENTITY_SET_MOVEMENT_TYPE(ent, GGSM_MOVEMENT_NONE)
GGSM_ENTITY_SET_ANIM_FRAME(ent, entData.iDefaultAnimFrame)
// set initial bits
IF (bIsFriendly)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED)
ent.iInvTimeMS = GET_GAME_TIMER() + GGSM_PLAYER_INITIAL_INV_TIME
ELSE
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_PLAYER_BOSS_INVUNERABLE)
ENDIF
// movement
ent.sLocal.vPosition = vLocalPos
ent.sLocal.fRotation = fLocalRot
ent.iPackedLocalOrigin = GGSM_PACK_VECTOR_2D_TO_INT(vLocalPos)
ent.fSpeed = entData.fBaseSpeed
// parenting
ent.iGroupID = GGSM_INVALID_GROUP_ID
GGSM_ENTITY_SET_PARENT_INDEX(ent, iParentEntity)
IF (iParentEntity != GGSM_INVALID_ENTITY)
GGSM_TRANSFORM_LOCAL_TO_WORLD(sGGSMData.sEntities[iParentEntity].sWorld, ent.sLocal, ent.sWorld)
GGSM_ENTITY_ATTACH_TO_PARENT(ent, sGGSMData.sEntities[iParentEntity], TRUE)
ELSE
ent.sWorld = ent.sLocal
ENDIF
IF NOT bIsFriendly
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
IF (bCountTowardsTakedown = FALSE)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_NO_TAKEDOWN_BONUS)
ELSE
sGGSMData.iEnemiesCreated ++
ENDIF
ENDIF
IF (ent.iGroupID != GGSM_INVALID_GROUP_ID)
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntityGroup[ent.iGroupID].eFlags, GGSM_GROUP_BIT_END_WHEN_ALL_OFFSCREEN)
SET_BITMASK_ENUM_AS_ENUM(ent.eFlags, GGSM_ENTITY_BIT_REMOVE_WHEN_OFFSCREEN)
ENDIF
ENDIF
CDEBUG1LN(DEBUG_MINIGAME, "GGSM_ENTITY_INIT - IND:", GGSM_ENTITY_GET_ARRAY_INDEX(ent), " TYPE:", GGSM_ENTITY_TYPE_TO_STRING(eType),
" LOCAL:[", ent.sLocal.vPosition.x, ", ", ent.sLocal.vPosition.y, "] ",
" WORLD:[", ent.sWorld.vPosition.x, ", ", ent.sWorld.vPosition.y, "]")
GGSM_COLLIDER_TRANSFORM(ent.sCollider, ent.sWorld, entData.sCollisionData)
//GGSM_ENTITY_UPDATE(ent)
ENDPROC
PROC GGSM_ENTITY_CONFIGURE_FROM_FORMATION(GGSM_ENTITY &ent, GGSM_FORMATION_DATA &dat)
GGSM_ENTITY_SET_MOVEMENT_TYPE(ent, GGSM_FORMATION_GET_MOVEMENT_TYPE(dat))
GGSM_ENTITY_SET_PATH_INDEX(ent, GGSM_FORMATION_GET_PATH_INDEX(dat))
IF (GGSM_FORMATION_GET_WEAPON_TYPE(dat) != GGSM_WEAPON_DEFAULT)
GGSM_ENTITY_SET_WEAPON(ent, GGSM_FORMATION_GET_WEAPON_TYPE(dat))
ENDIF
ENDPROC
PROC GGSM_DETONATE_ENTITY_GROUP(GGSM_ENTITY_GROUP &group)
INT i
REPEAT COUNT_OF(sGGSMData.sEntities) i
IF IS_BIT_SET(group.iMemberBitSet, i)
GGSM_ENTITY_EXPLODE(sGGSMData.sEntities[i])
ENDIF
ENDREPEAT
GGSM_RESET_ENTITY_GROUP(group)
ENDPROC
PROC GGSM_DETONATE_ENTITIES_OF_TYPE(GGSM_ENTITY_TYPE eType)
INT i
REPEAT COUNT_OF(sGGSMData.sEntities) i
IF (sGGSMData.sEntities[i].eType = eType)
GGSM_ENTITY_EXPLODE(sGGSMData.sEntities[i])
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_DESTROY_ENTITIES_OF_TYPE(GGSM_ENTITY_TYPE eType)
INT i
REPEAT COUNT_OF(sGGSMData.sEntities) i
IF (sGGSMData.sEntities[i].eType = eType)
GGSM_ENTITY_DESTROY(sGGSMData.sEntities[i])
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_DESTROY_ENTITIES_OF_FACTION(BOOL bAllied)
INT i
REPEAT COUNT_OF(sGGSMData.sEntities) i
IF (IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[i].eFlags, GGSM_ENTITY_BIT_IS_PLAYER_ALLIED) = bAllied)
GGSM_ENTITY_DESTROY(sGGSMData.sEntities[i])
ENDIF
ENDREPEAT
ENDPROC
//-------------------------------------------------
// BOSS CONTROLLER FUNCTION
//-------------------------------------------------
FUNC BOOL GGSM_BOSS_BREAD_INIT(GGSM_BOSS_CONTROLLER &boss)
INT iLeader, iGroupID, iChump
// find free group
iGroupID = GGSM_GET_FREE_ENTITY_GROUP(sGGSMData.sEntityGroup)
IF (iGroupID = GGSM_INVALID_GROUP_ID)
RETURN FALSE
ENDIF
// create smoothie
IF (GGSM_COUNT_FREE_ENTITIES(sGGSMData.sEntities) < 5)
RETURN FALSE
ENDIF
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iLeader, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
// create granana
GGSM_RESET_BOSS_CONTROLLER(boss, GGSM_BOSS_BREAD)
boss.iGroupID = iGroupID
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_DIE_WHEN_LEADER_DIES)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_ALLOW_OFFSCREEN)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iLeader], GGSM_ENTITY_BOSS_BREAD_FACE, INIT_VECTOR_2D(2400, 540))
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iLeader], GGSM_BOSS_SECTION_BREAD_FACE)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iLeader], iGroupID)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iLeader], 5)
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_BREAD_TOP_LEFT, INIT_VECTOR_2D(0, 0), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_BREAD_TOP_LEFT, FALSE)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 4)
IF (sGGSMData.bHardMode)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_ENEMYSPREAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
ENDIF
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_BREAD_BOTTOM_LEFT, INIT_VECTOR_2D(0, 0), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_BREAD_BASE_LEFT, FALSE)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 3)
IF (sGGSMData.bHardMode)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_ENEMYSPREAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
ENDIF
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_BREAD_TOP_RIGHT, INIT_VECTOR_2D(0, 0), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_BREAD_TOP_RIGHT, FALSE)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 2)
IF (sGGSMData.bHardMode)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_ENEMYSPREAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
ENDIF
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_BREAD_BOTTOM_RIGHT, INIT_VECTOR_2D(0, 0), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_BREAD_BASE_RIGHT, FALSE)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 1)
IF (sGGSMData.bHardMode)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_ENEMYSPREAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
ENDIF
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_BREAD_EYEBROWS, INIT_VECTOR_2D(-40, -80), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_BREAD_BROWS, FALSE)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 0)
GGSM_ENTITY_SET_ANIMATION(sGGSMData.sEntities[iChump], GGSM_SPRITE_ANIM_BREAD_EYEBROWS)
GGSM_TRIGGER_MUSIC_EVENT("ARCADE_SM_BOSS_START")
RETURN TRUE
ENDFUNC
FUNC BOOL GGSM_BOSS_DR_DANK_INIT(GGSM_BOSS_CONTROLLER &boss)
INT iLeader, iChump
INT iGroupID
// find free group
iGroupID = GGSM_GET_FREE_ENTITY_GROUP(sGGSMData.sEntityGroup)
IF (iGroupID = GGSM_INVALID_GROUP_ID)
RETURN FALSE
ENDIF
// create smoothie
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iLeader, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
// create granana
GGSM_RESET_BOSS_CONTROLLER(boss, GGSM_BOSS_DR_DANK)
boss.iGroupID = iGroupID
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_DIE_WHEN_LEADER_DIES)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_ALLOW_OFFSCREEN)
GGSM_RESET_BOSS_CONTROLLER(boss, GGSM_BOSS_DR_DANK)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iLeader], GGSM_ENTITY_BOSS_DR_DANK_TOP, INIT_VECTOR_2D(2400, 540))
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_DANK_SCATTER)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ALT_SPREAD)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iLeader], 2)
sGGSMData.sEntities[iLeader].iNumShotsOverride = 5
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iLeader], GGSM_BOSS_SECTION_DR_DANK_TOP)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iLeader], iGroupID)
// create wig
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_DR_DANK_MIDDLE, INIT_VECTOR_2D(0, 240), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_DR_DANK_MIDDLE, FALSE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DIE_ONLY_FROM_EXPLOSIONS)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 1)
// create glasses
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_DR_DANK_BOTTOM, INIT_VECTOR_2D(0, 290), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_DR_DANK_BOTTOM, FALSE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DIE_ONLY_FROM_EXPLOSIONS)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 0)
// create rockets launchers
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_DR_DANK_RLAUNCHER, INIT_VECTOR_2D(-137, -22), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_DR_DANK_ROCKET_L1, FALSE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DAMAGE_HURTS_ROOT)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 0)
ENDIF
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_DR_DANK_RLAUNCHER, INIT_VECTOR_2D(94, -21), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_DR_DANK_ROCKET_R1, FALSE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DAMAGE_HURTS_ROOT)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 0)
ENDIF
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_DR_DANK_RHLAUNCHER, INIT_VECTOR_2D(-93, 17), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_DR_DANK_ROCKET_L2, FALSE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DAMAGE_HURTS_ROOT)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 0)
ENDIF
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_DR_DANK_RHLAUNCHER, INIT_VECTOR_2D(34, 28), DEFAULT, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_DR_DANK_ROCKET_R2, FALSE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DAMAGE_HURTS_ROOT)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iChump], 0)
ENDIF
GGSM_TRIGGER_MUSIC_EVENT("ARCADE_SM_FINAL_BOSS_START")
RETURN TRUE
ENDFUNC
FUNC BOOL GGSM_BOSS_GRANANA_INIT(GGSM_BOSS_CONTROLLER &boss)
INT iLeader, iChump
INT iGroupID
// find free group
iGroupID = GGSM_GET_FREE_ENTITY_GROUP(sGGSMData.sEntityGroup)
IF (iGroupID = GGSM_INVALID_GROUP_ID)
RETURN FALSE
ENDIF
// create smoothie
IF (GGSM_COUNT_FREE_ENTITIES(sGGSMData.sEntities) < 3)
RETURN FALSE
ENDIF
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iLeader, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
// create granana
GGSM_RESET_BOSS_CONTROLLER(boss, GGSM_BOSS_GRANANA)
boss.iGroupID = iGroupID
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_DIE_WHEN_LEADER_DIES)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_ALLOW_OFFSCREEN)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iLeader], GGSM_ENTITY_BOSS_GRANANA, INIT_VECTOR_2D(2400, 540))
GGSM_ENTITY_SET_ANIMATION(sGGSMData.sEntities[iLeader], GGSM_SPRITE_ANIM_GRANANA)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_GRANANASPREAD1)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ALT_SPREAD)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iLeader], 2)
sGGSMData.sEntities[iLeader].iNumShotsOverride = 2
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iLeader], GGSM_BOSS_SECTION_GRANANA)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iLeader], iGroupID)
// create wig
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_GRANANA_HAIR, INIT_VECTOR_2D(-80, -160), 0, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_GRANANA_HAIR)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DAMAGE_HURTS_ROOT)
// create glasses
GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_GRANANA_GLASSES, INIT_VECTOR_2D(-20, -60), 0, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_GRANANA_GLASSES)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DAMAGE_HURTS_ROOT)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_ENEMYLASER)
GGSM_TRIGGER_MUSIC_EVENT("ARCADE_SM_BOSS_START")
RETURN TRUE
ENDFUNC
FUNC BOOL GGSM_BOSS_MARINE_INIT(GGSM_BOSS_CONTROLLER &boss)
INT iLeader, iGroupID
INT iChump
// find free group
iGroupID = GGSM_GET_FREE_ENTITY_GROUP(sGGSMData.sEntityGroup)
IF (iGroupID = GGSM_INVALID_GROUP_ID)
RETURN FALSE
ENDIF
// create smoothie
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iLeader, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
// create granana
GGSM_RESET_BOSS_CONTROLLER(boss, GGSM_BOSS_MARINE)
boss.iGroupID = iGroupID
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_DIE_WHEN_LEADER_DIES)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntityGroup[iGroupID].eFlags, GGSM_GROUP_BIT_ALLOW_OFFSCREEN)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iLeader], GGSM_ENTITY_BOSS_MARINE, INIT_VECTOR_2D(2400, 540))
GGSM_ENTITY_SET_ANIMATION(sGGSMData.sEntities[iLeader], GGSM_SPRITE_ANIM_MARINE)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iLeader], GGSM_BOSS_SECTION_MARINE)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iLeader], iGroupID)
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_BOSS_MARINE_LAUNCHER, INIT_VECTOR_2D(-176, 56), 0, DEFAULT, iLeader)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iChump], GGSM_BOSS_SECTION_MARINE_LAUNCHER, FALSE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_DAMAGE_HURTS_ROOT)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_NONE)
ENDIF
GGSM_TRIGGER_MUSIC_EVENT("ARCADE_SM_BOSS_START")
RETURN TRUE
ENDFUNC
PROC GGSM_BOSS_SMOOTHIE_CREATE_ORBITERS(GGSM_BOSS_CONTROLLER &boss, INT iNumber = 5)
INT n, iChump
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_SMOOTHIE)
IF (iLeader = GGSM_INVALID_ENTITY)
EXIT
ENDIF
IF (sGGSMData.bHardMode)
iNumber += 3
ENDIF
FLOAT fAngleDiff = 360.0 / TO_FLOAT(iNumber)
FLOAT fAngle = GET_RANDOM_FLOAT_IN_RANGE(0, 360)
REPEAT iNumber n
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_CIRCLE_SHIP_YELLOW, INIT_VECTOR_2D(0, 0), 0, FALSE, iLeader, FALSE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iChump], GGSM_MOVEMENT_ORBIT, 300.0, fAngle + (n * fAngleDiff), 0.5)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_ENEMYSHOT)
sGGSMData.sEntities[iChump].iHP = 3
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iChump].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
ENDIF
ENDREPEAT
ENDPROC
FUNC BOOL GGSM_BOSS_SMOOTHIE_INIT(GGSM_BOSS_CONTROLLER &boss)
INT iLeader, iGroupID
// find free group
iGroupID = GGSM_GET_FREE_ENTITY_GROUP(sGGSMData.sEntityGroup)
IF (iGroupID = GGSM_INVALID_GROUP_ID)
RETURN FALSE
ENDIF
// create smoothie
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iLeader, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
GGSM_RESET_BOSS_CONTROLLER(boss, GGSM_BOSS_SMOOTHIE)
boss.iGroupID = iGroupID
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGroupID].eFlags, GGSM_GROUP_BIT_DIE_WHEN_LEADER_DIES)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGroupID].eFlags, GGSM_GROUP_BIT_ALLOW_OFFSCREEN)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iLeader], GGSM_ENTITY_BOSS_SMOOTHIE, INIT_VECTOR_2D(2400, 540))
GGSM_ENTITY_SET_ANIMATION(sGGSMData.sEntities[iLeader], GGSM_SPRITE_ANIM_SMOOTHIE)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iLeader], iGroupID)
GGSM_ADD_ENTITY_TO_BOSS_CONTROLLER(boss, sGGSMData.sEntities[iLeader], GGSM_BOSS_SECTION_SMOOTHIE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_SMOOTHIESPREAD)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iLeader], 2)
GGSM_BOSS_SMOOTHIE_CREATE_ORBITERS(boss)
GGSM_TRIGGER_MUSIC_EVENT("ARCADE_SM_BOSS_START")
RETURN TRUE
ENDFUNC
FUNC BOOL GGSM_BOSS_INIT(GGSM_BOSS_CONTROLLER &boss)
SWITCH (boss.eType)
CASE GGSM_BOSS_BREAD RETURN GGSM_BOSS_BREAD_INIT(boss)
CASE GGSM_BOSS_DR_DANK RETURN GGSM_BOSS_DR_DANK_INIT(boss)
CASE GGSM_BOSS_GRANANA RETURN GGSM_BOSS_GRANANA_INIT(boss)
CASE GGSM_BOSS_MARINE RETURN GGSM_BOSS_MARINE_INIT(boss)
CASE GGSM_BOSS_SMOOTHIE RETURN GGSM_BOSS_SMOOTHIE_INIT(boss)
ENDSWITCH
RETURN FALSE
ENDFUNC
//-------------------------------------------------
// BOSS CONTROLLER SMOOTHIE FUNCTION
//-------------------------------------------------
PROC GGSM_BOSS_CONTROLLER_UPDATE_SMOOTHIE_PATTERN_A(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_SMOOTHIE)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (boss.fStateTimer >= 0.5)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_RANDOM_SPREAD)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_SMOOTHIESPREAD)
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(2, 4)
boss.iBossPatternState ++
ENDIF
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_BURST_RELOAD_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter <= 0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_SMOOTHIE_PATTERN_B(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_SMOOTHIE)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
IF (boss.fStateTimer >= 1.25)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_RANDOM_SPREAD)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_SMOOTHIEVULCAN)
boss.iInternalCounter = 0
boss.iBossPatternState ++
ENDIF
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_BURST_RELOAD_THIS_FRAME)
boss.iInternalCounter ++
ENDIF
IF (boss.iInternalCounter >= 3)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_SMOOTHIE_PATTERN_C(GGSM_BOSS_CONTROLLER &boss)
FLOAT f
INT iChump
VECTOR_2D v
FLOAT t
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_SMOOTHIE)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(2000, 540))
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
IF (boss.iSubGroupID != GGSM_INVALID_GROUP_ID)
GGSM_DETONATE_ENTITY_GROUP(sGGSMData.sEntityGroup[boss.iSubGroupID])
ENDIF
boss.iBossPatternState ++
BREAK
CASE 1
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
EXIT
ENDIF
GGSM_DESTROY_ENTITIES_OF_TYPE(GGSM_ENTITY_CIRCLE_SHIP_YELLOW)
boss.iSubGroupID = GGSM_GET_FREE_ENTITY_GROUP(sGGSMData.sEntityGroup)
IF (boss.iSubGroupID != GGSM_INVALID_GROUP_ID)
GGSM_RESET_ENTITY_GROUP(sGGSMData.sEntityGroup[boss.iSubGroupID])
IF (GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss) < 0.5)
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(6, 10)
ELSE
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(3, 5)
ENDIF
IF (sGGSMData.bHardMode)
boss.iInternalCounter += 3
ENDIF
boss.fStateTimer = 0.0
ENDIF
boss.iBossPatternState ++
BREAK
CASE 2
t = 0.25
IF (GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss) < 0.5)
t = 0.125
ENDIF
IF (boss.fStateTimer >= t)
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
f = GET_RANDOM_FLOAT_IN_RANGE(800.0, 900.0)
v = GGSM_GET_VECTOR_FROM_HEADING(GET_RANDOM_FLOAT_IN_RANGE(0, 360))
v.x *= f
v.y *= f
v.x += cfBASE_SCREEN_HALF_WIDTH
v.y += cfBASE_SCREEN_HALF_HEIGHT
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_CHERRYBOMB, v, 0, FALSE, DEFAULT, FALSE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iChump], GGSM_MOVEMENT_DESTRUCT_ON_IMPACT)
GGSM_ENTITY_SET_TARGET_WORLD_VECTOR(sGGSMData.sEntities[iChump], GGSM_GET_TARGET_AT_PLAYER_POSITION())
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_NONE)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iChump], boss.iSubGroupID)
boss.iInternalCounter --
boss.fStateTimer = 0.0
ENDIF
ENDIF
IF (boss.iInternalCounter <= 0)
boss.iBossPatternState ++
ENDIF
BREAK
CASE 3
IF (boss.iSubGroupID != GGSM_INVALID_GROUP_ID) AND GGSM_ENTITY_GROUP_HAS_GROUP_FINISHED(sGGSMData.sEntityGroup[boss.iSubGroupID])
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1450, 540))
iChump = 5
IF (GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss) < 0.5)
GGSM_BOSS_SMOOTHIE_CREATE_ORBITERS(boss, iChump + 2)
ELSE
GGSM_BOSS_SMOOTHIE_CREATE_ORBITERS(boss, iChump)
ENDIF
boss.iBossPatternState ++
ENDIF
BREAK
CASE 4
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_SMOOTHIE_SELECT_ATTACK_PATTERN(GGSM_BOSS_CONTROLLER &boss)
INT i = GET_RANDOM_INT_IN_RANGE(0, 3)
SWITCH (i)
CASE 0
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_PATTERN_B)
BREAK
CASE 1
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_PATTERN_C)
BREAK
DEFAULT
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_PATTERN_A)
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_SMOOTHIE(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_SMOOTHIE)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.eState)
CASE GGSM_BOSS_STATE_ARRIVE
IF (boss.bStateInit = FALSE)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1350, 540))
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.bStateInit = TRUE
ELIF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iLeader], GGSM_MOVEMENT_NONE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_SMOOTHIE_START)
GGSM_BOSS_CONTROLLER_SET_ATTACK_PATTERN(boss, GGSM_BOSS_STATE_PATTERN_A)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_ACTIVE
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_PATTERN_A
GGSM_BOSS_CONTROLLER_UPDATE_SMOOTHIE_PATTERN_A(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_B
GGSM_BOSS_CONTROLLER_UPDATE_SMOOTHIE_PATTERN_B(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_C
GGSM_BOSS_CONTROLLER_UPDATE_SMOOTHIE_PATTERN_C(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_D
CASE GGSM_BOSS_STATE_PATTERN_E
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
BREAK
CASE GGSM_BOSS_STATE_PLAYER_DEAD
IF (boss.bStateInit = FALSE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_SMOOTHIE_FAILED)
boss.bStateInit = TRUE
ENDIF
BREAK
CASE GGSM_BOSS_STATE_DEAD
IF (boss.bStateInit = FALSE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_SMOOTHIE_DEFEATED)
boss.bStateInit = TRUE
ELIF NOT GGSM_IS_DIALOG_ACTIVE()
GGSM_ENTITY_EXPLODE_ARRAY(sGGSMData.sEntities, FALSE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_DONE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------
// BOSS CONTROLLER BREAD FUNCTION
//-------------------------------------------------
FUNC INT GGSM_BOSS_CONTROLLER_COUNT_BREAD_SLICES(GGSM_BOSS_CONTROLLER &boss)
INT cnt = 0
INT i, ind
REPEAT 4 i
ind = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, 1 + i)
IF (ind != GGSM_INVALID_ENTITY) AND (sGGSMData.sEntities[ind].eState = GGSM_ENTITY_STATE_ALIVE)
cnt ++
ENDIF
ENDREPEAT
RETURN cnt
ENDFUNC
FUNC BOOL GGSM_BOSS_CONTROLLER_HAVE_BREAD_SLICES_STOPPED_MOVING(GGSM_BOSS_CONTROLLER &boss)
INT i, ind
REPEAT 4 i
ind = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, 1 + i)
IF (ind != GGSM_INVALID_ENTITY) AND (sGGSMData.sEntities[ind].eState = GGSM_ENTITY_STATE_ALIVE)
IF NOT GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[ind])
RETURN FALSE
ENDIF
ENDIF
ENDREPEAT
RETURN TRUE
ENDFUNC
PROC GGSM_BOSS_CONTROLLER_EXPLODE_BREAD(GGSM_BOSS_CONTROLLER &boss, FLOAT spdScalar = 1.0)
INT i, ind
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_FACE)
BOOL bPlaySound = FALSE
VECTOR_2D vPos, vDir
REPEAT 4 i
ind = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_BASE_LEFT + i)
IF (ind != GGSM_INVALID_ENTITY) AND (sGGSMData.sEntities[ind].eState = GGSM_ENTITY_STATE_ALIVE)
GGSM_ENTITY_DETACH_FROM_PARENT(sGGSMData.sEntities[ind])
vDir = GGSM_GET_VECTOR_FROM_HEADING(45.0 + (i * 90.0))
vPos = sGGSMData.sEntities[iLeader].sWorld.vPosition
vPos.x += (vDir.x * 800.0)
vPos.y += (vDir.y * 800.0)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[ind], vPos)
GGSM_ENTITY_SCALE_BASE_SPEED(sGGSMData.sEntities[ind], spdScalar)
bPlaySound = TRUE
ENDIF
ENDREPEAT
IF (bPlaySound)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_BOSS_BREAD_SPLIT, sGGSMData.sEntities[iLeader].sWorld.vPosition)
ENDIF
ENDPROC
PROC GGSM_BOSS_CONTROLLER_DISPERSE_BREAD(GGSM_BOSS_CONTROLLER &boss, VECTOR_2D vPos, FLOAT fDelay = 0.0, FLOAT spdScalar = 1.0)
INT i, ind
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_FACE)
BOOL bPlaySound = FALSE
REPEAT 4 i
ind = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, 1 + i)
IF (ind != GGSM_INVALID_ENTITY) AND (sGGSMData.sEntities[ind].eState = GGSM_ENTITY_STATE_ALIVE)
GGSM_ENTITY_DETACH_FROM_PARENT(sGGSMData.sEntities[ind])
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[ind], vPos, fDelay * i)
GGSM_ENTITY_SCALE_BASE_SPEED(sGGSMData.sEntities[ind], spdScalar)
bPlaySound = TRUE
ENDIF
ENDREPEAT
IF (bPlaySound)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_BOSS_BREAD_SPLIT, sGGSMData.sEntities[iLeader].sWorld.vPosition)
ENDIF
ENDPROC
PROC GGSM_BOSS_CONTROLLER_REASSEMBLE_BREAD(GGSM_BOSS_CONTROLLER &boss)
INT i, ind
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_FACE)
BOOL bPlaySound = FALSE
REPEAT 4 i
ind = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_BASE_LEFT + i)
IF (ind != GGSM_INVALID_ENTITY) AND (sGGSMData.sEntities[ind].eState = GGSM_ENTITY_STATE_ALIVE)
GGSM_ENTITY_ATTACH_TO_PARENT(sGGSMData.sEntities[ind], sGGSMData.sEntities[iLeader])
GGSM_ENTITY_RETURN_TO_ORIGIN(sGGSMData.sEntities[ind])
GGSM_ENTITY_RESET_BASE_SPEED(sGGSMData.sEntities[ind])
bPlaySound = TRUE
ENDIF
ENDREPEAT
IF (bPlaySound)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_BOSS_BREAD_ASSEMBLE, sGGSMData.sEntities[iLeader].sWorld.vPosition)
ENDIF
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_BREAD_PATTERN_A(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_FACE)
INT iMouthCover = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_BASE_LEFT)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (boss.fStateTimer > 1.0)
GGSM_ENTITY_RESET_BASE_SPEED(sGGSMData.sEntities[iLeader])
IF (iMouthCover != GGSM_INVALID_ENTITY)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
GGSM_BOSS_CONTROLLER_EXPLODE_BREAD(boss)
boss.iInternalCounter = 0
boss.iBossPatternState ++
ENDIF
BREAK
CASE 1
IF GGSM_BOSS_CONTROLLER_HAVE_BREAD_SLICES_STOPPED_MOVING(boss)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_BREADSPREAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_BOSS_CONTROLLER_DISPERSE_BREAD(boss, GGSM_GET_TARGET_AT_PLAYER_POSITION())
boss.iBossPatternState ++
ENDIF
BREAK
CASE 2
IF GGSM_BOSS_CONTROLLER_HAVE_BREAD_SLICES_STOPPED_MOVING(boss)
boss.iBossPatternState ++
boss.fStateTimer = 0.0
ENDIF
BREAK
CASE 3
IF (boss.fStateTimer > 3.0)
GGSM_BOSS_CONTROLLER_EXPLODE_BREAD(boss)
boss.iBossPatternState ++
ENDIF
BREAK
CASE 4
IF GGSM_BOSS_CONTROLLER_HAVE_BREAD_SLICES_STOPPED_MOVING(boss)
GGSM_BOSS_CONTROLLER_REASSEMBLE_BREAD(boss)
boss.iBossPatternState ++
ENDIF
BREAK
CASE 5
IF GGSM_BOSS_CONTROLLER_HAVE_BREAD_SLICES_STOPPED_MOVING(boss)
IF (iMouthCover != GGSM_INVALID_ENTITY)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_BREAD_PATTERN_B(GGSM_BOSS_CONTROLLER &boss)
VECTOR_2D vTarget
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_FACE)
INT iMouthCover = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_BASE_LEFT)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (boss.fStateTimer > 0.5)
vTarget = INIT_VECTOR_2D(540, 540)
IF (iMouthCover != GGSM_INVALID_ENTITY)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
IF (sGGSMData.sEntities[iLeader].sWorld.vPosition.x < cfBASE_SCREEN_HALF_WIDTH)
vTarget = INIT_VECTOR_2D(1400, 540)
ENDIF
GGSM_BOSS_CONTROLLER_DISPERSE_BREAD(boss, vTarget, 0.5, 2.0)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_BREADSPREAD)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], vTarget, 3.0)
GGSM_ENTITY_SCALE_BASE_SPEED(sGGSMData.sEntities[iLeader], 2.0)
boss.iInternalCounter = 0
boss.iBossPatternState ++
ENDIF
BREAK
CASE 1
IF GGSM_BOSS_CONTROLLER_HAVE_BREAD_SLICES_STOPPED_MOVING(boss)
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
boss.iBossPatternState ++
boss.fStateTimer = 0.0
ENDIF
ENDIF
BREAK
CASE 2
IF (boss.fStateTimer > 2.0)
IF (iMouthCover != GGSM_INVALID_ENTITY)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
GGSM_BOSS_CONTROLLER_DISPERSE_BREAD(boss, INIT_VECTOR_2D(1400, 540), 0.5, 2.0)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1400, 540), 3.0)
GGSM_ENTITY_SCALE_BASE_SPEED(sGGSMData.sEntities[iLeader], 2.0)
boss.iBossPatternState ++
ENDIF
BREAK
CASE 3
IF GGSM_BOSS_CONTROLLER_HAVE_BREAD_SLICES_STOPPED_MOVING(boss)
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
GGSM_BOSS_CONTROLLER_REASSEMBLE_BREAD(boss)
boss.iBossPatternState ++
ENDIF
ENDIF
BREAK
CASE 4
IF GGSM_BOSS_CONTROLLER_HAVE_BREAD_SLICES_STOPPED_MOVING(boss)
IF (iMouthCover != GGSM_INVALID_ENTITY)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
GGSM_ENTITY_RESET_BASE_SPEED(sGGSMData.sEntities[iLeader])
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_BREAD_PATTERN_C(GGSM_BOSS_CONTROLLER &boss)
INT iChump
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_FACE)
INT iMouthCover = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_BASE_LEFT)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (boss.fStateTimer > 0.25)
IF (iMouthCover != GGSM_INVALID_ENTITY)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1010.0, 300.0))
boss.iInternalCounter = 0
boss.iBossPatternState ++
ENDIF
BREAK
CASE 1
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
boss.iBossPatternState ++
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(7, 11)
boss.fStateTimer = 0.0
ENDIF
BREAK
CASE 2
IF (boss.fStateTimer > 0.25)
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iChump, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iChump], GGSM_ENTITY_CRUMB_METEOR, INIT_VECTOR_2D(GET_RANDOM_FLOAT_IN_RANGE(400.0, 1500.0), 0.0), 0, FALSE, DEFAULT, FALSE)
sGGSMData.sEntities[iChump].vLocalDirection = INIT_VECTOR_2D(0, 1)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iChump], GGSM_MOVEMENT_STRAIGHT)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iChump], GGSM_WEAPON_NONE)
GGSM_ENTITY_SCALE_BASE_SPEED(sGGSMData.sEntities[iChump], GET_RANDOM_FLOAT_IN_RANGE(0.75, 1.25))
boss.iInternalCounter --
boss.fStateTimer = 0.0
ENDIF
ENDIF
IF (boss.iInternalCounter <= 0)
boss.iBossPatternState ++
boss.fStateTimer = 0.0
ENDIF
BREAK
CASE 3
IF (boss.fStateTimer > 1.0)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1400.0, 540.0))
boss.iBossPatternState ++
ENDIF
BREAK
CASE 4
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
IF (iMouthCover != GGSM_INVALID_ENTITY)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_BREAD(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_BREAD_FACE)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.eState)
CASE GGSM_BOSS_STATE_ARRIVE
IF (boss.bStateInit = FALSE)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1400, 540))
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.bStateInit = TRUE
ELIF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iLeader], GGSM_MOVEMENT_NONE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_BREAD_START)
GGSM_BOSS_CONTROLLER_SET_ATTACK_PATTERN(boss, GGSM_BOSS_STATE_PATTERN_A)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_ACTIVE
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_PATTERN_A
GGSM_BOSS_CONTROLLER_UPDATE_BREAD_PATTERN_A(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_B
GGSM_BOSS_CONTROLLER_UPDATE_BREAD_PATTERN_B(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_C
GGSM_BOSS_CONTROLLER_UPDATE_BREAD_PATTERN_C(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_D
CASE GGSM_BOSS_STATE_PATTERN_E
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
BREAK
CASE GGSM_BOSS_STATE_PLAYER_DEAD
IF (boss.bStateInit = FALSE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_BREAD_FAILED)
boss.bStateInit = TRUE
ENDIF
BREAK
CASE GGSM_BOSS_STATE_DEAD
IF (boss.bStateInit = FALSE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_BREAD_DEFEATED)
boss.bStateInit = TRUE
ELIF NOT GGSM_IS_DIALOG_ACTIVE()
GGSM_ENTITY_EXPLODE_ARRAY(sGGSMData.sEntities, FALSE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_DONE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------
// BOSS CONTROLLER GRANANA FUNCTION
//-------------------------------------------------
PROC GGSM_BOSS_CONTROLLER_GRANANA_CRACK_GLASSES(GGSM_BOSS_CONTROLLER &boss)
INT iGlasses = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA_GLASSES)
IF (iGlasses = GGSM_INVALID_ENTITY)
EXIT
ENDIF
GGSM_ENTITY_SET_ANIM_FRAME(sGGSMData.sEntities[iGlasses], 2, TRUE)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_BOSS_GRAN_DAMAGE, sGGSMData.sEntities[iGlasses].sWorld.vPosition)
CPRINTLN(DEBUG_MINIGAME, "GGSM_BOSS_CONTROLLER_GRANANA_CRACK_GLASSES")
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_GRANANA_PATTERN_A(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA)
INT iGlasses = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA_GLASSES)
INT iHair = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA_HAIR)
FLOAT fHPPerc = GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
IF (fHPPerc > 0.5)
sGGSMData.sEntities[iLeader].iNumShotsOverride = 2
ELSE
sGGSMData.sEntities[iLeader].iNumShotsOverride = 4
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
ARCADE_GAMES_SOUND_STOP(ARCADE_GAMES_SOUND_GGSM_BOSS_GRAN_ATTACK_EYE_LASER_CHARGE_LOOP)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGlasses].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iHair].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ALT_SPREAD)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_GRANANASPREAD1)
GGSM_ENTITY_RESET_BASE_SPEED(sGGSMData.sEntities[iGlasses])
IF (fHPPerc > 0.5)
GGSM_ENTITY_SET_ANIM_FRAME(sGGSMData.sEntities[iGlasses], 0, TRUE)
GGSM_ENTITY_SCALE_BASE_SPEED(sGGSMData.sEntities[iGlasses], 2.0)
ELSE
GGSM_ENTITY_SET_ANIM_FRAME(sGGSMData.sEntities[iGlasses], 2, TRUE)
ENDIF
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(6, 12)
boss.iBossPatternState ++
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
boss.iInternalCounter --
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_ALT_SPREAD)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_GRANANASPREAD1A)
ELSE
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_GRANANASPREAD1)
ENDIF
ENDIF
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
IF (boss.iInternalCounter > 0)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(GET_RANDOM_FLOAT_IN_RANGE(1200, 1450), GET_RANDOM_FLOAT_IN_RANGE(500, 740)))
ELSE
boss.iBossPatternState ++
ENDIF
ENDIF
BREAK
CASE 2
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_GRANANA_PATTERN_B(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA)
INT iGlasses = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA_GLASSES)
INT iHair = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA_HAIR)
FLOAT fHPPerc = GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_ALT_SPREAD)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_GRANANASPREAD1A)
ELSE
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_GRANANASPREAD1)
ENDIF
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1355, 900))
GGSM_ENTITY_SCALE_BASE_SPEED(sGGSMData.sEntities[iLeader], 3.0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGlasses].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iHair].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.iBossPatternState ++
BREAK
CASE 1
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iLeader], GGSM_MOVEMENT_NONE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_GRANANASPREAD1)
GGSM_ENTITY_DETACH_FROM_PARENT(sGGSMData.sEntities[iHair])
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iHair], GGSM_WEAPON_GRANANAHAIR)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_BOSS_GRAN_DAMAGE_LOSE_WIG, sGGSMData.sEntities[iHair].sWorld.vPosition)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iHair].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iHair].eFlags, GGSM_ENTITY_BIT_WPN_FIRE_ON_ARRIVE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iHair], GGSM_MOVEMENT_TARGETTED_WANDER, 0.125)
IF (fHPPerc < 0.5)
sGGSMData.sEntities[iHair].iNumShotsOverride = 6
GGSM_ENTITY_SCALE_BASE_SPEED(sGGSMData.sEntities[iHair], 1.5)
ENDIF
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(4, 6)
boss.iBossPatternState ++
ENDIF
BREAK
CASE 2
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iHair].eMoveFlags, GGSM_MOVE_BIT_TARGET_POINT_REACHED_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter <= 0)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iHair], GGSM_WEAPON_NONE)
GGSM_ENTITY_ATTACH_TO_PARENT(sGGSMData.sEntities[iHair], sGGSMData.sEntities[iLeader])
GGSM_ENTITY_RETURN_TO_ORIGIN(sGGSMData.sEntities[iHair])
GGSM_ENTITY_RESET_BASE_SPEED(sGGSMData.sEntities[iHair])
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iHair].eFlags, GGSM_ENTITY_BIT_WPN_FIRE_ON_ARRIVE)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1450, 540))
GGSM_ENTITY_RESET_BASE_SPEED(sGGSMData.sEntities[iLeader])
boss.iBossPatternState ++
ENDIF
BREAK
CASE 3
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_GRANANA_PATTERN_C(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA)
INT iGlasses = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA_GLASSES)
FLOAT fHPPerc = GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
ARCADE_GAMES_SOUND_PLAY_LOOP(ARCADE_GAMES_SOUND_GGSM_BOSS_GRAN_ATTACK_EYE_LASER_CHARGE_LOOP)
IF (fHPPerc > 0.5)
GGSM_ENTITY_SET_ANIMATION(sGGSMData.sEntities[iGlasses], GGSM_SPRITE_ANIM_GRAN_GLASS)
ELSE
GGSM_ENTITY_SET_ANIMATION(sGGSMData.sEntities[iGlasses], GGSM_SPRITE_ANIM_GRAN_GLASSCRACK)
ENDIF
boss.iBossPatternState ++
BREAK
CASE 1
IF (boss.fStateTimer > 3.0)
IF (fHPPerc > 0.5)
GGSM_ENTITY_SET_ANIM_FRAME(sGGSMData.sEntities[iGlasses], 1, TRUE)
ELSE
GGSM_ENTITY_SET_ANIM_FRAME(sGGSMData.sEntities[iGlasses], 3, TRUE)
ENDIF
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iLeader], GGSM_MOVEMENT_NONE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGlasses].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.iBossPatternState ++
boss.fStateTimer = 0.0
ENDIF
BREAK
CASE 2
IF (boss.fStateTimer > 1.0)
ARCADE_GAMES_SOUND_STOP(ARCADE_GAMES_SOUND_GGSM_BOSS_GRAN_ATTACK_EYE_LASER_CHARGE_LOOP)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGlasses].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
IF (fHPPerc < 0.5)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iGlasses], GGSM_WEAPON_GRANANAGLASSES2)
ELSE
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iGlasses], GGSM_WEAPON_GRANANAGLASSES)
ENDIF
GGSM_ENTITY_FIRE_WEAPON(sGGSMData.sEntities[iGlasses])
boss.iBossPatternState ++
ENDIF
BREAK
CASE 3
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iGlasses].eFlags, GGSM_ENTITY_BIT_WPN_BURST_FIRED)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_GRANANA(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA)
INT iGlasses = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_GRANANA_GLASSES)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
IF (boss.eState > GGSM_BOSS_STATE_ARRIVE)
IF (boss.bFiftyPercent = FALSE) AND (GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss) < 0.5)
GGSM_BOSS_CONTROLLER_GRANANA_CRACK_GLASSES(boss)
boss.bFiftyPercent = TRUE
ENDIF
ENDIF
SWITCH (boss.eState)
CASE GGSM_BOSS_STATE_ARRIVE
IF (boss.bStateInit = FALSE)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1450, 540))
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGlasses].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.bStateInit = TRUE
ELIF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iLeader], GGSM_MOVEMENT_NONE)
GGSM_BOSS_CONTROLLER_SET_ATTACK_PATTERN(boss, GGSM_BOSS_STATE_PATTERN_A)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_GRANANA_START)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_ACTIVE
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_PATTERN_A
GGSM_BOSS_CONTROLLER_UPDATE_GRANANA_PATTERN_A(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_B
GGSM_BOSS_CONTROLLER_UPDATE_GRANANA_PATTERN_B(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_C
GGSM_BOSS_CONTROLLER_UPDATE_GRANANA_PATTERN_C(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_D
CASE GGSM_BOSS_STATE_PATTERN_E
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
BREAK
CASE GGSM_BOSS_STATE_PLAYER_DEAD
IF (boss.bStateInit = FALSE)
ARCADE_GAMES_SOUND_STOP(ARCADE_GAMES_SOUND_GGSM_BOSS_GRAN_ATTACK_EYE_LASER_CHARGE_LOOP)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_GRANANA_FAILED)
boss.bStateInit = TRUE
ENDIF
BREAK
CASE GGSM_BOSS_STATE_DEAD
IF (boss.bStateInit = FALSE)
ARCADE_GAMES_SOUND_STOP(ARCADE_GAMES_SOUND_GGSM_BOSS_GRAN_ATTACK_EYE_LASER_CHARGE_LOOP)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_GRANANA_DEFEATED)
boss.bStateInit = TRUE
ELIF NOT GGSM_IS_DIALOG_ACTIVE()
GGSM_ENTITY_EXPLODE_ARRAY(sGGSMData.sEntities, FALSE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_DONE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------
// BOSS CONTROLLER MARINE FUNCTION
//-------------------------------------------------
PROC GGSM_BOSS_CONTROLLER_MARINE_CHESTBURST(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE)
IF (iLeader = GGSM_INVALID_ENTITY)
EXIT
ENDIF
GGSM_ENTITY_SET_ANIMATION(sGGSMData.sEntities[iLeader], GGSM_SPRITE_ANIM_MARINE_ALIEN)
GGSM_PLAY_SOUND_FROM_POSITION(ARCADE_GAMES_SOUND_GGSM_BOSS_MARINE_CHESTBURST, sGGSMData.sEntities[iLeader].sWorld.vPosition)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_MARINE_PATTERN_A(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE)
INT iLauncher = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE_LAUNCHER)
IF (GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss) < 0.5)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
IF (iLeader = GGSM_INVALID_ENTITY) OR (iLauncher = GGSM_INVALID_ENTITY)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (boss.fStateTimer > 0.5)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLauncher].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_MARINE_SPREAD)
boss.iBossPatternState ++
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(7, 11)
ENDIF
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter <= 0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.iBossPatternState ++
boss.fStateTimer = 0
ENDIF
BREAK
CASE 2
IF (boss.fStateTimer > 0.5)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_MARINE_PATTERN_B(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE)
INT iLauncher = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE_LAUNCHER)
IF (GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss) < 0.5)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
IF (iLeader = GGSM_INVALID_ENTITY) OR (iLauncher = GGSM_INVALID_ENTITY)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (boss.fStateTimer > 0.5)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLauncher].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLauncher], GGSM_WEAPON_MARINE_LAUNCHER)
boss.iBossPatternState ++
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(5, 10)
ENDIF
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLauncher].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter <= 0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLauncher].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.iBossPatternState ++
boss.fStateTimer = 0
ENDIF
BREAK
CASE 2
IF (sGGSMData.iEnemyProjectilesActive = 0)
boss.iBossPatternState ++
boss.fStateTimer = 0
ENDIF
BREAK
CASE 3
IF (boss.fStateTimer > 0.1)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_MARINE_PATTERN_C(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE)
INT iLauncher = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE_LAUNCHER)
FLOAT hprc = GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss)
IF (hprc > 0.5) OR (hprc < 0.25)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
IF (iLeader = GGSM_INVALID_ENTITY)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (boss.fStateTimer > 0.5)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLauncher].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_RANDOM_SPREAD)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_ACID_VULKAN)
boss.iBossPatternState ++
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(10, 15)
ENDIF
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter <= 0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.iBossPatternState ++
boss.fStateTimer = 0
ENDIF
BREAK
CASE 2
IF (boss.fStateTimer > 0.5)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_MARINE_PATTERN_D(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE)
INT iLauncher = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE_LAUNCHER)
IF (GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss) > 0.25)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
IF (iLeader = GGSM_INVALID_ENTITY)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (boss.fStateTimer > 0.5)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLauncher].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_RANDOM_SPREAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_SINGLE_SHOT_MODE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_ACID)
boss.iBossPatternState ++
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(10, 15)
ENDIF
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter <= 0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.iBossPatternState ++
boss.fStateTimer = 0
ENDIF
BREAK
CASE 2
IF (boss.fStateTimer > 0.5)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_MARINE(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_MARINE)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
IF (boss.bFiftyPercent = FALSE)
IF (iLeader != GGSM_INVALID_ENTITY) AND (boss.eState > GGSM_BOSS_STATE_ARRIVE)
IF (GGSM_BOSS_CONTROLLER_GET_HP_PERCENTAGE(boss) < 0.5)
GGSM_BOSS_CONTROLLER_MARINE_CHESTBURST(boss)
boss.bFiftyPercent = TRUE
ENDIF
ENDIF
ENDIF
SWITCH (boss.eState)
CASE GGSM_BOSS_STATE_ARRIVE
IF (boss.bStateInit = FALSE)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1350, 540))
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ALT_SPREAD)
boss.bStateInit = TRUE
ELIF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iLeader], GGSM_MOVEMENT_NONE)
GGSM_BOSS_CONTROLLER_SET_ATTACK_PATTERN(boss, GGSM_BOSS_STATE_PATTERN_A)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_ENEMYCONESPREAD)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_MARINE_START)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_ACTIVE
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_PATTERN_A
GGSM_BOSS_CONTROLLER_UPDATE_MARINE_PATTERN_A(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_B
GGSM_BOSS_CONTROLLER_UPDATE_MARINE_PATTERN_B(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_C
GGSM_BOSS_CONTROLLER_UPDATE_MARINE_PATTERN_C(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_D
GGSM_BOSS_CONTROLLER_UPDATE_MARINE_PATTERN_D(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_E
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
BREAK
CASE GGSM_BOSS_STATE_PLAYER_DEAD
IF (boss.bStateInit = FALSE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_MARINE_FAILED)
boss.bStateInit = TRUE
ENDIF
BREAK
CASE GGSM_BOSS_STATE_DEAD
IF (boss.bStateInit = FALSE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_MARINE_DEFEATED)
boss.bStateInit = TRUE
ELIF NOT GGSM_IS_DIALOG_ACTIVE()
GGSM_ENTITY_EXPLODE_ARRAY(sGGSMData.sEntities, FALSE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_DONE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------
// BOSS CONTROLLER DR_DANK FUNCTION
//-------------------------------------------------
PROC GGSM_BOSS_CONTROLLER_DR_DANK_ACTIVATE_ROCKETS(GGSM_BOSS_CONTROLLER &boss, BOOL bOn = TRUE)
INT i, ind
REPEAT 4 i
ind = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_ROCKET_L1 + i)
IF (ind != GGSM_INVALID_ENTITY) AND (sGGSMData.sEntities[ind].eState = GGSM_ENTITY_STATE_ALIVE)
IF (bOn)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK_PATTERN_A(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_TOP)
INT iGunSection = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_BOTTOM)
VECTOR_2D vTarget, vPlayerPos
IF (iLeader = GGSM_INVALID_ENTITY)
EXIT
ENDIF
IF (iGunSection = GGSM_INVALID_ENTITY)
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0 // detach gun
GGSM_BOSS_CONTROLLER_DEACTIVATE_ALL_WEAPONS(boss)
GGSM_ENTITY_DETACH_FROM_PARENT(sGGSMData.sEntities[iGunSection])
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iGunSection], GGSM_WEAPON_DANK_CANNON)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iGunSection], INIT_VECTOR_2D(1890, 820))
boss.iBossPatternState ++
BREAK
CASE 1 // move gun to top of screen and move down wards
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iGunSection])
GGSM_ENTITY_SET_LOCAL_TRANSFORM(sGGSMData.sEntities[iGunSection], 1470, 0, 270)
GGSM_ENTITY_SET_ANIMATION(sGGSMData.sEntities[iGunSection], GGSM_SPRITE_ANIM_DANK_CANNON)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iGunSection], INIT_VECTOR_2D(1470, 70), 0.125)
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(2, 5)
boss.iBossPatternState ++
ENDIF
BREAK
CASE 2 // start moving gun across
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iGunSection])
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(2, 5)
GGSM_BOSS_CONTROLLER_DR_DANK_ACTIVATE_ROCKETS(boss, TRUE)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iGunSection], INIT_VECTOR_2D(390, 70))
boss.iBossPatternState ++
ENDIF
BREAK
CASE 3 // if player cross the line of gun shoot - if we've moved across so many times the return gun
vPlayerPos = GGSM_GET_TARGET_AT_PLAYER_POSITION()
IF (ABSF(vPlayerPos.x - sGGSMData.sEntities[iGunSection].sWorld.vPosition.x) < 128.0)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ELSE
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iGunSection])
boss.iInternalCounter --
vTarget = sGGSMData.sEntities[iGunSection].sWorld.vPosition
IF (boss.iInternalCounter <= 0)
vTarget = sGGSMData.sEntities[iGunSection].sWorld.vPosition
vTarget.y -= 100.0
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iGunSection], vTarget)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.iBossPatternState ++
EXIT
ENDIF
IF (sGGSMData.sEntities[iGunSection].vLocalDirection.x < 0)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iGunSection], INIT_VECTOR_2D(1470, vTarget.y))
ELSE
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iGunSection], INIT_VECTOR_2D(390, vTarget.y))
ENDIF
ENDIF
BREAK
CASE 4 // put gun back
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iGunSection])
GGSM_ENTITY_SET_ANIM_FRAME(sGGSMData.sEntities[iGunSection], DEFAULT, TRUE)
GGSM_ENTITY_SET_LOCAL_TRANSFORM(sGGSMData.sEntities[iGunSection], 1890, 820, 0)
GGSM_ENTITY_SET_PARENT_INDEX(sGGSMData.sEntities[iGunSection], iLeader)
GGSM_ENTITY_RETURN_TO_ORIGIN(sGGSMData.sEntities[iGunSection])
boss.iBossPatternState ++
ENDIF
BREAK
CASE 5 // turn rockets off
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iGunSection])
GGSM_BOSS_CONTROLLER_DR_DANK_ACTIVATE_ROCKETS(boss, FALSE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK_PATTERN_B(GGSM_BOSS_CONTROLLER &boss)
INT iGunSection = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_MIDDLE)
IF (iGunSection = GGSM_INVALID_ENTITY)
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iGunSection], GGSM_WEAPON_DANK_CLUSTERBOMB)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
sGGSMData.sEntities[iGunSection].iNumShotsOverride = 4
boss.iBossPatternState ++
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(4, 8)
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter < 0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK_PATTERN_C(GGSM_BOSS_CONTROLLER &boss)
INT iGunSection = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_MIDDLE)
IF (iGunSection = GGSM_INVALID_ENTITY)
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iGunSection], GGSM_WEAPON_DANK_SPREAD)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ALT_SPREAD)
sGGSMData.sEntities[iGunSection].iNumShotsOverride = 4
boss.iBossPatternState ++
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(4, 8)
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter < 0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK_PATTERN_D(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_TOP)
INT iGunSection = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_BOTTOM)
IF (iLeader = GGSM_INVALID_ENTITY)
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
EXIT
ENDIF
SWITCH (boss.iBossPatternState)
CASE 0
IF (iGunSection != GGSM_INVALID_ENTITY)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iGunSection], GGSM_WEAPON_DANK_CANNON)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iGunSection].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
ENDIF
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeader], GGSM_WEAPON_DANK_SCATTER)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.iBossPatternState ++
boss.iInternalCounter = GET_RANDOM_INT_IN_RANGE(10, 15)
BREAK
CASE 1
IF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_FIRED_THIS_FRAME)
boss.iInternalCounter --
ENDIF
IF (boss.iInternalCounter < 0)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ACTIVE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK(GGSM_BOSS_CONTROLLER &boss)
INT iLeader = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_TOP)
//INT iGunSection = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, GGSM_BOSS_SECTION_DR_DANK_BOTTOM)
IF (iLeader = GGSM_INVALID_ENTITY) AND (boss.eState >= GGSM_BOSS_STATE_ARRIVE)
EXIT
ENDIF
SWITCH (boss.eState)
CASE GGSM_BOSS_STATE_ARRIVE
IF (boss.bStateInit = FALSE)
GGSM_ENTITY_SET_MOVEMENT_TO_VECTOR(sGGSMData.sEntities[iLeader], INIT_VECTOR_2D(1350, 540))
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeader].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
boss.bStateInit = TRUE
ELIF IS_BITMASK_ENUM_AS_ENUM_SET(sGGSMData.sEntities[iLeader].eMoveFlags, GGSM_MOVE_BIT_COMPLETE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[iLeader], GGSM_MOVEMENT_NONE)
GGSM_BOSS_CONTROLLER_SET_ATTACK_PATTERN(boss, GGSM_BOSS_STATE_PATTERN_A)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_DR_DANK_START)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_ACTIVE
IF GGSM_ENTITY_IS_MOVEMENT_COMPLETE(sGGSMData.sEntities[iLeader])
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_PATTERN_A
GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK_PATTERN_A(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_B
GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK_PATTERN_B(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_C
GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK_PATTERN_C(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_D
GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK_PATTERN_D(boss)
BREAK
CASE GGSM_BOSS_STATE_PATTERN_E
GGSM_BOSS_CONTROLLER_SELECT_ATTACK_PATTERN(boss)
BREAK
CASE GGSM_BOSS_STATE_PLAYER_DEAD
IF (boss.bStateInit = FALSE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_DR_DANK_FAILED)
boss.bStateInit = TRUE
ENDIF
BREAK
CASE GGSM_BOSS_STATE_DEAD
IF (boss.bStateInit = FALSE)
GGSM_DIALOG_CONTROLLER_START_DIALOG(sGGSMData.sDialogController, GGSM_DIALOG_BOSS_DR_DANK_DEFEATED)
boss.bStateInit = TRUE
ELIF NOT GGSM_IS_DIALOG_ACTIVE()
GGSM_ENTITY_EXPLODE_ARRAY(sGGSMData.sEntities, FALSE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_DONE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------
// BOSS CONTROLLER FUNCTION
//-------------------------------------------------
FUNC BOOL GGSM_BOSS_CONTROLLER_UPDATE(GGSM_BOSS_CONTROLLER &boss)
INT i, ind
IF (boss.eType = GGSM_BOSS_NONE)
RETURN FALSE
ENDIF
IF (boss.eState = GGSM_BOSS_STATE_DONE)
RETURN FALSE
ENDIF
boss.fStateTimer += (GET_FRAME_TIME() * sGGSMData.fTimeScale)
boss.iTotalHP = GGSM_BOSS_CONTROLLER_GET_HP(boss)
// don't let people shoot while the boss is loading
IF (boss.eState >= GGSM_BOSS_STATE_LOADING) AND (boss.eState < GGSM_BOSS_STATE_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_DISABLE_WEAPONS)
GGSM_SET_PLAYER_INVUNERABLE()
ELSE
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_DISABLE_WEAPONS)
ENDIF
// handle defeated player state
IF (boss.eState != GGSM_BOSS_STATE_PLAYER_DEAD)
IF GGSM_IS_PLAYER_TOTALLY_DEFEATED()
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_PLAYER_DEAD)
ENDIF
ENDIF
// handle boss dead state
IF (boss.eState != GGSM_BOSS_STATE_DEAD)
IF (boss.iMaxTotalHP > 0) AND (boss.iTotalHP = 0)
boss.iMaxTotalHP = 0
GGSM_RESET_PROJECTILE_ARRAY(sGGSMData.sProjectiles)
IF (boss.iSubGroupID != GGSM_INVALID_GROUP_ID)
GGSM_DETONATE_ENTITY_GROUP(sGGSMData.sEntityGroup[boss.iSubGroupID])
boss.iSubGroupID = GGSM_INVALID_GROUP_ID
ENDIF
ARCADE_GAMES_SOUND_STOP(ARCADE_GAMES_SOUND_GGSM_BOSS_GRAN_ATTACK_EYE_LASER_CHARGE_LOOP)
GGSM_ENTITY_EXPLODE_ARRAY(sGGSMData.sEntities, FALSE)
GGSM_TRIGGER_MUSIC_EVENT("ARCADE_SM_STOP")
GGSM_SHUTDOWN_ALL_SPECIAL_WEAPONS(sGGSMData.sSpWeaponData)
IF boss.eType = GGSM_BOSS_DR_DANK
GGSM_PLAY_AVATAR_ALL_CLEAR_ANIM()
ELSE
GGSM_PLAY_AVATAR_BOSS_DEAD_ANIM()
ENDIF
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_PLAYER_BOSS_INVUNERABLE)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_DEAD)
ENDIF
ELSE
IF (boss.fStateTimer > 15.0)
SCRIPT_ASSERT("EMERGENCY TIME OUT")
GGSM_DESTROY_ENTITIES_OF_FACTION(FALSE)
ENDIF
ENDIF
// remove dead units from the boss controller
IF (boss.iMaxTotalHP > 0)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_BRING_CAMERA_TO_STOP)
REPEAT GGSM_MAX_BOSS_PARTS i
ind = GGSM_BOSS_CONTROLLER_GET_ENTITY_INDEX(boss, i)
IF (ind != GGSM_INVALID_ENTITY)
IF (sGGSMData.sEntities[ind].eState != GGSM_ENTITY_STATE_ALIVE)
GGSM_BOSS_CONTROLLER_SET_ENTITY_INDEX(boss, i, GGSM_INVALID_ENTITY)
ENDIF
ENDIF
ENDREPEAT
ENDIF
// update standard states
SWITCH (boss.eState)
CASE GGSM_BOSS_STATE_NONE
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_LOADING)
BREAK
CASE GGSM_BOSS_STATE_LOADING
IF GGSM_LOAD_TEXTURE_DICTS_FOR_BOSS(boss.eType)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_CREATE)
ENDIF
BREAK
CASE GGSM_BOSS_STATE_CREATE
IF GGSM_BOSS_INIT(boss)
GGSM_CLEAR_BOSS_CONTROLLER_USED_PATTERN_BITSET(boss)
GGSM_BOSS_CONTROLLER_SET_STATE(boss, GGSM_BOSS_STATE_ARRIVE)
ENDIF
BREAK
ENDSWITCH
// update boss specific states
SWITCH (boss.eType)
CASE GGSM_BOSS_BREAD
GGSM_BOSS_CONTROLLER_UPDATE_BREAD(boss)
BREAK
CASE GGSM_BOSS_DR_DANK
GGSM_BOSS_CONTROLLER_UPDATE_DR_DANK(boss)
BREAK
CASE GGSM_BOSS_GRANANA
GGSM_BOSS_CONTROLLER_UPDATE_GRANANA(boss)
BREAK
CASE GGSM_BOSS_MARINE
GGSM_BOSS_CONTROLLER_UPDATE_MARINE(boss)
BREAK
CASE GGSM_BOSS_SMOOTHIE
GGSM_BOSS_CONTROLLER_UPDATE_SMOOTHIE(boss)
BREAK
ENDSWITCH
RETURN TRUE
ENDFUNC
PROC GGSM_BOSS_CONTROLLER_UPDATE_ARRAY(GGSM_BOSS_CONTROLLER &array[])
INT i
REPEAT COUNT_OF(array) i
IF GGSM_BOSS_CONTROLLER_UPDATE(array[i])
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.eStageFlags, GGSM_STAGE_BIT_BOSSES_ACTIVE)
ENDIF
ENDREPEAT
ENDPROC
//-------------------------------------------------
// GROUP UPDATE FUNCTIONS
//-------------------------------------------------
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_STANDARD(GGSM_ENTITY_GROUP &group)
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
VECTOR_2D spacing = GGSM_FORMATION_GET_SPACING(group.sFormSettings)
FLOAT fDistBetweenUnits = FMAX(ABSF(spriteSize.x), ABSF(spriteSize.y)) * spacing.x
FLOAT spd = entData.fBaseSpeed
// move the dist counter so we can ensure the formation is reasonably spaced
group.fDistCounter += (spd * GET_FRAME_TIME() * sGGSMData.fTimeScale)
group.fDistCounter = CLAMP(group.fDistCounter, 0, fDistBetweenUnits)
IF (group.fDistCounter < fDistBetweenUnits)
EXIT
ENDIF
INT ind
VECTOR_2D vBasePos
// create leader
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
EXIT
ENDIF
vBasePos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], entType, vBasePos, 0)
sGGSMData.sEntities[ind].vLocalDirection = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
sGGSMData.sEntities[ind].fSpeed = spd
group.fDistCounter = 0
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_DELAYED_LINE(GGSM_ENTITY_GROUP &group, VECTOR_2D vSpreadDir)
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
VECTOR_2D spacing = GGSM_FORMATION_GET_SPACING(group.sFormSettings)
FLOAT fDistBetweenUnits = FMAX(ABSF(spriteSize.x), ABSF(spriteSize.y)) * spacing.x
FLOAT spd = entData.fBaseSpeed
// move the dist counter so we can ensure the formation is reasonably spaced
group.fDistCounter += (spd * GET_FRAME_TIME() * sGGSMData.fTimeScale)
group.fDistCounter = CLAMP(group.fDistCounter, 0, fDistBetweenUnits)
IF (group.fDistCounter < fDistBetweenUnits)
EXIT
ENDIF
INT ind
// create leader
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
EXIT
ENDIF
INT iFormIndex = GGSM_FORMATION_GET_MEMBER_COUNT(group.sFormSettings) - group.iMembersToCreate
VECTOR_2D vBasePos = GGSM_FORMATION_GET_POSITION_FOR_DELAYED_LINE(group.sFormSettings, vSpreadDir, iFormIndex, spriteSize)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], entType, vBasePos, 0)
sGGSMData.sEntities[ind].vLocalDirection = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
sGGSMData.sEntities[ind].fSpeed = spd
group.fDistCounter = 0
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_HORZLINE(GGSM_ENTITY_GROUP &group)
INT i, ind
VECTOR_2D vDir = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
VECTOR_2D vStoredOrigin
VECTOR_2D vStartPos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
VECTOR_2D vBasePos = vStartPos
FLOAT fDist
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
VECTOR_2D spacing = GGSM_FORMATION_GET_SPACING(group.sFormSettings)
FLOAT fDistBetweenUnits = ABSF(spriteSize.x) * spacing.x
FLOAT spd = entData.fBaseSpeed
fDist = 0
REPEAT group.sFormSettings.iFormationMembers i
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], entType, vBasePos, 0)
sGGSMData.sEntities[ind].vLocalDirection = vDir
sGGSMData.sEntities[ind].fSpeed = spd
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
vStoredOrigin = vStartPos
vStoredOrigin.x -= (vDir.x * fDist)
vStoredOrigin.y -= (vDir.y * fDist)
GGSM_ENTITY_SET_PACKED_ORIGIN(sGGSMData.sEntities[ind], vStoredOrigin)
fDist += fDistBetweenUnits
IF (vDir.x < 0)
vBasePos.x -= fDistBetweenUnits
ELSE
vBasePos.x += fDistBetweenUnits
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_VERTLINE(GGSM_ENTITY_GROUP &group)
INT i, ind
VECTOR_2D vBasePos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
VECTOR_2D spacing = GGSM_FORMATION_GET_SPACING(group.sFormSettings)
FLOAT fDistBetweenUnits = ABSF(spriteSize.y) * spacing.y
FLOAT spd = entData.fBaseSpeed
vBasePos.y += (TO_FLOAT(group.iMembersToCreate) / 2.0) * fDistBetweenUnits
REPEAT group.sFormSettings.iFormationMembers i
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], entType, vBasePos, 0)
sGGSMData.sEntities[ind].vLocalDirection = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
sGGSMData.sEntities[ind].fSpeed = spd
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
vBasePos.y -= fDistBetweenUnits
ENDIF
ENDREPEAT
group.iMembersToCreate = 0
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_FLYING_V(GGSM_ENTITY_GROUP &group)
INT i, ind
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D basePos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
VECTOR_2D dir = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
VECTOR_2D transRootPos, checkPos
VECTOR_2D spacing = GGSM_FORMATION_GET_SPACING(group.sFormSettings)
// move the group dist counter
group.fDistCounter += (entData.fBaseSpeed * GET_FRAME_TIME() * sGGSMData.fTimeScale)
transRootPos = basePos
GGSM_TRANSLATE_VECTOR_BY_DIRECTION(transRootPos, dir, group.fDistCounter)
REPEAT group.sFormSettings.iFormationMembers i
IF NOT IS_BIT_SET(group.iFormSpawnBitSet, i)
checkPos = GGSM_FORMATION_GET_POSITION_FOR_FLYING_V_EXPLICT(transRootPos, dir, i, spriteSize, spacing)
IF GGSM_IS_POINT_ON_SCREEN(checkPos, spriteSize)
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], entType, checkPos, 0)
sGGSMData.sEntities[ind].vLocalDirection = dir
sGGSMData.sEntities[ind].fSpeed = entData.fBaseSpeed
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
SET_BIT(group.iFormSpawnBitSet, i)
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_LINE(GGSM_ENTITY_GROUP &group)
INT i, ind
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D basePos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
VECTOR_2D dir = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
VECTOR_2D transRootPos, checkPos
VECTOR_2D spacing = GGSM_FORMATION_GET_SPACING(group.sFormSettings)
// move the group dist counter
group.fDistCounter += (entData.fBaseSpeed * GET_FRAME_TIME() * sGGSMData.fTimeScale)
transRootPos = basePos
GGSM_TRANSLATE_VECTOR_BY_DIRECTION(transRootPos, dir, group.fDistCounter)
REPEAT group.sFormSettings.iFormationMembers i
IF NOT IS_BIT_SET(group.iFormSpawnBitSet, i)
checkPos = GGSM_FORMATION_GET_POSITION_FOR_LINE_EXPLICT(transRootPos, dir, i, spriteSize, spacing)
IF GGSM_IS_POINT_ON_SCREEN(checkPos, spriteSize)
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], entType, checkPos, 0)
sGGSMData.sEntities[ind].vLocalDirection = dir
sGGSMData.sEntities[ind].fSpeed = entData.fBaseSpeed
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
SET_BIT(group.iFormSpawnBitSet, i)
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_MIRROR_X(GGSM_ENTITY_GROUP &group)
INT i
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
FLOAT fDistBetweenUnits = FMAX(ABSF(spriteSize.x), ABSF(spriteSize.y)) * 1.5
FLOAT spd = entData.fBaseSpeed
VECTOR_2D dir
// move the dist counter so we can ensure the formation is reasonably spaced
group.fDistCounter += (spd * GET_FRAME_TIME() * sGGSMData.fTimeScale)
group.fDistCounter = CLAMP(group.fDistCounter, 0, fDistBetweenUnits)
IF (group.fDistCounter < fDistBetweenUnits)
EXIT
ENDIF
INT ind
VECTOR_2D vBasePos
// create leader
dir = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
REPEAT 2 i
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
vBasePos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
IF (i = 1)
vBasePos.x = cfBASE_SCREEN_HALF_WIDTH - vBasePos.x
vBasePos.x += cfBASE_SCREEN_HALF_WIDTH
ENDIF
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], entType, vBasePos, 0)
sGGSMData.sEntities[ind].vLocalDirection = dir
sGGSMData.sEntities[ind].fSpeed = spd
IF (i = 1)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eMoveFlags, GGSM_MOVE_BIT_FLIP_RELATIVE_PATH_X)
ENDIF
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
ENDIF
ENDREPEAT
group.fDistCounter = 0
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_MIRROR_Y(GGSM_ENTITY_GROUP &group)
INT i
GGSM_ENTITY_TYPE entType = GGSM_FORMATION_GET_ENTITY_TYPE(group.sFormSettings)
GGSM_ENTITY_DATA entData = GGSM_ENTITY_DATA_GET(entType)
VECTOR_2D spriteSize = GGSM_GET_SPRITE_SIZE(entData.eSpriteType)
FLOAT fDistBetweenUnits = FMAX(ABSF(spriteSize.x), ABSF(spriteSize.y)) * 1.5
FLOAT spd = entData.fBaseSpeed
VECTOR_2D dir
// move the dist counter so we can ensure the formation is reasonably spaced
group.fDistCounter += (spd * GET_FRAME_TIME() * sGGSMData.fTimeScale)
group.fDistCounter = CLAMP(group.fDistCounter, 0, fDistBetweenUnits)
IF (group.fDistCounter < fDistBetweenUnits)
EXIT
ENDIF
INT ind
VECTOR_2D vBasePos
// create leader
dir = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
REPEAT 2 i
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
vBasePos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
IF (i = 1)
vBasePos.y = cfBASE_SCREEN_HALF_HEIGHT - vBasePos.y
vBasePos.y += cfBASE_SCREEN_HALF_HEIGHT
ENDIF
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], entType, vBasePos, 0)
sGGSMData.sEntities[ind].vLocalDirection = dir
sGGSMData.sEntities[ind].fSpeed = spd
IF (i = 1)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eMoveFlags, GGSM_MOVE_BIT_FLIP_RELATIVE_PATH_Y)
ENDIF
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
ENDIF
ENDREPEAT
group.fDistCounter = 0
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_POWER_UP(GGSM_ENTITY_GROUP &group)
GGSM_SPRITE_TYPE eType = GGSM_FORMATION_GET_POWER_UP_SPRITE(group.sFormSettings)
IF (eType = GGSM_SPRITE_NONE)
group.iMembersToCreate = 0
EXIT
ENDIF
VECTOR_2D vPos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
IF NOT GGSM_CREATE_POWER_UP_AT(eType, vPos, sGGSMData.sPowerUp)
EXIT
ENDIF
GGSM_DEACTIVATE_ENTITY_GROUP(group)
group.iMembersToCreate = 0
ENDPROC
PROC GGSM_ENTITY_GROUP_UPDATE_FORMATION_BANANA_SNAKE(GGSM_ENTITY_GROUP &group)
INT iParent
INT iLeader, ind, count, i
VECTOR_2D vBasePos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
// create leader
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
EXIT
ENDIF
SET_BITMASK_ENUM_AS_ENUM(group.eFlags, GGSM_GROUP_BIT_END_WHEN_ALL_OFFSCREEN | GGSM_GROUP_BIT_DIE_WHEN_LEADER_DIES)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_BANANASLICE_FRONT, vBasePos)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_FORCE_SCROLLING_TO_STOP)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[ind], 0)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[ind], group.sFormSettings)
// create segements
iLeader = ind
iParent = iLeader
count = group.sFormSettings.iFormationMembers - 2
REPEAT count i
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_BANANASLICE, INIT_VECTOR_2D(30.0, 0.0), 0, DEFAULT, iParent)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_SNAKE_SECTION)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[ind], i + 1)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
iParent = ind
ENDIF
ENDREPEAT
// create tail
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_BANANASLICE_BACK, INIT_VECTOR_2D(30 + 10, 0.0), 0, DEFAULT, iParent)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_SNAKE_SECTION)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eMoveFlags, GGSM_MOVE_BIT_ROTATE_TO_FACE_DIRECTION)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[ind], group.sFormSettings.iFormationMembers)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
ENDIF
group.iMembersToCreate = 0
ENDPROC
FUNC BOOL GGSM_ENTITY_GROUP_UPDATE_FORMATION_ARMOURED_FRUITBOWL(GGSM_ENTITY_GROUP &group)
INT iLeaderIndex, ind, count, n
VECTOR_2D vBasePos = GGSM_FORMATION_GET_START_POSITION(group.sFormSettings)
GGSM_FORMATION_TYPE eFormType = GGSM_FORMATION_GET_TYPE(group.sFormSettings)
// create leader
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, iLeaderIndex, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
SET_BITMASK_ENUM_AS_ENUM(group.eFlags, GGSM_GROUP_BIT_ALLOW_OFFSCREEN)
SET_BITMASK_ENUM_AS_ENUM(group.eFlags, GGSM_GROUP_BIT_DIE_WHEN_LEADER_DIES)
GGSM_ENTITY_INIT(sGGSMData.sEntities[iLeaderIndex], GGSM_ENTITY_ARMOUR_FRUITBOWL, vBasePos)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeaderIndex].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeaderIndex].eFlags, GGSM_ENTITY_BIT_WPN_ACTIVE)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[iLeaderIndex].eFlags, GGSM_ENTITY_BIT_FORCE_SCROLLING_TO_STOP)
sGGSMData.sEntities[iLeaderIndex].vLocalDirection = GGSM_FORMATION_GET_START_DIRECTION(group.sFormSettings)
GGSM_ENTITY_SET_Z_DEPTH(sGGSMData.sEntities[iLeaderIndex], 0)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[iLeaderIndex], group.iGroupID)
GGSM_ENTITY_CONFIGURE_FROM_FORMATION(sGGSMData.sEntities[iLeaderIndex], group.sFormSettings)
sGGSMData.sEntities[iLeaderIndex].sLocal.vScale = INIT_VECTOR_2D(1.25, 1.25)
FLOAT ang
SWITCH (eFormType)
CASE GGSM_FORMATION_ARMOUR_FRUITBOWL
DEFAULT
count = group.sFormSettings.iFormationMembers - 1
IF (count <= 0)
RETURN TRUE
ENDIF
IF (sGGSMData.bHardMode)
count += 2
ENDIF
ang = 360.0 / TO_FLOAT(count)
REPEAT count n
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_CIRCLE_SHIP_GREEN, INIT_VECTOR_2D(0, 0), 0, FALSE, iLeaderIndex)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_ORBIT, 150.0, (n * ang), 1.0)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[ind], GGSM_WEAPON_ENEMYSHOT_RAPID)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
sGGSMData.sEntities[ind].sLocal.vScale = INIT_VECTOR_2D(0.75, 0.75)
sGGSMData.sEntities[ind].iHP = 8
ENDIF
ENDREPEAT
BREAK
CASE GGSM_FORMATION_ARMOUR_FRUITBOWL1
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[iLeaderIndex], GGSM_WEAPON_ENEMYSPREAD)
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_CIRCLE_SHIP_YELLOW, INIT_VECTOR_2D(150, -150), 0, FALSE, iLeaderIndex)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_NONE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[ind], GGSM_WEAPON_ENEMYSPREAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
sGGSMData.sEntities[ind].sLocal.vScale = INIT_VECTOR_2D(0.75, 0.75)
sGGSMData.sEntities[ind].iHP = 8
ENDIF
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_CIRCLE_SHIP_YELLOW, INIT_VECTOR_2D(150, 150), 0, FALSE, iLeaderIndex)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_NONE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[ind], GGSM_WEAPON_ENEMYSPREAD)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
sGGSMData.sEntities[ind].sLocal.vScale = INIT_VECTOR_2D(0.75, 0.75)
sGGSMData.sEntities[ind].iHP = 8
ENDIF
BREAK
CASE GGSM_FORMATION_ARMOUR_FRUITBOWL2
count = group.sFormSettings.iFormationMembers - 1
IF (count <= 0)
RETURN TRUE
ENDIF
REPEAT count n
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_CIRCLE_SHIP_GREEN, INIT_VECTOR_2D(0, 0), 0, FALSE, iLeaderIndex)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_ORBIT, 150.0, (n * ang), 0.5)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[ind], GGSM_WEAPON_ENEMYSHOT)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
sGGSMData.sEntities[ind].sLocal.vScale = INIT_VECTOR_2D(0.75, 0.75)
sGGSMData.sEntities[ind].iHP = 4
ENDIF
ENDREPEAT
n = 0
REPEAT count n
IF GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_CIRCLE_SHIP_GREEN, INIT_VECTOR_2D(0, 0), 0, FALSE, iLeaderIndex)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_ORBIT, 300.0, (n * ang), -0.5)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[ind], GGSM_WEAPON_ENEMYVULCAN)
CLEAR_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_WPN_TARGETTED)
GGSM_ADD_ENTITY_TO_GROUP(sGGSMData.sEntities[ind], group.iGroupID)
sGGSMData.sEntities[ind].sLocal.vScale = INIT_VECTOR_2D(0.75, 0.75)
sGGSMData.sEntities[ind].iHP = 4
ENDIF
ENDREPEAT
BREAK
ENDSWITCH
RETURN TRUE
ENDFUNC
PROC GGSM_ENTITY_GROUP_UPDATE(GGSM_ENTITY_GROUP &group)
IF (group.iMembersToCreate <= 0)
EXIT
ENDIF
INT iBossController
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(group.eFlags, GGSM_GROUP_BIT_ACTIVE)
EXIT
ENDIF
sGGSMData.iGroupsActive ++
IF NOT IS_BITMASK_ENUM_AS_ENUM_SET(group.eFlags, GGSM_GROUP_BIT_IS_FORMATION)
EXIT
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(group.eFlags, GGSM_GROUP_BIT_IS_PLAYER_GROUP)
EXIT
ENDIF
GGSM_FORMATION_TYPE eType = GGSM_FORMATION_GET_TYPE(group.sFormSettings)
SWITCH (eType)
CASE GGSM_FORMATION_STANDARD
GGSM_ENTITY_GROUP_UPDATE_FORMATION_STANDARD(group)
BREAK
CASE GGSM_FORMATION_FLYING_V
GGSM_ENTITY_GROUP_UPDATE_FORMATION_FLYING_V(group)
BREAK
CASE GGSM_FORMATION_LINE
GGSM_ENTITY_GROUP_UPDATE_FORMATION_LINE(group)
BREAK
CASE GGSM_FORMATION_MIRROR_X
GGSM_ENTITY_GROUP_UPDATE_FORMATION_MIRROR_X(group)
BREAK
CASE GGSM_FORMATION_MIRROR_Y
GGSM_ENTITY_GROUP_UPDATE_FORMATION_MIRROR_Y(group)
BREAK
CASE GGSM_FORMATION_POWER_UP
GGSM_ENTITY_GROUP_UPDATE_FORMATION_POWER_UP(group)
BREAK
CASE GGSM_FORMATION_HORZLINE
GGSM_ENTITY_GROUP_UPDATE_FORMATION_HORZLINE(group)
BREAK
CASE GGSM_FORMATION_VERTLINE
GGSM_ENTITY_GROUP_UPDATE_FORMATION_VERTLINE(group)
BREAK
CASE GGSM_FORMATION_DELAYED_HLINE
GGSM_ENTITY_GROUP_UPDATE_FORMATION_DELAYED_LINE(group, INIT_VECTOR_2D(1, 0))
BREAK
CASE GGSM_FORMATION_DELAYED_VLINE
GGSM_ENTITY_GROUP_UPDATE_FORMATION_DELAYED_LINE(group, INIT_VECTOR_2D(0, 1))
BREAK
CASE GGSM_FORMATION_BANANASNAKE
GGSM_ENTITY_GROUP_UPDATE_FORMATION_BANANA_SNAKE(group)
BREAK
CASE GGSM_FORMATION_ARMOUR_FRUITBOWL
CASE GGSM_FORMATION_ARMOUR_FRUITBOWL1
CASE GGSM_FORMATION_ARMOUR_FRUITBOWL2
CASE GGSM_FORMATION_ARMOUR_FRUITBOWL3
CASE GGSM_FORMATION_ARMOUR_FRUITBOWL4
GGSM_ENTITY_GROUP_UPDATE_FORMATION_ARMOURED_FRUITBOWL(group)
BREAK
CASE GGSM_FORMATION_BOSS_MARINE
iBossController = GGSM_GET_FREE_BOSS_CONTROLLER_INDEX(sGGSMData.sBossController)
IF (iBossController = GGSM_INVALID_BOSS_CONTROLLER)
EXIT
ENDIF
GGSM_RESET_ENTITY_GROUP(group)
GGSM_RESET_BOSS_CONTROLLER(sGGSMData.sBossController[iBossController], GGSM_BOSS_MARINE)
BREAK
CASE GGSM_FORMATION_BOSS_BREAD
iBossController = GGSM_GET_FREE_BOSS_CONTROLLER_INDEX(sGGSMData.sBossController)
IF (iBossController = GGSM_INVALID_BOSS_CONTROLLER)
EXIT
ENDIF
GGSM_RESET_ENTITY_GROUP(group)
GGSM_RESET_BOSS_CONTROLLER(sGGSMData.sBossController[iBossController], GGSM_BOSS_BREAD)
BREAK
CASE GGSM_FORMATION_BOSS_SMOOTHIE
iBossController = GGSM_GET_FREE_BOSS_CONTROLLER_INDEX(sGGSMData.sBossController)
IF (iBossController = GGSM_INVALID_BOSS_CONTROLLER)
EXIT
ENDIF
GGSM_RESET_ENTITY_GROUP(group)
GGSM_RESET_BOSS_CONTROLLER(sGGSMData.sBossController[iBossController], GGSM_BOSS_SMOOTHIE)
BREAK
CASE GGSM_FORMATION_BOSS_DR_DANK
iBossController = GGSM_GET_FREE_BOSS_CONTROLLER_INDEX(sGGSMData.sBossController)
IF (iBossController = GGSM_INVALID_BOSS_CONTROLLER)
EXIT
ENDIF
GGSM_RESET_ENTITY_GROUP(group)
GGSM_RESET_BOSS_CONTROLLER(sGGSMData.sBossController[iBossController], GGSM_BOSS_DR_DANK)
BREAK
CASE GGSM_FORMATION_BOSS_GRANANA
iBossController = GGSM_GET_FREE_BOSS_CONTROLLER_INDEX(sGGSMData.sBossController)
IF (iBossController = GGSM_INVALID_BOSS_CONTROLLER)
EXIT
ENDIF
GGSM_RESET_ENTITY_GROUP(group)
GGSM_RESET_BOSS_CONTROLLER(sGGSMData.sBossController[iBossController], GGSM_BOSS_GRANANA)
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------
// SPECIAL INIT FUNCTIONS
//-------------------------------------------------
FUNC BOOL GGSM_PLAYER_SHIP_INIT(VECTOR_2D vLocalPos)
INT ind
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_PLAYERSHIP, vLocalPos, DEFAULT, TRUE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_PLAYER_CONTROLLED)
//GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[ind], GGSM_WEAPON_PLYRBACKVULCAN)
sGGSMData.iPlayerShipIndex = ind
sGGSMData.fKillStreakTimer = 0.0
sGGSMData.iKillStreak = 0
sGGSMData.bGrazed = FALSE
RETURN TRUE
ENDFUNC
FUNC BOOL GGSM_PLAYER_DECOY_INIT(VECTOR_2D vLocalPos)
INT ind
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_PLAYERDECOY, vLocalPos, DEFAULT, TRUE)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[ind], GGSM_WEAPON_NONE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_NONE)
sGGSMData.sEntities[ind].iPackedColor = GGSM_PACK_RGBA_COLOUR(0, 255, 0, 128)
sGGSMData.iPlayerDecoyIndex = ind
RETURN TRUE
ENDFUNC
FUNC BOOL GGSM_PLAYER_SHIELD_INIT(BOOL bReflectShield = FALSE)
INT ind
IF GGSM_DOES_PLAYER_SHIELD_EXIST()
GGSM_ENTITY_DESTROY(sGGSMData.sEntities[sGGSMData.iPlayerShieldIndex])
ENDIF
IF NOT GGSM_GET_FREE_ENTITY(sGGSMData.sEntities, ind, sGGSMData.iEntitySearchIndex)
RETURN FALSE
ENDIF
GGSM_ENTITY_INIT(sGGSMData.sEntities[ind], GGSM_ENTITY_PLAYERSHIELD, INIT_VECTOR_2D(0, 0), DEFAULT, TRUE, sGGSMData.iPlayerShipIndex)
GGSM_ENTITY_SET_WEAPON(sGGSMData.sEntities[ind], GGSM_WEAPON_NONE)
GGSM_ENTITY_SET_MOVEMENT_TYPE(sGGSMData.sEntities[ind], GGSM_MOVEMENT_NONE)
IF (bReflectShield)
SET_BITMASK_ENUM_AS_ENUM(sGGSMData.sEntities[ind].eFlags, GGSM_ENTITY_BIT_REFLECT_BULLETS)
sGGSMData.sEntities[ind].iPackedColor = GGSM_PACK_RGBA_COLOUR(0, 255, 0, 255)
ELSE
sGGSMData.sEntities[ind].iPackedColor = GGSM_PACK_RGBA_COLOUR(0, 0, 255, 255)
ENDIF
sGGSMData.iPlayerShieldIndex = ind
RETURN TRUE
ENDFUNC
FUNC FLOAT GGSM_GET_PLAYER_SHIP_HP_PERCENTAGE()
IF NOT GGSM_DOES_PLAYER_SHIP_EXIST()
RETURN 0.0
ENDIF
GGSM_ENTITY_DATA dat = GGSM_ENTITY_DATA_GET(sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].eType)
RETURN TO_FLOAT(sGGSMData.sEntities[sGGSMData.iPlayerShipIndex].iHP) / TO_FLOAT(dat.iMaxHP)
ENDFUNC