Skip to content

Commit ce9deb6

Browse files
committed
Added Channel spells, lightning hooks, and sound
* AB: Implemented Channeling on spells * AB: Implemented Lightning effects * AB: Added sound effects to buffs * AB: Implemented Aerial Shackles as an example spell * AB: Fixed spells not checking target viability before beginning spell actions * AB: Fixed major issues with when onBeginCasting and onEndCasting were called * AB: Fixed cooldown handling so the visual displays properly * AB: Fix the base spell to properly interrupt spell casts * AB: Fix toggle spells to start cooldown on activation, and avoid error message on deactivation * Core: Implemented Stun behavior, as pausing units stops things like HP regen * Core: Implemented SimulationRenderComponents for ability sounds, to allow ending sounds selectively * Core: Updated unit-targeted lightning methods with optional duration * Core: Fixed issue where some looping sounds didn't loop * Core: Fixed crash when unit has projectile attack with no art
1 parent a7df261 commit ce9deb6

49 files changed

Lines changed: 1545 additions & 612 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/assets/abilityBehaviors/humanHeroActives.json

Lines changed: 527 additions & 0 deletions
Large diffs are not rendered by default.

core/assets/abilityBehaviors/humanUnitActives.json

Lines changed: 213 additions & 480 deletions
Large diffs are not rendered by default.

core/src/com/etheller/warsmash/viewer5/AudioBufferSource.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ public void connect(final AudioPanner panner) {
1111
this.panner = panner;
1212
}
1313

