Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions assets/config/assets.onj
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,12 @@ shaders: [
constantArgs: {
}
},
{
name: "warp_shader",
file: "shaders/warp.glsl",
constantArgs: {
}
},
{
name: "slider_shader",
file: "shaders/slider.glsl",
Expand Down
19 changes: 19 additions & 0 deletions assets/config/cards.onj
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,25 @@ cards: [
tags: ["pool1", "rarity2"],
price: 50,
},
{
name: "wardenOfTimeBullet",
title: "Warden Of Time Bullet",
flavourText: "",
description: "3 revolver rotations after that this gets shot: destroy the bullet in slot 5 " + slot.5 + " and this bullet gets put there instead. If there is no bullet, nothing gets destroyed and this Bullet just appears.",
baseDamage: 4,
coverValue: 0,
traitEffects: [ "wardenOfTime" ],
rotation: $Right { amount: 1 },
highlightType: "standard",
effects: [
],
forceLoadCards: [],
dark: true,
cost: 2,
type: $Bullet { },
tags: ["pool2", "rarity1"],
price: 50,
},

],

Expand Down
2 changes: 1 addition & 1 deletion assets/screens/game_screen.onj
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ assets: {
"holster_sound", "money_sound", "revolver_drum_sound", "parry_sound", "swoosh_sound", "button_2_sound",
"card_destroy_sound", "holster_remove_sound", "metal_on_metal_sound",

"screen_shake_popout_shader",
"screen_shake_popout_shader", "warp_shader",

"map_theme", "encounter_theme",

Expand Down
2 changes: 1 addition & 1 deletion assets/screens/title_screen.onj
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ assets: {
"common_button_default", "common_button_hover",

"main_theme",
"warp_shader",
"swoosh_sound", "button_2_sound",
]
},
Expand Down Expand Up @@ -97,7 +98,6 @@ var confirmationPopup = $Box {
],
touchable: "enabled"
} children [

$Box {
styles: [
{
Expand Down
123 changes: 123 additions & 0 deletions assets/shaders/warp.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
~~~section vertex

%include shaders/includes/default_vertex.glsl

~~~section fragment

#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif

varying LOWP vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;

%uniform u_time
uniform float u_progress;// between 0-1
uniform vec2 u_center;// between 0-1 for x and y
uniform float u_depth;// recommended beween 3 and 30, how strong it zooms
uniform float u_rotation;// rotation in radians

// "streches" a value, fe. oldDistance 0.1=>0.23; 0.9=>0.96 (examplevalues with depth=2)
float getNewDist(float oldDist, float depthMulti){
float depth=15.0*depthMulti;
if (u_depth>1.0){
depth=u_depth*depthMulti;
}
float a = pow(2.0, depth);
a=a/(a-1.0);
return (a-a/pow(oldDist+1.0, depth));//from 0-1 returns 0-1 but "warped"
}

float hypo(vec2 oldDist){
return sqrt(oldDist.x*oldDist.x+oldDist.y*oldDist.y);
// return abs(oldDist.x)+abs(oldDist.y);
}

//the position where the extended line between the center and the pixel hits the (0|0),(0|1),(1|1),(1|0) square
vec2 getBorderIntersection(float k, vec2 pointOnLine, bool rightOfCenter){

float d = pointOnLine.y - pointOnLine.x * k;
vec2 borderIntersection;
if (rightOfCenter) borderIntersection= vec2(1.0, k+d);
else borderIntersection=vec2(0.0, d);

if (borderIntersection.y>1.0) borderIntersection=vec2((1.0-d) / k, 1.0);
else if (borderIntersection.y<0.0) borderIntersection=vec2(-d/k, 0.0);
return borderIntersection;
}

// rotates the slope, and the isRightOfCenter says if the direction is (oldSlope,1) if true or (-oldslope,-1) if false
vec2 rotate(float oldSlope, float rotation, bool isRightOfCenter){
float PI=radians(180.0);
float oldRotation=atan(oldSlope, 1.0);
if(!isRightOfCenter) oldRotation+=PI;
float newRotation=oldRotation+rotation;

if(cos(newRotation)>0.0) return vec2(tan(newRotation),1.0);
else return vec2(tan(newRotation),0.0);
}

// This programm takes the following steps:
// 1. calculate the line between the current position and the center
// 2. get the point on the line where it hits the min/max box (Rectangle with coordiantes: (0|0),(0|1),(1|1),(1|0))
// 3. calculate the distance from in percent between the center and that point for my position
// 4. strech this position, so that the closer you are to the center, the stronger it streches and goes away from it
// 5. then it rotates the line from 1
// 6. repeat step 2 with the new line
// 7. takes the percentages from point 3 and puts them on the line from point 6

void main() {

// float progress = (sin(abs(float(u_time*0.85)))+1.0)/2.0;
float progress = u_progress;

vec2 center = u_center;
if(u_center.x==0.0 && u_center.y==0.0) center=vec2(0.5, 0.8);

// float PI = radians(180.0);
// float rotation = getNewDist(progress, -0.1) * PI/2.0*16.0;
float rotation = getNewDist(progress, -0.1) * u_rotation;


vec2 tc = v_texCoords;
vec2 distToCenter= tc-center;

// 1.
float k = distToCenter.y/distToCenter.x; // slope of line from

bool pointRightOfCenter=tc.x>center.x;

// 2.
vec2 borderIntersection= getBorderIntersection(k, center, pointRightOfCenter);


// 3.
float maxDist=hypo(borderIntersection-center);


float maxDistToFurthestCorner=max(max(hypo(center),hypo(center-vec2(0.0,1.0))), max(hypo(center-vec2(1.0)),hypo(center-vec2(1.0,0.0))));
float outSideMultiplier=maxDist/maxDistToFurthestCorner; //this makes it from a rectangle to a ellipse
float oldPercent=hypo(distToCenter)/maxDist;

// 4.
float strechedPercent=getNewDist(oldPercent, outSideMultiplier);


//5.
vec2 newRot = rotate(k, rotation*(1.0-strechedPercent), pointRightOfCenter); //newRot[0]= new slope, newRot[1] == 1 if new point is right of center


//6
vec2 rotatedBorderToCenter = getBorderIntersection(newRot.x, center, newRot.y==1.0) - center;


//7
vec2 rotatedPos = center+rotatedBorderToCenter*oldPercent;
vec4 result = texture2D(u_texture, rotatedPos + (strechedPercent-oldPercent) * rotatedBorderToCenter * progress);

gl_FragColor = result;
}
42 changes: 40 additions & 2 deletions core/src/com/fourinachamber/fortyfive/game/GameController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ class GameController(onj: OnjNamedObject) : ScreenController() {

private var selectedCard: Card? = null

private val _limbo: MutableList<Card> = mutableListOf()
val limbo: List<Card>
get() = _limbo

/**
* counts up every turn; starts at 0, but gets immediately incremented to one
*/
Expand Down Expand Up @@ -368,6 +372,13 @@ class GameController(onj: OnjNamedObject) : ScreenController() {
updateStatusEffects()
updateGameAnimations()
updateTutorialText()
val limboTimeline = _limbo.mapNotNull { it.updateInLimbo(this) }
if (limboTimeline.isEmpty()) return
appendMainTimeline(limboTimeline.collectTimeline())
}

fun removeFromLimbo(card: Card) {
_limbo.remove(card)
}

private fun updateTutorialText() {
Expand Down Expand Up @@ -553,6 +564,30 @@ class GameController(onj: OnjNamedObject) : ScreenController() {
)
})

fun placeBulletInRevolverDirect(card: Card, slot: Int): Timeline = Timeline.timeline {
val triggerInfo = TriggerInformation(sourceCard = card, controller = this@GameController)
action {
revolver.setCard(slot, card)
card.onEnter(this@GameController)
}
includeLater(
{
encounterModifiers
.mapNotNull { it.executeAfterBulletWasPlacedInRevolver(card, this@GameController) }
.collectTimeline()
},
{ true }
)
includeLater(
{ checkEffectsSingleCard(Trigger.ON_ENTER, card, triggerInfo) },
{ true }
)
includeLater(
{ checkEffectsActiveCards(Trigger.ON_ANY_CARD_ENTER, triggerInfo) },
{ true }
)
}

fun putCardFromRevolverBackInHand(card: Card) {
FortyFiveLogger.debug(logTag, "returning card $card from the revolver to the hand")
revolver.removeCard(card)
Expand Down Expand Up @@ -707,13 +742,15 @@ class GameController(onj: OnjNamedObject) : ScreenController() {
curScreen.leaveState(showEnemyAttackPopupScreenState)
gameRenderPipeline.stopParryEffect()
if (parryCard.shouldRemoveAfterShot(this@GameController)) {
if (!parryCard.isUndead) {
if (!parryCard.isUndead && !parryCard.isWardenOfTime) {
SoundPlayer.situation("orb_anim_playing", curScreen)
gameRenderPipeline.addOrbAnimation(cardOrbAnim(parryCard.actor))
}
revolver.removeCard(parryCard)
if (parryCard.isUndead) {
cardHand.addCard(parryCard)
} else if (parryCard.isWardenOfTime) {
_limbo.add(parryCard)
} else {
putCardAtBottomOfStack(parryCard)
}
Expand Down Expand Up @@ -861,14 +898,15 @@ class GameController(onj: OnjNamedObject) : ScreenController() {
)
action {
if (cardToShoot.shouldRemoveAfterShot(this@GameController)) {
if (!cardToShoot.isUndead) {
if (!cardToShoot.isUndead && !cardToShoot.isWardenOfTime) {
putCardAtBottomOfStack(cardToShoot)
SoundPlayer.situation("orb_anim_playing", curScreen)
gameRenderPipeline.addOrbAnimation(cardOrbAnim(cardToShoot.actor))
}
revolver.removeCard(cardToShoot)
}
if (cardToShoot.isUndead) cardHand.addCard(cardToShoot)
if (cardToShoot.isWardenOfTime) _limbo.add(cardToShoot)
cardToShoot.afterShot(this@GameController)
}
}
Expand Down
63 changes: 62 additions & 1 deletion core/src/com/fourinachamber/fortyfive/game/card/Card.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ class Card(
private set
var isAlwaysAtTop: Boolean = false
private set
var isWardenOfTime: Boolean = false
private set

fun shouldRemoveAfterShot(controller: GameController): Boolean = !(
(isEverlasting && !controller.encounterModifiers.any { it.disableEverlasting() }) ||
Expand Down Expand Up @@ -255,6 +257,59 @@ class Card(
}
}

private var wardenOfTimeRotationCounter: Int = -1

fun updateInLimbo(controller: GameController): Timeline? {
if (!isWardenOfTime || wardenOfTimeRotationCounter == -1) return null
if (wardenOfTimeRotationCounter + 4 > controller.revolverRotationCounter) return null
wardenOfTimeRotationCounter = -1
val growAction = ScaleToAction()
growAction.duration = 0.3f
growAction.setScale(3f)
growAction.interpolation = Interpolation.pow5In
val shrinkAction = ScaleToAction()
shrinkAction.duration = 0.5f
shrinkAction.setScale(1f)
shrinkAction.interpolation = Interpolation.smoother

return Timeline.timeline {
action {
controller.removeFromLimbo(this@Card)
}
includeLater(
{ controller.destroyCardTimeline(controller.revolver.getCardInSlot(5)!!) },
{ controller.revolver.getCardInSlot(5) != null }
)
parallelActions(
Timeline.timeline {
delay(800)
parallelActions(
controller.placeBulletInRevolverDirect(this@Card, 5).asAction(),
controller.gameRenderPipeline.liftActor(1000, actor).asAction(),
Timeline.timeline {
action {
actor.setScale(0.3f)
actor.addAction(growAction)
}
delayUntil { growAction.isComplete }
action {
actor.removeAction(growAction)
actor.addAction(shrinkAction)
}
delayUntil { shrinkAction.isComplete }
action { actor.removeAction(shrinkAction) }
}.asAction(),
Timeline.timeline {
delay(250)
include(controller.gameRenderPipeline.getScreenShakePopoutTimeline())
}.asAction()
)
}.asAction(),
controller.gameRenderPipeline.getTimeWarpTimeline().asAction()
)
}
}

fun activeModifiers(controller: GameController): List<CardModifier> =
modifiers.filter { it.activeChecker(controller) }

Expand All @@ -268,6 +323,7 @@ class Card(
* called by gameScreenController when the card was shot
*/
fun afterShot(controller: GameController) {
wardenOfTimeRotationCounter = controller.revolverRotationCounter
if (shouldRemoveAfterShot(controller)) leaveGame()
if (protectingModifiers.isNotEmpty()) {
val effect = protectingModifiers.first()
Expand Down Expand Up @@ -621,6 +677,7 @@ class Card(
"rotten" -> card.isRotten = true
"alwaysAtBottom" -> card.isAlwaysAtBottom = true
"alwaysAtTop" -> card.isAlwaysAtTop = true
"wardenOfTime" -> card.isWardenOfTime = true

else -> throw RuntimeException("unknown trait effect $effect")
}
Expand Down Expand Up @@ -666,13 +723,16 @@ class CardActor(
override val screen: OnjScreen,
val enableHoverDetails: Boolean
) : Widget(), ZIndexActor, KeySelectableActor, DisplayDetailsOnHoverActor, HoverStateActor, HasOnjScreen, StyledActor,
OffSettable, AnimationActor {
OffSettable, AnimationActor, LiftableActor {

override val actor: Actor = this

override var actorTemplate: String = "card_hover_detail" // TODO: fix
override var detailActor: Actor? = null

override var inLift: Boolean = false
override var inLiftRender: Boolean = false

override var inAnimation: Boolean = false

override var mainHoverDetailActor: String? = "cardHoverDetailMain"
Expand Down Expand Up @@ -781,6 +841,7 @@ class CardActor(
}

override fun draw(batch: Batch?, parentAlpha: Float) {
if (!shouldRender) return
validate()
if (drawPixmapMessage?.isFinished ?: false) {
finishPixmapDrawing()
Expand Down
Loading