Simple battle mechanics for display dialog-based game

I’m currently working on an AppleScript text adventure game using “display dialog”, and recently came up with a simple battle system. The script basically defines properties for the player’s HP and the monster’s HP (in this case a goblin). It then calculates damage output to both you and the monster using “random number”.

I’m thinking of having different weapons with different damage outputs (e.g. a dagger would deal damage between 1–10, a longsword would deal damage between 10–20, etc.). Further customization is very possible.

Any input on the code is most welcome. I hope someone finds this useful!

property yourHP : 20
property goblinHP : 20
property yourHPHurt : 0
property enemyHPHurt : 0

on loseGoblin()
	display dialog "The goblin hits you with a mortal wound. You die..." & return & return & "Your HP: " & yourHPHurt & "/" & yourHP & return & "Enemy HP :" & enemyHPHurt & "/" & goblinHP buttons {"Game over..."} default button 1
	set yourHPHurt to yourHP
	set enemyHPHurt to goblinHP
end loseGoblin

on winGoblin()
	display dialog "You hit the goblin with a mortal wound. It dies..." & return & return & "Your HP: " & yourHPHurt & "/" & yourHP & return & "Enemy HP :" & enemyHPHurt & "/" & goblinHP buttons {"Victory!"} default button 1
	set yourHPHurt to yourHP
	set enemyHPHurt to goblinHP
end winGoblin

on tieGoblin()
	display dialog "You hit the goblin with a mortal wound, but before it succumbs, it manages to counterattack and kill you. You both die..." & return & return & "Your HP: " & yourHPHurt & "/" & yourHP & return & "Enemy HP :" & enemyHPHurt & "/" & goblinHP buttons {"Game over..."} default button 1
	set yourHPHurt to yourHP
	set enemyHPHurt to goblinHP
end tieGoblin

on combatGoblin()
	set damageDoneToEnemy to (random number from 1 to 10)
	set damageDoneToYou to (random number from 1 to 10)
	if yourHPHurt = yourHP and enemyHPHurt = goblinHP then -- beginning of battle
		set enemyHPHurt to goblinHP - damageDoneToEnemy
		set yourHPHurt to yourHP - damageDoneToYou
		display dialog "You hit the goblin for " & damageDoneToEnemy & " damage! The goblin hits you for " & damageDoneToYou & " damage!" & return & return & "Your HP: " & yourHPHurt & "/" & yourHP & return & "Enemy HP :" & enemyHPHurt & "/" & goblinHP buttons {"Flee", "Attack!"} default button 2
		if button returned of the result is "Attack!" then
			combatGoblin()
		else -- reset properties
			set yourHPHurt to yourHP
			set enemyHPHurt to goblinHP
		end if
	else if yourHPHurt ≠ yourHP and enemyHPHurt ≠ goblinHP then -- damage has been done
		set enemyHPHurt to enemyHPHurt - damageDoneToEnemy
		set yourHPHurt to yourHPHurt - damageDoneToYou
		if yourHPHurt ≤ 0 and enemyHPHurt > 0 then
			loseGoblin()
		else if yourHPHurt > 0 and enemyHPHurt ≤ 0 then
			winGoblin()
		else if yourHPHurt ≤ 0 and enemyHPHurt ≤ 0 then
			tieGoblin()
		else if yourHPHurt > 0 and enemyHPHurt > 0 then
			display dialog "You hit the goblin for " & damageDoneToEnemy & " damage! The goblin hits you for " & damageDoneToYou & " damage!" & return & return & "Your HP: " & yourHPHurt & "/" & yourHP & return & "Enemy HP :" & enemyHPHurt & "/" & goblinHP buttons {"Flee", "Attack!"} default button 2
			if button returned of the result is "Attack!" then
				combatGoblin()
			else -- reset properties
				set yourHPHurt to yourHP
				set enemyHPHurt to goblinHP
			end if
		end if
	end if
end combatGoblin

on encounterGoblin()
	display dialog "COMBAT!" & return & return & "You encounter a goblin. It wields a viscious-looking scimitar." & return & return & "Your HP: " & yourHP & "/" & yourHP & return & "Enemy HP: " & goblinHP & "/" & goblinHP buttons {"Flee", "Attack!"} default button 2 with icon 0
	if button returned of the result is "Attack!" then
		set yourHPHurt to yourHP
		set enemyHPHurt to goblinHP
		combatGoblin()
	end if