14-
public void start(final int value, final float volume, final float pitch, final boolean looping) {
14+
public long start(final int value, final float volume, final float pitch, final boolean looping) {
1515
if (this.buffer != null) {
1616
if (!this.panner.listener.is3DSupported() || this.panner.isWithinListenerDistance()) {
17-
Extensions.audio.play(this.buffer, volume, pitch, this.panner.x, this.panner.y, this.panner.z,
17+
return Extensions.audio.play(this.buffer, volume, pitch, this.panner.x, this.panner.y, this.panner.z,
1818
this.panner.listener.is3DSupported(), this.panner.maxDistance, this.panner.refDistance,
1919
looping);
2020
}
2121
}
22+
return -1;
2223
}
2324
}

core/src/com/etheller/warsmash/viewer5/gl/AudioExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ public interface AudioExtension {
88

99
float getDuration(Sound sound);
1010

11-
void play(Sound buffer, final float volume, final float pitch, final float x, final float y, final float z,
11+
long play(Sound buffer, final float volume, final float pitch, final float x, final float y, final float z,
1212
final boolean is3DSound, float maxDistance, float refDistance, boolean looping);
1313
}

core/src/com/etheller/warsmash/viewer5/handlers/w3x/UnitSound.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,25 +106,33 @@ public boolean playUnitResponse(final AudioContext audioContext, final RenderUni
106106
if (millisTime < unit.lastUnitResponseEndTimeMillis) {
107107
return false;
108108
}
109-
if (play(audioContext, unit.location[0], unit.location[1], unit.location[2], index)) {
109+
if (play(audioContext, unit.location[0], unit.location[1], unit.location[2], index) != -1) {
110110
final float duration = Extensions.audio.getDuration(this.lastPlayedSound);
111111
unit.lastUnitResponseEndTimeMillis = millisTime + (long) (1000 * duration);
112112
return true;
113113
}
114114
return false;
115115
}
116116

117-
public boolean play(final AudioContext audioContext, final float x, final float y, final float z) {
117+
public long play(final AudioContext audioContext, final float x, final float y, final float z, final boolean loopOverride) {
118+
return play(audioContext, x, y, z, (int) (Math.random() * this.sounds.size()), loopOverride);
119+
}
120+
121+
public long play(final AudioContext audioContext, final float x, final float y, final float z) {
118122
return play(audioContext, x, y, z, (int) (Math.random() * this.sounds.size()));
119123
}
120124

121-
public boolean play(final AudioContext audioContext, final float x, final float y, final float z, final int index) {
125+
public long play(final AudioContext audioContext, final float x, final float y, final float z, final int index) {
126+
return play(audioContext, x, y, z, index, null);
127+
}
128+
129+
public long play(final AudioContext audioContext, final float x, final float y, final float z, final int index, final Boolean loopOverride) {
122130
if (this.sounds.isEmpty()) {
123-
return false;
131+
return -1;
124132
}
125133

126134
if (audioContext == null) {
127-
return true;
135+
return -1;
128136
}
129137
final AudioPanner panner = audioContext.createPanner();
130138
final AudioBufferSource source = audioContext.createBufferSource();
@@ -139,10 +147,16 @@ public boolean play(final AudioContext audioContext, final float x, final float
139147
source.connect(panner);
140148

141149
// Make a sound.
142-
source.start(0, this.volume,
143-
(this.pitch + ((float) Math.random() * this.pitchVariance * 2)) - this.pitchVariance, this.looping);
150+
long soundId = -1;
151+
if (loopOverride == null) {
152+
soundId = source.start(0, this.volume,
153+
(this.pitch + ((float) Math.random() * this.pitchVariance * 2)) - this.pitchVariance, this.looping);
154+
} else {
155+
soundId = source.start(0, this.volume,
156+
(this.pitch + ((float) Math.random() * this.pitchVariance * 2)) - this.pitchVariance, loopOverride);
157+
}
144158
this.lastPlayedSound = source.buffer;
145-
return true;
159+
return soundId;
146160
}
147161

148162
public int getSoundCount() {
@@ -154,4 +168,12 @@ public void stop() {
154168
sound.stop();
155169
}
156170
}
171+
172+
public void stop(long soundId) {
173+
// This may misbehave if called for a list longer than 1, due to the random index used when starting?
174+
// Not sure if IDs are unique per source
175+
for (final Sound sound : this.sounds) {
176+
sound.stop(soundId);
177+
}
178+
}
157179
}

core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -630,20 +630,22 @@ public CAttackProjectile createAttackProjectile(final CSimulation simulation, fi
630630
projectileSpeed, target, source, damage, unitAttack, bounceIndex, attackListener);
631631

632632
final MdxModel model = loadModelMdx(missileArt);
633-
final MdxComplexInstance modelInstance = (MdxComplexInstance) model.addInstance();
634-
modelInstance.setTeamColor(getRenderPeer(source).playerIndex);
635-
modelInstance.setScene(War3MapViewer.this.worldScene);
636-
if (bounceIndex == 0) {
637-
SequenceUtils.randomBirthSequence(modelInstance);
638-
}
639-
else {
640-
SequenceUtils.randomStandSequence(modelInstance);
633+
if (model != null) {
634+
final MdxComplexInstance modelInstance = (MdxComplexInstance) model.addInstance();
635+
modelInstance.setTeamColor(getRenderPeer(source).playerIndex);
636+
modelInstance.setScene(War3MapViewer.this.worldScene);
637+
if (bounceIndex == 0) {
638+
SequenceUtils.randomBirthSequence(modelInstance);
639+
}
640+
else {
641+
SequenceUtils.randomStandSequence(modelInstance);
642+
}
643+
modelInstance.setLocation(x, y, height);
644+
final RenderProjectile renderAttackProjectile = new RenderProjectile(simulationAttackProjectile,
645+
modelInstance, height, projectileArc, War3MapViewer.this);
646+
647+
War3MapViewer.this.projectiles.add(renderAttackProjectile);
641648
}
642-
modelInstance.setLocation(x, y, height);
643-
final RenderProjectile renderAttackProjectile = new RenderProjectile(simulationAttackProjectile,
644-
modelInstance, height, projectileArc, War3MapViewer.this);
645-
646-
War3MapViewer.this.projectiles.add(renderAttackProjectile);
647649

648650
return simulationAttackProjectile;
649651
}
@@ -734,6 +736,13 @@ public SimulationRenderComponentLightning createLightning(CSimulation simulation
734736
return War3MapViewer.this.createLightning(lightningId, renderPeerSource, renderPeerTarget);
735737
}
736738

739+
@Override
740+
public SimulationRenderComponentLightning createLightning(CSimulation simulation, War3ID lightningId, CUnit source, CUnit target, Float duration) {
741+
final RenderUnit renderPeerSource = War3MapViewer.this.getRenderPeer(source);
742+
final RenderWidget renderPeerTarget = War3MapViewer.this.getRenderPeer(target);
743+
return War3MapViewer.this.createLightning(lightningId, renderPeerSource, renderPeerTarget, duration);
744+
}
745+
737746
@Override
738747
public void spawnDamageSound(final CWidget damagedDestructable, final String weaponSound,
739748
final String armorType) {
@@ -1300,25 +1309,45 @@ public void spawnUIUnitDropItemSound(final CUnit cUnit, final CItem item) {
13001309
}
13011310

13021311
@Override
1303-
public void spawnAbilitySoundEffect(final CUnit caster, final War3ID alias) {
1312+
public SimulationRenderComponent spawnAbilitySoundEffect(final CUnit caster, final War3ID alias) {
13041313
final RenderUnit renderPeer = War3MapViewer.this.unitToRenderPeer.get(caster);
13051314
final AbilityUI abilityUi = War3MapViewer.this.abilityDataUI.getUI(alias);
1306-
if (abilityUi.getEffectSound() != null) {
1307-
War3MapViewer.this.uiSounds.getSound(abilityUi.getEffectSound()).play(
1315+
if (abilityUi.getEffectSound() == null) {
1316+
return SimulationRenderComponent.DO_NOTHING;
1317+
}
1318+
final long soundId = War3MapViewer.this.uiSounds.getSound(abilityUi.getEffectSound()).play(
13081319
War3MapViewer.this.worldScene.audioContext, renderPeer.getX(), renderPeer.getY(),
13091320
renderPeer.getZ());
1321+
if (soundId == -1) {
1322+
return SimulationRenderComponent.DO_NOTHING;
13101323
}
1324+
return new SimulationRenderComponent() {
1325+
@Override
1326+
public void remove() {
1327+
War3MapViewer.this.uiSounds.getSound(abilityUi.getEffectSoundLooped()).stop(soundId);;
1328+
}
1329+
};
13111330
}
13121331

13131332
@Override
1314-
public void loopAbilitySoundEffect(final CUnit caster, final War3ID alias) {
1333+
public SimulationRenderComponent loopAbilitySoundEffect(final CUnit caster, final War3ID alias) {
13151334
final RenderUnit renderPeer = War3MapViewer.this.unitToRenderPeer.get(caster);
13161335
final AbilityUI abilityUi = War3MapViewer.this.abilityDataUI.getUI(alias);
1317-
if (abilityUi.getEffectSoundLooped() != null) {
1318-
War3MapViewer.this.uiSounds.getSound(abilityUi.getEffectSoundLooped()).play(
1336+
if (abilityUi.getEffectSound() == null) {
1337+
return SimulationRenderComponent.DO_NOTHING;
1338+
}
1339+
final long soundId = War3MapViewer.this.uiSounds.getSound(abilityUi.getEffectSoundLooped()).play(
13191340
War3MapViewer.this.worldScene.audioContext, renderPeer.getX(), renderPeer.getY(),
1320-
renderPeer.getZ());
1341+
renderPeer.getZ(), true);
1342+
if (soundId == -1) {
1343+
return SimulationRenderComponent.DO_NOTHING;
13211344
}
1345+
return new SimulationRenderComponent() {
1346+
@Override
1347+
public void remove() {
1348+
War3MapViewer.this.uiSounds.getSound(abilityUi.getEffectSoundLooped()).stop(soundId);;
1349+
}
1350+
};
13221351
}
13231352

13241353
@Override
@@ -1360,6 +1389,10 @@ public void setBlight(final float x, final float y, final float radius, final bo
13601389
}
13611390

13621391
public SimulationRenderComponentLightning createLightning(War3ID lightningId, RenderUnit renderPeerSource, RenderWidget renderPeerTarget) {
1392+
return this.createLightning(lightningId, renderPeerSource, renderPeerTarget, null);
1393+
}
1394+
1395+
public SimulationRenderComponentLightning createLightning(War3ID lightningId, RenderUnit renderPeerSource, RenderWidget renderPeerTarget, Float duration) {
13631396
LightningEffectModel lightningEffectModel = lightningTypeToModel.get(lightningId);
13641397
if(lightningEffectModel != null) {
13651398
LightningEffectNode source = (LightningEffectNode)lightningEffectModel.addInstance();
@@ -1369,8 +1402,15 @@ public SimulationRenderComponentLightning createLightning(War3ID lightningId, Re
13691402
// on the other arbitrary (non-unit-bound) createLightning func.
13701403
// Later on we might want a user API for creating unit-attached lightnings that have a duration specified
13711404
// by user code
1372-
source.setLifeSpanRemaining(lightningEffectModel.getDuration());
1373-
target.setLifeSpanRemaining(lightningEffectModel.getDuration());
1405+
if (duration != null) {
1406+
if (duration >= 0) {
1407+
source.setLifeSpanRemaining(duration);
1408+
target.setLifeSpanRemaining(duration);
1409+
}
1410+
} else {
1411+
source.setLifeSpanRemaining(lightningEffectModel.getDuration());
1412+
target.setLifeSpanRemaining(lightningEffectModel.getDuration());
1413+
}
13741414
source.setFriend(target);
13751415
target.setFriend(source);
13761416
source.setSource(true);
@@ -2926,7 +2966,9 @@ public List<RenderSpellEffect> spawnSpellEffectOnUnitEx(final CUnit unit, final
29262966
final String modelPath = effect.getModelPath();
29272967
final List<String> attachmentPoint = effect.getAttachmentPoint();
29282968
final RenderSpellEffect specialEffect = addSpecialEffectTarget(modelPath, unit, attachmentPoint);
2929-
renderEffects.add(specialEffect);
2969+
if (specialEffect != null) {
2970+
renderEffects.add(specialEffect);
2971+
}
29302972
}
29312973
return renderEffects;
29322974
}

core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.JassGameEventsWar3;
6262
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.enumtypes.CEffectType;
6363
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType;
64+
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderComponent;
6465
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderComponentLightning;
6566
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderComponentModel;
6667
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
@@ -363,6 +364,11 @@ public SimulationRenderComponentLightning createLightning(final CUnit source, fi
363364
return this.simulationRenderController.createLightning(this, lightningId, source, target);
364365
}
365366

367+
public SimulationRenderComponentLightning createLightning(final CUnit source, final War3ID lightningId,
368+
final CUnit target, final Float duration) {
369+
return this.simulationRenderController.createLightning(this, lightningId, source, target, duration);
370+
}
371+
366372
public void createInstantAttackEffect(final CUnit source, final CUnitAttackInstant attack, final CWidget target) {
367373
this.simulationRenderController.createInstantAttackEffect(this, source, attack, target);
368374
}
@@ -664,12 +670,12 @@ public SimulationRenderComponentModel createPersistentSpellEffectOnUnit(final CU
664670
return this.simulationRenderController.spawnPersistentSpellEffectOnUnit(unit, alias, effectType, index);
665671
}
666672

667-
public void unitSoundEffectEvent(final CUnit caster, final War3ID alias) {
668-
this.simulationRenderController.spawnAbilitySoundEffect(caster, alias);
673+
public SimulationRenderComponent unitSoundEffectEvent(final CUnit caster, final War3ID alias) {
674+
return this.simulationRenderController.spawnAbilitySoundEffect(caster, alias);
669675
}
670676

671-
public void unitLoopSoundEffectEvent(final CUnit caster, final War3ID alias) {
672-
this.simulationRenderController.loopAbilitySoundEffect(caster, alias);
677+
public SimulationRenderComponent unitLoopSoundEffectEvent(final CUnit caster, final War3ID alias) {
678+
return this.simulationRenderController.loopAbilitySoundEffect(caster, alias);
673679
}
674680

675681
public void unitStopSoundEffectEvent(final CUnit caster, final War3ID alias) {

core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorMove;
5454
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorPatrol;
5555
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorStop;
56+
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorStun;
5657
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.build.AbilityDisableWhileUpgradingVisitor;
5758
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.harvest.CBehaviorReturnResources;
5859
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType;
@@ -432,7 +433,7 @@ public void removeAllStateModBuffs(StateModBuffType type) {
432433
}
433434
}
434435

435-
public void computeUnitState(StateModBuffType type) {
436+
public void computeUnitState(CSimulation game, StateModBuffType type) {
436437
switch (type) {
437438
case DISABLE_ATTACK:
438439
case ETHEREAL:
@@ -444,8 +445,6 @@ public void computeUnitState(StateModBuffType type) {
444445
isDisableAttack = true;
445446
}
446447
}
447-
}
448-
for (StateModBuff buff : stateModBuffs) {
449448
if (buff.getBuffType() == StateModBuffType.ETHEREAL) {
450449
if (buff.getValue() != 0) {
451450
isEthereal = true;
@@ -512,21 +511,40 @@ public void computeUnitState(StateModBuffType type) {
512511
this.resistant = isResistant;
513512
break;
514513
case SLEEPING:
514+
case STUN:
515515
boolean isSleeping = false;
516+
boolean isStun = false;
516517
for (StateModBuff buff : stateModBuffs) {
517518
if (buff.getBuffType() == StateModBuffType.SLEEPING) {
518519
if (buff.getValue() != 0) {
519520
isSleeping = true;
520521
}
521522
}
523+
if (buff.getBuffType() == StateModBuffType.STUN) {
524+
if (buff.getValue() != 0) {
525+
isStun = true;
526+
}
527+
}
528+
}
529+
if (isSleeping || isStun) {
530+
if (this.currentBehavior != null) {
531+
this.currentBehavior.end(game, true);
532+
}
533+
this.currentBehavior = new CBehaviorStun(this);
534+
this.currentBehavior.begin(game);
535+
this.setAcceptingOrders(false);
536+
this.stateNotifier.ordersChanged();
537+
} else {
538+
this.setAcceptingOrders(true);
539+
this.currentBehavior = this.pollNextOrderBehavior(game);
540+
this.stateNotifier.ordersChanged();
522541
}
542+
523543
if (isSleeping) {
524-
this.setPaused(true);
525544
if (!this.damageTakenListeners.contains(CUnitDefaultSleepListener.INSTANCE)) {
526545
this.addDamageTakenListener(CUnitDefaultSleepListener.INSTANCE);
527546
}
528547
} else {
529-
this.setPaused(false);
530548
if (this.damageTakenListeners.contains(CUnitDefaultSleepListener.INSTANCE)) {
531549
this.removeDamageTakenListener(CUnitDefaultSleepListener.INSTANCE);
532550
}

core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilitybuilder/ability/AbilityBuilderAbility.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
import com.etheller.warsmash.util.War3ID;
77
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
88
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
9+
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
910
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic.SingleOrderAbility;
11+
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget;
1012
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilitybuilder.parser.AbilityBuilderConfiguration;
1113
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilitybuilder.types.impl.CAbilityTypeAbilityBuilderLevelData;
14+
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver;
1215

1316
public interface AbilityBuilderAbility extends SingleOrderAbility {
1417
public List<CAbilityTypeAbilityBuilderLevelData> getLevelData();
@@ -18,8 +21,19 @@ public interface AbilityBuilderAbility extends SingleOrderAbility {
1821
public Map<String, Object> getLocalStore();
1922

2023
public int getLevel();
24+
25+
public int getUIManaCost();
2126

2227
public War3ID getAlias();
23-
28+
2429
public void startCooldown(CSimulation game, CUnit unit);
30+
31+
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target,
32+
final AbilityTargetCheckReceiver<CWidget> receiver);
33+
34+
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId,
35+
final AbilityPointTarget target, final AbilityTargetCheckReceiver<AbilityPointTarget> receiver);
36+
37+
public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId,
38+
final AbilityTargetCheckReceiver<Void> receiver);
2539
}

core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilitybuilder/ability/CAbilityAbilityBuilderActiveFlexTargetSimple.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ protected void innerCheckCanTargetSpell(CSimulation game, CUnit unit, int orderI
226226
} else {
227227
receiver.targetOk(targetUnit);
228228
}
229-
this.localStore.remove(ABLocalStoreKeys.ABILITYTARGETEDUNIT + castId);
230229
}
231230
}
232231

0 commit comments

Comments
 (0)