Skip to content

Commit

Permalink
UndeadTargetingComponent tries to reissue attack orders against speci…
Browse files Browse the repository at this point in the history
…fic units

Fixed a bug where spawn waves would trigger for the first player once before everyone else
  • Loading branch information
jlfarris91 committed Mar 29, 2021
1 parent f74ba86 commit f77667d
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 161 deletions.
2 changes: 1 addition & 1 deletion _build/dependencies/WurstCore
142 changes: 58 additions & 84 deletions wurst/Composition/UndeadTargetingComponent.wurst
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,23 @@ import Unit_WoodFence
import Unit_StoneWall
import Unit_GateCommon
import Unit_MetalWall
import GameConstants
import Orders
import TargetUtility
import UnitExtensions
import ClosureTimers
import TimerUtils
import RealTime
import Dispatcher

constant real ATTACK_TIMER_INTERVAL = 1.0
constant real ATTACK_TIMER_DURATION = 10.0

HashMap<player, LinkedList<unit>> g_validHumanStructureTargets
HashList<int> g_validTargetStructureIds
conditionfunc g_filterUnitIsValidTargetStructure = Condition(function filterUnitIsValidTargetStructure)
conditionfunc g_filterUnitIsValidTargetNonStructure = Condition(function filterUnitIsValidTargetNonStructure)
HashMap<unit, HashList<UndeadTargetingComponent>> g_targetedUnitToUTCMap = new HashMap<unit, HashList<UndeadTargetingComponent>>()
HashList<UndeadTargetingComponent> g_activeUndeadTargetingComponents = new HashList<UndeadTargetingComponent>()
Dispatcher g_issueOrderDispatcher