end encounterGoblin

encounterGoblin()

Hi kitkut. Welcome to MacScripter.

That looks like fun. :slight_smile:

Just a couple of comments about the logic in combatGoblin(), although it works perfectly well as it is.

Since damage is done to both parties in every skirmish, yourHPHurt ≠ yourHP and enemyHPHurt ≠ goblinHP is always true when yourHPHurt = yourHP and enemyHPHurt = goblinHP isn’t. So there’s no need to test for it in the else line:

if yourHPHurt = yourHP and enemyHPHurt = goblinHP then -- beginning of battle
    --
else -- damage has been done
    --
end if

In the first (if) section, since yourHPHurt = yourHP and enemyHPHurt = goblinHP, the first two lines could instead be:

set enemyHPHurt to enemyHPHurt - damageDoneToEnemy
set yourHPHurt to yourHPHurt - damageDoneToYou

— the same as in the else section. This would make the action for the first skirmish exactly the same as the action for all the following skirmishes where both combatants are still alive. So, with a test at the beginning of the handler to compare the HPHurt and HP values before the former get changed, the logic could be compressed to this:

on combatGoblin()
	set beginningOfBattle to (yourHPHurt = yourHP and enemyHPHurt = goblinHP) -- true first time round, false on recursions.
	
	set damageDoneToEnemy to (random number from 1 to 10)
	set damageDoneToYou to (random number from 1 to 10)
	set enemyHPHurt to enemyHPHurt - damageDoneToEnemy
	set yourHPHurt to yourHPHurt - damageDoneToYou
	
	if (beginningOfBattle) or (yourHPHurt > 0 and enemyHPHurt > 0) then -- beginning of battle or non-mortal damage has been done.
		display dialog "You hit the goblin for " & damageDoneToEnemy & " damage! The goblin hits you for " & damageDoneToYou & " damage!" & return & return & "Your HP: " & yourHPHurt & "/" & yourHP & return & "Enemy HP :" & enemyHPHurt & "/" & goblinHP buttons {"Flee", "Attack!"} default button 2
		if button returned of the result is "Attack!" then
			combatGoblin()
		else -- reset properties
			set yourHPHurt to yourHP
			set enemyHPHurt to goblinHP
		end if
	else if yourHPHurt ≤ 0 and enemyHPHurt > 0 then
		loseGoblin()
	else if yourHPHurt > 0 and enemyHPHurt ≤ 0 then
		winGoblin()
	else
		tieGoblin()
	end if
end combatGoblin

In a stand-alone situation, even the beginningOfBattle stuff isn’t necessary, since yourHPHurt and enemyHPHurt always come out of the first skirmish greater than zero anyway.

Similarly, the loseGoblin(), winGoblin(), and tieGoblin() handlers are identical apart from some of the text. You may find it more convenient to have just one handler and pass the relevant differences to it. eg.:

on showBattleResult(outcome, buttonText)
	display dialog outcome & return & return & "Your HP: " & yourHPHurt & "/" & yourHP & return & "Enemy HP :" & enemyHPHurt & "/" & goblinHP buttons {buttonText} default button 1
	set yourHPHurt to yourHP
	set enemyHPHurt to goblinHP
end showBattleResult

showBattleResult("The goblin hits you with a mortal wound. You die...", "Game over...")
showBattleResult("You hit the goblin with a mortal wound. It dies...", "Victory!")
showBattleResult("You hit the goblin with a mortal wound, but before it succumbs, it manages to counterattack and kill you. You both die...", "Game over...")

Hi Nigel – thank you for your input!! Aha, I just knew you can specify handler parameters – this just simplified my game’s code by leaps and bounds. I previously used different handlers for different display dialog descriptions… :expressionless: (As you can probably tell I’m new to AppleScript (and coding in general) and am learning it piecemeal through trial and error.)

By the way, has anyone ever released a full-fledged text adventure game made with AppleScript before? It would be fun to play one and crack it open.

Not for public consumption, as far as I know. Purely AppleScript programs aren’t very interesting to look at. But it can be fun and informative to try putting something together for your own amusement. We’ve had Tic Tac Toe and Fox, Goose, Beans here in the past.