/*

Expand Down Expand Up @@ -59,21 +63,15 @@ public class UndeadTargetingComponent extends UnitComponent
private player m_targetPlayer
private Event m_targetPlayerChangedEvent
private unit m_targetUnit
private CallbackManual m_attackTimer

// Not owned!
private HashList<int> m_ignoreOrderIdsList
private int m_attackTime
private bool m_awaitingOrder

// --------------------------------------------------------------------------
construct(IUnitMetadata owner)
super(owner)
m_targetPlayer = null
m_targetUnit = null

m_attackTimer = getTimer().doManual(ATTACK_TIMER_DURATION, true) () ->
m_targetUnit = null
issueOrderTargetingPlayer()

// --------------------------------------------------------------------------
ondestroy
if (m_targetPlayerChangedEvent != null)
Expand All @@ -84,6 +82,16 @@ public class UndeadTargetingComponent extends UnitComponent
override function getTypeId() returns int
return UndeadTargetingComponent.typeId

// --------------------------------------------------------------------------
override function onEnabled()
super.onEnabled()
g_activeUndeadTargetingComponents.add(this)

// --------------------------------------------------------------------------
override function onDisabled()
super.onDisabled()
g_activeUndeadTargetingComponents.remove(this)

// --------------------------------------------------------------------------
function getTargetPlayerChangedEvent() returns Event
if (m_targetPlayerChangedEvent == null)
Expand All @@ -109,15 +117,29 @@ public class UndeadTargetingComponent extends UnitComponent
return true

// --------------------------------------------------------------------------
function getIgnoreOrderIdsList() returns HashList<int>
return m_ignoreOrderIdsList
function issueOrderTargetingPlayer()

// --------------------------------------------------------------------------
function setIgnoreOrderIdsList(HashList<int> orderIds)
m_ignoreOrderIdsList = orderIds
if (not getOwnerUnit().isAlive())
return

// We're already awaiting a call to issueOrderTargetingPlayer_impl so early out
if (m_awaitingOrder)
return

m_awaitingOrder = true

// Dispatch this operation to limit how often this gets executed because
// finding the nearest unit can be expensive
g_issueOrderDispatcher.invoke(() -> issueOrderTargetingPlayer_impl(), DispatchPriority.LOW)

// --------------------------------------------------------------------------
function issueOrderTargetingPlayer()
private function issueOrderTargetingPlayer_impl()

m_awaitingOrder = false
restartAttackTimer()

if (not getOwnerUnit().isAlive())
return

if (tryReissueOrder())
return
Expand All @@ -129,7 +151,7 @@ public class UndeadTargetingComponent extends UnitComponent
return

// At this point the target unit must not have been valid
m_targetUnit = null
setTargetUnit(null)

// 3. Try attack-moving to the target player's camp center (the OT or the hero)
if (tryTargetCampCenter())
Expand Down Expand Up @@ -219,10 +241,6 @@ public class UndeadTargetingComponent extends UnitComponent
undeadUnit.issuePointOrder("attack", campCenter)
return true

// --------------------------------------------------------------------------
protected function increaseAcquisitionRange()
getOwnerUnit().setField(UNIT_RF_ACQUISITION_RANGE, 1024)

// --------------------------------------------------------------------------
protected function onTargetUnitDied()
setTargetUnit(null)
Expand All @@ -238,9 +256,18 @@ public class UndeadTargetingComponent extends UnitComponent
m_targetPlayerChangedEvent.call()

// --------------------------------------------------------------------------
private function restartAttackTimer()
if (m_attackTimer != null)
m_attackTimer.restart()
protected function onAttackTimer()
if (m_awaitingOrder)
return
// If the timer has expired then this unit hasn't been able to attack
// it's target in a while so try to target a new enemy
if (getRealTimeSeconds() - m_attackTime > ATTACK_TIMER_DURATION)
setTargetUnit(null)
issueOrderTargetingPlayer()

// --------------------------------------------------------------------------
protected function restartAttackTimer()
m_attackTime = getRealTimeSeconds()

// ============================================================================
public function IUnitMetadata.getUndeadTargetingComponent() returns UndeadTargetingComponent
Expand Down Expand Up @@ -347,60 +374,6 @@ function populateInitialValidHumanStructureTargets()
cond.destr()
tempGroup.release()

// ============================================================================
function onUnitIssuedTargetOrder()
let orderedUnit = GetOrderedUnit()
let orderId = GetIssuedOrderId()

if (orderedUnit.getOwner() != PLAYER_UNDEAD or orderId == OrderIds.attack)
return

let comp = orderedUnit.getMetadata().getUndeadTargetingComponent()
if (comp == null)
return

let ignoreList = comp.getIgnoreOrderIdsList()
if (ignoreList != null and ignoreList.has(orderId))
return

comp.issueOrderTargetingPlayer()

// ============================================================================
function onUnitIssuedOrder()
let orderedUnit = GetOrderedUnit()
let orderId = GetIssuedOrderId()

if (orderedUnit.getOwner() != PLAYER_UNDEAD or orderId == OrderIds.attack)
return

let comp = orderedUnit.getMetadata().getUndeadTargetingComponent()
if (comp == null)
return

let ignoreList = comp.getIgnoreOrderIdsList()
if (ignoreList != null and ignoreList.has(orderId))
return

comp.issueOrderTargetingPlayer()

// ============================================================================
function onUnitIssuedPointOrder()
let orderedUnit = GetOrderedUnit()
let orderId = GetIssuedOrderId()

if (orderedUnit.getOwner() != PLAYER_UNDEAD or orderId == OrderIds.attack)
return

let comp = orderedUnit.getMetadata().getUndeadTargetingComponent()
if (comp == null)
return

let ignoreList = comp.getIgnoreOrderIdsList()
if (ignoreList != null and ignoreList.has(orderId))
return

comp.issueOrderTargetingPlayer()

// ============================================================================
function registerTargetedUnit(unit targetedUnit, UndeadTargetingComponent comp)
HashList<UndeadTargetingComponent> attackerList
Expand Down Expand Up @@ -432,6 +405,8 @@ function unregisterTargetedUnit(unit targetedUnit)
init
g_validHumanStructureTargets = new HashMap<player, LinkedList<unit>>()

g_issueOrderDispatcher = new Dispatcher(1024, 1024, 1024)..setMaxExecutions(50)

for p in g_PlayingHumanPlayers
g_validHumanStructureTargets.put(p, new LinkedList<unit>())

Expand All @@ -453,10 +428,9 @@ init
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function onUnitDeath)
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function onUnitAttacked)

// If an undead unit is issued ANY order other than attack the given target unit then reissue the order to attack the target unit
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function onUnitIssuedPointOrder)
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function onUnitIssuedTargetOrder)
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function onUnitIssuedOrder)

// Populate with initial structures (for debug purposes)
populateInitialValidHumanStructureTargets()
populateInitialValidHumanStructureTargets()

doPeriodically(ATTACK_TIMER_INTERVAL) (CallbackPeriodic cb) ->
for comp in g_activeUndeadTargetingComponents
g_issueOrderDispatcher.invoke(() -> comp.onAttackTimer(), DispatchPriority.HIGH)
42 changes: 25 additions & 17 deletions wurst/Game/Waves/Generation/NormalNightWaveGenerator.wurst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ constant time NIGHT_WAVE_END_TIME = NIGHT_WAVE_START_TIME + WAVE_NIGHT_LENGTH
constant int MAX_SPAWN_WAVES = 2

SpawnWaveDefinition array[TOTAL_NIGHTS * MAX_SPAWN_WAVES] g_spawnWaveDefinitions
ItemSet array[TOTAL_NIGHTS] g_coinItemSets

// ============================================================================
function setSpawnWaveDef(int night, int spawnWaveIndex, SpawnWaveDefinition def)
Expand Down Expand Up @@ -133,6 +134,22 @@ function defineSpawnWaves()
// 14 Wraiths (Invade, Ethereal, Melee)
setSpawnWaveDef(gameProgress.nextWave(), 1, createSpawnWave_Wraiths(gameProgress))

// Generate coin item sets for each wave
gameProgress.setCurrentWaveNumber(1)
for i = 0 to TOTAL_NIGHTS - 1
gameProgress.nextWave()
let gameT = gameProgress.getCurrentGameProgress()
let coinItemSet = new ItemSet("Coins", COLOR_GOLD.withoutAlpha())
let weightSmall = g_coinSmallWeightTFunc.call(gameT)
let weightMedium = g_coinMediumWeightTFunc.call(gameT)
let weightLarge = g_coinLargeWeightTFunc.call(gameT)
let weightEpic = g_coinEpicWeightTFunc.call(gameT)
coinItemSet.add(TlsItemIds.coinSmall, weightSmall)
coinItemSet.add(TlsItemIds.coinMedium, weightMedium)
coinItemSet.add(TlsItemIds.coinLarge, weightLarge)
coinItemSet.add(TlsItemIds.coinEpic, weightEpic)
g_coinItemSets[i] = coinItemSet

// ============================================================================
public class NormalNightWaveGenerator implements IWaveGenerator<NightWave>

Expand Down Expand Up @@ -721,20 +738,8 @@ function getProgressForSpawnWave(IGameWaveProgress gameProgress) returns IProgre
..setDuration(WAVE_NIGHT_LENGTH)

// ============================================================================
function getCoinItemSetForWave(real gameT) returns ItemSet
let itemSet = new ItemSet("Coins", COLOR_GOLD.withoutAlpha())

let weightSmall = g_coinSmallWeightTFunc.call(gameT)
let weightMedium = g_coinMediumWeightTFunc.call(gameT)
let weightLarge = g_coinLargeWeightTFunc.call(gameT)
let weightEpic = g_coinEpicWeightTFunc.call(gameT)

itemSet.add(TlsItemIds.coinSmall, weightSmall)
itemSet.add(TlsItemIds.coinMedium, weightMedium)
itemSet.add(TlsItemIds.coinLarge, weightLarge)
itemSet.add(TlsItemIds.coinEpic, weightEpic)

return itemSet
function getCoinItemSetForWave(int waveIndex) returns ItemSet
return g_coinItemSets[waveIndex]

// ============================================================================
function unit.standardEliteMaxHp(real gameT)
Expand Down Expand Up @@ -813,13 +818,16 @@ function unit.standardGrantCoinsOnDeath(real gameT)

// ============================================================================
function unit.standardGrantCoinsOnDeath(real gameT, real chanceScale)
let waveNumber = (TOTAL_NIGHTS * gameT).floor()
let coinItemSet = getCoinItemSetForWave(waveNumber)
let chance = lerp(COIN_CHANCE_RANGE.min, COIN_CHANCE_RANGE.max, gameT) * chanceScale
let itemSet = getCoinItemSetForWave(gameT)
this.awardRandomItemOnDeath(itemSet, chance)
this.awardRandomItemOnDeath(coinItemSet, chance)

// ============================================================================
function unit.guaranteeGrantCoinsOnDeath(real gameT)
this.awardRandomItemOnDeath(getCoinItemSetForWave(gameT), 1.0)
let waveNumber = (TOTAL_NIGHTS * gameT).floor()
let coinItemSet = getCoinItemSetForWave(waveNumber)
this.awardRandomItemOnDeath(coinItemSet, 1.0)

// ============================================================================
init
Expand Down
Loading

0 comments on commit f77667d

Please sign in to comment.